blob: 39c45343987fa231214265f78851490fed3f589d [file] [log] [blame]
Fred Drakef0193242001-10-12 20:56:29 +00001import _hotshot
2import os.path
3import parser
4import symbol
5import sys
6
7from _hotshot import \
8 WHAT_ENTER, \
9 WHAT_EXIT, \
10 WHAT_LINENO, \
11 WHAT_DEFINE_FILE, \
12 WHAT_ADD_INFO
13
14
15__all__ = ["LogReader", "ENTER", "EXIT", "LINE"]
16
17
18ENTER = WHAT_ENTER
19EXIT = WHAT_EXIT
20LINE = WHAT_LINENO
21
22
23try:
24 StopIteration
25except NameError:
26 StopIteration = IndexError
27
28
29class LogReader:
30 def __init__(self, logfn):
31 # fileno -> filename
32 self._filemap = {}
33 # (fileno, lineno) -> filename, funcname
34 self._funcmap = {}
35
36 self._info = {}
Fred Draked62f1512001-10-13 02:55:40 +000037 self._reader = _hotshot.logreader(logfn)
38 self._nextitem = self._reader.next
Fred Drakef0193242001-10-12 20:56:29 +000039 self._stack = []
40
41 # Iteration support:
42 # This adds an optional (& ignored) parameter to next() so that the
43 # same bound method can be used as the __getitem__() method -- this
44 # avoids using an additional method call which kills the performance.
45
46 def next(self, index=0):
47 try:
48 what, tdelta, fileno, lineno = self._nextitem()
49 except TypeError:
50 # logreader().next() returns None at the end
Fred Draked62f1512001-10-13 02:55:40 +000051 self._reader.close()
Fred Drakef0193242001-10-12 20:56:29 +000052 raise StopIteration()
53 if what == WHAT_DEFINE_FILE:
54 self._filemap[fileno] = tdelta
55 return self.next()
56 if what == WHAT_ADD_INFO:
57 key = tdelta.lower()
58 try:
59 L = self._info[key]
60 except KeyError:
61 L = []
62 self._info[key] = L
63 L.append(lineno)
64 if key == "current-directory":
65 self.cwd = lineno
66 return self.next()
67 if what == WHAT_ENTER:
68 t = self._decode_location(fileno, lineno)
69 filename, funcname = t
70 self._stack.append((filename, funcname, lineno))
71 elif what == WHAT_EXIT:
72 filename, funcname, lineno = self._stack.pop()
73 else:
74 filename, funcname, firstlineno = self._stack[-1]
75 return what, (filename, lineno, funcname), tdelta
76
77 if sys.version < "2.2":
78 # Don't add this for newer Python versions; we only want iteration
79 # support, not general sequence support.
80 __getitem__ = next
81 else:
82 def __iter__(self):
83 return self
84
85 #
86 # helpers
87 #
88
89 def _decode_location(self, fileno, lineno):
90 try:
91 return self._funcmap[(fileno, lineno)]
92 except KeyError:
93 if self._loadfile(fileno):
94 filename = funcname = None
95 try:
96 filename, funcname = self._funcmap[(fileno, lineno)]
97 except KeyError:
98 filename = self._filemap.get(fileno)
99 funcname = None
100 self._funcmap[(fileno, lineno)] = (filename, funcname)
101 return filename, funcname
102
103 def _loadfile(self, fileno):
104 try:
105 filename = self._filemap[fileno]
106 except KeyError:
107 print "Could not identify fileId", fileno
108 return 1
109 if filename is None:
110 return 1
111 absname = os.path.normcase(os.path.join(self.cwd, filename))
112
113 try:
114 fp = open(absname)
115 except IOError:
116 return
117 st = parser.suite(fp.read())
118 fp.close()
119
120 # Scan the tree looking for def and lambda nodes, filling in
121 # self._funcmap with all the available information.
122 funcdef = symbol.funcdef
123 lambdef = symbol.lambdef
124
125 stack = [st.totuple(1)]
126
127 while stack:
128 tree = stack.pop()
129 try:
130 sym = tree[0]
131 except (IndexError, TypeError):
132 continue
133 if sym == funcdef:
134 self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1]
135 elif sym == lambdef:
136 self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>"
137 stack.extend(list(tree[1:]))