14adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport _hotshot 24adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport os.path 34adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport parser 44adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoimport symbol 54adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 64adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaofrom _hotshot import \ 74adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao WHAT_ENTER, \ 84adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao WHAT_EXIT, \ 94adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao WHAT_LINENO, \ 104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao WHAT_DEFINE_FILE, \ 114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao WHAT_DEFINE_FUNC, \ 124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao WHAT_ADD_INFO 134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao__all__ = ["LogReader", "ENTER", "EXIT", "LINE"] 164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 184adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoENTER = WHAT_ENTER 194adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoEXIT = WHAT_EXIT 204adfde8bc82dd39f59e0445588c3e599ada477dJosh GaoLINE = WHAT_LINENO 214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gaoclass LogReader: 244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def __init__(self, logfn): 254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # fileno -> filename 264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._filemap = {} 274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # (fileno, lineno) -> filename, funcname 284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._funcmap = {} 294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._reader = _hotshot.logreader(logfn) 314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._nextitem = self._reader.next 324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._info = self._reader.info 334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if 'current-directory' in self._info: 344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.cwd = self._info['current-directory'] 354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.cwd = None 374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # This mirrors the call stack of the profiled code as the log 394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # is read back in. It contains tuples of the form: 404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # 414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # (file name, line number of function def, function name) 424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # 434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._stack = [] 444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._append = self._stack.append 454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._pop = self._stack.pop 464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def close(self): 484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._reader.close() 494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def fileno(self): 514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """Return the file descriptor of the log reader's log file.""" 524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return self._reader.fileno() 534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def addinfo(self, key, value): 554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """This method is called for each additional ADD_INFO record. 564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao This can be overridden by applications that want to receive 584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao these events. The default implementation does not need to be 594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao called by alternate implementations. 604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao The initial set of ADD_INFO records do not pass through this 624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao mechanism; this is only needed to receive notification when 634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao new values are added. Subclasses can inspect self._info after 644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao calling LogReader.__init__(). 654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao """ 664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao pass 674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def get_filename(self, fileno): 694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return self._filemap[fileno] 714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except KeyError: 724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao raise ValueError, "unknown fileno" 734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def get_filenames(self): 754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return self._filemap.values() 764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def get_fileno(self, filename): 784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename = os.path.normcase(os.path.normpath(filename)) 794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao for fileno, name in self._filemap.items(): 804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if name == filename: 814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return fileno 824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao raise ValueError, "unknown filename" 834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def get_funcname(self, fileno, lineno): 854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return self._funcmap[(fileno, lineno)] 874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except KeyError: 884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao raise ValueError, "unknown function location" 894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Iteration support: 914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # This adds an optional (& ignored) parameter to next() so that the 924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # same bound method can be used as the __getitem__() method -- this 934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # avoids using an additional method call which kills the performance. 944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 954adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def next(self, index=0): 964adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao while 1: 974adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # This call may raise StopIteration: 984adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao what, tdelta, fileno, lineno = self._nextitem() 994adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1004adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # handle the most common cases first 1014adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1024adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if what == WHAT_ENTER: 1034adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename, funcname = self._decode_location(fileno, lineno) 1044adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao t = (filename, lineno, funcname) 1054adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._append(t) 1064adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return what, t, tdelta 1074adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1084adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if what == WHAT_EXIT: 1094adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 1104adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return what, self._pop(), tdelta 1114adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except IndexError: 1124adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao raise StopIteration 1134adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1144adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if what == WHAT_LINENO: 1154adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename, firstlineno, funcname = self._stack[-1] 1164adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return what, (filename, lineno, funcname), tdelta 1174adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1184adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if what == WHAT_DEFINE_FILE: 1194adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename = os.path.normcase(os.path.normpath(tdelta)) 1204adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._filemap[fileno] = filename 1214adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif what == WHAT_DEFINE_FUNC: 1224adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename = self._filemap[fileno] 1234adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._funcmap[(fileno, lineno)] = (filename, tdelta) 1244adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif what == WHAT_ADD_INFO: 1254adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # value already loaded into self.info; call the 1264adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # overridable addinfo() handler so higher-level code 1274adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # can pick up the new value 1284adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if tdelta == 'current-directory': 1294adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.cwd = lineno 1304adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self.addinfo(tdelta, lineno) 1314adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao else: 1324adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao raise ValueError, "unknown event type" 1334adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1344adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def __iter__(self): 1354adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return self 1364adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1374adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # 1384adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # helpers 1394adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # 1404adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1414adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def _decode_location(self, fileno, lineno): 1424adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 1434adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return self._funcmap[(fileno, lineno)] 1444adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except KeyError: 1454adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # 1464adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # This should only be needed when the log file does not 1474adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # contain all the DEFINE_FUNC records needed to allow the 1484adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # function name to be retrieved from the log file. 1494adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # 1504adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if self._loadfile(fileno): 1514adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename = funcname = None 1524adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 1534adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename, funcname = self._funcmap[(fileno, lineno)] 1544adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except KeyError: 1554adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename = self._filemap.get(fileno) 1564adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao funcname = None 1574adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._funcmap[(fileno, lineno)] = (filename, funcname) 1584adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return filename, funcname 1594adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1604adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao def _loadfile(self, fileno): 1614adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 1624adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao filename = self._filemap[fileno] 1634adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except KeyError: 1644adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao print "Could not identify fileId", fileno 1654adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return 1 1664adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if filename is None: 1674adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return 1 1684adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao absname = os.path.normcase(os.path.join(self.cwd, filename)) 1694adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1704adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 1714adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fp = open(absname) 1724adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except IOError: 1734adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao return 1744adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao st = parser.suite(fp.read()) 1754adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao fp.close() 1764adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1774adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # Scan the tree looking for def and lambda nodes, filling in 1784adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao # self._funcmap with all the available information. 1794adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao funcdef = symbol.funcdef 1804adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao lambdef = symbol.lambdef 1814adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1824adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao stack = [st.totuple(1)] 1834adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao 1844adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao while stack: 1854adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao tree = stack.pop() 1864adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao try: 1874adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao sym = tree[0] 1884adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao except (IndexError, TypeError): 1894adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao continue 1904adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao if sym == funcdef: 1914adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1] 1924adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao elif sym == lambdef: 1934adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>" 1944adfde8bc82dd39f59e0445588c3e599ada477dJosh Gao stack.extend(list(tree[1:])) 195