stats.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. """Statistics analyzer for HotShot."""
  2. import profile
  3. import pstats
  4. import hotshot.log
  5. from hotshot.log import ENTER, EXIT
  6. def load(filename):
  7. return StatsLoader(filename).load()
  8. class StatsLoader:
  9. def __init__(self, logfn):
  10. self._logfn = logfn
  11. self._code = {}
  12. self._stack = []
  13. self.pop_frame = self._stack.pop
  14. def load(self):
  15. # The timer selected by the profiler should never be used, so make
  16. # sure it doesn't work:
  17. p = Profile()
  18. p.get_time = _brokentimer
  19. log = hotshot.log.LogReader(self._logfn)
  20. taccum = 0
  21. for event in log:
  22. what, (filename, lineno, funcname), tdelta = event
  23. if tdelta > 0:
  24. taccum += tdelta
  25. # We multiply taccum to convert from the microseconds we
  26. # have to the seconds that the profile/pstats module work
  27. # with; this allows the numbers to have some basis in
  28. # reality (ignoring calibration issues for now).
  29. if what == ENTER:
  30. frame = self.new_frame(filename, lineno, funcname)
  31. p.trace_dispatch_call(frame, taccum * .000001)
  32. taccum = 0
  33. elif what == EXIT:
  34. frame = self.pop_frame()
  35. p.trace_dispatch_return(frame, taccum * .000001)
  36. taccum = 0
  37. # no further work for line events
  38. assert not self._stack
  39. return pstats.Stats(p)
  40. def new_frame(self, *args):
  41. # args must be filename, firstlineno, funcname
  42. # our code objects are cached since we don't need to create
  43. # new ones every time
  44. try:
  45. code = self._code[args]
  46. except KeyError:
  47. code = FakeCode(*args)
  48. self._code[args] = code
  49. # frame objects are create fresh, since the back pointer will
  50. # vary considerably
  51. if self._stack:
  52. back = self._stack[-1]
  53. else:
  54. back = None
  55. frame = FakeFrame(code, back)
  56. self._stack.append(frame)
  57. return frame
  58. class Profile(profile.Profile):
  59. def simulate_cmd_complete(self):
  60. pass
  61. class FakeCode:
  62. def __init__(self, filename, firstlineno, funcname):
  63. self.co_filename = filename
  64. self.co_firstlineno = firstlineno
  65. self.co_name = self.__name__ = funcname
  66. class FakeFrame:
  67. def __init__(self, code, back):
  68. self.f_back = back
  69. self.f_code = code
  70. def _brokentimer():
  71. raise RuntimeError, "this timer should not be called"