profile.py 22.8 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", "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
if os.name == "mac":
96
    import MacOS
97 98 99 100 101 102 103 104
    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]

105 106 107 108 109 110 111 112 113 114 115 116 117 118
# 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
119

120
class Profile:
Tim Peters's avatar
Tim Peters committed
121 122 123 124 125 126 127 128
    """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
129 130 131 132 133 134 135
    [-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
136
           subfunctions (this latter is tallied in cur[2]).
Tim Peters's avatar
Tim Peters committed
137
    [ 2] = Total time spent in subfunctions, excluding time executing the
138
           frame's function (this latter is tallied in cur[1]).
139
    [-3] = Name of the function that corresponds to this frame.
140 141
    [-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
142 143

    Timing data for each function is stored as a 5-tuple in the dictionary
144
    self.timings[].  The index is always the name stored in self.cur[-3].
Tim Peters's avatar
Tim Peters committed
145 146 147 148 149 150 151 152 153 154
    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.
155
    [4] = A dictionary indicating for each function name, the number of times
Tim Peters's avatar
Tim Peters committed
156 157 158
          it was called by us.
    """

159 160 161
    bias = 0  # calibration constant

    def __init__(self, timer=None, bias=None):
Tim Peters's avatar
Tim Peters committed
162 163 164
        self.timings = {}
        self.cur = None
        self.cmd = ""
165
        self.c_func_name = ""
Tim Peters's avatar
Tim Peters committed
166

167 168 169 170
        if bias is None:
            bias = self.bias
        self.bias = bias     # Materialize in local dict for lookup speed.

171 172 173 174 175 176
        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
177 178
                self.timer = MacOS.GetTicks
                self.dispatcher = self.trace_dispatch_mac
179
                self.get_time = _get_time_mac
Tim Peters's avatar
Tim Peters committed
180
            elif hasattr(time, 'clock'):
181
                self.timer = self.get_time = time.clock
Tim Peters's avatar
Tim Peters committed
182 183 184 185
                self.dispatcher = self.trace_dispatch_i
            elif hasattr(os, 'times'):
                self.timer = os.times
                self.dispatcher = self.trace_dispatch
186
                self.get_time = _get_time_times
Tim Peters's avatar
Tim Peters committed
187
            else:
188
                self.timer = self.get_time = time.time
Tim Peters's avatar
Tim Peters committed
189 190 191 192 193
                self.dispatcher = self.trace_dispatch_i
        else:
            self.timer = timer
            t = self.timer() # test out timer function
            try:
194 195 196 197 198 199
                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
200 201 202
                    self.dispatcher = self.trace_dispatch
                else:
                    self.dispatcher = self.trace_dispatch_l
203 204 205 206 207
                # 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.
208 209
                def get_time_timer(timer=timer, sum=sum):
                    return sum(timer())
210
                self.get_time = get_time_timer
Tim Peters's avatar
Tim Peters committed
211 212 213 214 215 216
        self.t = self.get_time()
        self.simulate_call('profiler')

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

    def trace_dispatch(self, frame, event, arg):
217 218
        timer = self.timer
        t = timer()
219
        t = t[0] + t[1] - self.t - self.bias
Tim Peters's avatar
Tim Peters committed
220

221
        if event == "c_call":
222
            self.c_func_name = arg.__name__
223

224 225
        if self.dispatch[event](self, frame,t):
            t = timer()
Tim Peters's avatar
Tim Peters committed
226 227
            self.t = t[0] + t[1]
        else:
228
            r = timer()
Tim Peters's avatar
Tim Peters committed
229 230
            self.t = r[0] + r[1] - t # put back unrecorded delta

231 232
    # 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
233 234

    def trace_dispatch_i(self, frame, event, arg):
235
        timer = self.timer
236
        t = timer() - self.t - self.bias
237 238

        if event == "c_call":
239
            self.c_func_name = arg.__name__
240 241

        if self.dispatch[event](self, frame, t):
242
            self.t = timer()
Tim Peters's avatar
Tim Peters committed
243
        else:
244
            self.t = timer() - t  # put back unrecorded delta
