xref: /aosp_15_r20/system/extras/simpleperf/scripts/inferno/inferno.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*288bf522SAndroid Build Coastguard Worker#
3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2016 The Android Open Source Project
4*288bf522SAndroid Build Coastguard Worker#
5*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*288bf522SAndroid Build Coastguard Worker#
9*288bf522SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*288bf522SAndroid Build Coastguard Worker#
11*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*288bf522SAndroid Build Coastguard Worker# limitations under the License.
16*288bf522SAndroid Build Coastguard Worker#
17*288bf522SAndroid Build Coastguard Worker
18*288bf522SAndroid Build Coastguard Worker"""
19*288bf522SAndroid Build Coastguard Worker    Inferno is a tool to generate flamegraphs for android programs. It was originally written
20*288bf522SAndroid Build Coastguard Worker    to profile surfaceflinger (Android compositor) but it can be used for other C++ program.
21*288bf522SAndroid Build Coastguard Worker    It uses simpleperf to collect data. Programs have to be compiled with frame pointers which
22*288bf522SAndroid Build Coastguard Worker    excludes ART based programs for the time being.
23*288bf522SAndroid Build Coastguard Worker
24*288bf522SAndroid Build Coastguard Worker    Here is how it works:
25*288bf522SAndroid Build Coastguard Worker
26*288bf522SAndroid Build Coastguard Worker    1/ Data collection is started via simpleperf and pulled locally as "perf.data".
27*288bf522SAndroid Build Coastguard Worker    2/ The raw format is parsed, callstacks are merged to form a flamegraph data structure.
28*288bf522SAndroid Build Coastguard Worker    3/ The data structure is used to generate a SVG embedded into an HTML page.
29*288bf522SAndroid Build Coastguard Worker    4/ Javascript is injected to allow flamegraph navigation, search, coloring model.
30*288bf522SAndroid Build Coastguard Worker
31*288bf522SAndroid Build Coastguard Worker"""
32*288bf522SAndroid Build Coastguard Worker
33*288bf522SAndroid Build Coastguard Workerimport argparse
34*288bf522SAndroid Build Coastguard Workerimport datetime
35*288bf522SAndroid Build Coastguard Workerimport logging
36*288bf522SAndroid Build Coastguard Workerimport os
37*288bf522SAndroid Build Coastguard Workerimport subprocess
38*288bf522SAndroid Build Coastguard Workerimport sys
39*288bf522SAndroid Build Coastguard Worker
40*288bf522SAndroid Build Coastguard Worker# fmt: off
41*288bf522SAndroid Build Coastguard Worker# pylint: disable=wrong-import-position
42*288bf522SAndroid Build Coastguard WorkerSCRIPTS_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
43*288bf522SAndroid Build Coastguard Workersys.path.append(SCRIPTS_PATH)
44*288bf522SAndroid Build Coastguard Workerfrom simpleperf_report_lib import ReportLib
45*288bf522SAndroid Build Coastguard Workerfrom simpleperf_utils import (log_exit, log_fatal, AdbHelper, open_report_in_browser,
46*288bf522SAndroid Build Coastguard Worker                              BaseArgumentParser)
47*288bf522SAndroid Build Coastguard Worker
48*288bf522SAndroid Build Coastguard Workerfrom data_types import Process
49*288bf522SAndroid Build Coastguard Workerfrom svg_renderer import get_proper_scaled_time_string, render_svg
50*288bf522SAndroid Build Coastguard Worker# fmt: on
51*288bf522SAndroid Build Coastguard Worker
52*288bf522SAndroid Build Coastguard Worker
53*288bf522SAndroid Build Coastguard Workerdef collect_data(args):
54*288bf522SAndroid Build Coastguard Worker    """ Run app_profiler.py to generate record file. """
55*288bf522SAndroid Build Coastguard Worker    app_profiler_args = [sys.executable, os.path.join(SCRIPTS_PATH, "app_profiler.py"), "-nb"]
56*288bf522SAndroid Build Coastguard Worker    if args.app:
57*288bf522SAndroid Build Coastguard Worker        app_profiler_args += ["-p", args.app]
58*288bf522SAndroid Build Coastguard Worker    elif args.native_program:
59*288bf522SAndroid Build Coastguard Worker        app_profiler_args += ["-np", args.native_program]
60*288bf522SAndroid Build Coastguard Worker    elif args.pid != -1:
61*288bf522SAndroid Build Coastguard Worker        app_profiler_args += ['--pid', str(args.pid)]
62*288bf522SAndroid Build Coastguard Worker    elif args.system_wide:
63*288bf522SAndroid Build Coastguard Worker        app_profiler_args += ['--system_wide']
64*288bf522SAndroid Build Coastguard Worker    else:
65*288bf522SAndroid Build Coastguard Worker        log_exit("Please set profiling target with -p, -np, --pid or --system_wide option.")
66*288bf522SAndroid Build Coastguard Worker    if args.compile_java_code:
67*288bf522SAndroid Build Coastguard Worker        app_profiler_args.append("--compile_java_code")
68*288bf522SAndroid Build Coastguard Worker    if args.disable_adb_root:
69*288bf522SAndroid Build Coastguard Worker        app_profiler_args.append("--disable_adb_root")
70*288bf522SAndroid Build Coastguard Worker    record_arg_str = ""
71*288bf522SAndroid Build Coastguard Worker    if args.dwarf_unwinding:
72*288bf522SAndroid Build Coastguard Worker        record_arg_str += "-g "
73*288bf522SAndroid Build Coastguard Worker    else:
74*288bf522SAndroid Build Coastguard Worker        record_arg_str += "--call-graph fp "
75*288bf522SAndroid Build Coastguard Worker    if args.events:
76*288bf522SAndroid Build Coastguard Worker        tokens = args.events.split()
77*288bf522SAndroid Build Coastguard Worker        if len(tokens) == 2:
78*288bf522SAndroid Build Coastguard Worker            num_events = tokens[0]
79*288bf522SAndroid Build Coastguard Worker            event_name = tokens[1]
80*288bf522SAndroid Build Coastguard Worker            record_arg_str += "-c %s -e %s " % (num_events, event_name)
81*288bf522SAndroid Build Coastguard Worker        else:
82*288bf522SAndroid Build Coastguard Worker            log_exit("Event format string of -e option cann't be recognized.")
83*288bf522SAndroid Build Coastguard Worker        logging.info("Using event sampling (-c %s -e %s)." % (num_events, event_name))
84*288bf522SAndroid Build Coastguard Worker    else:
85*288bf522SAndroid Build Coastguard Worker        record_arg_str += "-f %d " % args.sample_frequency
86*288bf522SAndroid Build Coastguard Worker        logging.info("Using frequency sampling (-f %d)." % args.sample_frequency)
87*288bf522SAndroid Build Coastguard Worker    record_arg_str += "--duration %d " % args.capture_duration
88*288bf522SAndroid Build Coastguard Worker    app_profiler_args += ["-r", record_arg_str]
89*288bf522SAndroid Build Coastguard Worker    returncode = subprocess.call(app_profiler_args)
90*288bf522SAndroid Build Coastguard Worker    return returncode == 0
91*288bf522SAndroid Build Coastguard Worker
92*288bf522SAndroid Build Coastguard Worker
93*288bf522SAndroid Build Coastguard Workerdef parse_samples(process, args, sample_filter_fn):
94*288bf522SAndroid Build Coastguard Worker    """Read samples from record file.
95*288bf522SAndroid Build Coastguard Worker        process: Process object
96*288bf522SAndroid Build Coastguard Worker        args: arguments
97*288bf522SAndroid Build Coastguard Worker        sample_filter_fn: if not None, is used to modify and filter samples.
98*288bf522SAndroid Build Coastguard Worker                          It returns false for samples should be filtered out.
99*288bf522SAndroid Build Coastguard Worker    """
100*288bf522SAndroid Build Coastguard Worker
101*288bf522SAndroid Build Coastguard Worker    record_file = args.record_file
102*288bf522SAndroid Build Coastguard Worker    symfs_dir = args.symfs
103*288bf522SAndroid Build Coastguard Worker    kallsyms_file = args.kallsyms
104*288bf522SAndroid Build Coastguard Worker
105*288bf522SAndroid Build Coastguard Worker    lib = ReportLib()
106*288bf522SAndroid Build Coastguard Worker
107*288bf522SAndroid Build Coastguard Worker    lib.ShowIpForUnknownSymbol()
108*288bf522SAndroid Build Coastguard Worker    if symfs_dir:
109*288bf522SAndroid Build Coastguard Worker        lib.SetSymfs(symfs_dir)
110*288bf522SAndroid Build Coastguard Worker    if record_file:
111*288bf522SAndroid Build Coastguard Worker        lib.SetRecordFile(record_file)
112*288bf522SAndroid Build Coastguard Worker    if kallsyms_file:
113*288bf522SAndroid Build Coastguard Worker        lib.SetKallsymsFile(kallsyms_file)
114*288bf522SAndroid Build Coastguard Worker    lib.SetReportOptions(args.report_lib_options)
115*288bf522SAndroid Build Coastguard Worker    process.cmd = lib.GetRecordCmd()
116*288bf522SAndroid Build Coastguard Worker    product_props = lib.MetaInfo().get("product_props")
117*288bf522SAndroid Build Coastguard Worker    if product_props:
118*288bf522SAndroid Build Coastguard Worker        manufacturer, model, name = product_props.split(':')
119*288bf522SAndroid Build Coastguard Worker        process.props['ro.product.manufacturer'] = manufacturer
120*288bf522SAndroid Build Coastguard Worker        process.props['ro.product.model'] = model
121*288bf522SAndroid Build Coastguard Worker        process.props['ro.product.name'] = name
122*288bf522SAndroid Build Coastguard Worker    if lib.MetaInfo().get('trace_offcpu') == 'true':
123*288bf522SAndroid Build Coastguard Worker        process.props['trace_offcpu'] = True
124*288bf522SAndroid Build Coastguard Worker        if args.one_flamegraph:
125*288bf522SAndroid Build Coastguard Worker            log_exit("It doesn't make sense to report with --one-flamegraph for perf.data " +
126*288bf522SAndroid Build Coastguard Worker                     "recorded with --trace-offcpu.""")
127*288bf522SAndroid Build Coastguard Worker    else:
128*288bf522SAndroid Build Coastguard Worker        process.props['trace_offcpu'] = False
129*288bf522SAndroid Build Coastguard Worker
130*288bf522SAndroid Build Coastguard Worker    while True:
131*288bf522SAndroid Build Coastguard Worker        sample = lib.GetNextSample()
132*288bf522SAndroid Build Coastguard Worker        if sample is None:
133*288bf522SAndroid Build Coastguard Worker            lib.Close()
134*288bf522SAndroid Build Coastguard Worker            break
135*288bf522SAndroid Build Coastguard Worker        symbol = lib.GetSymbolOfCurrentSample()
136*288bf522SAndroid Build Coastguard Worker        callchain = lib.GetCallChainOfCurrentSample()
137*288bf522SAndroid Build Coastguard Worker        if sample_filter_fn and not sample_filter_fn(sample, symbol, callchain):
138*288bf522SAndroid Build Coastguard Worker            continue
139*288bf522SAndroid Build Coastguard Worker        process.add_sample(sample, symbol, callchain)
140*288bf522SAndroid Build Coastguard Worker
141*288bf522SAndroid Build Coastguard Worker    if process.pid == 0:
142*288bf522SAndroid Build Coastguard Worker        main_threads = [thread for thread in process.threads.values() if thread.tid == thread.pid]
143*288bf522SAndroid Build Coastguard Worker        if main_threads:
144*288bf522SAndroid Build Coastguard Worker            process.name = main_threads[0].name
145*288bf522SAndroid Build Coastguard Worker            process.pid = main_threads[0].pid
146*288bf522SAndroid Build Coastguard Worker
147*288bf522SAndroid Build Coastguard Worker    for thread in process.threads.values():
148*288bf522SAndroid Build Coastguard Worker        min_event_count = thread.num_events * args.min_callchain_percentage * 0.01
149*288bf522SAndroid Build Coastguard Worker        thread.flamegraph.trim_callchain(min_event_count, args.max_callchain_depth)
150*288bf522SAndroid Build Coastguard Worker
151*288bf522SAndroid Build Coastguard Worker    logging.info("Parsed %s callchains." % process.num_samples)
152*288bf522SAndroid Build Coastguard Worker
153*288bf522SAndroid Build Coastguard Worker
154*288bf522SAndroid Build Coastguard Workerdef get_local_asset_content(local_path):
155*288bf522SAndroid Build Coastguard Worker    """
156*288bf522SAndroid Build Coastguard Worker    Retrieves local package text content
157*288bf522SAndroid Build Coastguard Worker    :param local_path: str, filename of local asset
158*288bf522SAndroid Build Coastguard Worker    :return: str, the content of local_path
159*288bf522SAndroid Build Coastguard Worker    """
160*288bf522SAndroid Build Coastguard Worker    with open(os.path.join(os.path.dirname(__file__), local_path), 'r') as f:
161*288bf522SAndroid Build Coastguard Worker        return f.read()
162*288bf522SAndroid Build Coastguard Worker
163*288bf522SAndroid Build Coastguard Worker
164*288bf522SAndroid Build Coastguard Workerdef output_report(process, args):
165*288bf522SAndroid Build Coastguard Worker    """
166*288bf522SAndroid Build Coastguard Worker    Generates a HTML report representing the result of simpleperf sampling as flamegraph
167*288bf522SAndroid Build Coastguard Worker    :param process: Process object
168*288bf522SAndroid Build Coastguard Worker    :return: str, absolute path to the file
169*288bf522SAndroid Build Coastguard Worker    """
170*288bf522SAndroid Build Coastguard Worker    f = open(args.report_path, 'w')
171*288bf522SAndroid Build Coastguard Worker    filepath = os.path.realpath(f.name)
172*288bf522SAndroid Build Coastguard Worker    if not args.embedded_flamegraph:
173*288bf522SAndroid Build Coastguard Worker        f.write("<html><body>")
174*288bf522SAndroid Build Coastguard Worker    f.write("<div id='flamegraph_id' style='font-family: Monospace; %s'>" % (
175*288bf522SAndroid Build Coastguard Worker        "display: none;" if args.embedded_flamegraph else ""))
176*288bf522SAndroid Build Coastguard Worker    f.write("""<style type="text/css"> .s { stroke:black; stroke-width:0.5; cursor:pointer;}
177*288bf522SAndroid Build Coastguard Worker            </style>""")
178*288bf522SAndroid Build Coastguard Worker    f.write('<style type="text/css"> .t:hover { cursor:pointer; } </style>')
179*288bf522SAndroid Build Coastguard Worker    f.write('<img height="180" alt = "Embedded Image" src ="data')
180*288bf522SAndroid Build Coastguard Worker    f.write(get_local_asset_content("inferno.b64"))
181*288bf522SAndroid Build Coastguard Worker    f.write('"/>')
182*288bf522SAndroid Build Coastguard Worker    process_entry = ("Process : %s (%d)<br/>" % (process.name, process.pid)) if process.pid else ""
183*288bf522SAndroid Build Coastguard Worker    thread_entry = '' if args.one_flamegraph else ('Threads: %d<br/>' % len(process.threads))
184*288bf522SAndroid Build Coastguard Worker    if process.props['trace_offcpu']:
185*288bf522SAndroid Build Coastguard Worker        event_entry = 'Total time: %s<br/>' % get_proper_scaled_time_string(process.num_events)
186*288bf522SAndroid Build Coastguard Worker    else:
187*288bf522SAndroid Build Coastguard Worker        event_entry = 'Event count: %s<br/>' % ("{:,}".format(process.num_events))
188*288bf522SAndroid Build Coastguard Worker    # TODO: collect capture duration info from perf.data.
189*288bf522SAndroid Build Coastguard Worker    duration_entry = ("Duration: %s seconds<br/>" % args.capture_duration
190*288bf522SAndroid Build Coastguard Worker                      ) if args.capture_duration else ""
191*288bf522SAndroid Build Coastguard Worker    f.write("""<div style='display:inline-block;'>
192*288bf522SAndroid Build Coastguard Worker                  <font size='8'>
193*288bf522SAndroid Build Coastguard Worker                  Inferno Flamegraph Report%s</font><br/><br/>
194*288bf522SAndroid Build Coastguard Worker                  %s
195*288bf522SAndroid Build Coastguard Worker                  Date&nbsp;&nbsp;&nbsp;&nbsp;: %s<br/>
196*288bf522SAndroid Build Coastguard Worker                  %s
197*288bf522SAndroid Build Coastguard Worker                  Samples : %d<br/>
198*288bf522SAndroid Build Coastguard Worker                  %s
199*288bf522SAndroid Build Coastguard Worker                  %s""" % ((': ' + args.title) if args.title else '',
200*288bf522SAndroid Build Coastguard Worker                           process_entry,
201*288bf522SAndroid Build Coastguard Worker                           datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
202*288bf522SAndroid Build Coastguard Worker                           thread_entry,
203*288bf522SAndroid Build Coastguard Worker                           process.num_samples,
204*288bf522SAndroid Build Coastguard Worker                           event_entry,
205*288bf522SAndroid Build Coastguard Worker                           duration_entry))
206*288bf522SAndroid Build Coastguard Worker    if 'ro.product.model' in process.props:
207*288bf522SAndroid Build Coastguard Worker        f.write(
208*288bf522SAndroid Build Coastguard Worker            "Machine : %s (%s) by %s<br/>" %
209*288bf522SAndroid Build Coastguard Worker            (process.props["ro.product.model"],
210*288bf522SAndroid Build Coastguard Worker             process.props["ro.product.name"],
211*288bf522SAndroid Build Coastguard Worker             process.props["ro.product.manufacturer"]))
212*288bf522SAndroid Build Coastguard Worker    if process.cmd:
213*288bf522SAndroid Build Coastguard Worker        f.write("Capture : %s<br/><br/>" % process.cmd)
214*288bf522SAndroid Build Coastguard Worker    f.write("</div>")
215*288bf522SAndroid Build Coastguard Worker    f.write("""<br/><br/>
216*288bf522SAndroid Build Coastguard Worker            <div>Navigate with WASD, zoom in with SPACE, zoom out with BACKSPACE.</div>""")
217*288bf522SAndroid Build Coastguard Worker    f.write("<script>%s</script>" % get_local_asset_content("script.js"))
218*288bf522SAndroid Build Coastguard Worker    if not args.embedded_flamegraph:
219*288bf522SAndroid Build Coastguard Worker        f.write("<script>document.addEventListener('DOMContentLoaded', flamegraphInit);</script>")
220*288bf522SAndroid Build Coastguard Worker
221*288bf522SAndroid Build Coastguard Worker    # Sort threads by the event count in a thread.
222*288bf522SAndroid Build Coastguard Worker    for thread in sorted(process.threads.values(), key=lambda x: x.num_events, reverse=True):
223*288bf522SAndroid Build Coastguard Worker        thread_name = 'One flamegraph' if args.one_flamegraph else ('Thread %d (%s)' %
224*288bf522SAndroid Build Coastguard Worker                                                                    (thread.tid, thread.name))
225*288bf522SAndroid Build Coastguard Worker        f.write("<br/><br/><b>%s (%d samples):</b><br/>\n\n\n\n" %
226*288bf522SAndroid Build Coastguard Worker                (thread_name, thread.num_samples))
227*288bf522SAndroid Build Coastguard Worker        render_svg(process, thread.flamegraph, f, args.color)
228*288bf522SAndroid Build Coastguard Worker
229*288bf522SAndroid Build Coastguard Worker    f.write("</div>")
230*288bf522SAndroid Build Coastguard Worker    if not args.embedded_flamegraph:
231*288bf522SAndroid Build Coastguard Worker        f.write("</body></html")
232*288bf522SAndroid Build Coastguard Worker    f.close()
233*288bf522SAndroid Build Coastguard Worker    return "file://" + filepath
234*288bf522SAndroid Build Coastguard Worker
235*288bf522SAndroid Build Coastguard Worker
236*288bf522SAndroid Build Coastguard Workerdef generate_threads_offsets(process):
237*288bf522SAndroid Build Coastguard Worker    for thread in process.threads.values():
238*288bf522SAndroid Build Coastguard Worker        thread.flamegraph.generate_offset(0)
239*288bf522SAndroid Build Coastguard Worker
240*288bf522SAndroid Build Coastguard Worker
241*288bf522SAndroid Build Coastguard Workerdef collect_machine_info(process):
242*288bf522SAndroid Build Coastguard Worker    adb = AdbHelper()
243*288bf522SAndroid Build Coastguard Worker    process.props = {}
244*288bf522SAndroid Build Coastguard Worker    process.props['ro.product.model'] = adb.get_property('ro.product.model')
245*288bf522SAndroid Build Coastguard Worker    process.props['ro.product.name'] = adb.get_property('ro.product.name')
246*288bf522SAndroid Build Coastguard Worker    process.props['ro.product.manufacturer'] = adb.get_property('ro.product.manufacturer')
247*288bf522SAndroid Build Coastguard Worker
248*288bf522SAndroid Build Coastguard Worker
249*288bf522SAndroid Build Coastguard Workerdef main():
250*288bf522SAndroid Build Coastguard Worker    # Allow deep callchain with length >1000.
251*288bf522SAndroid Build Coastguard Worker    sys.setrecursionlimit(1500)
252*288bf522SAndroid Build Coastguard Worker    parser = BaseArgumentParser(description="""Report samples in perf.data. Default option
253*288bf522SAndroid Build Coastguard Worker                                               is: "-np surfaceflinger -f 6000 -t 10".""")
254*288bf522SAndroid Build Coastguard Worker    record_group = parser.add_argument_group('Record options')
255*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-du', '--dwarf_unwinding', action='store_true', help="""Perform
256*288bf522SAndroid Build Coastguard Worker                              unwinding using dwarf instead of fp.""")
257*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-e', '--events', default="", help="""Sample based on event
258*288bf522SAndroid Build Coastguard Worker                              occurences instead of frequency. Format expected is
259*288bf522SAndroid Build Coastguard Worker                              "event_counts event_name". e.g: "10000 cpu-cyles". A few examples
260*288bf522SAndroid Build Coastguard Worker                              of event_name: cpu-cycles, cache-references, cache-misses,
261*288bf522SAndroid Build Coastguard Worker                              branch-instructions, branch-misses""")
262*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-f', '--sample_frequency', type=int, default=6000, help="""Sample
263*288bf522SAndroid Build Coastguard Worker                              frequency""")
264*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('--compile_java_code', action='store_true',
265*288bf522SAndroid Build Coastguard Worker                              help="""On Android N and Android O, we need to compile Java code
266*288bf522SAndroid Build Coastguard Worker                                      into native instructions to profile Java code. Android O
267*288bf522SAndroid Build Coastguard Worker                                      also needs wrap.sh in the apk to use the native
268*288bf522SAndroid Build Coastguard Worker                                      instructions.""")
269*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-np', '--native_program', default="surfaceflinger", help="""Profile
270*288bf522SAndroid Build Coastguard Worker                              a native program. The program should be running on the device.
271*288bf522SAndroid Build Coastguard Worker                              Like -np surfaceflinger.""")
272*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-p', '--app', help="""Profile an Android app, given the package
273*288bf522SAndroid Build Coastguard Worker                              name. Like -p com.example.android.myapp.""")
274*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('--pid', type=int, default=-1, help="""Profile a native program
275*288bf522SAndroid Build Coastguard Worker                              with given pid, the pid should exist on the device.""")
276*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('--record_file', default='perf.data', help='Default is perf.data.')
277*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-sc', '--skip_collection', action='store_true', help="""Skip data
278*288bf522SAndroid Build Coastguard Worker                              collection""")
279*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('--system_wide', action='store_true', help='Profile system wide.')
280*288bf522SAndroid Build Coastguard Worker    record_group.add_argument('-t', '--capture_duration', type=int, default=10, help="""Capture
281*288bf522SAndroid Build Coastguard Worker                              duration in seconds.""")
282*288bf522SAndroid Build Coastguard Worker
283*288bf522SAndroid Build Coastguard Worker    report_group = parser.add_argument_group('Report options')
284*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('-c', '--color', default='hot', choices=['hot', 'dso', 'legacy'],
285*288bf522SAndroid Build Coastguard Worker                              help="""Color theme: hot=percentage of samples, dso=callsite DSO
286*288bf522SAndroid Build Coastguard Worker                                      name, legacy=brendan style""")
287*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--embedded_flamegraph', action='store_true', help="""Generate
288*288bf522SAndroid Build Coastguard Worker                              embedded flamegraph.""")
289*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--kallsyms', help='Set the path to find kernel symbols.')
290*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--min_callchain_percentage', default=0.01, type=float, help="""
291*288bf522SAndroid Build Coastguard Worker                              Set min percentage of callchains shown in the report.
292*288bf522SAndroid Build Coastguard Worker                              It is used to limit nodes shown in the flamegraph. For example,
293*288bf522SAndroid Build Coastguard Worker                              when set to 0.01, only callchains taking >= 0.01%% of the event
294*288bf522SAndroid Build Coastguard Worker                              count of the owner thread are collected in the report.""")
295*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--max_callchain_depth', default=1000000000, type=int, help="""
296*288bf522SAndroid Build Coastguard Worker                              Set maximum depth of callchains shown in the report. It is used
297*288bf522SAndroid Build Coastguard Worker                              to limit the nodes shown in the flamegraph and avoid processing
298*288bf522SAndroid Build Coastguard Worker                              limits. For example, when set to 10, callstacks will be cut after
299*288bf522SAndroid Build Coastguard Worker                              the tenth frame.""")
300*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--no_browser', action='store_true', help="""Don't open report
301*288bf522SAndroid Build Coastguard Worker                              in browser.""")
302*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('-o', '--report_path', default='report.html', help="""Set report
303*288bf522SAndroid Build Coastguard Worker                              path.""")
304*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--one-flamegraph', action='store_true', help="""Generate one
305*288bf522SAndroid Build Coastguard Worker                              flamegraph instead of one for each thread.""")
306*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--symfs', help="""Set the path to find binaries with symbols and
307*288bf522SAndroid Build Coastguard Worker                              debug info.""")
308*288bf522SAndroid Build Coastguard Worker    report_group.add_argument('--title', help='Show a title in the report.')
309*288bf522SAndroid Build Coastguard Worker    parser.add_report_lib_options(
310*288bf522SAndroid Build Coastguard Worker        report_group, sample_filter_group=report_group, sample_filter_with_pid_shortcut=False)
311*288bf522SAndroid Build Coastguard Worker
312*288bf522SAndroid Build Coastguard Worker    debug_group = parser.add_argument_group('Debug options')
313*288bf522SAndroid Build Coastguard Worker    debug_group.add_argument('--disable_adb_root', action='store_true', help="""Force adb to run
314*288bf522SAndroid Build Coastguard Worker                             in non root mode.""")
315*288bf522SAndroid Build Coastguard Worker    args = parser.parse_args()
316*288bf522SAndroid Build Coastguard Worker    process = Process("", 0)
317*288bf522SAndroid Build Coastguard Worker
318*288bf522SAndroid Build Coastguard Worker    if not args.skip_collection:
319*288bf522SAndroid Build Coastguard Worker        if args.pid != -1:
320*288bf522SAndroid Build Coastguard Worker            process.pid = args.pid
321*288bf522SAndroid Build Coastguard Worker            args.native_program = ''
322*288bf522SAndroid Build Coastguard Worker        if args.system_wide:
323*288bf522SAndroid Build Coastguard Worker            process.pid = -1
324*288bf522SAndroid Build Coastguard Worker            args.native_program = ''
325*288bf522SAndroid Build Coastguard Worker
326*288bf522SAndroid Build Coastguard Worker        if args.system_wide:
327*288bf522SAndroid Build Coastguard Worker            process.name = 'system_wide'
328*288bf522SAndroid Build Coastguard Worker        else:
329*288bf522SAndroid Build Coastguard Worker            process.name = args.app or args.native_program or ('Process %d' % args.pid)
330*288bf522SAndroid Build Coastguard Worker        logging.info("Starting data collection stage for '%s'." % process.name)
331*288bf522SAndroid Build Coastguard Worker        if not collect_data(args):
332*288bf522SAndroid Build Coastguard Worker            log_exit("Unable to collect data.")
333*288bf522SAndroid Build Coastguard Worker        if process.pid == 0:
334*288bf522SAndroid Build Coastguard Worker            result, output = AdbHelper().run_and_return_output(['shell', 'pidof', process.name])
335*288bf522SAndroid Build Coastguard Worker            if result:
336*288bf522SAndroid Build Coastguard Worker                try:
337*288bf522SAndroid Build Coastguard Worker                    process.pid = int(output)
338*288bf522SAndroid Build Coastguard Worker                except ValueError:
339*288bf522SAndroid Build Coastguard Worker                    process.pid = 0
340*288bf522SAndroid Build Coastguard Worker        collect_machine_info(process)
341*288bf522SAndroid Build Coastguard Worker    else:
342*288bf522SAndroid Build Coastguard Worker        args.capture_duration = 0
343*288bf522SAndroid Build Coastguard Worker
344*288bf522SAndroid Build Coastguard Worker    sample_filter_fn = None
345*288bf522SAndroid Build Coastguard Worker    if args.one_flamegraph:
346*288bf522SAndroid Build Coastguard Worker        def filter_fn(sample, _symbol, _callchain):
347*288bf522SAndroid Build Coastguard Worker            sample.pid = sample.tid = process.pid
348*288bf522SAndroid Build Coastguard Worker            return True
349*288bf522SAndroid Build Coastguard Worker        sample_filter_fn = filter_fn
350*288bf522SAndroid Build Coastguard Worker        if not args.title:
351*288bf522SAndroid Build Coastguard Worker            args.title = ''
352*288bf522SAndroid Build Coastguard Worker        args.title += '(One Flamegraph)'
353*288bf522SAndroid Build Coastguard Worker
354*288bf522SAndroid Build Coastguard Worker    try:
355*288bf522SAndroid Build Coastguard Worker        parse_samples(process, args, sample_filter_fn)
356*288bf522SAndroid Build Coastguard Worker        generate_threads_offsets(process)
357*288bf522SAndroid Build Coastguard Worker        report_path = output_report(process, args)
358*288bf522SAndroid Build Coastguard Worker        if not args.no_browser:
359*288bf522SAndroid Build Coastguard Worker            open_report_in_browser(report_path)
360*288bf522SAndroid Build Coastguard Worker    except RuntimeError as r:
361*288bf522SAndroid Build Coastguard Worker        if 'maximum recursion depth' in r.__str__():
362*288bf522SAndroid Build Coastguard Worker            log_fatal("Recursion limit exceeded (%s), try --max_callchain_depth." % r)
363*288bf522SAndroid Build Coastguard Worker        raise r
364*288bf522SAndroid Build Coastguard Worker
365*288bf522SAndroid Build Coastguard Worker    logging.info("Flamegraph generated at '%s'." % report_path)
366*288bf522SAndroid Build Coastguard Worker
367*288bf522SAndroid Build Coastguard Worker
368*288bf522SAndroid Build Coastguard Workerif __name__ == "__main__":
369*288bf522SAndroid Build Coastguard Worker    main()
370