mirror of
				https://github.com/python/cpython.git
				synced 2025-11-02 22:51:25 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			192 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import _hotshot
 | 
						|
import os.path
 | 
						|
import parser
 | 
						|
import symbol
 | 
						|
import sys
 | 
						|
 | 
						|
from _hotshot import \
 | 
						|
     WHAT_ENTER, \
 | 
						|
     WHAT_EXIT, \
 | 
						|
     WHAT_LINENO, \
 | 
						|
     WHAT_DEFINE_FILE, \
 | 
						|
     WHAT_DEFINE_FUNC, \
 | 
						|
     WHAT_ADD_INFO
 | 
						|
 | 
						|
 | 
						|
__all__ = ["LogReader", "ENTER", "EXIT", "LINE"]
 | 
						|
 | 
						|
 | 
						|
ENTER = WHAT_ENTER
 | 
						|
EXIT  = WHAT_EXIT
 | 
						|
LINE  = WHAT_LINENO
 | 
						|
 | 
						|
 | 
						|
class LogReader:
 | 
						|
    def __init__(self, logfn):
 | 
						|
        # fileno -> filename
 | 
						|
        self._filemap = {}
 | 
						|
        # (fileno, lineno) -> filename, funcname
 | 
						|
        self._funcmap = {}
 | 
						|
 | 
						|
        self._reader = _hotshot.logreader(logfn)
 | 
						|
        self._nextitem = self._reader.next
 | 
						|
        self._info = self._reader.info
 | 
						|
        if self._info.has_key('current-directory'):
 | 
						|
            self.cwd = self._info['current-directory']
 | 
						|
        else:
 | 
						|
            self.cwd = None
 | 
						|
 | 
						|
        # This mirrors the call stack of the profiled code as the log
 | 
						|
        # is read back in.  It contains tuples of the form:
 | 
						|
        #
 | 
						|
        #   (file name, line number of function def, function name)
 | 
						|
        #
 | 
						|
        self._stack = []
 | 
						|
        self._append = self._stack.append
 | 
						|
        self._pop = self._stack.pop
 | 
						|
 | 
						|
    def close(self):
 | 
						|
        self._reader.close()
 | 
						|
 | 
						|
    def fileno(self):
 | 
						|
        """Return the file descriptor of the log reader's log file."""
 | 
						|
        return self._reader.fileno()
 | 
						|
 | 
						|
    def addinfo(self, key, value):
 | 
						|
        """This method is called for each additional ADD_INFO record.
 | 
						|
 | 
						|
        This can be overridden by applications that want to receive
 | 
						|
        these events.  The default implementation does not need to be
 | 
						|
        called by alternate implementations.
 | 
						|
 | 
						|
        The initial set of ADD_INFO records do not pass through this
 | 
						|
        mechanism; this is only needed to receive notification when
 | 
						|
        new values are added.  Subclasses can inspect self._info after
 | 
						|
        calling LogReader.__init__().
 | 
						|
        """
 | 
						|
        pass
 | 
						|
 | 
						|
    def get_filename(self, fileno):
 | 
						|
        try:
 | 
						|
            return self._filemap[fileno]
 | 
						|
        except KeyError:
 | 
						|
            raise ValueError, "unknown fileno"
 | 
						|
 | 
						|
    def get_filenames(self):
 | 
						|
        return self._filemap.values()
 | 
						|
 | 
						|
    def get_fileno(self, filename):
 | 
						|
        filename = os.path.normcase(os.path.normpath(filename))
 | 
						|
        for fileno, name in self._filemap.items():
 | 
						|
            if name == filename:
 | 
						|
                return fileno
 | 
						|
        raise ValueError, "unknown filename"
 | 
						|
 | 
						|
    def get_funcname(self, fileno, lineno):
 | 
						|
        try:
 | 
						|
            return self._funcmap[(fileno, lineno)]
 | 
						|
        except KeyError:
 | 
						|
            raise ValueError, "unknown function location"
 | 
						|
 | 
						|
    # Iteration support:
 | 
						|
    # This adds an optional (& ignored) parameter to next() so that the
 | 
						|
    # same bound method can be used as the __getitem__() method -- this
 | 
						|
    # avoids using an additional method call which kills the performance.
 | 
						|
 | 
						|
    def next(self, index=0):
 | 
						|
        while 1:
 | 
						|
            # This call may raise StopIteration:
 | 
						|
            what, tdelta, fileno, lineno = self._nextitem()
 | 
						|
 | 
						|
            # handle the most common cases first
 | 
						|
 | 
						|
            if what == WHAT_ENTER:
 | 
						|
                filename, funcname = self._decode_location(fileno, lineno)
 | 
						|
                t = (filename, lineno, funcname)
 | 
						|
                self._append(t)
 | 
						|
                return what, t, tdelta
 | 
						|
 | 
						|
            if what == WHAT_EXIT:
 | 
						|
                return what, self._pop(), tdelta
 | 
						|
 | 
						|
            if what == WHAT_LINENO:
 | 
						|
                filename, firstlineno, funcname = self._stack[-1]
 | 
						|
                return what, (filename, lineno, funcname), tdelta
 | 
						|
 | 
						|
            if what == WHAT_DEFINE_FILE:
 | 
						|
                filename = os.path.normcase(os.path.normpath(tdelta))
 | 
						|
                self._filemap[fileno] = filename
 | 
						|
            elif what == WHAT_DEFINE_FUNC:
 | 
						|
                filename = self._filemap[fileno]
 | 
						|
                self._funcmap[(fileno, lineno)] = (filename, tdelta)
 | 
						|
            elif what == WHAT_ADD_INFO:
 | 
						|
                # value already loaded into self.info; call the
 | 
						|
                # overridable addinfo() handler so higher-level code
 | 
						|
                # can pick up the new value
 | 
						|
                if tdelta == 'current-directory':
 | 
						|
                    self.cwd = lineno
 | 
						|
                self.addinfo(tdelta, lineno)
 | 
						|
            else:
 | 
						|
                raise ValueError, "unknown event type"
 | 
						|
 | 
						|
    def __iter__(self):
 | 
						|
        return self
 | 
						|
 | 
						|
    #
 | 
						|
    #  helpers
 | 
						|
    #
 | 
						|
 | 
						|
    def _decode_location(self, fileno, lineno):
 | 
						|
        try:
 | 
						|
            return self._funcmap[(fileno, lineno)]
 | 
						|
        except KeyError:
 | 
						|
            #
 | 
						|
            # This should only be needed when the log file does not
 | 
						|
            # contain all the DEFINE_FUNC records needed to allow the
 | 
						|
            # function name to be retrieved from the log file.
 | 
						|
            #
 | 
						|
            if self._loadfile(fileno):
 | 
						|
                filename = funcname = None
 | 
						|
            try:
 | 
						|
                filename, funcname = self._funcmap[(fileno, lineno)]
 | 
						|
            except KeyError:
 | 
						|
                filename = self._filemap.get(fileno)
 | 
						|
                funcname = None
 | 
						|
                self._funcmap[(fileno, lineno)] = (filename, funcname)
 | 
						|
        return filename, funcname
 | 
						|
 | 
						|
    def _loadfile(self, fileno):
 | 
						|
        try:
 | 
						|
            filename = self._filemap[fileno]
 | 
						|
        except KeyError:
 | 
						|
            print "Could not identify fileId", fileno
 | 
						|
            return 1
 | 
						|
        if filename is None:
 | 
						|
            return 1
 | 
						|
        absname = os.path.normcase(os.path.join(self.cwd, filename))
 | 
						|
 | 
						|
        try:
 | 
						|
            fp = open(absname)
 | 
						|
        except IOError:
 | 
						|
            return
 | 
						|
        st = parser.suite(fp.read())
 | 
						|
        fp.close()
 | 
						|
 | 
						|
        # Scan the tree looking for def and lambda nodes, filling in
 | 
						|
        # self._funcmap with all the available information.
 | 
						|
        funcdef = symbol.funcdef
 | 
						|
        lambdef = symbol.lambdef
 | 
						|
 | 
						|
        stack = [st.totuple(1)]
 | 
						|
 | 
						|
        while stack:
 | 
						|
            tree = stack.pop()
 | 
						|
            try:
 | 
						|
                sym = tree[0]
 | 
						|
            except (IndexError, TypeError):
 | 
						|
                continue
 | 
						|
            if sym == funcdef:
 | 
						|
                self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1]
 | 
						|
            elif sym == lambdef:
 | 
						|
                self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>"
 | 
						|
            stack.extend(list(tree[1:]))
 |