xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/cProfile.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
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