profile.py 23 KB
Newer Older
1
#! /usr/bin/env python
Guido van Rossum's avatar
Guido van Rossum committed
2
#
3
# Class for profiling python code. rev 1.0  6/2/94
Guido van Rossum's avatar
Guido van Rossum committed
4
#
5 6 7
# Based on prior profile module by Sjoerd Mullender...
#   which was hacked somewhat by: Guido van Rossum

8
"""Class for profiling Python code."""
9 10 11

# Copyright 1994, by InfoSeek Corporation, all rights reserved.
# Written by James Roskind
Tim Peters's avatar
Tim Peters committed
12
#
13 14 15 16 17 18 19 20 21 22 23 24
# Permission to use, copy, modify, and distribute this Python software
# and its associated documentation for any purpose (subject to the
# restriction in the following sentence) without fee is hereby granted,
# provided that the above copyright notice appears in all copies, and
# that both that copyright notice and this permission notice appear in
# supporting documentation, and that the name of InfoSeek not be used in
# advertising or publicity pertaining to distribution of the software
# without specific, written prior permission.  This permission is
# explicitly restricted to the copying and modification of the software
# to remain in Python, compiled Python, or other languages (such as C)
# wherein the modified or derived code is exclusively imported into a
# Python module.
Tim Peters's avatar
Tim Peters committed
25
#
26 27 28 29 30 31 32 33 34
# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY
# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


Guido van Rossum's avatar
Guido van Rossum committed
35 36

import sys
37
import os
38
import time
39
import marshal
40
from optparse import OptionParser
Guido van Rossum's avatar
Guido van Rossum committed
41

42
__all__ = ["run", "runctx", "help", "Profile"]
43

Tim Peters's avatar
Tim Peters committed
44
# Sample timer for use with
45 46
#i_count = 0
#def integer_timer():
Tim Peters's avatar
Tim Peters committed
47 48 49
#       global i_count
#       i_count = i_count + 1
#       return i_count
50 51 52 53 54 55 56
#itimes = integer_timer # replace with C coded timer returning integers

#**************************************************************************
# The following are the static member functions for the profiler class
# Note that an instance of Profile() is *not* needed to call them.
#**************************************************************************

57
def run(statement, filename=None, sort=-1):
58
    """Run statement under profiler optionally saving results in filename
59

60 61 62 63 64 65 66 67
    This function takes a single argument that can be passed to the
    "exec" statement, and an optional file name.  In all cases this
    routine attempts to "exec" its first argument and gather profiling
    statistics from the execution. If no file name is present, then this
    function automatically prints a simple profiling report, sorted by the
    standard name string (file/line/function-name) that is presented in
    each line.
    """
Tim Peters's avatar
Tim Peters committed
68 69 70 71 72
    prof = Profile()
    try:
        prof = prof.run(statement)
    except SystemExit:
        pass
73 74
    if filename is not None:
        prof.dump_stats(filename)
Tim Peters's avatar
Tim Peters committed
75
    else:
76
        return prof.print_stats(sort)
77

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
def runctx(statement, globals, locals, filename=None):
    """Run statement under profiler, supplying your own globals and locals,
    optionally saving results in filename.

    statement and filename have the same semantics as profile.run
    """
    prof = Profile()
    try:
        prof = prof.runctx(statement, globals, locals)
    except SystemExit:
        pass

    if filename is not None:
        prof.dump_stats(filename)
    else:
        return prof.print_stats()

95
# Backwards compatibility.
96
def help():
97 98
    print "Documentation for the profile module can be found "
    print "in the Python Library Reference, section 'The Python Profiler'."
99

100
if os.name == "mac":
101
    import MacOS
102 103 104 105 106 107 108 109
    def _get_time_mac(timer=MacOS.GetTicks):
        return timer() / 60.0

if hasattr(os, "times"):
    def _get_time_times(timer=os.times):
        t = timer()
        return t[0] + t[1]

110 111 112 113 114 115 116 117 118 119 120 121 122 123
# Using getrusage(3) is better than clock(3) if available:
# on some systems (e.g. FreeBSD), getrusage has a higher resolution
# Furthermore, on a POSIX system, returns microseconds, which
# wrap around after 36min.
_has_res = 0
try:
    import resource
    resgetrusage = lambda: resource.getrusage(resource.RUSAGE_SELF)
    def _get_time_resource(timer=resgetrusage):
        t = timer()
        return t[0] + t[1]
    _has_res = 1
