10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#! /usr/bin/env python
20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#
30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Class for profiling python code. rev 1.0  6/2/94
40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#
50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Written by James Roskind
60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Based on prior profile module by Sjoerd Mullender...
70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#   which was hacked somewhat by: Guido van Rossum
80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""Class for profiling Python code."""
100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Copyright Disney Enterprises, Inc.  All Rights Reserved.
120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Licensed to PSF under a Contributor Agreement
130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#
140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Licensed under the Apache License, Version 2.0 (the "License");
150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# you may not use this file except in compliance with the License.
160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# You may obtain a copy of the License at
170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#
180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# http://www.apache.org/licenses/LICENSE-2.0
190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#
200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Unless required by applicable law or agreed to in writing, software
210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# distributed under the License is distributed on an "AS IS" BASIS,
220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# either express or implied.  See the License for the specific language
240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# governing permissions and limitations under the License.
250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport sys
280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport os
290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport time
300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport marshal
310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom optparse import OptionParser
320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi__all__ = ["run", "runctx", "help", "Profile"]
340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Sample timer for use with
360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#i_count = 0
370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#def integer_timer():
380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#       global i_count
390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#       i_count = i_count + 1
400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#       return i_count
410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#itimes = integer_timer # replace with C coded timer returning integers
420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#**************************************************************************
440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# The following are the static member functions for the profiler class
450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Note that an instance of Profile() is *not* needed to call them.
460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#**************************************************************************
470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef run(statement, filename=None, sort=-1):
490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """Run statement under profiler optionally saving results in filename
500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    This function takes a single argument that can be passed to the
520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    "exec" statement, and an optional file name.  In all cases this
530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    routine attempts to "exec" its first argument and gather profiling
540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    statistics from the execution. If no file name is present, then this
550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    function automatically prints a simple profiling report, sorted by the
560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    standard name string (file/line/function-name) that is presented in
570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    each line.
580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """
590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    prof = Profile()
600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    try:
610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        prof = prof.run(statement)
620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    except SystemExit:
630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pass
640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    if filename is not None:
650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        prof.dump_stats(filename)
660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    else:
670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return prof.print_stats(sort)
680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef runctx(statement, globals, locals, filename=None, sort=-1):
700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """Run statement under profiler, supplying your own globals and locals,
710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    optionally saving results in filename.
720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    statement and filename have the same semantics as profile.run
740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """
750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    prof = Profile()
760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    try:
770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        prof = prof.runctx(statement, globals, locals)
780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    except SystemExit:
790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pass
800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    if filename is not None:
820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        prof.dump_stats(filename)
830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    else:
840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return prof.print_stats(sort)
850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Backwards compatibility.
870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef help():
880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    print "Documentation for the profile module can be found "
890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    print "in the Python Library Reference, section 'The Python Profiler'."
900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiif hasattr(os, "times"):
920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _get_time_times(timer=os.times):
930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = timer()
940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return t[0] + t[1]
950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Using getrusage(3) is better than clock(3) if available:
970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# on some systems (e.g. FreeBSD), getrusage has a higher resolution
980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# Furthermore, on a POSIX system, returns microseconds, which
990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# wrap around after 36min.
1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi_has_res = 0
1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yitry:
1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    import resource
1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    resgetrusage = lambda: resource.getrusage(resource.RUSAGE_SELF)
1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _get_time_resource(timer=resgetrusage):
1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = timer()
1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return t[0] + t[1]
1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    _has_res = 1
1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiexcept ImportError:
1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    pass
1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass Profile:
1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """Profiler class.
1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    self.cur is always a tuple.  Each such tuple corresponds to a stack
1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    frame that is currently active (self.cur[-2]).  The following are the
1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    definitions of its members.  We use this external "parallel stack" to
1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    avoid contaminating the program that we are profiling. (old profiler
1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    used to write into the frames local dictionary!!) Derived classes
1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    can change the definition of some entries, as long as they leave
1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [-2:] intact (frame and previous tuple).  In case an internal error is
1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    detected, the -3 element is used as the function name.
1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [ 0] = Time that needs to be charged to the parent frame's function.
1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           It is used so that a function call will not have to access the
1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           timing data for the parent frame.
1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [ 1] = Total time spent in this frame's function, excluding time in
1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           subfunctions (this latter is tallied in cur[2]).
1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [ 2] = Total time spent in subfunctions, excluding time executing the
1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           frame's function (this latter is tallied in cur[1]).
1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [-3] = Name of the function that corresponds to this frame.
1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [-2] = Actual frame that we correspond to (used to sync exception handling).
1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [-1] = Our parent 6-tuple (corresponds to frame.f_back).
1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    Timing data for each function is stored as a 5-tuple in the dictionary
1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    self.timings[].  The index is always the name stored in self.cur[-3].
1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    The following are the definitions of the members:
1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [0] = The number of times this function was called, not counting direct
1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi          or indirect recursion,
1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [1] = Number of times this function appears on the stack, minus one
1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [2] = Total time spent internal to this function
1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [3] = Cumulative time that this function was present on the stack.  In
1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi          non-recursive functions, this is the total execution time from start
1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi          to finish of each invocation of a function, including time spent in
1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi          all subfunctions.
1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    [4] = A dictionary indicating for each function name, the number of times
1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi          it was called by us.
1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    """
1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    bias = 0  # calibration constant
1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def __init__(self, timer=None, bias=None):
1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.timings = {}
1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cur = None
1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cmd = ""
1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.c_func_name = ""
1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if bias is None:
1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            bias = self.bias
1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.bias = bias     # Materialize in local dict for lookup speed.
1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not timer:
1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if _has_res:
1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.timer = resgetrusage
1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.dispatcher = self.trace_dispatch
1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.get_time = _get_time_resource
1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            elif hasattr(time, 'clock'):
1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.timer = self.get_time = time.clock
1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.dispatcher = self.trace_dispatch_i
1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            elif hasattr(os, 'times'):
1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.timer = os.times
1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.dispatcher = self.trace_dispatch
1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.get_time = _get_time_times
1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.timer = self.get_time = time.time
1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.dispatcher = self.trace_dispatch_i
1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.timer = timer
1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            t = self.timer() # test out timer function
1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            try:
1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                length = len(t)
1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            except TypeError:
1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.get_time = timer
1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.dispatcher = self.trace_dispatch_i
1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if length == 2:
1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    self.dispatcher = self.trace_dispatch
1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                else:
1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    self.dispatcher = self.trace_dispatch_l
1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # This get_time() implementation needs to be defined
1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # here to capture the passed-in timer in the parameter
1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # list (for performance).  Note that we can't assume
1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # the timer() result contains two values in all
1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # cases.
1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                def get_time_timer(timer=timer, sum=sum):
1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    return sum(timer())
1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.get_time = get_time_timer
1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.t = self.get_time()
1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.simulate_call('profiler')
2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Heavily optimized dispatch routine for os.times() timer
2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch(self, frame, event, arg):
2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timer = self.timer
2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = timer()
2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = t[0] + t[1] - self.t - self.bias
2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if event == "c_call":
2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.c_func_name = arg.__name__
2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.dispatch[event](self, frame,t):
2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            t = timer()
2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = t[0] + t[1]
2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            r = timer()
2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = r[0] + r[1] - t # put back unrecorded delta
2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Dispatch routine for best timer program (return = scalar, fastest if
2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # an integer but float works too -- and time.clock() relies on that).
2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_i(self, frame, event, arg):
2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timer = self.timer
2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = timer() - self.t - self.bias
2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if event == "c_call":
2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.c_func_name = arg.__name__
2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.dispatch[event](self, frame, t):
2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = timer()
2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = timer() - t  # put back unrecorded delta
2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Dispatch routine for macintosh (timer returns time in ticks of
2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # 1/60th second)
2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_mac(self, frame, event, arg):
2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timer = self.timer
2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = timer()/60.0 - self.t - self.bias
2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if event == "c_call":
2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.c_func_name = arg.__name__
2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.dispatch[event](self, frame, t):
2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = timer()/60.0
2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = timer()/60.0 - t  # put back unrecorded delta
2470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # SLOW generic dispatch routine for timer returning lists of numbers
2490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_l(self, frame, event, arg):
2510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        get_time = self.get_time
2520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = get_time() - self.t - self.bias
2530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if event == "c_call":
2550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.c_func_name = arg.__name__
2560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.dispatch[event](self, frame, t):
2580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = get_time()
2590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
2600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.t = get_time() - t # put back unrecorded delta
2610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # In the event handlers, the first 3 elements of self.cur are unpacked
2630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # into vrbls w/ 3-letter names.  The last two characters are meant to be
2640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # mnemonic:
2650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #     _pt  self.cur[0] "parent time"   time to be charged to parent frame
2660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #     _it  self.cur[1] "internal time" time spent directly in the function
2670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #     _et  self.cur[2] "external time" time spent in subfunctions
2680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_exception(self, frame, t):
2700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        rpt, rit, ret, rfn, rframe, rcur = self.cur
2710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if (rframe is not frame) and rcur:
2720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return self.trace_dispatch_return(rframe, t)
2730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cur = rpt, rit+t, ret, rfn, rframe, rcur
2740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return 1
2750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_call(self, frame, t):
2780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.cur and frame.f_back is not self.cur[-2]:
2790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            rpt, rit, ret, rfn, rframe, rcur = self.cur
2800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not isinstance(rframe, Profile.fake_frame):
2810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                assert rframe.f_back is frame.f_back, ("Bad call", rfn,
2820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                                       rframe, rframe.f_back,
2830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                                       frame, frame.f_back)
2840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.trace_dispatch_return(rframe, 0)
2850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                assert (self.cur is None or \
2860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                        frame.f_back is self.cur[-2]), ("Bad call",
2870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                                        self.cur[-3])
2880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        fcode = frame.f_code
2890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
2900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cur = (t, 0, 0, fn, frame, self.cur)
2910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timings = self.timings
2920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if fn in timings:
2930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            cc, ns, tt, ct, callers = timings[fn]
2940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            timings[fn] = cc, ns + 1, tt, ct, callers
2950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
2960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            timings[fn] = 0, 0, 0, 0, {}
2970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return 1
2980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_c_call (self, frame, t):
3000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        fn = ("", 0, self.c_func_name)
3010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cur = (t, 0, 0, fn, frame, self.cur)
3020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timings = self.timings
3030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if fn in timings:
3040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            cc, ns, tt, ct, callers = timings[fn]
3050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            timings[fn] = cc, ns+1, tt, ct, callers
3060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
3070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            timings[fn] = 0, 0, 0, 0, {}
3080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return 1
3090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def trace_dispatch_return(self, frame, t):
3110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if frame is not self.cur[-2]:
3120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3])
3130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.trace_dispatch_return(self.cur[-2], 0)
3140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Prefix "r" means part of the Returning or exiting frame.
3160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Prefix "p" means part of the Previous or Parent or older frame.
3170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        rpt, rit, ret, rfn, frame, rcur = self.cur
3190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        rit = rit + t
3200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        frame_total = rit + ret
3210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ppt, pit, pet, pfn, pframe, pcur = rcur
3230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur
3240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timings = self.timings
3260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        cc, ns, tt, ct, callers = timings[rfn]
3270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not ns:
3280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # This is the only occurrence of the function on the stack.
3290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Else this is a (directly or indirectly) recursive call, and
3300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # its cumulative time will get updated when the topmost call to
3310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # it returns.
3320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            ct = ct + frame_total
3330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            cc = cc + 1
3340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if pfn in callers:
3360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            callers[pfn] = callers[pfn] + 1  # hack: gather more
3370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # stats such as the amount of time added to ct courtesy
3380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # of this specific call, and the contribution to cc
3390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # courtesy of this call.
3400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
3410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            callers[pfn] = 1
3420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        timings[rfn] = cc, ns - 1, tt + rit, ct, callers
3440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return 1
3460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    dispatch = {
3490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        "call": trace_dispatch_call,
3500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        "exception": trace_dispatch_exception,
3510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        "return": trace_dispatch_return,
3520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        "c_call": trace_dispatch_c_call,
3530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        "c_exception": trace_dispatch_return,  # the C function returned
3540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        "c_return": trace_dispatch_return,
3550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        }
3560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # The next few functions play with self.cmd. By carefully preloading
3590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # our parallel stack, we can force the profiled result to include
3600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # an arbitrary string as the name of the calling function.
3610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # We use self.cmd as that string, and the resulting stats look
3620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # very nice :-).
3630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def set_cmd(self, cmd):
3650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.cur[-1]: return   # already set
3660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.cmd = cmd
3670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.simulate_call(cmd)
3680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    class fake_code:
3700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        def __init__(self, filename, line, name):
3710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.co_filename = filename
3720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.co_line = line
3730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.co_name = name
3740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.co_firstlineno = 0
3750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        def __repr__(self):
3770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return repr((self.co_filename, self.co_line, self.co_name))
3780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    class fake_frame:
3800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        def __init__(self, code, prior):
3810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.f_code = code
3820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.f_back = prior
3830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def simulate_call(self, name):
3850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        code = self.fake_code('profile', 0, name)
3860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.cur:
3870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            pframe = self.cur[-2]
3880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
3890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            pframe = None
3900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        frame = self.fake_frame(code, pframe)
3910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.dispatch['call'](self, frame, 0)
3920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # collect stats from pending stack, including getting final
3940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # timings for self.cmd frame.
3950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def simulate_cmd_complete(self):
3970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        get_time = self.get_time
3980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t = get_time() - self.t
3990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        while self.cur[-1]:
4000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # We *can* cause assertion errors here if
4010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # dispatch_trace_return checks for a frame match!
4020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.dispatch['return'](self, self.cur[-2], t)
4030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            t = 0
4040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.t = get_time() - t
4050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def print_stats(self, sort=-1):
4080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        import pstats
4090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        pstats.Stats(self).strip_dirs().sort_stats(sort). \
4100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                  print_stats()
4110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def dump_stats(self, file):
4130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f = open(file, 'wb')
4140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.create_stats()
4150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        marshal.dump(self.stats, f)
4160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f.close()
4170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def create_stats(self):
4190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.simulate_cmd_complete()
4200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.snapshot_stats()
4210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def snapshot_stats(self):
4230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.stats = {}
4240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for func, (cc, ns, tt, ct, callers) in self.timings.iteritems():
4250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            callers = callers.copy()
4260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            nc = 0
4270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for callcnt in callers.itervalues():
4280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                nc += callcnt
4290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.stats[func] = cc, nc, tt, ct, callers
4300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # The following two methods can be called by clients to use
4330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # a profiler to profile a statement, given as a string.
4340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def run(self, cmd):
4360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        import __main__
4370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dict = __main__.__dict__
4380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return self.runctx(cmd, dict, dict)
4390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def runctx(self, cmd, globals, locals):
4410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.set_cmd(cmd)
4420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        sys.setprofile(self.dispatcher)
4430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
4440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            exec cmd in globals, locals
4450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        finally:
4460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            sys.setprofile(None)
4470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return self
4480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # This method is more useful to profile a single function call.
4500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def runcall(self, func, *args, **kw):
4510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.set_cmd(repr(func))
4520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        sys.setprofile(self.dispatcher)
4530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
4540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return func(*args, **kw)
4550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        finally:
4560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            sys.setprofile(None)
4570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #******************************************************************
4600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # The following calculates the overhead for using a profiler.  The
4610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # problem is that it takes a fair amount of time for the profiler
4620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # to stop the stopwatch (from the time it receives an event).
4630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Similarly, there is a delay from the time that the profiler
4640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # re-starts the stopwatch before the user's code really gets to
4650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # continue.  The following code tries to measure the difference on
4660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # a per-event basis.
4670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #
4680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # Note that this difference is only significant if there are a lot of
4690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # events, and relatively little user code per event.  For example,
4700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # code with small functions will typically benefit from having the
4710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # profiler calibrated for the current platform.  This *could* be
4720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # done on the fly during init() time, but it is not worth the
4730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # effort.  Also note that if too large a value specified, then
4740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # execution time on some functions will actually appear as a
4750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # negative number.  It is *normal* for some functions (with very
4760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # low call counts) to have such negative stats, even if the
4770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # calibration figure is "correct."
4780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #
4790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # One alternative to profile-time calibration adjustments (i.e.,
4800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # adding in the magic little delta during each event) is to track
4810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # more carefully the number of events (and cumulatively, the number
4820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # of events during sub functions) that are seen.  If this were
4830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # done, then the arithmetic could be done after the fact (i.e., at
4840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # display time).  Currently, we track only call/return events.
4850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # These values can be deduced by examining the callees and callers
4860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # vectors for each functions.  Hence we *can* almost correct the
4870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # internal time figure at print time (note that we currently don't
4880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # track exception event processing counts).  Unfortunately, there
4890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # is currently no similar information for cumulative sub-function
4900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # time.  It would not be hard to "get all this info" at profiler
4910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # time.  Specifically, we would have to extend the tuples to keep
4920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # counts of this in each frame, and then extend the defs of timing
4930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # tuples to include the significant two figures. I'm a bit fearful
4940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # that this additional feature will slow the heavily optimized
4950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # event/time ratio (i.e., the profiler would run slower, fur a very
4960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    # low "value added" feature.)
4970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    #**************************************************************
4980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
4990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def calibrate(self, m, verbose=0):
5000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.__class__ is not Profile:
5010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise TypeError("Subclasses must override .calibrate().")
5020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        saved_bias = self.bias
5040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.bias = 0
5050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        try:
5060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return self._calibrate_inner(m, verbose)
5070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        finally:
5080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.bias = saved_bias
5090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def _calibrate_inner(self, m, verbose):
5110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        get_time = self.get_time
5120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Set up a test case to be run with and without profiling.  Include
5140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # lots of calls, because we're trying to quantify stopwatch overhead.
5150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Do not raise any exceptions, though, because we want to know
5160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # exactly how many profile events are generated (one call event, +
5170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # one return event, per Python-level call).
5180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        def f1(n):
5200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for i in range(n):
5210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                x = 1
5220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        def f(m, f1=f1):
5240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for i in range(m):
5250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                f1(100)
5260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f(m)    # warm up the cache
5280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # elapsed_noprofile <- time f(m) takes without profiling.
5300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t0 = get_time()
5310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        f(m)
5320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t1 = get_time()
5330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elapsed_noprofile = t1 - t0
5340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if verbose:
5350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            print "elapsed time without profiling =", elapsed_noprofile
5360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # elapsed_profile <- time f(m) takes with profiling.  The difference
5380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # is profiling overhead, only some of which the profiler subtracts
5390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # out on its own.
5400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        p = Profile()
5410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t0 = get_time()
5420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        p.runctx('f(m)', globals(), locals())
5430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        t1 = get_time()
5440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elapsed_profile = t1 - t0
5450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if verbose:
5460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            print "elapsed time with profiling =", elapsed_profile
5470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # reported_time <- "CPU seconds" the profiler charged to f and f1.
5490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        total_calls = 0.0
5500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        reported_time = 0.0
5510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for (filename, line, funcname), (cc, ns, tt, ct, callers) in \
5520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                p.timings.items():
5530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if funcname in ("f", "f1"):
5540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                total_calls += cc
5550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                reported_time += tt
5560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if verbose:
5580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            print "'CPU seconds' profiler reported =", reported_time
5590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            print "total # calls =", total_calls
5600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if total_calls != m + 1:
5610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise ValueError("internal error: total calls = %d" % total_calls)
5620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # reported_time - elapsed_noprofile = overhead the profiler wasn't
5640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # able to measure.  Divide by twice the number of calls (since there
5650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # are two profiler events per call in this test) to get the hidden
5660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # overhead per event.
5670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        mean = (reported_time - elapsed_noprofile) / 2.0 / total_calls
5680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if verbose:
5690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            print "mean stopwatch overhead per profile event =", mean
5700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return mean
5710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi#****************************************************************************
5730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef Stats(*args):
5740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    print 'Report generating functions are in the "pstats" module\a'
5750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yidef main():
5770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    usage = "profile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
5780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    parser = OptionParser(usage=usage)
5790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    parser.allow_interspersed_args = False
5800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    parser.add_option('-o', '--outfile', dest="outfile",
5810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        help="Save stats to <outfile>", default=None)
5820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    parser.add_option('-s', '--sort', dest="sort",
5830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        help="Sort order when printing to stdout, based on pstats.Stats class",
5840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        default=-1)
5850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    if not sys.argv[1:]:
5870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        parser.print_usage()
5880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        sys.exit(2)
5890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    (options, args) = parser.parse_args()
5910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    sys.argv[:] = args
5920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
5930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    if len(args) > 0:
5940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        progname = args[0]
5950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        sys.path.insert(0, os.path.dirname(progname))
5960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        with open(progname, 'rb') as fp:
5970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            code = compile(fp.read(), progname, 'exec')
5980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        globs = {
5990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            '__file__': progname,
6000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            '__name__': '__main__',
6010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            '__package__': None,
6020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        }
6030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        runctx(code, globs, None, options.outfile, options.sort)
6040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    else:
6050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        parser.print_usage()
6060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    return parser
6070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
6080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi# When invoked as main program, invoke the profiler on a script
6090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiif __name__ == '__main__':
6100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    main()
611