10a8c90248264a8b26970b4473770bcc3df8515fJosh Gao"""Statistics analyzer for HotShot."""
20a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
30a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport profile
40a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport pstats
50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
60a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport hotshot.log
70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
80a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom hotshot.log import ENTER, EXIT
90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
110a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef load(filename):
120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    return StatsLoader(filename).load()
130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
150a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass StatsLoader:
160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, logfn):
170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._logfn = logfn
180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._code = {}
190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._stack = []
200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.pop_frame = self._stack.pop
210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def load(self):
230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # The timer selected by the profiler should never be used, so make
240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # sure it doesn't work:
250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        p = Profile()
260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        p.get_time = _brokentimer
270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        log = hotshot.log.LogReader(self._logfn)
280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        taccum = 0
290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        for event in log:
300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            what, (filename, lineno, funcname), tdelta = event
310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if tdelta > 0:
320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                taccum += tdelta
330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # We multiply taccum to convert from the microseconds we
350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # have to the seconds that the profile/pstats module work
360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # with; this allows the numbers to have some basis in
370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # reality (ignoring calibration issues for now).
380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            if what == ENTER:
400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                frame = self.new_frame(filename, lineno, funcname)
410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                p.trace_dispatch_call(frame, taccum * .000001)
420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                taccum = 0
430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            elif what == EXIT:
450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                frame = self.pop_frame()
460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                p.trace_dispatch_return(frame, taccum * .000001)
470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao                taccum = 0
480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            # no further work for line events
500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        assert not self._stack
520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return pstats.Stats(p)
530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def new_frame(self, *args):
550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # args must be filename, firstlineno, funcname
560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # our code objects are cached since we don't need to create
570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # new ones every time
580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        try:
590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            code = self._code[args]
600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        except KeyError:
610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            code = FakeCode(*args)
620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            self._code[args] = code
630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # frame objects are create fresh, since the back pointer will
640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        # vary considerably
650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        if self._stack:
660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            back = self._stack[-1]
670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        else:
680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao            back = None
690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        frame = FakeFrame(code, back)
700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self._stack.append(frame)
710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        return frame
720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
740a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass Profile(profile.Profile):
750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def simulate_cmd_complete(self):
760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        pass
770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
790a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass FakeCode:
800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, filename, firstlineno, funcname):
810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.co_filename = filename
820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.co_firstlineno = firstlineno
830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.co_name = self.__name__ = funcname
840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
860a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass FakeFrame:
870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    def __init__(self, code, back):
880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.f_back = back
890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao        self.f_code = code
900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao
920a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef _brokentimer():
930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao    raise RuntimeError, "this timer should not be called"
94