10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport _hotshot 20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport os.path 30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport parser 40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport symbol 50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom _hotshot import \ 70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi WHAT_ENTER, \ 80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi WHAT_EXIT, \ 90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi WHAT_LINENO, \ 100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi WHAT_DEFINE_FILE, \ 110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi WHAT_DEFINE_FUNC, \ 120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi WHAT_ADD_INFO 130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi__all__ = ["LogReader", "ENTER", "EXIT", "LINE"] 160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiENTER = WHAT_ENTER 190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiEXIT = WHAT_EXIT 200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiLINE = WHAT_LINENO 210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass LogReader: 240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __init__(self, logfn): 250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # fileno -> filename 260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._filemap = {} 270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # (fileno, lineno) -> filename, funcname 280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._funcmap = {} 290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._reader = _hotshot.logreader(logfn) 310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._nextitem = self._reader.next 320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._info = self._reader.info 330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if 'current-directory' in self._info: 340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.cwd = self._info['current-directory'] 350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.cwd = None 370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This mirrors the call stack of the profiled code as the log 390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # is read back in. It contains tuples of the form: 400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # 410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # (file name, line number of function def, function name) 420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # 430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._stack = [] 440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._append = self._stack.append 450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._pop = self._stack.pop 460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def close(self): 480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._reader.close() 490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def fileno(self): 510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """Return the file descriptor of the log reader's log file.""" 520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self._reader.fileno() 530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def addinfo(self, key, value): 550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """This method is called for each additional ADD_INFO record. 560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi This can be overridden by applications that want to receive 580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi these events. The default implementation does not need to be 590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi called by alternate implementations. 600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi The initial set of ADD_INFO records do not pass through this 620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi mechanism; this is only needed to receive notification when 630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi new values are added. Subclasses can inspect self._info after 640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi calling LogReader.__init__(). 650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi """ 660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi pass 670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def get_filename(self, fileno): 690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self._filemap[fileno] 710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except KeyError: 720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise ValueError, "unknown fileno" 730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def get_filenames(self): 750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self._filemap.values() 760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def get_fileno(self, filename): 780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename = os.path.normcase(os.path.normpath(filename)) 790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi for fileno, name in self._filemap.items(): 800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if name == filename: 810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return fileno 820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise ValueError, "unknown filename" 830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def get_funcname(self, fileno, lineno): 850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self._funcmap[(fileno, lineno)] 870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except KeyError: 880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise ValueError, "unknown function location" 890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Iteration support: 910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This adds an optional (& ignored) parameter to next() so that the 920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # same bound method can be used as the __getitem__() method -- this 930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # avoids using an additional method call which kills the performance. 940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def next(self, index=0): 960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while 1: 970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This call may raise StopIteration: 980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi what, tdelta, fileno, lineno = self._nextitem() 990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # handle the most common cases first 1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if what == WHAT_ENTER: 1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename, funcname = self._decode_location(fileno, lineno) 1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi t = (filename, lineno, funcname) 1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._append(t) 1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return what, t, tdelta 1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if what == WHAT_EXIT: 1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return what, self._pop(), tdelta 1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except IndexError: 1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise StopIteration 1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if what == WHAT_LINENO: 1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename, firstlineno, funcname = self._stack[-1] 1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return what, (filename, lineno, funcname), tdelta 1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if what == WHAT_DEFINE_FILE: 1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename = os.path.normcase(os.path.normpath(tdelta)) 1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._filemap[fileno] = filename 1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi elif what == WHAT_DEFINE_FUNC: 1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename = self._filemap[fileno] 1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._funcmap[(fileno, lineno)] = (filename, tdelta) 1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi elif what == WHAT_ADD_INFO: 1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # value already loaded into self.info; call the 1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # overridable addinfo() handler so higher-level code 1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # can pick up the new value 1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if tdelta == 'current-directory': 1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.cwd = lineno 1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self.addinfo(tdelta, lineno) 1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi else: 1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi raise ValueError, "unknown event type" 1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def __iter__(self): 1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self 1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # 1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # helpers 1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # 1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def _decode_location(self, fileno, lineno): 1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return self._funcmap[(fileno, lineno)] 1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except KeyError: 1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # 1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # This should only be needed when the log file does not 1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # contain all the DEFINE_FUNC records needed to allow the 1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # function name to be retrieved from the log file. 1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # 1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if self._loadfile(fileno): 1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename = funcname = None 1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename, funcname = self._funcmap[(fileno, lineno)] 1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except KeyError: 1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename = self._filemap.get(fileno) 1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi funcname = None 1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._funcmap[(fileno, lineno)] = (filename, funcname) 1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return filename, funcname 1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi def _loadfile(self, fileno): 1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi filename = self._filemap[fileno] 1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except KeyError: 1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi print "Could not identify fileId", fileno 1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return 1 1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if filename is None: 1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return 1 1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi absname = os.path.normcase(os.path.join(self.cwd, filename)) 1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi fp = open(absname) 1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except IOError: 1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi return 1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi st = parser.suite(fp.read()) 1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi fp.close() 1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # Scan the tree looking for def and lambda nodes, filling in 1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi # self._funcmap with all the available information. 1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi funcdef = symbol.funcdef 1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi lambdef = symbol.lambdef 1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stack = [st.totuple(1)] 1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi 1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi while stack: 1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi tree = stack.pop() 1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi try: 1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi sym = tree[0] 1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi except (IndexError, TypeError): 1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi continue 1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi if sym == funcdef: 1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1] 1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi elif sym == lambdef: 1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>" 1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi stack.extend(list(tree[1:])) 195