/pypy/module/gc/referents.py

https://github.com/alemacgo/pypy · Python · 182 lines · 137 code · 19 blank · 26 comment · 38 complexity · 35e32cb626ad54c36f156ab150ea77b9 MD5 · raw file

  1. from pypy.rlib import rgc
  2. from pypy.interpreter.baseobjspace import W_Root, Wrappable
  3. from pypy.interpreter.typedef import TypeDef
  4. from pypy.interpreter.gateway import unwrap_spec
  5. from pypy.interpreter.error import wrap_oserror, OperationError
  6. from pypy.rlib.objectmodel import we_are_translated
  7. class W_GcRef(Wrappable):
  8. def __init__(self, gcref):
  9. self.gcref = gcref
  10. W_GcRef.typedef = TypeDef("GcRef")
  11. def try_cast_gcref_to_w_root(gcref):
  12. w_obj = rgc.try_cast_gcref_to_instance(W_Root, gcref)
  13. # Ignore the instances of W_Root that are not really valid as Python
  14. # objects. There is e.g. WeakrefLifeline in module/_weakref that
  15. # inherits from W_Root for internal reasons. Such instances don't
  16. # have a typedef at all (or have a null typedef after translation).
  17. if not we_are_translated():
  18. if not hasattr(w_obj, 'typedef'):
  19. return None
  20. else:
  21. if w_obj is None or not w_obj.typedef:
  22. return None
  23. return w_obj
  24. def wrap(space, gcref):
  25. w_obj = try_cast_gcref_to_w_root(gcref)
  26. if w_obj is None:
  27. w_obj = space.wrap(W_GcRef(gcref))
  28. return w_obj
  29. def unwrap(space, w_obj):
  30. gcrefobj = space.interpclass_w(w_obj)
  31. if isinstance(gcrefobj, W_GcRef):
  32. gcref = gcrefobj.gcref
  33. else:
  34. gcref = rgc.cast_instance_to_gcref(w_obj)
  35. return gcref
  36. def missing_operation(space):
  37. return OperationError(space.w_NotImplementedError,
  38. space.wrap("operation not implemented by this GC"))
  39. def get_rpy_roots(space):
  40. lst = rgc.get_rpy_roots()
  41. if lst is None:
  42. raise missing_operation(space)
  43. return space.newlist([wrap(space, gcref) for gcref in lst if gcref])
  44. def get_rpy_referents(space, w_obj):
  45. """Return a list of all the referents, as reported by the GC.
  46. This is likely to contain a lot of GcRefs."""
  47. gcref = unwrap(space, w_obj)
  48. lst = rgc.get_rpy_referents(gcref)
  49. if lst is None:
  50. raise missing_operation(space)
  51. return space.newlist([wrap(space, gcref) for gcref in lst])
  52. def get_rpy_memory_usage(space, w_obj):
  53. """Return the memory usage of just the given object or GcRef.
  54. This does not include the internal structures of the object."""
  55. gcref = unwrap(space, w_obj)
  56. size = rgc.get_rpy_memory_usage(gcref)
  57. if size < 0:
  58. raise missing_operation(space)
  59. return space.wrap(size)
  60. def get_rpy_type_index(space, w_obj):
  61. """Return an integer identifying the RPython type of the given
  62. object or GcRef. The number starts at 1; it is an index in the
  63. file typeids.txt produced at translation."""
  64. gcref = unwrap(space, w_obj)
  65. index = rgc.get_rpy_type_index(gcref)
  66. if index < 0:
  67. raise missing_operation(space)
  68. return space.wrap(index)
  69. def _list_w_obj_referents(gcref, result_w):
  70. # Get all W_Root reachable directly from gcref, and add them to
  71. # the list 'result_w'. The logic here is not robust against gc
  72. # moves, and may return the same object several times.
  73. seen = {} # map {current_addr: obj}
  74. pending = [gcref]
  75. i = 0
  76. while i < len(pending):
  77. gcrefparent = pending[i]
  78. i += 1
  79. for gcref in rgc.get_rpy_referents(gcrefparent):
  80. key = rgc.cast_gcref_to_int(gcref)
  81. if gcref == seen.get(key, rgc.NULL_GCREF):
  82. continue # already in 'seen'
  83. seen[key] = gcref
  84. w_obj = try_cast_gcref_to_w_root(gcref)
  85. if w_obj is not None:
  86. result_w.append(w_obj)
  87. else:
  88. pending.append(gcref)
  89. def _get_objects_from_rpy(list_of_gcrefs):
  90. # given a list of gcrefs that may or may not be W_Roots, build a list
  91. # of W_Roots obtained by following references from there.
  92. result_w = [] # <- list of W_Roots
  93. for gcref in list_of_gcrefs:
  94. if gcref:
  95. w_obj = try_cast_gcref_to_w_root(gcref)
  96. if w_obj is not None:
  97. result_w.append(w_obj)
  98. else:
  99. _list_w_obj_referents(gcref, result_w)
  100. return result_w
  101. def get_objects(space):
  102. """Return a list of all app-level objects."""
  103. roots = rgc.get_rpy_roots()
  104. pending_w = _get_objects_from_rpy(roots)
  105. # continue by following every W_Root. Note that this will force a hash
  106. # on every W_Root, which is kind of bad, but not on every RPython object,
  107. # which is really good.
  108. result_w = {}
  109. while len(pending_w) > 0:
  110. previous_w = pending_w
  111. pending_w = []
  112. for w_obj in previous_w:
  113. if w_obj not in result_w:
  114. result_w[w_obj] = None
  115. gcref = rgc.cast_instance_to_gcref(w_obj)
  116. _list_w_obj_referents(gcref, pending_w)
  117. return space.newlist(result_w.keys())
  118. def get_referents(space, args_w):
  119. """Return a list of objects directly referred to by any of the arguments.
  120. Approximative: follow references recursively until it finds
  121. app-level objects. May return several times the same object, too."""
  122. result = []
  123. for w_obj in args_w:
  124. gcref = rgc.cast_instance_to_gcref(w_obj)
  125. _list_w_obj_referents(gcref, result)
  126. return space.newlist(result)
  127. def get_referrers(space, args_w):
  128. """Return the list of objects that directly refer to any of objs."""
  129. roots = rgc.get_rpy_roots()
  130. pending_w = _get_objects_from_rpy(roots)
  131. arguments_w = {}
  132. for w_obj in args_w:
  133. arguments_w[w_obj] = None
  134. # continue by following every W_Root. Same remark about hashes as
  135. # in get_objects().
  136. result_w = {}
  137. seen_w = {}
  138. while len(pending_w) > 0:
  139. previous_w = pending_w
  140. pending_w = []
  141. for w_obj in previous_w:
  142. if w_obj not in seen_w:
  143. seen_w[w_obj] = None
  144. gcref = rgc.cast_instance_to_gcref(w_obj)
  145. referents_w = []
  146. _list_w_obj_referents(gcref, referents_w)
  147. for w_subobj in referents_w:
  148. if w_subobj in arguments_w:
  149. result_w[w_obj] = None
  150. pending_w += referents_w
  151. return space.newlist(result_w.keys())
  152. @unwrap_spec(fd=int)
  153. def _dump_rpy_heap(space, fd):
  154. try:
  155. ok = rgc.dump_rpy_heap(fd)
  156. except OSError, e:
  157. raise wrap_oserror(space, e)
  158. if not ok:
  159. raise missing_operation(space)
  160. def get_typeids_z(space):
  161. a = rgc.get_typeids_z()
  162. s = ''.join([a[i] for i in range(len(a))])
  163. return space.wrap(s)