diff options
author | Fred Drake <fdrake@acm.org> | 2001-10-12 20:56:29 (GMT) |
---|---|---|
committer | Fred Drake <fdrake@acm.org> | 2001-10-12 20:56:29 (GMT) |
commit | f019324b5c7a77f4983c06e5abb03b7751ea7305 (patch) | |
tree | 4f8febf4704eb20f4fb38a65f245b80459ceea24 | |
parent | de3cdcadce9ea43fb0c1e3619f07e6fb63d41981 (diff) | |
download | cpython-f019324b5c7a77f4983c06e5abb03b7751ea7305.zip cpython-f019324b5c7a77f4983c06e5abb03b7751ea7305.tar.gz cpython-f019324b5c7a77f4983c06e5abb03b7751ea7305.tar.bz2 |
Preliminary user-level interface to HotShot. We still need the analysis
tool; look for that on Monday.
-rw-r--r-- | Lib/hotshot/__init__.py | 38 | ||||
-rw-r--r-- | Lib/hotshot/log.py | 135 |
2 files changed, 173 insertions, 0 deletions
diff --git a/Lib/hotshot/__init__.py b/Lib/hotshot/__init__.py new file mode 100644 index 0000000..b0e58f6 --- /dev/null +++ b/Lib/hotshot/__init__.py @@ -0,0 +1,38 @@ +"""High-perfomance logging profiler, mostly written in C.""" + +import _hotshot + +from _hotshot import ProfilerError + + +class Profile: + def __init__(self, logfn, lineevents=0, linetimings=1): + self.lineevents = lineevents and 1 or 0 + self.linetimings = (linetimings and lineevents) and 1 or 0 + self._prof = p = _hotshot.profiler( + logfn, self.lineevents, self.linetimings) + + def close(self): + self._prof.close() + + def start(self): + self._prof.start() + + def stop(self): + self._prof.stop() + + # These methods offer the same interface as the profile.Profile class, + # but delegate most of the work to the C implementation underneath. + + def run(self, cmd): + import __main__ + dict = __main__.__dict__ + return self.runctx(cmd, dict, dict) + + def runctx(self, cmd, globals, locals): + code = compile(cmd, "<string>", "exec") + self._prof.runcode(code, globals, locals) + return self + + def runcall(self, func, *args, **kw): + self._prof.runcall(func, args, kw) diff --git a/Lib/hotshot/log.py b/Lib/hotshot/log.py new file mode 100644 index 0000000..cd85353 --- /dev/null +++ b/Lib/hotshot/log.py @@ -0,0 +1,135 @@ +import _hotshot +import os.path +import parser +import symbol +import sys + +from _hotshot import \ + WHAT_ENTER, \ + WHAT_EXIT, \ + WHAT_LINENO, \ + WHAT_DEFINE_FILE, \ + WHAT_ADD_INFO + + +__all__ = ["LogReader", "ENTER", "EXIT", "LINE"] + + +ENTER = WHAT_ENTER +EXIT = WHAT_EXIT +LINE = WHAT_LINENO + + +try: + StopIteration +except NameError: + StopIteration = IndexError + + +class LogReader: + def __init__(self, logfn): + # fileno -> filename + self._filemap = {} + # (fileno, lineno) -> filename, funcname + self._funcmap = {} + + self._info = {} + self._nextitem = _hotshot.logreader(logfn).next + self._stack = [] + + # 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): + try: + what, tdelta, fileno, lineno = self._nextitem() + except TypeError: + # logreader().next() returns None at the end + raise StopIteration() + if what == WHAT_DEFINE_FILE: + self._filemap[fileno] = tdelta + return self.next() + if what == WHAT_ADD_INFO: + key = tdelta.lower() + try: + L = self._info[key] + except KeyError: + L = [] + self._info[key] = L + L.append(lineno) + if key == "current-directory": + self.cwd = lineno + return self.next() + if what == WHAT_ENTER: + t = self._decode_location(fileno, lineno) + filename, funcname = t + self._stack.append((filename, funcname, lineno)) + elif what == WHAT_EXIT: + filename, funcname, lineno = self._stack.pop() + else: + filename, funcname, firstlineno = self._stack[-1] + return what, (filename, lineno, funcname), tdelta + + if sys.version < "2.2": + # Don't add this for newer Python versions; we only want iteration + # support, not general sequence support. + __getitem__ = next + else: + def __iter__(self): + return self + + # + # helpers + # + + def _decode_location(self, fileno, lineno): + try: + return self._funcmap[(fileno, lineno)] + except KeyError: + 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:])) |