except ImportError:
    pass
124

125
class Profile:
Tim Peters's avatar
Tim Peters committed
126 127 128 129 130 131 132 133
    """Profiler class.

    self.cur is always a tuple.  Each such tuple corresponds to a stack
    frame that is currently active (self.cur[-2]).  The following are the
    definitions of its members.  We use this external "parallel stack" to
    avoid contaminating the program that we are profiling. (old profiler
    used to write into the frames local dictionary!!) Derived classes
    can change the definition of some entries, as long as they leave
134 135 136 137 138 139 140
    [-2:] intact (frame and previous tuple).  In case an internal error is
    detected, the -3 element is used as the function name.

    [ 0] = Time that needs to be charged to the parent frame's function.
           It is used so that a function call will not have to access the
           timing data for the parent frame.
    [ 1] = Total time spent in this frame's function, excluding time in
141
           subfunctions (this latter is tallied in cur[2]).
Tim Peters's avatar
Tim Peters committed
142
    [ 2] = Total time spent in subfunctions, excluding time executing the
143
           frame's function (this latter is tallied in cur[1]).
144
    [-3] = Name of the function that corresponds to this frame.
145 146
    [-2] = Actual frame that we correspond to (used to sync exception handling).
    [-1] = Our parent 6-tuple (corresponds to frame.f_back).
Tim Peters's avatar
Tim Peters committed
147 148

    Timing data for each function is stored as a 5-tuple in the dictionary
149
    self.timings[].  The index is always the name stored in self.cur[-3].
Tim Peters's avatar
Tim Peters committed
150 151 152 153 154 155 156 157 158 159
    The following are the definitions of the members:

    [0] = The number of times this function was called, not counting direct
          or indirect recursion,
    [1] = Number of times this function appears on the stack, minus one
    [2] = Total time spent internal to this function
    [3] = Cumulative time that this function was present on the stack.  In
          non-recursive functions, this is the total execution time from start
          to finish of each invocation of a function, including time spent in
          all subfunctions.
160
    [4] = A dictionary indicating for each function name, the number of times
Tim Peters's avatar
Tim Peters committed
161 162 163
          it was called by us.
    """

164 165 166
    bias = 0  # calibration constant

    def __init__(self, timer=None, bias=None):
Tim Peters's avatar
Tim Peters committed
167 168 169
        self.timings = {}
        self.cur = None
        self.cmd = ""
170
        self.c_func_name = ""
Tim Peters's avatar
Tim Peters committed
171

172 173 174 175
        if bias is None:
            bias = self.bias
        self.bias = bias     # Materialize in local dict for lookup speed.

176 177 178 179 180 181
        if not timer:
            if _has_res:
                self.timer = resgetrusage
                self.dispatcher = self.trace_dispatch
                self.get_time = _get_time_resource
            elif os.name == 'mac':
Tim Peters's avatar
Tim Peters committed
182 183
                self.timer = MacOS.GetTicks
                self.dispatcher = self.trace_dispatch_mac
184
                self.get_time = _get_time_mac
Tim Peters's avatar
Tim Peters committed
185
            elif hasattr(time, 'clock'):
186
                self.timer = self.get_time = time.clock
Tim Peters's avatar
Tim Peters committed
187 188 189 190
                self.dispatcher = self.trace_dispatch_i
            elif hasattr(os, 'times'):
                self.timer = os.times
                self.dispatcher = self.trace_dispatch
191
                self.get_time = _get_time_times
Tim Peters's avatar
Tim Peters committed
192
            else:
193
                self.timer = self.get_time = time.time
Tim Peters's avatar
Tim Peters committed
194 195 196 197 198
                self.dispatcher = self.trace_dispatch_i
        else:
            self.timer = timer
            t = self.timer() # test out timer function
            try:
199 200 201 202 203 204
                length = len(t)
            except TypeError:
                self.get_time = timer
                self.dispatcher = self.trace_dispatch_i
            else:
                if length == 2:
Tim Peters's avatar
Tim Peters committed
205 206 207
                    self.dispatcher = self.trace_dispatch
                else:
                    self.dispatcher = self.trace_dispatch_l
