0001 import _hotshot 0002 import os.path 0003 import parser 0004 import symbol 0005 import sys 0006 0007 from _hotshot import \ 0008 WHAT_ENTER, \ 0009 WHAT_EXIT, \ 0010 WHAT_LINENO, \ 0011 WHAT_DEFINE_FILE, \ 0012 WHAT_DEFINE_FUNC, \ 0013 WHAT_ADD_INFO 0014 0015 0016 __all__ = ["LogReader", "ENTER", "EXIT", "LINE"] 0017 0018 0019 ENTER = WHAT_ENTER 0020 EXIT = WHAT_EXIT 0021 LINE = WHAT_LINENO 0022 0023 0024 class LogReader: 0025 def __init__(self, logfn): 0026 # fileno -> filename 0027 self._filemap = {} 0028 # (fileno, lineno) -> filename, funcname 0029 self._funcmap = {} 0030 0031 self._reader = _hotshot.logreader(logfn) 0032 self._nextitem = self._reader.next 0033 self._info = self._reader.info 0034 if self._info.has_key('current-directory'): 0035 self.cwd = self._info['current-directory'] 0036 else: 0037 self.cwd = None 0038 0039 # This mirrors the call stack of the profiled code as the log 0040 # is read back in. It contains tuples of the form: 0041 # 0042 # (file name, line number of function def, function name) 0043 # 0044 self._stack = [] 0045 self._append = self._stack.append 0046 self._pop = self._stack.pop 0047 0048 def close(self): 0049 self._reader.close() 0050 0051 def fileno(self): 0052 """Return the file descriptor of the log reader's log file.""" 0053 return self._reader.fileno() 0054 0055 def addinfo(self, key, value): 0056 """This method is called for each additional ADD_INFO record. 0057 0058 This can be overridden by applications that want to receive 0059 these events. The default implementation does not need to be 0060 called by alternate implementations. 0061 0062 The initial set of ADD_INFO records do not pass through this 0063 mechanism; this is only needed to receive notification when 0064 new values are added. Subclasses can inspect self._info after 0065 calling LogReader.__init__(). 0066 """ 0067 pass 0068 0069 def get_filename(self, fileno): 0070 try: 0071 return self._filemap[fileno] 0072 except KeyError: 0073 raise ValueError, "unknown fileno" 0074 0075 def get_filenames(self): 0076 return self._filemap.values() 0077 0078 def get_fileno(self, filename): 0079 filename = os.path.normcase(os.path.normpath(filename)) 0080 for fileno, name in self._filemap.items(): 0081 if name == filename: 0082 return fileno 0083 raise ValueError, "unknown filename" 0084 0085 def get_funcname(self, fileno, lineno): 0086 try: 0087 return self._funcmap[(fileno, lineno)] 0088 except KeyError: 0089 raise ValueError, "unknown function location" 0090 0091 # Iteration support: 0092 # This adds an optional (& ignored) parameter to next() so that the 0093 # same bound method can be used as the __getitem__() method -- this 0094 # avoids using an additional method call which kills the performance. 0095 0096 def next(self, index=0): 0097 while 1: 0098 # This call may raise StopIteration: 0099 what, tdelta, fileno, lineno = self._nextitem() 0100 0101 # handle the most common cases first 0102 0103 if what == WHAT_ENTER: 0104 filename, funcname = self._decode_location(fileno, lineno) 0105 t = (filename, lineno, funcname) 0106 self._append(t) 0107 return what, t, tdelta 0108 0109 if what == WHAT_EXIT: 0110 return what, self._pop(), tdelta 0111 0112 if what == WHAT_LINENO: 0113 filename, firstlineno, funcname = self._stack[-1] 0114 return what, (filename, lineno, funcname), tdelta 0115 0116 if what == WHAT_DEFINE_FILE: 0117 filename = os.path.normcase(os.path.normpath(tdelta)) 0118 self._filemap[fileno] = filename 0119 elif what == WHAT_DEFINE_FUNC: 0120 filename = self._filemap[fileno] 0121 self._funcmap[(fileno, lineno)] = (filename, tdelta) 0122 elif what == WHAT_ADD_INFO: 0123 # value already loaded into self.info; call the 0124 # overridable addinfo() handler so higher-level code 0125 # can pick up the new value 0126 if tdelta == 'current-directory': 0127 self.cwd = lineno 0128 self.addinfo(tdelta, lineno) 0129 else: 0130 raise ValueError, "unknown event type" 0131 0132 def __iter__(self): 0133 return self 0134 0135 # 0136 # helpers 0137 # 0138 0139 def _decode_location(self, fileno, lineno): 0140 try: 0141 return self._funcmap[(fileno, lineno)] 0142 except KeyError: 0143 # 0144 # This should only be needed when the log file does not 0145 # contain all the DEFINE_FUNC records needed to allow the 0146 # function name to be retrieved from the log file. 0147 # 0148 if self._loadfile(fileno): 0149 filename = funcname = None 0150 try: 0151 filename, funcname = self._funcmap[(fileno, lineno)] 0152 except KeyError: 0153 filename = self._filemap.get(fileno) 0154 funcname = None 0155 self._funcmap[(fileno, lineno)] = (filename, funcname) 0156 return filename, funcname 0157 0158 def _loadfile(self, fileno): 0159 try: 0160 filename = self._filemap[fileno] 0161 except KeyError: 0162 print "Could not identify fileId", fileno 0163 return 1 0164 if filename is None: 0165 return 1 0166 absname = os.path.normcase(os.path.join(self.cwd, filename)) 0167 0168 try: 0169 fp = open(absname) 0170 except IOError: 0171 return 0172 st = parser.suite(fp.read()) 0173 fp.close() 0174 0175 # Scan the tree looking for def and lambda nodes, filling in 0176 # self._funcmap with all the available information. 0177 funcdef = symbol.funcdef 0178 lambdef = symbol.lambdef 0179 0180 stack = [st.totuple(1)] 0181 0182 while stack: 0183 tree = stack.pop() 0184 try: 0185 sym = tree[0] 0186 except (IndexError, TypeError): 0187 continue 0188 if sym == funcdef: 0189 self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1] 0190 elif sym == lambdef: 0191 self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>" 0192 stack.extend(list(tree[1:])) 0193
Generated by PyXR 0.9.4