Tim Peters's avatar
Tim Peters committed
245

246 247
    # Dispatch routine for macintosh (timer returns time in ticks of
    # 1/60th second)
Tim Peters's avatar
Tim Peters committed
248 249

    def trace_dispatch_mac(self, frame, event, arg):
250
        timer = self.timer
251
        t = timer()/60.0 - self.t - self.bias
252 253

        if event == "c_call":
254
            self.c_func_name = arg.__name__
255

256
        if self.dispatch[event](self, frame, t):
257
            self.t = timer()/60.0
Tim Peters's avatar
Tim Peters committed
258
        else:
259
            self.t = timer()/60.0 - t  # put back unrecorded delta
Tim Peters's avatar
Tim Peters committed
260 261 262 263

    # SLOW generic dispatch routine for timer returning lists of numbers

    def trace_dispatch_l(self, frame, event, arg):
264
        get_time = self.get_time
265
        t = get_time() - self.t - self.bias
Tim Peters's avatar
Tim Peters committed
266

267
        if event == "c_call":
268
            self.c_func_name = arg.__name__
269

270
        if self.dispatch[event](self, frame, t):
271
            self.t = get_time()
Tim Peters's avatar
Tim Peters committed
272
        else:
273
            self.t = get_time() - t # put back unrecorded delta
Tim Peters's avatar
Tim Peters committed
274

275 276 277 278 279 280
    # 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
281 282

    def trace_dispatch_exception(self, frame, t):
283
        rpt, rit, ret, rfn, rframe, rcur = self.cur
284
        if (rframe is not frame) and rcur:
Tim Peters's avatar
Tim Peters committed
285
            return self.trace_dispatch_return(rframe, t)
286
        self.cur = rpt, rit+t, ret, rfn, rframe, rcur
287
        return 1
Tim Peters's avatar
Tim Peters committed
288 289 290


    def trace_dispatch_call(self, frame, t):
291
        if self.cur and frame.f_back is not self.cur[-2]:
292
            rpt, rit, ret, rfn, rframe, rcur = self.cur
293
            if not isinstance(rframe, Profile.fake_frame):
294 295 296
                assert rframe.f_back is frame.f_back, ("Bad call", rfn,
                                                       rframe, rframe.f_back,
                                                       frame, frame.f_back)
297
                self.trace_dispatch_return(rframe, 0)
298 299 300
                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
301 302 303
        fcode = frame.f_code
        fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name)
        self.cur = (t, 0, 0, fn, frame, self.cur)
304
        timings = self.timings
305
        if fn in timings:
306 307
            cc, ns, tt, ct, callers = timings[fn]
            timings[fn] = cc, ns + 1, tt, ct, callers
Tim Peters's avatar
Tim Peters committed
308
        else:
309
            timings[fn] = 0, 0, 0, 0, {}
Tim Peters's avatar
Tim Peters committed
310 311
        return 1

312 313 314 315
    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
316
        if fn in timings:
317 318 319 320 321 322
            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
323
    def trace_dispatch_return(self, frame, t):
324
        if frame is not self.cur[-2]:
325 326
            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
327

328 329
        # 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
330

331 332 333
        rpt, rit, ret, rfn, frame, rcur = self.cur
        rit = rit + t
        frame_total = rit + ret
Tim Peters's avatar
Tim Peters committed
334

335 336
        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
337

338 339
        timings = self.timings
        cc, ns, tt, ct, callers = timings[rfn]
Tim Peters's avatar
Tim Peters committed
340
        if not ns:
341 342 343 344 345
            # 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
346
            cc = cc + 1
347

348
        if pfn in callers:
Tim Peters's avatar
Tim Peters committed
349 350 351 352 353 354
            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
355 356

        timings[rfn] = cc, ns - 1, tt + rit, ct, callers
Tim Peters's avatar
Tim Peters committed
357 358 359

        return 1

360 361 362 363 364

    dispatch = {
        "call": trace_dispatch_call,
        "exception": trace_dispatch_exception,
        "return": trace_dispatch_return,
365
        "c_call": trace_dispatch_c_call,
366
        "c_exception": trace_dispatch_return,  # the C function returned
367
        "c_return": trace_dispatch_return,
368 369 370
        }