208 209 210 211 212
                # This get_time() implementation needs to be defined
                # here to capture the passed-in timer in the parameter
                # list (for performance).  Note that we can't assume
                # the timer() result contains two values in all
                # cases.
213 214
                def get_time_timer(timer=timer, sum=sum):
                    return sum(timer())
215
                self.get_time = get_time_timer
Tim Peters's avatar
Tim Peters committed
216 217 218 219 220 221
        self.t = self.get_time()
        self.simulate_call('profiler')

    # Heavily optimized dispatch routine for os.times() timer

    def trace_dispatch(self, frame, event, arg):
222 223
        timer = self.timer
        t = timer()
224
        t = t[0] + t[1] - self.t - self.bias
Tim Peters's avatar
Tim Peters committed
225

226
        if event == "c_call":
227
            self.c_func_name = arg.__name__
228

229 230
        if self.dispatch[event](self, frame,t):
            t = timer()
Tim Peters's avatar
Tim Peters committed
231 232
            self.t = t[0] + t[1]
        else:
233
            r = timer()
Tim Peters's avatar
Tim Peters committed
234 235
            self.t = r[0] + r[1] - t # put back unrecorded delta

236 237
    # Dispatch routine for best timer program (return = scalar, fastest if
    # an integer but float works too -- and time.clock() relies on that).
Tim Peters's avatar
Tim Peters committed
238 239

    def trace_dispatch_i(self, frame, event, arg):
240
        timer = self.timer
241
        t = timer() - self.t - self.bias
242 243

        if event == "c_call":
244
            self.c_func_name = arg.__name__
245 246

        if self.dispatch[event](self, frame, t):
247
            self.t = timer()
Tim Peters's avatar
Tim Peters committed
248
        else:
249
            self.t = timer() - t  # put back unrecorded delta
Tim Peters's avatar
Tim Peters committed
250

251 252
    # Dispatch routine for macintosh (timer returns time in ticks of
    # 1/60th second)
Tim Peters's avatar
Tim Peters committed
253 254

    def trace_dispatch_mac(self, frame, event, arg):
255
        timer = self.timer
256
        t = timer()/60.0 - self.t - self.bias
257 258

        if event == "c_call":
259
            self.c_func_name = arg.__name__
260

261
        if self.dispatch[event](self, frame, t):
262
            self.t = timer()/60.0
Tim Peters's avatar
Tim Peters committed
263
        else:
264
            self.t = timer()/60.0 - t  # put back unrecorded delta
Tim Peters's avatar
Tim Peters committed
265 266 267 268

    # SLOW generic dispatch routine for timer returning lists of numbers

    def trace_dispatch_l(self, frame, event, arg):
269
        get_time = self.get_time
270
        t = get_time() - self.t - self.bias
Tim Peters's avatar
Tim Peters committed
271

272
        if event == "c_call":
273
            self.c_func_name = arg.__name__
274

275
        if self.dispatch[event](self, frame, t):
276
            self.t = get_time()
Tim Peters's avatar
Tim Peters committed
277
        else:
278
            self.t = get_time() - t # put back unrecorded delta
Tim Peters's avatar
Tim Peters committed
279

280 281 282 283 284 285
    # In the event handlers, the first 3 elements of self.cur are unpacked
    # into vrbls w/ 3-letter names.  The last two characters are meant to be
    # mnemonic:
    #     _pt  self.cur[0] "parent time"   time to be charged to parent frame
    #     _it  self.cur[1] "internal time" time spent directly in the function
    #     _et  self.cur[2] "external time" time spent in subfunctions
Tim Peters's avatar
Tim Peters committed
286 287

    def trace_dispatch_exception(self, frame, t):
288
        rpt, rit, ret, rfn, rframe, rcur = self.cur
289
        if (rframe is not frame) and rcur:
Tim Peters's avatar
Tim Peters committed
290
            return self.trace_dispatch_return(rframe, t)
291
        self.cur = rpt, rit+t, ret, rfn, rframe, rcur
292
        return 1
Tim Peters's avatar
Tim Peters committed
293 294 295


    def trace_dispatch_call(self, frame, t):
296
        if self.cur and frame.f_back is not self.cur[-2]:
297
            rpt, rit, ret, rfn, rframe, rcur = self.cur
298
            if not isinstance(rframe, Profile.fake_frame):
299 300 301
                assert rframe.f_back is frame.f_back, ("Bad call", rfn,
                                                       rframe, rframe.f_back,
                                                       frame, frame.f_back)
