Back to index

python3.2  3.2.2
cProfile.py
Go to the documentation of this file.
00001 #! /usr/bin/env python3
00002 
00003 """Python interface for the 'lsprof' profiler.
00004    Compatible with the 'profile' module.
00005 """
00006 
00007 __all__ = ["run", "runctx", "Profile"]
00008 
00009 import _lsprof
00010 
00011 # ____________________________________________________________
00012 # Simple interface
00013 
00014 def run(statement, filename=None, sort=-1):
00015     """Run statement under profiler optionally saving results in filename
00016 
00017     This function takes a single argument that can be passed to the
00018     "exec" statement, and an optional file name.  In all cases this
00019     routine attempts to "exec" its first argument and gather profiling
00020     statistics from the execution. If no file name is present, then this
00021     function automatically prints a simple profiling report, sorted by the
00022     standard name string (file/line/function-name) that is presented in
00023     each line.
00024     """
00025     prof = Profile()
00026     result = None
00027     try:
00028         try:
00029             prof = prof.run(statement)
00030         except SystemExit:
00031             pass
00032     finally:
00033         if filename is not None:
00034             prof.dump_stats(filename)
00035         else:
00036             result = prof.print_stats(sort)
00037     return result
00038 
00039 def runctx(statement, globals, locals, filename=None, sort=-1):
00040     """Run statement under profiler, supplying your own globals and locals,
00041     optionally saving results in filename.
00042 
00043     statement and filename have the same semantics as profile.run
00044     """
00045     prof = Profile()
00046     result = None
00047     try:
00048         try:
00049             prof = prof.runctx(statement, globals, locals)
00050         except SystemExit:
00051             pass
00052     finally:
00053         if filename is not None:
00054             prof.dump_stats(filename)
00055         else:
00056             result = prof.print_stats(sort)
00057     return result
00058 
00059 # ____________________________________________________________
00060 
00061 class Profile(_lsprof.Profiler):
00062     """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True)
00063 
00064     Builds a profiler object using the specified timer function.
00065     The default timer is a fast built-in one based on real time.
00066     For custom timer functions returning integers, time_unit can
00067     be a float specifying a scale (i.e. how long each integer unit
00068     is, in seconds).
00069     """
00070 
00071     # Most of the functionality is in the base class.
00072     # This subclass only adds convenient and backward-compatible methods.
00073 
00074     def print_stats(self, sort=-1):
00075         import pstats
00076         pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats()
00077 
00078     def dump_stats(self, file):
00079         import marshal
00080         f = open(file, 'wb')
00081         self.create_stats()
00082         marshal.dump(self.stats, f)
00083         f.close()
00084 
00085     def create_stats(self):
00086         self.disable()
00087         self.snapshot_stats()
00088 
00089     def snapshot_stats(self):
00090         entries = self.getstats()
00091         self.stats = {}
00092         callersdicts = {}
00093         # call information
00094         for entry in entries:
00095             func = label(entry.code)
00096             nc = entry.callcount         # ncalls column of pstats (before '/')
00097             cc = nc - entry.reccallcount # ncalls column of pstats (after '/')
00098             tt = entry.inlinetime        # tottime column of pstats
00099             ct = entry.totaltime         # cumtime column of pstats
00100             callers = {}
00101             callersdicts[id(entry.code)] = callers
00102             self.stats[func] = cc, nc, tt, ct, callers
00103         # subcall information
00104         for entry in entries:
00105             if entry.calls:
00106                 func = label(entry.code)
00107                 for subentry in entry.calls:
00108                     try:
00109                         callers = callersdicts[id(subentry.code)]
00110                     except KeyError:
00111                         continue
00112                     nc = subentry.callcount
00113                     cc = nc - subentry.reccallcount
00114                     tt = subentry.inlinetime
00115                     ct = subentry.totaltime
00116                     if func in callers:
00117                         prev = callers[func]
00118                         nc += prev[0]
00119                         cc += prev[1]
00120                         tt += prev[2]
00121                         ct += prev[3]
00122                     callers[func] = nc, cc, tt, ct
00123 
00124     # The following two methods can be called by clients to use
00125     # a profiler to profile a statement, given as a string.
00126 
00127     def run(self, cmd):
00128         import __main__
00129         dict = __main__.__dict__
00130         return self.runctx(cmd, dict, dict)
00131 
00132     def runctx(self, cmd, globals, locals):
00133         self.enable()
00134         try:
00135             exec(cmd, globals, locals)
00136         finally:
00137             self.disable()
00138         return self
00139 
00140     # This method is more useful to profile a single function call.
00141     def runcall(self, func, *args, **kw):
00142         self.enable()
00143         try:
00144             return func(*args, **kw)
00145         finally:
00146             self.disable()
00147 
00148 # ____________________________________________________________
00149 
00150 def label(code):
00151     if isinstance(code, str):
00152         return ('~', 0, code)    # built-in functions ('~' sorts at the end)
00153     else:
00154         return (code.co_filename, code.co_firstlineno, code.co_name)
00155 
00156 # ____________________________________________________________
00157 
00158 def main():
00159     import os, sys
00160     from optparse import OptionParser
00161     usage = "cProfile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
00162     parser = OptionParser(usage=usage)
00163     parser.allow_interspersed_args = False
00164     parser.add_option('-o', '--outfile', dest="outfile",
00165         help="Save stats to <outfile>", default=None)
00166     parser.add_option('-s', '--sort', dest="sort",
00167         help="Sort order when printing to stdout, based on pstats.Stats class",
00168         default=-1)
00169 
00170     if not sys.argv[1:]:
00171         parser.print_usage()
00172         sys.exit(2)
00173 
00174     (options, args) = parser.parse_args()
00175     sys.argv[:] = args
00176 
00177     if len(args) > 0:
00178         progname = args[0]
00179         sys.path.insert(0, os.path.dirname(progname))
00180         with open(progname, 'rb') as fp:
00181             code = compile(fp.read(), progname, 'exec')
00182         globs = {
00183             '__file__': progname,
00184             '__name__': '__main__',
00185             '__package__': None,
00186             '__cached__': None,
00187         }
00188         runctx(code, globs, None, options.outfile, options.sort)
00189     else:
00190         parser.print_usage()
00191     return parser
00192 
00193 # When invoked as main program, invoke the profiler on a script
00194 if __name__ == '__main__':
00195     main()