371
    # The next few functions play with self.cmd. By carefully preloading
Tim Peters's avatar
Tim Peters committed
372 373 374 375 376 377
    # 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):
378
        if self.cur[-1]: return   # already set
Tim Peters's avatar
Tim Peters committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
        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:
400
            pframe = self.cur[-2]
Tim Peters's avatar
Tim Peters committed
401 402 403
        else:
            pframe = None
        frame = self.fake_frame(code, pframe)
404
        self.dispatch['call'](self, frame, 0)
Tim Peters's avatar
Tim Peters committed
405 406 407 408 409

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

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


420
    def print_stats(self, sort=-1):
Tim Peters's avatar
Tim Peters committed
421
        import pstats
422
        pstats.Stats(self).strip_dirs().sort_stats(sort). \
Tim Peters's avatar
Tim Peters committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436
                  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 = {}
437
        for func, (cc, ns, tt, ct, callers) in self.timings.items():
Tim Peters's avatar
Tim Peters committed
438 439
            callers = callers.copy()
            nc = 0
440
            for callcnt in callers.values():
441
                nc += callcnt
Tim Peters's avatar
Tim Peters committed
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
            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:
457
            exec(cmd, globals, locals)
Tim Peters's avatar
Tim Peters committed
458 459 460 461 462
        finally:
            sys.setprofile(None)
        return self

    # This method is more useful to profile a single function call.
463
    def runcall(self, func, *args, **kw):
464
        self.set_cmd(repr(func))
Tim Peters's avatar
Tim Peters committed
465 466
        sys.setprofile(self.dispatcher)
        try:
467
            return func(*args, **kw)
Tim Peters's avatar
Tim Peters committed
468 469 470 471 472 473 474 475 476 477 478
        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
479 480 481
    # a per-event basis.
    #
    # Note that this difference is only significant if there are a lot of
Tim Peters's avatar
Tim Peters committed
482 483 484 485 486 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
    # 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.)
    #**************************************************************

512
    def calibrate(self, m, verbose=0):
513 514 515 516 517 518
        if self.__class__ is not Profile:
            raise TypeError("Subclasses must override .calibrate().")

        saved_bias = self.bias
        self.bias = 0
        try:
519
            return self._calibrate_inner(m, verbose)
520 521 522
        finally:
            self.bias = saved_bias

523
    def _calibrate_inner(self, m, verbose):
524
        get_time = self.get_time
Tim Peters's avatar
Tim Peters committed
525

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
        # 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:
548
            print("elapsed time without profiling =", elapsed_noprofile)
549 550 551 552 553 554 555 556 557 558

        # 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:
559
            print("elapsed time with profiling =", elapsed_profile)
560 561 562 563 564 565 566 567 568 569 570

        # 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:
571 572
            print("'CPU seconds' profiler reported =", reported_time)
            print("total # calls =", total_calls)
573 574 575 576 577 578 579 580 581
        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:
582
            print("mean stopwatch overhead per profile event =", mean)
583
        return mean
584 585

#****************************************************************************
586

587
def main():
588
    usage = "profile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
589
    parser = OptionParser(usage=usage)
590
    parser.allow_interspersed_args = False
Tim Peters's avatar
Tim Peters committed
591
    parser.add_option('-o', '--outfile', dest="outfile",
592 593 594
        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
595

596 597 598
    if not sys.argv[1:]:
        parser.print_usage()
        sys.exit(2)
Tim Peters's avatar
Tim Peters committed
599

600
    (options, args) = parser.parse_args()
Tim Peters's avatar
Tim Peters committed
601

602 603
    if (len(args) > 0):
        sys.argv[:] = args
604
        sys.path.insert(0, os.path.dirname(sys.argv[0]))
605 606 607 608 609 610
        fp = open(sys.argv[0])
        try:
            script = fp.read()
        finally:
            fp.close()
        run('exec(%r)' % script, options.outfile, options.sort)
611
    else:
612 613 614 615 616 617
        parser.print_usage()
    return parser

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