302
                self.trace_dispatch_return(rframe, 0)
303 304 305
                assert (self.cur is None or \
                        frame.f_back is self.cur[-2]), ("Bad call",
                                                        self.cur[-3])
Tim Peters's avatar
Tim Peters committed
306 307 308
        fcode = frame.f_code
        fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
        self.cur = (t, 0, 0, fn, frame, self.cur)
309
        timings = self.timings
310
        if fn in timings:
311 312
            cc, ns, tt, ct, callers = timings[fn]
            timings[fn] = cc, ns + 1, tt, ct, callers
Tim Peters's avatar
Tim Peters committed
313
        else:
314
            timings[fn] = 0, 0, 0, 0, {}
Tim Peters's avatar
Tim Peters committed
315 316
        return 1

317 318 319 320
    def trace_dispatch_c_call (self, frame, t):
        fn = ("", 0, self.c_func_name)
        self.cur = (t, 0, 0, fn, frame, self.cur)
        timings = self.timings
321
        if fn in timings:
322 323 324 325 326 327
            cc, ns, tt, ct, callers = timings[fn]
            timings[fn] = cc, ns+1, tt, ct, callers
        else:
            timings[fn] = 0, 0, 0, 0, {}
        return 1

Tim Peters's avatar
Tim Peters committed
328
    def trace_dispatch_return(self, frame, t):
329
        if frame is not self.cur[-2]:
330 331
            assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3])
            self.trace_dispatch_return(self.cur[-2], 0)
Tim Peters's avatar
Tim Peters committed
332

333 334
        # Prefix "r" means part of the Returning or exiting frame.
        # Prefix "p" means part of the Previous or Parent or older frame.
Tim Peters's avatar
Tim Peters committed
335

336 337 338
        rpt, rit, ret, rfn, frame, rcur = self.cur
        rit = rit + t
        frame_total = rit + ret
Tim Peters's avatar
Tim Peters committed
339

340 341
        ppt, pit, pet, pfn, pframe, pcur = rcur
        self.cur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur
Tim Peters's avatar
Tim Peters committed
342

343 344
        timings = self.timings
        cc, ns, tt, ct, callers = timings[rfn]
Tim Peters's avatar
Tim Peters committed
345
        if not ns:
346 347 348 349 350
            # This is the only occurrence of the function on the stack.
            # Else this is a (directly or indirectly) recursive call, and
            # its cumulative time will get updated when the topmost call to
            # it returns.
            ct = ct + frame_total
Tim Peters's avatar
Tim Peters committed
351
            cc = cc + 1
352

353
        if pfn in callers:
Tim Peters's avatar
Tim Peters committed
354 355 356 357 358 359
            callers[pfn] = callers[pfn] + 1  # hack: gather more
            # stats such as the amount of time added to ct courtesy
            # of this specific call, and the contribution to cc
            # courtesy of this call.
        else:
            callers[pfn] = 1
360 361

        timings[rfn] = cc, ns - 1, tt + rit, ct, callers
Tim Peters's avatar
Tim Peters committed
362 363 364

        return 1

365 366 367 368 369

    dispatch = {
        "call": trace_dispatch_call,
        "exception": trace_dispatch_exception,
        "return": trace_dispatch_return,
370
        "c_call": trace_dispatch_c_call,
371
        "c_exception": trace_dispatch_return,  # the C function returned
372
        "c_return": trace_dispatch_return,
373 374 375
        }


376
    # The next few functions play with self.cmd. By carefully preloading
Tim Peters's avatar
Tim Peters committed
377 378 379 380 381 382
    # our parallel stack, we can force the profiled result to include
    # an arbitrary string as the name of the calling function.
    # We use self.cmd as that string, and the resulting stats look
    # very nice :-).

    def set_cmd(self, cmd):
383
        if self.cur[-1]: return   # already set
Tim Peters's avatar
Tim Peters committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
        self.cmd = cmd
        self.simulate_call(cmd)

    class fake_code:
        def __init__(self, filename, line, name):
            self.co_filename = filename
            self.co_line = line
            self.co_name = name
            self.co_firstlineno = 0

        def __repr__(self):
            return repr((self.co_filename, self.co_line, self.co_name))

    class fake_frame:
        def __init__(self, code, prior):
            self.f_code = code
            self.f_back = prior

    def simulate_call(self, name):
        code = self.fake_code('profile', 0, name)
        if self.cur:
