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