14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport _hotshot 24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport os.path 34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport parser 44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport symbol 54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmfrom _hotshot import \ 74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WHAT_ENTER, \ 84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WHAT_EXIT, \ 94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WHAT_LINENO, \ 104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WHAT_DEFINE_FILE, \ 114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WHAT_DEFINE_FUNC, \ 124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm WHAT_ADD_INFO 134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = ["LogReader", "ENTER", "EXIT", "LINE"] 164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmENTER = WHAT_ENTER 194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmEXIT = WHAT_EXIT 204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmLINE = WHAT_LINENO 214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass LogReader: 244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, logfn): 254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # fileno -> filename 264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._filemap = {} 274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # (fileno, lineno) -> filename, funcname 284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._funcmap = {} 294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._reader = _hotshot.logreader(logfn) 314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._nextitem = self._reader.next 324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._info = self._reader.info 334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if 'current-directory' in self._info: 344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.cwd = self._info['current-directory'] 354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.cwd = None 374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # This mirrors the call stack of the profiled code as the log 394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # is read back in. It contains tuples of the form: 404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # (file name, line number of function def, function name) 424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._stack = [] 444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._append = self._stack.append 454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._pop = self._stack.pop 464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def close(self): 484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._reader.close() 494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def fileno(self): 514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """Return the file descriptor of the log reader's log file.""" 524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._reader.fileno() 534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def addinfo(self, key, value): 554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """This method is called for each additional ADD_INFO record. 564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm This can be overridden by applications that want to receive 584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm these events. The default implementation does not need to be 594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm called by alternate implementations. 604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm The initial set of ADD_INFO records do not pass through this 624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm mechanism; this is only needed to receive notification when 634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm new values are added. Subclasses can inspect self._info after 644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm calling LogReader.__init__(). 654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm """ 664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm pass 674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_filename(self, fileno): 694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._filemap[fileno] 714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyError: 724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise ValueError, "unknown fileno" 734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_filenames(self): 754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._filemap.values() 764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_fileno(self, filename): 784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = os.path.normcase(os.path.normpath(filename)) 794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for fileno, name in self._filemap.items(): 804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if name == filename: 814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return fileno 824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise ValueError, "unknown filename" 834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def get_funcname(self, fileno, lineno): 854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._funcmap[(fileno, lineno)] 874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyError: 884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise ValueError, "unknown function location" 894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Iteration support: 914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # This adds an optional (& ignored) parameter to next() so that the 924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # same bound method can be used as the __getitem__() method -- this 934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # avoids using an additional method call which kills the performance. 944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def next(self, index=0): 964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # This call may raise StopIteration: 984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm what, tdelta, fileno, lineno = self._nextitem() 994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # handle the most common cases first 1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if what == WHAT_ENTER: 1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename, funcname = self._decode_location(fileno, lineno) 1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm t = (filename, lineno, funcname) 1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._append(t) 1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return what, t, tdelta 1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if what == WHAT_EXIT: 1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return what, self._pop(), tdelta 1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IndexError: 1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise StopIteration 1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if what == WHAT_LINENO: 1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename, firstlineno, funcname = self._stack[-1] 1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return what, (filename, lineno, funcname), tdelta 1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if what == WHAT_DEFINE_FILE: 1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = os.path.normcase(os.path.normpath(tdelta)) 1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._filemap[fileno] = filename 1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif what == WHAT_DEFINE_FUNC: 1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = self._filemap[fileno] 1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._funcmap[(fileno, lineno)] = (filename, tdelta) 1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif what == WHAT_ADD_INFO: 1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # value already loaded into self.info; call the 1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # overridable addinfo() handler so higher-level code 1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # can pick up the new value 1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if tdelta == 'current-directory': 1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.cwd = lineno 1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.addinfo(tdelta, lineno) 1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise ValueError, "unknown event type" 1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __iter__(self): 1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self 1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # helpers 1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _decode_location(self, fileno, lineno): 1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return self._funcmap[(fileno, lineno)] 1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyError: 1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # This should only be needed when the log file does not 1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # contain all the DEFINE_FUNC records needed to allow the 1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # function name to be retrieved from the log file. 1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # 1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if self._loadfile(fileno): 1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = funcname = None 1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename, funcname = self._funcmap[(fileno, lineno)] 1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyError: 1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = self._filemap.get(fileno) 1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm funcname = None 1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._funcmap[(fileno, lineno)] = (filename, funcname) 1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return filename, funcname 1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def _loadfile(self, fileno): 1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm filename = self._filemap[fileno] 1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyError: 1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print "Could not identify fileId", fileno 1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 1 1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if filename is None: 1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 1 1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm absname = os.path.normcase(os.path.join(self.cwd, filename)) 1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp = open(absname) 1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IOError: 1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm st = parser.suite(fp.read()) 1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm fp.close() 1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # Scan the tree looking for def and lambda nodes, filling in 1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm # self._funcmap with all the available information. 1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm funcdef = symbol.funcdef 1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm lambdef = symbol.lambdef 1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm stack = [st.totuple(1)] 1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while stack: 1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm tree = stack.pop() 1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sym = tree[0] 1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except (IndexError, TypeError): 1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sym == funcdef: 1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1] 1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif sym == lambdef: 1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>" 1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm stack.extend(list(tree[1:])) 195