1"""Statistics analyzer for HotShot.""" 2 3import profile 4import pstats 5 6import hotshot.log 7 8from hotshot.log import ENTER, EXIT 9 10 11def load(filename): 12 return StatsLoader(filename).load() 13 14 15class 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 74class Profile(profile.Profile): 75 def simulate_cmd_complete(self): 76 pass 77 78 79class 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 86class FakeFrame: 87 def __init__(self, code, back): 88 self.f_back = back 89 self.f_code = code 90 91 92def _brokentimer(): 93 raise RuntimeError, "this timer should not be called" 94