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 : %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