405
            pframe = self.cur[-2]
Tim Peters's avatar
Tim Peters committed
406 407 408
        else:
            pframe = None
        frame = self.fake_frame(code, pframe)
409
        self.dispatch['call'](self, frame, 0)
Tim Peters's avatar
Tim Peters committed
410 411 412 413 414

    # collect stats from pending stack, including getting final
    # timings for self.cmd frame.

    def simulate_cmd_complete(self):
415 416
        get_time = self.get_time
        t = get_time() - self.t
417
        while self.cur[-1]:
Tim Peters's avatar
Tim Peters committed
418 419
            # We *can* cause assertion errors here if
            # dispatch_trace_return checks for a frame match!
420
            self.dispatch['return'](self, self.cur[-2], t)
Tim Peters's avatar
Tim Peters committed
421
            t = 0
422
        self.t = get_time() - t
Tim Peters's avatar
Tim Peters committed
423 424


425
    def print_stats(self, sort=-1):
Tim Peters's avatar
Tim Peters committed
426
        import pstats
427
        pstats.Stats(self).strip_dirs().sort_stats(sort). \
Tim Peters's avatar
Tim Peters committed
428 429 430 431 432 433 434 435 436 437 438 439 440 441
                  print_stats()

    def dump_stats(self, file):
        f = open(file, 'wb')
        self.create_stats()
        marshal.dump(self.stats, f)
        f.close()

    def create_stats(self):
        self.simulate_cmd_complete()
        self.snapshot_stats()

    def snapshot_stats(self):
        self.stats = {}
442
        for func, (cc, ns, tt, ct, callers) in self.timings.iteritems():
Tim Peters's avatar
Tim Peters committed
443 444
            callers = callers.copy()
            nc = 0
445 446
            for callcnt in callers.itervalues():
                nc += callcnt
Tim Peters's avatar
Tim Peters committed
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
            self.stats[func] = cc, nc, tt, ct, callers


    # The following two methods can be called by clients to use
    # a profiler to profile a statement, given as a string.

    def run(self, cmd):
        import __main__
        dict = __main__.__dict__
        return self.runctx(cmd, dict, dict)

    def runctx(self, cmd, globals, locals):
        self.set_cmd(cmd)
        sys.setprofile(self.dispatcher)
        try:
            exec cmd in globals, locals
        finally:
            sys.setprofile(None)
        return self

    # This method is more useful to profile a single function call.
468
    def runcall(self, func, *args, **kw):
469
        self.set_cmd(repr(func))
Tim Peters's avatar
Tim Peters committed
470 471
        sys.setprofile(self.dispatcher)
        try:
472
            return func(*args, **kw)
Tim Peters's avatar
Tim Peters committed
473 474 475 476 477 478 479 480 481 482 483
        finally:
            sys.setprofile(None)


    #******************************************************************
    # The following calculates the overhead for using a profiler.  The
    # problem is that it takes a fair amount of time for the profiler
    # to stop the stopwatch (from the time it receives an event).
    # Similarly, there is a delay from the time that the profiler
    # re-starts the stopwatch before the user's code really gets to
    # continue.  The following code tries to measure the difference on
484 485 486
    # a per-event basis.
    #
    # Note that this difference is only significant if there are a lot of
Tim Peters's avatar
Tim Peters committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
    # events, and relatively little user code per event.  For example,
    # code with small functions will typically benefit from having the
    # profiler calibrated for the current platform.  This *could* be
    # done on the fly during init() time, but it is not worth the
    # effort.  Also note that if too large a value specified, then
    # execution time on some functions will actually appear as a
    # negative number.  It is *normal* for some functions (with very
    # low call counts) to have such negative stats, even if the
    # calibration figure is "correct."
    #
    # One alternative to profile-time calibration adjustments (i.e.,
    # adding in the magic little delta during each event) is to track
    # more carefully the number of events (and cumulatively, the number
    # of events during sub functions) that are seen.  If this were
    # done, then the arithmetic could be done after the fact (i.e., at
    # display time).  Currently, we track only call/return events.
    # These values can be deduced by examining the callees and callers
    # vectors for each functions.  Hence we *can* almost correct the
    # internal time figure at print time (note that we currently don't
    # track exception event processing counts).  Unfortunately, there
    # is currently no similar information for cumulative sub-function
    # time.  It would not be hard to "get all this info" at profiler
    # time.  Specifically, we would have to extend the tuples to keep
    # counts of this in each frame, and then extend the defs of timing
    # tuples to include the significant two figures. I'm a bit fearful
    # that this additional feature will slow the heavily optimized
    # event/time ratio (i.e., the profiler would run slower, fur a very
    # low "value added" feature.)
    #**************************************************************

