blob: 55dda217f2fcf27410a5aedcd0476f03456e4953 [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, \
Fred Draked5d5a042001-10-15 22:05:32 +000012 WHAT_DEFINE_FUNC, \
Fred Drakef0193242001-10-12 20:56:29 +000013 WHAT_ADD_INFO
14
15
16__all__ = ["LogReader", "ENTER", "EXIT", "LINE"]
17
18
19ENTER = WHAT_ENTER
20EXIT = WHAT_EXIT
21LINE = WHAT_LINENO
22
23
24try:
25 StopIteration
26except NameError:
27 StopIteration = IndexError
28
29
30class LogReader:
31 def __init__(self, logfn):
32 # fileno -> filename
33 self._filemap = {}
34 # (fileno, lineno) -> filename, funcname
35 self._funcmap = {}
36
Fred Draked62f1512001-10-13 02:55:40 +000037 self._reader = _hotshot.logreader(logfn)
38 self._nextitem = self._reader.next
Fred Drake8e26b522001-10-29 20:57:23 +000039 self._info = self._reader.info
Fred Drakef0193242001-10-12 20:56:29 +000040 self._stack = []
41
Fred Drake8e26b522001-10-29 20:57:23 +000042 def addinfo(self, key, value):
43 """This method is called for each additional ADD_INFO record.
44
45 This can be overridden by applications that want to receive
46 these events. The default implementation does not need to be
47 called by alternate implementations.
48
49 The initial set of ADD_INFO records do not pass through this
50 mechanism; this is only needed to receive notification when
51 new values are added. Subclasses can inspect self._info after
52 calling LogReader.__init__().
53 """
54 pass
55
Fred Drakef0193242001-10-12 20:56:29 +000056 # Iteration support:
57 # This adds an optional (& ignored) parameter to next() so that the
58 # same bound method can be used as the __getitem__() method -- this
59 # avoids using an additional method call which kills the performance.
60
61 def next(self, index=0):
Fred Draked5d5a042001-10-15 22:05:32 +000062 while 1:
Fred Drakef0193242001-10-12 20:56:29 +000063 try:
Fred Draked5d5a042001-10-15 22:05:32 +000064 what, tdelta, fileno, lineno = self._nextitem()
65 except TypeError:
66 # logreader().next() returns None at the end
67 self._reader.close()
68 raise StopIteration()
69 if what == WHAT_DEFINE_FILE:
70 self._filemap[fileno] = tdelta
71 continue
72 if what == WHAT_DEFINE_FUNC:
73 filename = self._filemap[fileno]
74 self._funcmap[(fileno, lineno)] = (filename, tdelta)
75 continue
76 if what == WHAT_ADD_INFO:
Fred Drake8e26b522001-10-29 20:57:23 +000077 # value already loaded into self.info; call the
78 # overridable addinfo() handler so higher-level code
79 # can pick up the new value
80 self.addinfo(tdelta, lineno)
Fred Draked5d5a042001-10-15 22:05:32 +000081 continue
82 if what == WHAT_ENTER:
83 t = self._decode_location(fileno, lineno)
84 filename, funcname = t
85 self._stack.append((filename, funcname, lineno))
86 elif what == WHAT_EXIT:
87 filename, funcname, lineno = self._stack.pop()
88 else:
89 filename, funcname, firstlineno = self._stack[-1]
90 return what, (filename, lineno, funcname), tdelta
Fred Drakef0193242001-10-12 20:56:29 +000091
92 if sys.version < "2.2":
93 # Don't add this for newer Python versions; we only want iteration
94 # support, not general sequence support.
95 __getitem__ = next
96 else:
97 def __iter__(self):
98 return self
99
100 #
101 # helpers
102 #
103
104 def _decode_location(self, fileno, lineno):
105 try:
106 return self._funcmap[(fileno, lineno)]
107 except KeyError:
Fred Draked5d5a042001-10-15 22:05:32 +0000108 #
109 # This should only be needed when the log file does not
110 # contain all the DEFINE_FUNC records needed to allow the
111 # function name to be retrieved from the log file.
112 #
Fred Drakef0193242001-10-12 20:56:29 +0000113 if self._loadfile(fileno):
114 filename = funcname = None
115 try:
116 filename, funcname = self._funcmap[(fileno, lineno)]
117 except KeyError:
118 filename = self._filemap.get(fileno)
119 funcname = None
120 self._funcmap[(fileno, lineno)] = (filename, funcname)
121 return filename, funcname
122
123 def _loadfile(self, fileno):
124 try:
125 filename = self._filemap[fileno]
126 except KeyError:
127 print "Could not identify fileId", fileno
128 return 1
129 if filename is None:
130 return 1
131 absname = os.path.normcase(os.path.join(self.cwd, filename))
132
133 try:
134 fp = open(absname)
135 except IOError:
136 return
137 st = parser.suite(fp.read())
138 fp.close()
139
140 # Scan the tree looking for def and lambda nodes, filling in
141 # self._funcmap with all the available information.
142 funcdef = symbol.funcdef
143 lambdef = symbol.lambdef
144
145 stack = [st.totuple(1)]
146
147 while stack:
148 tree = stack.pop()
149 try:
150 sym = tree[0]
151 except (IndexError, TypeError):
152 continue
153 if sym == funcdef:
154 self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1]
155 elif sym == lambdef:
156 self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>"
157 stack.extend(list(tree[1:]))