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