1ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#! /usr/bin/env python
2ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
3ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Class for profiling python code. rev 1.0  6/2/94
4ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
5ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Written by James Roskind
6ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Based on prior profile module by Sjoerd Mullender...
7ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#   which was hacked somewhat by: Guido van Rossum
8ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
9ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh"""Class for profiling Python code."""
10ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
11ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Copyright Disney Enterprises, Inc.  All Rights Reserved.
12ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Licensed to PSF under a Contributor Agreement
13ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
14ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Licensed under the Apache License, Version 2.0 (the "License");
15ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# you may not use this file except in compliance with the License.
16ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# You may obtain a copy of the License at
17ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
18ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# http://www.apache.org/licenses/LICENSE-2.0
19ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#
20ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Unless required by applicable law or agreed to in writing, software
21ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# distributed under the License is distributed on an "AS IS" BASIS,
22ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
23ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# either express or implied.  See the License for the specific language
24ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# governing permissions and limitations under the License.
25ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
26ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
27ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport sys
28ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport os
29ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport time
30ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport marshal
31ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom optparse import OptionParser
32ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
33ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh__all__ = ["run", "runctx", "help", "Profile"]
34ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
35ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Sample timer for use with
36ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#i_count = 0
37ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#def integer_timer():
38ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#       global i_count
39ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#       i_count = i_count + 1
40ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#       return i_count
41ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#itimes = integer_timer # replace with C coded timer returning integers
42ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
43ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#**************************************************************************
44ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# The following are the static member functions for the profiler class
45ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Note that an instance of Profile() is *not* needed to call them.
46ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#**************************************************************************
47ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
48ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef run(statement, filename=None, sort=-1):
49ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Run statement under profiler optionally saving results in filename
50ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
51ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    This function takes a single argument that can be passed to the
52ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    "exec" statement, and an optional file name.  In all cases this
53ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    routine attempts to "exec" its first argument and gather profiling
54ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    statistics from the execution. If no file name is present, then this
55ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    function automatically prints a simple profiling report, sorted by the
56ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    standard name string (file/line/function-name) that is presented in
57ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    each line.
58ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """
59ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    prof = Profile()
60ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    try:
61ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        prof = prof.run(statement)
62ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    except SystemExit:
63ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        pass
64ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if filename is not None:
65ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        prof.dump_stats(filename)
66ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    else:
67ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return prof.print_stats(sort)
68ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
69ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef runctx(statement, globals, locals, filename=None, sort=-1):
70ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Run statement under profiler, supplying your own globals and locals,
71ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    optionally saving results in filename.
72ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
73ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    statement and filename have the same semantics as profile.run
74ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """
75ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    prof = Profile()
76ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    try:
77ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        prof = prof.runctx(statement, globals, locals)
78ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    except SystemExit:
79ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        pass
80ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
81ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if filename is not None:
82ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        prof.dump_stats(filename)
83ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    else:
84ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return prof.print_stats(sort)
85ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
86ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Backwards compatibility.
87ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef help():
88ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    print "Documentation for the profile module can be found "
89ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    print "in the Python Library Reference, section 'The Python Profiler'."
90ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
91ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehif hasattr(os, "times"):
92ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _get_time_times(timer=os.times):
93ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = timer()
94ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return t[0] + t[1]
95ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
96ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Using getrusage(3) is better than clock(3) if available:
97ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# on some systems (e.g. FreeBSD), getrusage has a higher resolution
98ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Furthermore, on a POSIX system, returns microseconds, which
99ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# wrap around after 36min.
100ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh_has_res = 0
101ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehtry:
102ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    import resource
103ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    resgetrusage = lambda: resource.getrusage(resource.RUSAGE_SELF)
104ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _get_time_resource(timer=resgetrusage):
105ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = timer()
106ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return t[0] + t[1]
107ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    _has_res = 1
108ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehexcept ImportError:
109ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    pass
110ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
111ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass Profile:
112ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """Profiler class.
113ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
114ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    self.cur is always a tuple.  Each such tuple corresponds to a stack
115ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    frame that is currently active (self.cur[-2]).  The following are the
116ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    definitions of its members.  We use this external "parallel stack" to
117ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    avoid contaminating the program that we are profiling. (old profiler
118ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    used to write into the frames local dictionary!!) Derived classes
119ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    can change the definition of some entries, as long as they leave
120ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [-2:] intact (frame and previous tuple).  In case an internal error is
121ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    detected, the -3 element is used as the function name.
122ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
123ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [ 0] = Time that needs to be charged to the parent frame's function.
124ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh           It is used so that a function call will not have to access the
125ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh           timing data for the parent frame.
126ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [ 1] = Total time spent in this frame's function, excluding time in
127ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh           subfunctions (this latter is tallied in cur[2]).
128ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [ 2] = Total time spent in subfunctions, excluding time executing the
129ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh           frame's function (this latter is tallied in cur[1]).
130ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [-3] = Name of the function that corresponds to this frame.
131ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [-2] = Actual frame that we correspond to (used to sync exception handling).
132ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [-1] = Our parent 6-tuple (corresponds to frame.f_back).
133ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
134ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    Timing data for each function is stored as a 5-tuple in the dictionary
135ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    self.timings[].  The index is always the name stored in self.cur[-3].
136ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    The following are the definitions of the members:
137ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
138ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [0] = The number of times this function was called, not counting direct
139ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh          or indirect recursion,
140ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [1] = Number of times this function appears on the stack, minus one
141ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [2] = Total time spent internal to this function
142ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [3] = Cumulative time that this function was present on the stack.  In
143ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh          non-recursive functions, this is the total execution time from start
144ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh          to finish of each invocation of a function, including time spent in
145ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh          all subfunctions.
146ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    [4] = A dictionary indicating for each function name, the number of times
147ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh          it was called by us.
148ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    """
149ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
150ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    bias = 0  # calibration constant
151ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
152ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def __init__(self, timer=None, bias=None):
153ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.timings = {}
154ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cur = None
155ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cmd = ""
156ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.c_func_name = ""
157ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
158ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if bias is None:
159ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bias = self.bias
160ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.bias = bias     # Materialize in local dict for lookup speed.
161ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
162ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not timer:
163ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if _has_res:
164ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.timer = resgetrusage
165ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.dispatcher = self.trace_dispatch
166ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.get_time = _get_time_resource
167ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            elif hasattr(time, 'clock'):
168ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.timer = self.get_time = time.clock
169ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.dispatcher = self.trace_dispatch_i
170ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            elif hasattr(os, 'times'):
171ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.timer = os.times
172ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.dispatcher = self.trace_dispatch
173ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.get_time = _get_time_times
174ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
175ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.timer = self.get_time = time.time
176ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.dispatcher = self.trace_dispatch_i
177ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
178ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.timer = timer
179ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            t = self.timer() # test out timer function
180ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            try:
181ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                length = len(t)
182ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            except TypeError:
183ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.get_time = timer
184ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.dispatcher = self.trace_dispatch_i
185ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
186ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if length == 2:
187ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    self.dispatcher = self.trace_dispatch
188ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                else:
189ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    self.dispatcher = self.trace_dispatch_l
190ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # This get_time() implementation needs to be defined
191ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # here to capture the passed-in timer in the parameter
192ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # list (for performance).  Note that we can't assume
193ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # the timer() result contains two values in all
194ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # cases.
195ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                def get_time_timer(timer=timer, sum=sum):
196ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    return sum(timer())
197ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.get_time = get_time_timer
198ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.t = self.get_time()
199ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.simulate_call('profiler')
200ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
201ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # Heavily optimized dispatch routine for os.times() timer
202ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
203ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch(self, frame, event, arg):
204ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timer = self.timer
205ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = timer()
206ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = t[0] + t[1] - self.t - self.bias
207ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
208ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if event == "c_call":
209ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.c_func_name = arg.__name__
210ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
211ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.dispatch[event](self, frame,t):
212ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            t = timer()
213ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = t[0] + t[1]
214ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
215ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            r = timer()
216ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = r[0] + r[1] - t # put back unrecorded delta
217ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
218ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # Dispatch routine for best timer program (return = scalar, fastest if
219ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # an integer but float works too -- and time.clock() relies on that).
220ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
221ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_i(self, frame, event, arg):
222ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timer = self.timer
223ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = timer() - self.t - self.bias
224ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
225ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if event == "c_call":
226ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.c_func_name = arg.__name__
227ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
228ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.dispatch[event](self, frame, t):
229ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = timer()
230ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
231ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = timer() - t  # put back unrecorded delta
232ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
233ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # Dispatch routine for macintosh (timer returns time in ticks of
234ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # 1/60th second)
235ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
236ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_mac(self, frame, event, arg):
237ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timer = self.timer
238ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = timer()/60.0 - self.t - self.bias
239ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
240ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if event == "c_call":
241ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.c_func_name = arg.__name__
242ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
243ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.dispatch[event](self, frame, t):
244ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = timer()/60.0
245ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
246ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = timer()/60.0 - t  # put back unrecorded delta
247ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
248ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # SLOW generic dispatch routine for timer returning lists of numbers
249ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
250ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_l(self, frame, event, arg):
251ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        get_time = self.get_time
252ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = get_time() - self.t - self.bias
253ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
254ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if event == "c_call":
255ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.c_func_name = arg.__name__
256ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
257ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.dispatch[event](self, frame, t):
258ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = get_time()
259ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
260ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.t = get_time() - t # put back unrecorded delta
261ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
262ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # In the event handlers, the first 3 elements of self.cur are unpacked
263ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # into vrbls w/ 3-letter names.  The last two characters are meant to be
264ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # mnemonic:
265ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #     _pt  self.cur[0] "parent time"   time to be charged to parent frame
266ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #     _it  self.cur[1] "internal time" time spent directly in the function
267ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #     _et  self.cur[2] "external time" time spent in subfunctions
268ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
269ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_exception(self, frame, t):
270ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        rpt, rit, ret, rfn, rframe, rcur = self.cur
271ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if (rframe is not frame) and rcur:
272ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return self.trace_dispatch_return(rframe, t)
273ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cur = rpt, rit+t, ret, rfn, rframe, rcur
274ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return 1
275ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
276ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
277ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_call(self, frame, t):
278ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.cur and frame.f_back is not self.cur[-2]:
279ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            rpt, rit, ret, rfn, rframe, rcur = self.cur
280ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if not isinstance(rframe, Profile.fake_frame):
281ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                assert rframe.f_back is frame.f_back, ("Bad call", rfn,
282ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                                       rframe, rframe.f_back,
283ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                                       frame, frame.f_back)
284ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                self.trace_dispatch_return(rframe, 0)
285ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                assert (self.cur is None or \
286ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                        frame.f_back is self.cur[-2]), ("Bad call",
287ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                                        self.cur[-3])
288ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        fcode = frame.f_code
289ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
290ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cur = (t, 0, 0, fn, frame, self.cur)
291ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timings = self.timings
292ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if fn in timings:
293ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            cc, ns, tt, ct, callers = timings[fn]
294ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            timings[fn] = cc, ns + 1, tt, ct, callers
295ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
296ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            timings[fn] = 0, 0, 0, 0, {}
297ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return 1
298ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
299ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_c_call (self, frame, t):
300ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        fn = ("", 0, self.c_func_name)
301ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cur = (t, 0, 0, fn, frame, self.cur)
302ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timings = self.timings
303ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if fn in timings:
304ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            cc, ns, tt, ct, callers = timings[fn]
305ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            timings[fn] = cc, ns+1, tt, ct, callers
306ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
307ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            timings[fn] = 0, 0, 0, 0, {}
308ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return 1
309ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
310ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def trace_dispatch_return(self, frame, t):
311ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if frame is not self.cur[-2]:
312ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3])
313ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.trace_dispatch_return(self.cur[-2], 0)
314ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
315ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Prefix "r" means part of the Returning or exiting frame.
316ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Prefix "p" means part of the Previous or Parent or older frame.
317ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
318ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        rpt, rit, ret, rfn, frame, rcur = self.cur
319ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        rit = rit + t
320ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        frame_total = rit + ret
321ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
322ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        ppt, pit, pet, pfn, pframe, pcur = rcur
323ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur
324ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
325ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timings = self.timings
326ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        cc, ns, tt, ct, callers = timings[rfn]
327ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not ns:
328ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # This is the only occurrence of the function on the stack.
329ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # Else this is a (directly or indirectly) recursive call, and
330ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # its cumulative time will get updated when the topmost call to
331ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # it returns.
332ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            ct = ct + frame_total
333ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            cc = cc + 1
334ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
335ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if pfn in callers:
336ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            callers[pfn] = callers[pfn] + 1  # hack: gather more
337ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # stats such as the amount of time added to ct courtesy
338ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # of this specific call, and the contribution to cc
339ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # courtesy of this call.
340ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
341ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            callers[pfn] = 1
342ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
343ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        timings[rfn] = cc, ns - 1, tt + rit, ct, callers
344ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
345ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return 1
346ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
347ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
348ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    dispatch = {
349ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        "call": trace_dispatch_call,
350ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        "exception": trace_dispatch_exception,
351ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        "return": trace_dispatch_return,
352ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        "c_call": trace_dispatch_c_call,
353ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        "c_exception": trace_dispatch_return,  # the C function returned
354ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        "c_return": trace_dispatch_return,
355ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        }
356ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
357ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
358ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # The next few functions play with self.cmd. By carefully preloading
359ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # our parallel stack, we can force the profiled result to include
360ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # an arbitrary string as the name of the calling function.
361ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # We use self.cmd as that string, and the resulting stats look
362ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # very nice :-).
363ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
364ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def set_cmd(self, cmd):
365ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.cur[-1]: return   # already set
366ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.cmd = cmd
367ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.simulate_call(cmd)
368ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
369ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    class fake_code:
370ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        def __init__(self, filename, line, name):
371ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.co_filename = filename
372ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.co_line = line
373ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.co_name = name
374ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.co_firstlineno = 0
375ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
376ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        def __repr__(self):
377ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return repr((self.co_filename, self.co_line, self.co_name))
378ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
379ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    class fake_frame:
380ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        def __init__(self, code, prior):
381ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.f_code = code
382ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.f_back = prior
383ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
384ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def simulate_call(self, name):
385ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        code = self.fake_code('profile', 0, name)
386ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.cur:
387ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            pframe = self.cur[-2]
388ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
389ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            pframe = None
390ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        frame = self.fake_frame(code, pframe)
391ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.dispatch['call'](self, frame, 0)
392ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
393ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # collect stats from pending stack, including getting final
394ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # timings for self.cmd frame.
395ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
396ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def simulate_cmd_complete(self):
397ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        get_time = self.get_time
398ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t = get_time() - self.t
399ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        while self.cur[-1]:
400ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # We *can* cause assertion errors here if
401ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # dispatch_trace_return checks for a frame match!
402ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.dispatch['return'](self, self.cur[-2], t)
403ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            t = 0
404ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.t = get_time() - t
405ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
406ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
407ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def print_stats(self, sort=-1):
408ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        import pstats
409ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        pstats.Stats(self).strip_dirs().sort_stats(sort). \
410ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                  print_stats()
411ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
412ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def dump_stats(self, file):
413ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        f = open(file, 'wb')
414ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.create_stats()
415ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        marshal.dump(self.stats, f)
416ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        f.close()
417ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
418ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def create_stats(self):
419ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.simulate_cmd_complete()
420ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.snapshot_stats()
421ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
422ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def snapshot_stats(self):
423ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.stats = {}
424ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for func, (cc, ns, tt, ct, callers) in self.timings.iteritems():
425ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            callers = callers.copy()
426ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            nc = 0
427ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for callcnt in callers.itervalues():
428ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                nc += callcnt
429ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.stats[func] = cc, nc, tt, ct, callers
430ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
431ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
432ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # The following two methods can be called by clients to use
433ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # a profiler to profile a statement, given as a string.
434ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
435ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def run(self, cmd):
436ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        import __main__
437ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        dict = __main__.__dict__
438ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self.runctx(cmd, dict, dict)
439ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
440ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def runctx(self, cmd, globals, locals):
441ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.set_cmd(cmd)
442ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.setprofile(self.dispatcher)
443ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
444ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            exec cmd in globals, locals
445ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
446ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sys.setprofile(None)
447ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return self
448ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
449ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # This method is more useful to profile a single function call.
450ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def runcall(self, func, *args, **kw):
451ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.set_cmd(repr(func))
452ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.setprofile(self.dispatcher)
453ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
454ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return func(*args, **kw)
455ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
456ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sys.setprofile(None)
457ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
458ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
459ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #******************************************************************
460ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # The following calculates the overhead for using a profiler.  The
461ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # problem is that it takes a fair amount of time for the profiler
462ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # to stop the stopwatch (from the time it receives an event).
463ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # Similarly, there is a delay from the time that the profiler
464ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # re-starts the stopwatch before the user's code really gets to
465ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # continue.  The following code tries to measure the difference on
466ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # a per-event basis.
467ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #
468ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # Note that this difference is only significant if there are a lot of
469ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # events, and relatively little user code per event.  For example,
470ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # code with small functions will typically benefit from having the
471ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # profiler calibrated for the current platform.  This *could* be
472ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # done on the fly during init() time, but it is not worth the
473ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # effort.  Also note that if too large a value specified, then
474ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # execution time on some functions will actually appear as a
475ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # negative number.  It is *normal* for some functions (with very
476ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # low call counts) to have such negative stats, even if the
477ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # calibration figure is "correct."
478ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #
479ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # One alternative to profile-time calibration adjustments (i.e.,
480ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # adding in the magic little delta during each event) is to track
481ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # more carefully the number of events (and cumulatively, the number
482ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # of events during sub functions) that are seen.  If this were
483ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # done, then the arithmetic could be done after the fact (i.e., at
484ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # display time).  Currently, we track only call/return events.
485ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # These values can be deduced by examining the callees and callers
486ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # vectors for each functions.  Hence we *can* almost correct the
487ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # internal time figure at print time (note that we currently don't
488ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # track exception event processing counts).  Unfortunately, there
489ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # is currently no similar information for cumulative sub-function
490ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # time.  It would not be hard to "get all this info" at profiler
491ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # time.  Specifically, we would have to extend the tuples to keep
492ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # counts of this in each frame, and then extend the defs of timing
493ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # tuples to include the significant two figures. I'm a bit fearful
494ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # that this additional feature will slow the heavily optimized
495ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # event/time ratio (i.e., the profiler would run slower, fur a very
496ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # low "value added" feature.)
497ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    #**************************************************************
498ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
499ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def calibrate(self, m, verbose=0):
500ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.__class__ is not Profile:
501ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            raise TypeError("Subclasses must override .calibrate().")
502ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
503ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        saved_bias = self.bias
504ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.bias = 0
505ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
506ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return self._calibrate_inner(m, verbose)
507ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
508ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.bias = saved_bias
509ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
510ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def _calibrate_inner(self, m, verbose):
511ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        get_time = self.get_time
512ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
513ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Set up a test case to be run with and without profiling.  Include
514ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # lots of calls, because we're trying to quantify stopwatch overhead.
515ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Do not raise any exceptions, though, because we want to know
516ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # exactly how many profile events are generated (one call event, +
517ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # one return event, per Python-level call).
518ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
519ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        def f1(n):
520ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for i in range(n):
521ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                x = 1
522ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
523ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        def f(m, f1=f1):
524ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for i in range(m):
525ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                f1(100)
526ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
527ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        f(m)    # warm up the cache
528ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
529ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # elapsed_noprofile <- time f(m) takes without profiling.
530ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t0 = get_time()
531ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        f(m)
532ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t1 = get_time()
533ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elapsed_noprofile = t1 - t0
534ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if verbose:
535ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "elapsed time without profiling =", elapsed_noprofile
536ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
537ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # elapsed_profile <- time f(m) takes with profiling.  The difference
538ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # is profiling overhead, only some of which the profiler subtracts
539ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # out on its own.
540ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        p = Profile()
541ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t0 = get_time()
542ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        p.runctx('f(m)', globals(), locals())
543ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        t1 = get_time()
544ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        elapsed_profile = t1 - t0
545ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if verbose:
546ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "elapsed time with profiling =", elapsed_profile
547ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
548ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # reported_time <- "CPU seconds" the profiler charged to f and f1.
549ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        total_calls = 0.0
550ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        reported_time = 0.0
551ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for (filename, line, funcname), (cc, ns, tt, ct, callers) in \
552ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                p.timings.items():
553ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if funcname in ("f", "f1"):
554ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                total_calls += cc
555ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                reported_time += tt
556ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
557ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if verbose:
558ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "'CPU seconds' profiler reported =", reported_time
559ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "total # calls =", total_calls
560ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if total_calls != m + 1:
561ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            raise ValueError("internal error: total calls = %d" % total_calls)
562ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
563ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # reported_time - elapsed_noprofile = overhead the profiler wasn't
564ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # able to measure.  Divide by twice the number of calls (since there
565ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # are two profiler events per call in this test) to get the hidden
566ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # overhead per event.
567ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        mean = (reported_time - elapsed_noprofile) / 2.0 / total_calls
568ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if verbose:
569ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            print "mean stopwatch overhead per profile event =", mean
570ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return mean
571ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
572ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh#****************************************************************************
573ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef Stats(*args):
574ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    print 'Report generating functions are in the "pstats" module\a'
575ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
576ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehdef main():
577ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    usage = "profile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
578ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    parser = OptionParser(usage=usage)
579ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    parser.allow_interspersed_args = False
580ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    parser.add_option('-o', '--outfile', dest="outfile",
581ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        help="Save stats to <outfile>", default=None)
582ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    parser.add_option('-s', '--sort', dest="sort",
583ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        help="Sort order when printing to stdout, based on pstats.Stats class",
584ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        default=-1)
585ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
586ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if not sys.argv[1:]:
587ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        parser.print_usage()
588ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.exit(2)
589ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
590ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    (options, args) = parser.parse_args()
591ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    sys.argv[:] = args
592ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
593ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    if len(args) > 0:
594ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        progname = args[0]
595ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.path.insert(0, os.path.dirname(progname))
596ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        with open(progname, 'rb') as fp:
597ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            code = compile(fp.read(), progname, 'exec')
598ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        globs = {
599ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            '__file__': progname,
600ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            '__name__': '__main__',
601ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            '__package__': None,
602ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        }
603ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        runctx(code, globs, None, options.outfile, options.sort)
604ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    else:
605ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        parser.print_usage()
606ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    return parser
607ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
608ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# When invoked as main program, invoke the profiler on a script
609ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehif __name__ == '__main__':
610ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    main()
611