diff options
Diffstat (limited to 'lib/python2.7/hotshot/log.py')
-rw-r--r-- | lib/python2.7/hotshot/log.py | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/lib/python2.7/hotshot/log.py b/lib/python2.7/hotshot/log.py new file mode 100644 index 0000000..17e8b50 --- /dev/null +++ b/lib/python2.7/hotshot/log.py @@ -0,0 +1,194 @@ +import _hotshot +import os.path +import parser +import symbol + +from _hotshot import \ + WHAT_ENTER, \ + WHAT_EXIT, \ + WHAT_LINENO, \ + WHAT_DEFINE_FILE, \ + WHAT_DEFINE_FUNC, \ + WHAT_ADD_INFO + + +__all__ = ["LogReader", "ENTER", "EXIT", "LINE"] + + +ENTER = WHAT_ENTER +EXIT = WHAT_EXIT +LINE = WHAT_LINENO + + +class LogReader: + def __init__(self, logfn): + # fileno -> filename + self._filemap = {} + # (fileno, lineno) -> filename, funcname + self._funcmap = {} + + self._reader = _hotshot.logreader(logfn) + self._nextitem = self._reader.next + self._info = self._reader.info + if 'current-directory' in self._info: + self.cwd = self._info['current-directory'] + else: + self.cwd = None + + # This mirrors the call stack of the profiled code as the log + # is read back in. It contains tuples of the form: + # + # (file name, line number of function def, function name) + # + self._stack = [] + self._append = self._stack.append + self._pop = self._stack.pop + + def close(self): + self._reader.close() + + def fileno(self): + """Return the file descriptor of the log reader's log file.""" + return self._reader.fileno() + + def addinfo(self, key, value): + """This method is called for each additional ADD_INFO record. + + This can be overridden by applications that want to receive + these events. The default implementation does not need to be + called by alternate implementations. + + The initial set of ADD_INFO records do not pass through this + mechanism; this is only needed to receive notification when + new values are added. Subclasses can inspect self._info after + calling LogReader.__init__(). + """ + pass + + def get_filename(self, fileno): + try: + return self._filemap[fileno] + except KeyError: + raise ValueError, "unknown fileno" + + def get_filenames(self): + return self._filemap.values() + + def get_fileno(self, filename): + filename = os.path.normcase(os.path.normpath(filename)) + for fileno, name in self._filemap.items(): + if name == filename: + return fileno + raise ValueError, "unknown filename" + + def get_funcname(self, fileno, lineno): + try: + return self._funcmap[(fileno, lineno)] + except KeyError: + raise ValueError, "unknown function location" + + # Iteration support: + # This adds an optional (& ignored) parameter to next() so that the + # same bound method can be used as the __getitem__() method -- this + # avoids using an additional method call which kills the performance. + + def next(self, index=0): + while 1: + # This call may raise StopIteration: + what, tdelta, fileno, lineno = self._nextitem() + + # handle the most common cases first + + if what == WHAT_ENTER: + filename, funcname = self._decode_location(fileno, lineno) + t = (filename, lineno, funcname) + self._append(t) + return what, t, tdelta + + if what == WHAT_EXIT: + try: + return what, self._pop(), tdelta + except IndexError: + raise StopIteration + + if what == WHAT_LINENO: + filename, firstlineno, funcname = self._stack[-1] + return what, (filename, lineno, funcname), tdelta + + if what == WHAT_DEFINE_FILE: + filename = os.path.normcase(os.path.normpath(tdelta)) + self._filemap[fileno] = filename + elif what == WHAT_DEFINE_FUNC: + filename = self._filemap[fileno] + self._funcmap[(fileno, lineno)] = (filename, tdelta) + elif what == WHAT_ADD_INFO: + # value already loaded into self.info; call the + # overridable addinfo() handler so higher-level code + # can pick up the new value + if tdelta == 'current-directory': + self.cwd = lineno + self.addinfo(tdelta, lineno) + else: + raise ValueError, "unknown event type" + + def __iter__(self): + return self + + # + # helpers + # + + def _decode_location(self, fileno, lineno): + try: + return self._funcmap[(fileno, lineno)] + except KeyError: + # + # This should only be needed when the log file does not + # contain all the DEFINE_FUNC records needed to allow the + # function name to be retrieved from the log file. + # + if self._loadfile(fileno): + filename = funcname = None + try: + filename, funcname = self._funcmap[(fileno, lineno)] + except KeyError: + filename = self._filemap.get(fileno) + funcname = None + self._funcmap[(fileno, lineno)] = (filename, funcname) + return filename, funcname + + def _loadfile(self, fileno): + try: + filename = self._filemap[fileno] + except KeyError: + print "Could not identify fileId", fileno + return 1 + if filename is None: + return 1 + absname = os.path.normcase(os.path.join(self.cwd, filename)) + + try: + fp = open(absname) + except IOError: + return + st = parser.suite(fp.read()) + fp.close() + + # Scan the tree looking for def and lambda nodes, filling in + # self._funcmap with all the available information. + funcdef = symbol.funcdef + lambdef = symbol.lambdef + + stack = [st.totuple(1)] + + while stack: + tree = stack.pop() + try: + sym = tree[0] + except (IndexError, TypeError): + continue + if sym == funcdef: + self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1] + elif sym == lambdef: + self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>" + stack.extend(list(tree[1:])) |