PageRenderTime 40ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/rlib/rvmprof/rvmprof.py

https://bitbucket.org/pypy/pypy/
Python | 220 lines | 194 code | 12 blank | 14 comment | 4 complexity | 30b466c391925d406af8c94c1a3d32f9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys, os
  2. from rpython.rlib.objectmodel import specialize, we_are_translated
  3. from rpython.rlib import jit, rposix
  4. from rpython.rlib.rvmprof import cintf
  5. from rpython.rtyper.annlowlevel import cast_instance_to_gcref
  6. from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
  7. from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
  8. from rpython.rtyper.lltypesystem.lloperation import llop
  9. from rpython.rlib.rweaklist import RWeakListMixin
  10. MAX_FUNC_NAME = 1023
  11. # ____________________________________________________________
  12. # keep in sync with vmprof_stack.h
  13. VMPROF_CODE_TAG = 1
  14. VMPROF_BLACKHOLE_TAG = 2
  15. VMPROF_JITTED_TAG = 3
  16. VMPROF_JITTING_TAG = 4
  17. VMPROF_GC_TAG = 5
  18. class VMProfError(Exception):
  19. def __init__(self, msg):
  20. self.msg = msg
  21. def __str__(self):
  22. return self.msg
  23. class FakeWeakCodeObjectList(object):
  24. def add_handle(self, handle):
  25. pass
  26. class VMProf(object):
  27. _immutable_fields_ = ['is_enabled?']
  28. use_weaklist = True # False for tests
  29. def __init__(self):
  30. "NOT_RPYTHON: use _get_vmprof()"
  31. self._code_classes = set()
  32. self._gather_all_code_objs = lambda: None
  33. self._cleanup_()
  34. self._code_unique_id = 4
  35. self.cintf = cintf.setup()
  36. def _cleanup_(self):
  37. self.is_enabled = False
  38. @jit.dont_look_inside
  39. @specialize.argtype(1)
  40. def register_code(self, code, full_name_func):
  41. """Register the code object. Call when a new code object is made.
  42. """
  43. if code._vmprof_unique_id == 0:
  44. # Add 4 to the next unique_id, so that all returned numbers are
  45. # multiples of 4. This is also a workaround for a bug (in some
  46. # revision) of vmprof-python, where it will look up the name
  47. # corresponding the 'uid + 1' instead of 'uid': if the next one
  48. # is at 'uid + 4', then the lookup will give the right answer
  49. # anyway.
  50. uid = self._code_unique_id + 4
  51. code._vmprof_unique_id = uid
  52. self._code_unique_id = uid
  53. if self.is_enabled:
  54. self._write_code_registration(uid, full_name_func(code))
  55. elif self.use_weaklist:
  56. code._vmprof_weak_list.add_handle(code)
  57. def register_code_object_class(self, CodeClass, full_name_func):
  58. """NOT_RPYTHON
  59. Register statically the class 'CodeClass' as containing user
  60. code objects.
  61. full_name_func() is a function called at runtime with an
  62. instance of CodeClass and it should return a string. This
  63. is the string stored in the vmprof file identifying the code
  64. object. It can be directly an unbound method of CodeClass.
  65. IMPORTANT: the name returned must be at most MAX_FUNC_NAME
  66. characters long, and with exactly 3 colons, i.e. of the form
  67. class:func_name:func_line:filename
  68. where 'class' is 'py' for PyPy.
  69. Instances of the CodeClass will have a new attribute called
  70. '_vmprof_unique_id', but that's managed internally.
  71. """
  72. if CodeClass in self._code_classes:
  73. return
  74. CodeClass._vmprof_unique_id = 0 # default value: "unknown"
  75. immut = CodeClass.__dict__.get('_immutable_fields_', [])
  76. CodeClass._immutable_fields_ = list(immut) + ['_vmprof_unique_id']
  77. self._code_classes.add(CodeClass)
  78. #
  79. class WeakCodeObjectList(RWeakListMixin):
  80. def __init__(self):
  81. self.initialize()
  82. if self.use_weaklist:
  83. CodeClass._vmprof_weak_list = WeakCodeObjectList()
  84. else:
  85. CodeClass._vmprof_weak_list = FakeWeakCodeObjectList()
  86. #
  87. def gather_all_code_objs():
  88. all_code_wrefs = CodeClass._vmprof_weak_list.get_all_handles()
  89. for wref in all_code_wrefs:
  90. code = wref()
  91. if code is not None:
  92. uid = code._vmprof_unique_id
  93. if uid != 0:
  94. self._write_code_registration(uid, full_name_func(code))
  95. prev()
  96. # make a chained list of the gather() functions for all
  97. # the types of code objects
  98. prev = self._gather_all_code_objs
  99. self._gather_all_code_objs = gather_all_code_objs
  100. @jit.dont_look_inside
  101. def enable(self, fileno, interval):
  102. """Enable vmprof. Writes go to the given 'fileno'.
  103. The sampling interval is given by 'interval' as a number of
  104. seconds, as a float which must be smaller than 1.0.
  105. Raises VMProfError if something goes wrong.
  106. """
  107. assert fileno >= 0
  108. if self.is_enabled:
  109. raise VMProfError("vmprof is already enabled")
  110. p_error = self.cintf.vmprof_init(fileno, interval, "pypy")
  111. if p_error:
  112. raise VMProfError(rffi.charp2str(p_error))
  113. self._gather_all_code_objs()
  114. res = self.cintf.vmprof_enable()
  115. if res < 0:
  116. raise VMProfError(os.strerror(rposix.get_saved_errno()))
  117. self.is_enabled = True
  118. @jit.dont_look_inside
  119. def disable(self):
  120. """Disable vmprof.
  121. Raises VMProfError if something goes wrong.
  122. """
  123. if not self.is_enabled:
  124. raise VMProfError("vmprof is not enabled")
  125. self.is_enabled = False
  126. res = self.cintf.vmprof_disable()
  127. if res < 0:
  128. raise VMProfError(os.strerror(rposix.get_saved_errno()))
  129. def _write_code_registration(self, uid, name):
  130. assert name.count(':') == 3 and len(name) <= MAX_FUNC_NAME, (
  131. "the name must be 'class:func_name:func_line:filename' "
  132. "and at most %d characters; got '%s'" % (MAX_FUNC_NAME, name))
  133. if self.cintf.vmprof_register_virtual_function(name, uid, 500000) < 0:
  134. raise VMProfError("vmprof buffers full! disk full or too slow")
  135. def vmprof_execute_code(name, get_code_fn, result_class=None,
  136. _hack_update_stack_untranslated=False):
  137. """Decorator to be used on the function that interprets a code object.
  138. 'name' must be a unique name.
  139. 'get_code_fn(*args)' is called to extract the code object from the
  140. arguments given to the decorated function.
  141. 'result_class' is ignored (backward compatibility).
  142. """
  143. if _hack_update_stack_untranslated:
  144. from rpython.rtyper.annlowlevel import llhelper
  145. enter_code = llhelper(lltype.Ptr(
  146. lltype.FuncType([lltype.Signed], cintf.PVMPROFSTACK)),
  147. cintf.enter_code)
  148. leave_code = llhelper(lltype.Ptr(
  149. lltype.FuncType([cintf.PVMPROFSTACK], lltype.Void)),
  150. cintf.leave_code)
  151. else:
  152. enter_code = cintf.enter_code
  153. leave_code = cintf.leave_code
  154. def decorate(func):
  155. try:
  156. _get_vmprof()
  157. except cintf.VMProfPlatformUnsupported:
  158. return func
  159. @jit.oopspec("rvmprof.jitted(unique_id)")
  160. def decorated_jitted_function(unique_id, *args):
  161. return func(*args)
  162. def decorated_function(*args):
  163. unique_id = get_code_fn(*args)._vmprof_unique_id
  164. unique_id = rffi.cast(lltype.Signed, unique_id)
  165. # ^^^ removes the "known non-negative" hint for annotation
  166. if not jit.we_are_jitted():
  167. x = enter_code(unique_id)
  168. try:
  169. return func(*args)
  170. finally:
  171. leave_code(x)
  172. else:
  173. return decorated_jitted_function(unique_id, *args)
  174. decorated_function.__name__ = func.__name__ + '_rvmprof'
  175. return decorated_function
  176. return decorate
  177. @specialize.memo()
  178. def _was_registered(CodeClass):
  179. return hasattr(CodeClass, '_vmprof_unique_id')
  180. _vmprof_instance = None
  181. @specialize.memo()
  182. def _get_vmprof():
  183. global _vmprof_instance
  184. if _vmprof_instance is None:
  185. _vmprof_instance = VMProf()
  186. return _vmprof_instance