517
    def calibrate(self, m, verbose=0):
518 519 520 521 522 523
        if self.__class__ is not Profile:
            raise TypeError("Subclasses must override .calibrate().")

        saved_bias = self.bias
        self.bias = 0
        try:
524
            return self._calibrate_inner(m, verbose)
525 526 527
        finally:
            self.bias = saved_bias

528
    def _calibrate_inner(self, m, verbose):
529
        get_time = self.get_time
Tim Peters's avatar
Tim Peters committed
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
        # Set up a test case to be run with and without profiling.  Include
        # lots of calls, because we're trying to quantify stopwatch overhead.
        # Do not raise any exceptions, though, because we want to know
        # exactly how many profile events are generated (one call event, +
        # one return event, per Python-level call).

        def f1(n):
            for i in range(n):
                x = 1

        def f(m, f1=f1):
            for i in range(m):
                f1(100)

        f(m)    # warm up the cache

        # elapsed_noprofile <- time f(m) takes without profiling.
        t0 = get_time()
        f(m)
        t1 = get_time()
        elapsed_noprofile = t1 - t0
        if verbose:
            print "elapsed time without profiling =", elapsed_noprofile

        # elapsed_profile <- time f(m) takes with profiling.  The difference
        # is profiling overhead, only some of which the profiler subtracts
        # out on its own.
        p = Profile()
        t0 = get_time()
        p.runctx('f(m)', globals(), locals())
        t1 = get_time()
        elapsed_profile = t1 - t0
        if verbose:
            print "elapsed time with profiling =", elapsed_profile

        # reported_time <- "CPU seconds" the profiler charged to f and f1.
        total_calls = 0.0
        reported_time = 0.0
        for (filename, line, funcname), (cc, ns, tt, ct, callers) in \
                p.timings.items():
            if funcname in ("f", "f1"):
                total_calls += cc
                reported_time += tt

        if verbose:
            print "'CPU seconds' profiler reported =", reported_time
            print "total # calls =", total_calls
        if total_calls != m + 1:
            raise ValueError("internal error: total calls = %d" % total_calls)

        # reported_time - elapsed_noprofile = overhead the profiler wasn't
        # able to measure.  Divide by twice the number of calls (since there
        # are two profiler events per call in this test) to get the hidden
        # overhead per event.
        mean = (reported_time - elapsed_noprofile) / 2.0 / total_calls
        if verbose:
            print "mean stopwatch overhead per profile event =", mean
        return mean
589 590 591

#****************************************************************************
def Stats(*args):
Tim Peters's avatar
Tim Peters committed
592
    print 'Report generating functions are in the "pstats" module\a'
593

594
def main():
595
    usage = "profile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
596
    parser = OptionParser(usage=usage)
597
    parser.allow_interspersed_args = False
Tim Peters's avatar
Tim Peters committed
598
    parser.add_option('-o', '--outfile', dest="outfile",
599 600 601
        help="Save stats to <outfile>", default=None)
    parser.add_option('-s', '--sort', dest="sort",
        help="Sort order when printing to stdout, based on pstats.Stats class", default=-1)
Tim Peters's avatar
Tim Peters committed
602

603 604 605
    if not sys.argv[1:]:
        parser.print_usage()
        sys.exit(2)
Tim Peters's avatar
Tim Peters committed
606

607
    (options, args) = parser.parse_args()
Tim Peters's avatar
Tim Peters committed
608

609 610
    if (len(args) > 0):
        sys.argv[:] = args
611 612 613
        sys.path.insert(0, os.path.dirname(sys.argv[0]))
        run('execfile(%r)' % (sys.argv[0],), options.outfile, options.sort)
    else:
614 615 616 617 618 619
        parser.print_usage()
    return parser

# When invoked as main program, invoke the profiler on a script
if __name__ == '__main__':
    main()