1*cda5da8dSAndroid Build Coastguard Worker#! /usr/bin/env python3 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Worker"""Python interface for the 'lsprof' profiler. 4*cda5da8dSAndroid Build Coastguard Worker Compatible with the 'profile' module. 5*cda5da8dSAndroid Build Coastguard Worker""" 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard Worker__all__ = ["run", "runctx", "Profile"] 8*cda5da8dSAndroid Build Coastguard Worker 9*cda5da8dSAndroid Build Coastguard Workerimport _lsprof 10*cda5da8dSAndroid Build Coastguard Workerimport io 11*cda5da8dSAndroid Build Coastguard Workerimport profile as _pyprofile 12*cda5da8dSAndroid Build Coastguard Worker 13*cda5da8dSAndroid Build Coastguard Worker# ____________________________________________________________ 14*cda5da8dSAndroid Build Coastguard Worker# Simple interface 15*cda5da8dSAndroid Build Coastguard Worker 16*cda5da8dSAndroid Build Coastguard Workerdef run(statement, filename=None, sort=-1): 17*cda5da8dSAndroid Build Coastguard Worker return _pyprofile._Utils(Profile).run(statement, filename, sort) 18*cda5da8dSAndroid Build Coastguard Worker 19*cda5da8dSAndroid Build Coastguard Workerdef runctx(statement, globals, locals, filename=None, sort=-1): 20*cda5da8dSAndroid Build Coastguard Worker return _pyprofile._Utils(Profile).runctx(statement, globals, locals, 21*cda5da8dSAndroid Build Coastguard Worker filename, sort) 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Workerrun.__doc__ = _pyprofile.run.__doc__ 24*cda5da8dSAndroid Build Coastguard Workerrunctx.__doc__ = _pyprofile.runctx.__doc__ 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Worker# ____________________________________________________________ 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Workerclass Profile(_lsprof.Profiler): 29*cda5da8dSAndroid Build Coastguard Worker """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Worker Builds a profiler object using the specified timer function. 32*cda5da8dSAndroid Build Coastguard Worker The default timer is a fast built-in one based on real time. 33*cda5da8dSAndroid Build Coastguard Worker For custom timer functions returning integers, timeunit can 34*cda5da8dSAndroid Build Coastguard Worker be a float specifying a scale (i.e. how long each integer unit 35*cda5da8dSAndroid Build Coastguard Worker is, in seconds). 36*cda5da8dSAndroid Build Coastguard Worker """ 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard Worker # Most of the functionality is in the base class. 39*cda5da8dSAndroid Build Coastguard Worker # This subclass only adds convenient and backward-compatible methods. 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Worker def print_stats(self, sort=-1): 42*cda5da8dSAndroid Build Coastguard Worker import pstats 43*cda5da8dSAndroid Build Coastguard Worker pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats() 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker def dump_stats(self, file): 46*cda5da8dSAndroid Build Coastguard Worker import marshal 47*cda5da8dSAndroid Build Coastguard Worker with open(file, 'wb') as f: 48*cda5da8dSAndroid Build Coastguard Worker self.create_stats() 49*cda5da8dSAndroid Build Coastguard Worker marshal.dump(self.stats, f) 50*cda5da8dSAndroid Build Coastguard Worker 51*cda5da8dSAndroid Build Coastguard Worker def create_stats(self): 52*cda5da8dSAndroid Build Coastguard Worker self.disable() 53*cda5da8dSAndroid Build Coastguard Worker self.snapshot_stats() 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard Worker def snapshot_stats(self): 56*cda5da8dSAndroid Build Coastguard Worker entries = self.getstats() 57*cda5da8dSAndroid Build Coastguard Worker self.stats = {} 58*cda5da8dSAndroid Build Coastguard Worker callersdicts = {} 59*cda5da8dSAndroid Build Coastguard Worker # call information 60*cda5da8dSAndroid Build Coastguard Worker for entry in entries: 61*cda5da8dSAndroid Build Coastguard Worker func = label(entry.code) 62*cda5da8dSAndroid Build Coastguard Worker nc = entry.callcount # ncalls column of pstats (before '/') 63*cda5da8dSAndroid Build Coastguard Worker cc = nc - entry.reccallcount # ncalls column of pstats (after '/') 64*cda5da8dSAndroid Build Coastguard Worker tt = entry.inlinetime # tottime column of pstats 65*cda5da8dSAndroid Build Coastguard Worker ct = entry.totaltime # cumtime column of pstats 66*cda5da8dSAndroid Build Coastguard Worker callers = {} 67*cda5da8dSAndroid Build Coastguard Worker callersdicts[id(entry.code)] = callers 68*cda5da8dSAndroid Build Coastguard Worker self.stats[func] = cc, nc, tt, ct, callers 69*cda5da8dSAndroid Build Coastguard Worker # subcall information 70*cda5da8dSAndroid Build Coastguard Worker for entry in entries: 71*cda5da8dSAndroid Build Coastguard Worker if entry.calls: 72*cda5da8dSAndroid Build Coastguard Worker func = label(entry.code) 73*cda5da8dSAndroid Build Coastguard Worker for subentry in entry.calls: 74*cda5da8dSAndroid Build Coastguard Worker try: 75*cda5da8dSAndroid Build Coastguard Worker callers = callersdicts[id(subentry.code)] 76*cda5da8dSAndroid Build Coastguard Worker except KeyError: 77*cda5da8dSAndroid Build Coastguard Worker continue 78*cda5da8dSAndroid Build Coastguard Worker nc = subentry.callcount 79*cda5da8dSAndroid Build Coastguard Worker cc = nc - subentry.reccallcount 80*cda5da8dSAndroid Build Coastguard Worker tt = subentry.inlinetime 81*cda5da8dSAndroid Build Coastguard Worker ct = subentry.totaltime 82*cda5da8dSAndroid Build Coastguard Worker if func in callers: 83*cda5da8dSAndroid Build Coastguard Worker prev = callers[func] 84*cda5da8dSAndroid Build Coastguard Worker nc += prev[0] 85*cda5da8dSAndroid Build Coastguard Worker cc += prev[1] 86*cda5da8dSAndroid Build Coastguard Worker tt += prev[2] 87*cda5da8dSAndroid Build Coastguard Worker ct += prev[3] 88*cda5da8dSAndroid Build Coastguard Worker callers[func] = nc, cc, tt, ct 89*cda5da8dSAndroid Build Coastguard Worker 90*cda5da8dSAndroid Build Coastguard Worker # The following two methods can be called by clients to use 91*cda5da8dSAndroid Build Coastguard Worker # a profiler to profile a statement, given as a string. 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Worker def run(self, cmd): 94*cda5da8dSAndroid Build Coastguard Worker import __main__ 95*cda5da8dSAndroid Build Coastguard Worker dict = __main__.__dict__ 96*cda5da8dSAndroid Build Coastguard Worker return self.runctx(cmd, dict, dict) 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker def runctx(self, cmd, globals, locals): 99*cda5da8dSAndroid Build Coastguard Worker self.enable() 100*cda5da8dSAndroid Build Coastguard Worker try: 101*cda5da8dSAndroid Build Coastguard Worker exec(cmd, globals, locals) 102*cda5da8dSAndroid Build Coastguard Worker finally: 103*cda5da8dSAndroid Build Coastguard Worker self.disable() 104*cda5da8dSAndroid Build Coastguard Worker return self 105*cda5da8dSAndroid Build Coastguard Worker 106*cda5da8dSAndroid Build Coastguard Worker # This method is more useful to profile a single function call. 107*cda5da8dSAndroid Build Coastguard Worker def runcall(self, func, /, *args, **kw): 108*cda5da8dSAndroid Build Coastguard Worker self.enable() 109*cda5da8dSAndroid Build Coastguard Worker try: 110*cda5da8dSAndroid Build Coastguard Worker return func(*args, **kw) 111*cda5da8dSAndroid Build Coastguard Worker finally: 112*cda5da8dSAndroid Build Coastguard Worker self.disable() 113*cda5da8dSAndroid Build Coastguard Worker 114*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 115*cda5da8dSAndroid Build Coastguard Worker self.enable() 116*cda5da8dSAndroid Build Coastguard Worker return self 117*cda5da8dSAndroid Build Coastguard Worker 118*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *exc_info): 119*cda5da8dSAndroid Build Coastguard Worker self.disable() 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Worker# ____________________________________________________________ 122*cda5da8dSAndroid Build Coastguard Worker 123*cda5da8dSAndroid Build Coastguard Workerdef label(code): 124*cda5da8dSAndroid Build Coastguard Worker if isinstance(code, str): 125*cda5da8dSAndroid Build Coastguard Worker return ('~', 0, code) # built-in functions ('~' sorts at the end) 126*cda5da8dSAndroid Build Coastguard Worker else: 127*cda5da8dSAndroid Build Coastguard Worker return (code.co_filename, code.co_firstlineno, code.co_name) 128*cda5da8dSAndroid Build Coastguard Worker 129*cda5da8dSAndroid Build Coastguard Worker# ____________________________________________________________ 130*cda5da8dSAndroid Build Coastguard Worker 131*cda5da8dSAndroid Build Coastguard Workerdef main(): 132*cda5da8dSAndroid Build Coastguard Worker import os 133*cda5da8dSAndroid Build Coastguard Worker import sys 134*cda5da8dSAndroid Build Coastguard Worker import runpy 135*cda5da8dSAndroid Build Coastguard Worker import pstats 136*cda5da8dSAndroid Build Coastguard Worker from optparse import OptionParser 137*cda5da8dSAndroid Build Coastguard Worker usage = "cProfile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." 138*cda5da8dSAndroid Build Coastguard Worker parser = OptionParser(usage=usage) 139*cda5da8dSAndroid Build Coastguard Worker parser.allow_interspersed_args = False 140*cda5da8dSAndroid Build Coastguard Worker parser.add_option('-o', '--outfile', dest="outfile", 141*cda5da8dSAndroid Build Coastguard Worker help="Save stats to <outfile>", default=None) 142*cda5da8dSAndroid Build Coastguard Worker parser.add_option('-s', '--sort', dest="sort", 143*cda5da8dSAndroid Build Coastguard Worker help="Sort order when printing to stdout, based on pstats.Stats class", 144*cda5da8dSAndroid Build Coastguard Worker default=2, 145*cda5da8dSAndroid Build Coastguard Worker choices=sorted(pstats.Stats.sort_arg_dict_default)) 146*cda5da8dSAndroid Build Coastguard Worker parser.add_option('-m', dest="module", action="store_true", 147*cda5da8dSAndroid Build Coastguard Worker help="Profile a library module", default=False) 148*cda5da8dSAndroid Build Coastguard Worker 149*cda5da8dSAndroid Build Coastguard Worker if not sys.argv[1:]: 150*cda5da8dSAndroid Build Coastguard Worker parser.print_usage() 151*cda5da8dSAndroid Build Coastguard Worker sys.exit(2) 152*cda5da8dSAndroid Build Coastguard Worker 153*cda5da8dSAndroid Build Coastguard Worker (options, args) = parser.parse_args() 154*cda5da8dSAndroid Build Coastguard Worker sys.argv[:] = args 155*cda5da8dSAndroid Build Coastguard Worker 156*cda5da8dSAndroid Build Coastguard Worker # The script that we're profiling may chdir, so capture the absolute path 157*cda5da8dSAndroid Build Coastguard Worker # to the output file at startup. 158*cda5da8dSAndroid Build Coastguard Worker if options.outfile is not None: 159*cda5da8dSAndroid Build Coastguard Worker options.outfile = os.path.abspath(options.outfile) 160*cda5da8dSAndroid Build Coastguard Worker 161*cda5da8dSAndroid Build Coastguard Worker if len(args) > 0: 162*cda5da8dSAndroid Build Coastguard Worker if options.module: 163*cda5da8dSAndroid Build Coastguard Worker code = "run_module(modname, run_name='__main__')" 164*cda5da8dSAndroid Build Coastguard Worker globs = { 165*cda5da8dSAndroid Build Coastguard Worker 'run_module': runpy.run_module, 166*cda5da8dSAndroid Build Coastguard Worker 'modname': args[0] 167*cda5da8dSAndroid Build Coastguard Worker } 168*cda5da8dSAndroid Build Coastguard Worker else: 169*cda5da8dSAndroid Build Coastguard Worker progname = args[0] 170*cda5da8dSAndroid Build Coastguard Worker sys.path.insert(0, os.path.dirname(progname)) 171*cda5da8dSAndroid Build Coastguard Worker with io.open_code(progname) as fp: 172*cda5da8dSAndroid Build Coastguard Worker code = compile(fp.read(), progname, 'exec') 173*cda5da8dSAndroid Build Coastguard Worker globs = { 174*cda5da8dSAndroid Build Coastguard Worker '__file__': progname, 175*cda5da8dSAndroid Build Coastguard Worker '__name__': '__main__', 176*cda5da8dSAndroid Build Coastguard Worker '__package__': None, 177*cda5da8dSAndroid Build Coastguard Worker '__cached__': None, 178*cda5da8dSAndroid Build Coastguard Worker } 179*cda5da8dSAndroid Build Coastguard Worker try: 180*cda5da8dSAndroid Build Coastguard Worker runctx(code, globs, None, options.outfile, options.sort) 181*cda5da8dSAndroid Build Coastguard Worker except BrokenPipeError as exc: 182*cda5da8dSAndroid Build Coastguard Worker # Prevent "Exception ignored" during interpreter shutdown. 183*cda5da8dSAndroid Build Coastguard Worker sys.stdout = None 184*cda5da8dSAndroid Build Coastguard Worker sys.exit(exc.errno) 185*cda5da8dSAndroid Build Coastguard Worker else: 186*cda5da8dSAndroid Build Coastguard Worker parser.print_usage() 187*cda5da8dSAndroid Build Coastguard Worker return parser 188*cda5da8dSAndroid Build Coastguard Worker 189*cda5da8dSAndroid Build Coastguard Worker# When invoked as main program, invoke the profiler on a script 190*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 191*cda5da8dSAndroid Build Coastguard Worker main() 192