Home | History | Annotate | Download | only in python2.7
      1 #! /usr/bin/env python
      2 
      3 """Python interface for the 'lsprof' profiler.
      4    Compatible with the 'profile' module.
      5 """
      6 
      7 __all__ = ["run", "runctx", "help", "Profile"]
      8 
      9 import _lsprof
     10 
     11 # ____________________________________________________________
     12 # Simple interface
     13 
     14 def run(statement, filename=None, sort=-1):
     15     """Run statement under profiler optionally saving results in filename
     16 
     17     This function takes a single argument that can be passed to the
     18     "exec" statement, and an optional file name.  In all cases this
     19     routine attempts to "exec" its first argument and gather profiling
     20     statistics from the execution. If no file name is present, then this
     21     function automatically prints a simple profiling report, sorted by the
     22     standard name string (file/line/function-name) that is presented in
     23     each line.
     24     """
     25     prof = Profile()
     26     result = None
     27     try:
     28         try:
     29             prof = prof.run(statement)
     30         except SystemExit:
     31             pass
     32     finally:
     33         if filename is not None:
     34             prof.dump_stats(filename)
     35         else:
     36             result = prof.print_stats(sort)
     37     return result
     38 
     39 def runctx(statement, globals, locals, filename=None, sort=-1):
     40     """Run statement under profiler, supplying your own globals and locals,
     41     optionally saving results in filename.
     42 
     43     statement and filename have the same semantics as profile.run
     44     """
     45     prof = Profile()
     46     result = None
     47     try:
     48         try:
     49             prof = prof.runctx(statement, globals, locals)
     50         except SystemExit:
     51             pass
     52     finally:
     53         if filename is not None:
     54             prof.dump_stats(filename)
     55         else:
     56             result = prof.print_stats(sort)
     57     return result
     58 
     59 # Backwards compatibility.
     60 def help():
     61     print "Documentation for the profile/cProfile modules can be found "
     62     print "in the Python Library Reference, section 'The Python Profiler'."
     63 
     64 # ____________________________________________________________
     65 
     66 class Profile(_lsprof.Profiler):
     67     """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True)
     68 
     69     Builds a profiler object using the specified timer function.
     70     The default timer is a fast built-in one based on real time.
     71     For custom timer functions returning integers, time_unit can
     72     be a float specifying a scale (i.e. how long each integer unit
     73     is, in seconds).
     74     """
     75 
     76     # Most of the functionality is in the base class.
     77     # This subclass only adds convenient and backward-compatible methods.
     78 
     79     def print_stats(self, sort=-1):
     80         import pstats
     81         pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats()
     82 
     83     def dump_stats(self, file):
     84         import marshal
     85         f = open(file, 'wb')
     86         self.create_stats()
     87         marshal.dump(self.stats, f)
     88         f.close()
     89 
     90     def create_stats(self):
     91         self.disable()
     92         self.snapshot_stats()
     93 
     94     def snapshot_stats(self):
     95         entries = self.getstats()
     96         self.stats = {}
     97         callersdicts = {}
     98         # call information
     99         for entry in entries:
    100             func = label(entry.code)
    101             nc = entry.callcount         # ncalls column of pstats (before '/')
    102             cc = nc - entry.reccallcount # ncalls column of pstats (after '/')
    103             tt = entry.inlinetime        # tottime column of pstats
    104             ct = entry.totaltime         # cumtime column of pstats
    105             callers = {}
    106             callersdicts[id(entry.code)] = callers
    107             self.stats[func] = cc, nc, tt, ct, callers
    108         # subcall information
    109         for entry in entries:
    110             if entry.calls:
    111                 func = label(entry.code)
    112                 for subentry in entry.calls:
    113                     try:
    114                         callers = callersdicts[id(subentry.code)]
    115                     except KeyError:
    116                         continue
    117                     nc = subentry.callcount
    118                     cc = nc - subentry.reccallcount
    119                     tt = subentry.inlinetime
    120                     ct = subentry.totaltime
    121                     if func in callers:
    122                         prev = callers[func]
    123                         nc += prev[0]
    124                         cc += prev[1]
    125                         tt += prev[2]
    126                         ct += prev[3]
    127                     callers[func] = nc, cc, tt, ct
    128 
    129     # The following two methods can be called by clients to use
    130     # a profiler to profile a statement, given as a string.
    131 
    132     def run(self, cmd):
    133         import __main__
    134         dict = __main__.__dict__
    135         return self.runctx(cmd, dict, dict)
    136 
    137     def runctx(self, cmd, globals, locals):
    138         self.enable()
    139         try:
    140             exec cmd in globals, locals
    141         finally:
    142             self.disable()
    143         return self
    144 
    145     # This method is more useful to profile a single function call.
    146     def runcall(self, func, *args, **kw):
    147         self.enable()
    148         try:
    149             return func(*args, **kw)
    150         finally:
    151             self.disable()
    152 
    153 # ____________________________________________________________
    154 
    155 def label(code):
    156     if isinstance(code, str):
    157         return ('~', 0, code)    # built-in functions ('~' sorts at the end)
    158     else:
    159         return (code.co_filename, code.co_firstlineno, code.co_name)
    160 
    161 # ____________________________________________________________
    162 
    163 def main():
    164     import os, sys
    165     from optparse import OptionParser
    166     usage = "cProfile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
    167     parser = OptionParser(usage=usage)
    168     parser.allow_interspersed_args = False
    169     parser.add_option('-o', '--outfile', dest="outfile",
    170         help="Save stats to <outfile>", default=None)
    171     parser.add_option('-s', '--sort', dest="sort",
    172         help="Sort order when printing to stdout, based on pstats.Stats class",
    173         default=-1)
    174 
    175     if not sys.argv[1:]:
    176         parser.print_usage()
    177         sys.exit(2)
    178 
    179     (options, args) = parser.parse_args()
    180     sys.argv[:] = args
    181 
    182     if len(args) > 0:
    183         progname = args[0]
    184         sys.path.insert(0, os.path.dirname(progname))
    185         with open(progname, 'rb') as fp:
    186             code = compile(fp.read(), progname, 'exec')
    187         globs = {
    188             '__file__': progname,
    189             '__name__': '__main__',
    190             '__package__': None,
    191         }
    192         runctx(code, globs, None, options.outfile, options.sort)
    193     else:
    194         parser.print_usage()
    195     return parser
    196 
    197 # When invoked as main program, invoke the profiler on a script
    198 if __name__ == '__main__':
    199     main()
    200