1#!/usr/bin/env python3 2# Copyright (C) 2021 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 17# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools 18# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 19 20import atexit 21import argparse 22import datetime 23import hashlib 24import http.server 25import os 26import re 27import shutil 28import socketserver 29import subprocess 30import sys 31import time 32import webbrowser 33 34 35# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py 36# This file has been generated by: tools/roll-prebuilts v46.0 37TRACEBOX_MANIFEST = [{ 38 'arch': 39 'mac-amd64', 40 'file_name': 41 'tracebox', 42 'file_size': 43 1597432, 44 'url': 45 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/mac-amd64/tracebox', 46 'sha256': 47 'fda9aa1a57fc6bd85a7f332a436ae0ba8629eac81f5fd0e21a72fe3673b2d609', 48 'platform': 49 'darwin', 50 'machine': ['x86_64'] 51}, { 52 'arch': 53 'mac-arm64', 54 'file_name': 55 'tracebox', 56 'file_size': 57 1459128, 58 'url': 59 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/mac-arm64/tracebox', 60 'sha256': 61 '9a7ee198c0b2ca41edd73afc313193e6f643aaa1a88cafb1e515b76d6dcc47dd', 62 'platform': 63 'darwin', 64 'machine': ['arm64'] 65}, { 66 'arch': 67 'linux-amd64', 68 'file_name': 69 'tracebox', 70 'file_size': 71 2333576, 72 'url': 73 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/linux-amd64/tracebox', 74 'sha256': 75 '3567682e999c9bc36c9c757fe1fc56067963e226573377da21747b7686238012', 76 'platform': 77 'linux', 78 'machine': ['x86_64'] 79}, { 80 'arch': 81 'linux-arm', 82 'file_name': 83 'tracebox', 84 'file_size': 85 1422204, 86 'url': 87 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/linux-arm/tracebox', 88 'sha256': 89 '66890d26ab8f88241b3608ce099e45dee7179ddeca3966dab6e7c1ade78963e3', 90 'platform': 91 'linux', 92 'machine': ['armv6l', 'armv7l', 'armv8l'] 93}, { 94 'arch': 95 'linux-arm64', 96 'file_name': 97 'tracebox', 98 'file_size': 99 2229176, 100 'url': 101 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/linux-arm64/tracebox', 102 'sha256': 103 '425d7a8e88054b5b314bea3db884722a6646d9d7e8230b8ebebc044e57207739', 104 'platform': 105 'linux', 106 'machine': ['aarch64'] 107}, { 108 'arch': 109 'android-arm', 110 'file_name': 111 'tracebox', 112 'file_size': 113 1314720, 114 'url': 115 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/android-arm/tracebox', 116 'sha256': 117 'e3a6905bf8db5af2bd2b83512a745cfd3d86cf842fc81b1d1b3a05f4fb9f15d0' 118}, { 119 'arch': 120 'android-arm64', 121 'file_name': 122 'tracebox', 123 'file_size': 124 2086288, 125 'url': 126 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/android-arm64/tracebox', 127 'sha256': 128 'b506b076e19470afa4ca3f625d596f894a3778b3fe2fd7ad9f97f9e136f25542' 129}, { 130 'arch': 131 'android-x86', 132 'file_name': 133 'tracebox', 134 'file_size': 135 2264088, 136 'url': 137 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/android-x86/tracebox', 138 'sha256': 139 '953cb01053d8094a5e39e05acc2f5b81a73836e699e4e0a7469c0cfa7c820364' 140}, { 141 'arch': 142 'android-x64', 143 'file_name': 144 'tracebox', 145 'file_size': 146 2114328, 147 'url': 148 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v46.0/android-x64/tracebox', 149 'sha256': 150 '570d475684bcc93ae8b6b8a5566887337d94aff91dea2270a0feb1c0bec00a8b' 151}] 152 153# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py 154 155# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py 156# Copyright (C) 2021 The Android Open Source Project 157# 158# Licensed under the Apache License, Version 2.0 (the "License"); 159# you may not use this file except in compliance with the License. 160# You may obtain a copy of the License at 161# 162# http://www.apache.org/licenses/LICENSE-2.0 163# 164# Unless required by applicable law or agreed to in writing, software 165# distributed under the License is distributed on an "AS IS" BASIS, 166# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 167# See the License for the specific language governing permissions and 168# limitations under the License. 169""" 170Functions to fetch pre-pinned Perfetto prebuilts. 171 172This function is used in different places: 173- Into the //tools/{trace_processor, traceconv} scripts, which are just plain 174 wrappers around executables. 175- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain 176 some other hand-written python code. 177 178The manifest argument looks as follows: 179TRACECONV_MANIFEST = [ 180 { 181 'arch': 'mac-amd64', 182 'file_name': 'traceconv', 183 'file_size': 7087080, 184 'url': https://commondatastorage.googleapis.com/.../trace_to_text', 185 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490', 186 'platform': 'darwin', 187 'machine': 'x86_64' 188 }, 189 ... 190] 191 192The intended usage is: 193 194 from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST 195 bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST) 196 subprocess.call(bin_path, ...) 197""" 198 199import hashlib 200import os 201import platform 202import random 203import subprocess 204import sys 205 206 207def download_or_get_cached(file_name, url, sha256): 208 """ Downloads a prebuilt or returns a cached version 209 210 The first time this is invoked, it downloads the |url| and caches it into 211 ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it 212 just runs the cached version. 213 """ 214 dir = os.path.join( 215 os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts') 216 os.makedirs(dir, exist_ok=True) 217 bin_path = os.path.join(dir, file_name) 218 sha256_path = os.path.join(dir, file_name + '.sha256') 219 needs_download = True 220 221 # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last 222 # download is cached into file_name.sha256, just check if that matches. 223 if os.path.exists(bin_path) and os.path.exists(sha256_path): 224 with open(sha256_path, 'rb') as f: 225 digest = f.read().decode() 226 if digest == sha256: 227 needs_download = False 228 229 if needs_download: # The file doesn't exist or the SHA256 doesn't match. 230 # Use a unique random file to guard against concurrent executions. 231 # See https://github.com/google/perfetto/issues/786 . 232 tmp_path = '%s.%d.tmp' % (bin_path, random.randint(0, 100000)) 233 print('Downloading ' + url) 234 subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url]) 235 with open(tmp_path, 'rb') as fd: 236 actual_sha256 = hashlib.sha256(fd.read()).hexdigest() 237 if actual_sha256 != sha256: 238 raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' % 239 (url, actual_sha256, sha256)) 240 os.chmod(tmp_path, 0o755) 241 os.replace(tmp_path, bin_path) 242 with open(tmp_path, 'w') as f: 243 f.write(sha256) 244 os.replace(tmp_path, sha256_path) 245 return bin_path 246 247 248def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None): 249 """ Downloads the prebuilt, if necessary, and returns its path on disk. """ 250 plat = sys.platform.lower() 251 machine = platform.machine().lower() 252 manifest_entry = None 253 for entry in manifest: 254 # If the caller overrides the arch, just match that (for Android prebuilts). 255 if arch: 256 if entry.get('arch') == arch: 257 manifest_entry = entry 258 break 259 continue 260 # Otherwise guess the local machine arch. 261 if entry.get('platform') == plat and machine in entry.get('machine', []): 262 manifest_entry = entry 263 break 264 if manifest_entry is None: 265 if soft_fail: 266 return None 267 raise Exception( 268 ('No prebuilts available for %s-%s\n' % (plat, machine)) + 269 'See https://perfetto.dev/docs/contributing/build-instructions') 270 271 return download_or_get_cached( 272 file_name=manifest_entry['file_name'], 273 url=manifest_entry['url'], 274 sha256=manifest_entry['sha256']) 275 276 277def run_perfetto_prebuilt(manifest): 278 bin_path = get_perfetto_prebuilt(manifest) 279 if sys.platform.lower() == 'win32': 280 sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]])) 281 os.execv(bin_path, [bin_path] + sys.argv[1:]) 282 283# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py 284 285# ----- Amalgamator: begin of python/perfetto/common/repo_utils.py 286# Copyright (C) 2021 The Android Open Source Project 287# 288# Licensed under the Apache License, Version 2.0 (the "License"); 289# you may not use this file except in compliance with the License. 290# You may obtain a copy of the License at 291# 292# http://www.apache.org/licenses/LICENSE-2.0 293# 294# Unless required by applicable law or agreed to in writing, software 295# distributed under the License is distributed on an "AS IS" BASIS, 296# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 297# See the License for the specific language governing permissions and 298# limitations under the License. 299 300import os 301 302 303def repo_root(): 304 """ Finds the repo root by traversing up the hierarchy 305 306 This is for use in scripts that get amalgamated, where _file_ can be either 307 python/perfetto/... or tools/amalgamated_tool. 308 """ 309 path = os.path.dirname(os.path.abspath(__file__)) # amalgamator:nocheck 310 last_dir = '' 311 while path and path != last_dir: 312 if os.path.exists(os.path.join(path, 'perfetto.rc')): 313 return path 314 last_dir = path 315 path = os.path.dirname(path) 316 return None 317 318 319def repo_dir(rel_path): 320 return os.path.join(repo_root() or '', rel_path) 321 322# ----- Amalgamator: end of python/perfetto/common/repo_utils.py 323 324# This is not required. It's only used as a fallback if no adb is found on the 325# PATH. It's fine if it doesn't exist so this script can be copied elsewhere. 326HERMETIC_ADB_PATH = repo_dir('buildtools/android_sdk/platform-tools/adb') 327 328# Translates the Android ro.product.cpu.abi into the GN's target_cpu. 329ABI_TO_ARCH = { 330 'armeabi-v7a': 'arm', 331 'arm64-v8a': 'arm64', 332 'x86': 'x86', 333 'x86_64': 'x64', 334} 335 336MAX_ADB_FAILURES = 15 # 2 seconds between retries, 30 seconds total. 337 338devnull = open(os.devnull, 'rb') 339adb_path = None 340procs = [] 341 342 343class ANSI: 344 END = '\033[0m' 345 BOLD = '\033[1m' 346 RED = '\033[91m' 347 BLACK = '\033[30m' 348 BLUE = '\033[94m' 349 BG_YELLOW = '\033[43m' 350 BG_BLUE = '\033[44m' 351 352 353# HTTP Server used to open the trace in the browser. 354class HttpHandler(http.server.SimpleHTTPRequestHandler): 355 356 def end_headers(self): 357 self.send_header('Access-Control-Allow-Origin', self.server.allow_origin) 358 self.send_header('Cache-Control', 'no-cache') 359 super().end_headers() 360 361 def do_GET(self): 362 if self.path != '/' + self.server.expected_fname: 363 self.send_error(404, "File not found") 364 return 365 366 self.server.fname_get_completed = True 367 super().do_GET() 368 369 def do_POST(self): 370 self.send_error(404, "File not found") 371 372 373def main(): 374 atexit.register(kill_all_subprocs_on_exit) 375 default_out_dir_str = '~/traces/' 376 default_out_dir = os.path.expanduser(default_out_dir_str) 377 378 examples = '\n'.join([ 379 ANSI.BOLD + 'Examples' + ANSI.END, ' -t 10s -b 32mb sched gfx wm -a*', 380 ' -t 5s sched/sched_switch raw_syscalls/sys_enter raw_syscalls/sys_exit', 381 ' -c /path/to/full-textual-trace.config', '', 382 ANSI.BOLD + 'Long traces' + ANSI.END, 383 'If you want to record a hours long trace and stream it into a file ', 384 'you need to pass a full trace config and set write_into_file = true.', 385 'See https://perfetto.dev/docs/concepts/config#long-traces .' 386 ]) 387 parser = argparse.ArgumentParser( 388 epilog=examples, formatter_class=argparse.RawTextHelpFormatter) 389 390 help = 'Output file or directory (default: %s)' % default_out_dir_str 391 parser.add_argument('-o', '--out', default=default_out_dir, help=help) 392 393 help = 'Don\'t open or serve the trace' 394 parser.add_argument('-n', '--no-open', action='store_true', help=help) 395 396 help = 'Don\'t open in browser, but still serve trace (good for remote use)' 397 parser.add_argument('--no-open-browser', action='store_true', help=help) 398 399 help = 'The web address used to open trace files' 400 parser.add_argument('--origin', default='https://ui.perfetto.dev', help=help) 401 402 help = 'Force the use of the sideloaded binaries rather than system daemons' 403 parser.add_argument('--sideload', action='store_true', help=help) 404 405 help = ('Sideload the given binary rather than downloading it. ' + 406 'Implies --sideload') 407 parser.add_argument('--sideload-path', default=None, help=help) 408 409 help = 'Ignores any tracing guardrails which might be used' 410 parser.add_argument('--no-guardrails', action='store_true', help=help) 411 412 help = 'Don\'t run `adb root` run as user (only when sideloading)' 413 parser.add_argument('-u', '--user', action='store_true', help=help) 414 415 help = 'Specify the ADB device serial' 416 parser.add_argument('--serial', '-s', default=None, help=help) 417 418 grp = parser.add_argument_group( 419 'Short options: (only when not using -c/--config)') 420 421 help = 'Trace duration N[s,m,h] (default: trace until stopped)' 422 grp.add_argument('-t', '--time', default='0s', help=help) 423 424 help = 'Ring buffer size N[mb,gb] (default: 32mb)' 425 grp.add_argument('-b', '--buffer', default='32mb', help=help) 426 427 help = ('Android (atrace) app names. Can be specified multiple times.\n-a*' + 428 'for all apps (without space between a and * or bash will expand it)') 429 grp.add_argument( 430 '-a', 431 '--app', 432 metavar='com.myapp', 433 action='append', 434 default=[], 435 help=help) 436 437 help = 'sched, gfx, am, wm (see --list)' 438 grp.add_argument('events', metavar='Atrace events', nargs='*', help=help) 439 440 help = 'sched/sched_switch kmem/kmem (see --list-ftrace)' 441 grp.add_argument('_', metavar='Ftrace events', nargs='*', help=help) 442 443 help = 'Lists all the categories available' 444 grp.add_argument('--list', action='store_true', help=help) 445 446 help = 'Lists all the ftrace events available' 447 grp.add_argument('--list-ftrace', action='store_true', help=help) 448 449 section = ('Full trace config (only when not using short options)') 450 grp = parser.add_argument_group(section) 451 452 help = 'Can be generated with https://ui.perfetto.dev/#!/record' 453 grp.add_argument('-c', '--config', default=None, help=help) 454 455 help = 'Parse input from --config as binary proto (default: parse as text)' 456 grp.add_argument('--bin', action='store_true', help=help) 457 458 help = ('Pass the trace through the trace reporter API. Only works when ' 459 'using the full trace config (-c) with the reporter package name ' 460 "'android.perfetto.cts.reporter' and the reporter class name " 461 "'android.perfetto.cts.reporter.PerfettoReportService' with the " 462 'reporter installed on the device (see ' 463 'tools/install_test_reporter_app.py).') 464 grp.add_argument('--reporter-api', action='store_true', help=help) 465 466 args = parser.parse_args() 467 args.sideload = args.sideload or args.sideload_path is not None 468 469 if args.serial: 470 os.environ["ANDROID_SERIAL"] = args.serial 471 472 find_adb() 473 474 if args.list: 475 adb('shell', 'atrace', '--list_categories').wait() 476 sys.exit(0) 477 478 if args.list_ftrace: 479 adb('shell', 'cat /d/tracing/available_events | tr : /').wait() 480 sys.exit(0) 481 482 if args.config is not None and not os.path.exists(args.config): 483 prt('Config file not found: %s' % args.config, ANSI.RED) 484 sys.exit(1) 485 486 if len(args.events) == 0 and args.config is None: 487 prt('Must either pass short options (e.g. -t 10s sched) or a --config file', 488 ANSI.RED) 489 parser.print_help() 490 sys.exit(1) 491 492 if args.config is None and args.events and os.path.exists(args.events[0]): 493 prt(('The passed event name "%s" is a local file. ' % args.events[0] + 494 'Did you mean to pass -c / --config ?'), ANSI.RED) 495 sys.exit(1) 496 497 if args.reporter_api and not args.config: 498 prt('Must pass --config when using --reporter-api', ANSI.RED) 499 parser.print_help() 500 sys.exit(1) 501 502 perfetto_cmd = 'perfetto' 503 device_dir = '/data/misc/perfetto-traces/' 504 505 # Check the version of android. If too old (< Q) sideload tracebox. Also use 506 # use /data/local/tmp as /data/misc/perfetto-traces was introduced only later. 507 probe_cmd = 'getprop ro.build.version.sdk; getprop ro.product.cpu.abi; whoami' 508 probe = adb('shell', probe_cmd, stdout=subprocess.PIPE) 509 lines = probe.communicate()[0].decode().strip().split('\n') 510 lines = [x.strip() for x in lines] # To strip \r(s) on Windows. 511 if probe.returncode != 0: 512 prt('ADB connection failed', ANSI.RED) 513 sys.exit(1) 514 api_level = int(lines[0]) 515 abi = lines[1] 516 arch = ABI_TO_ARCH.get(abi) 517 if arch is None: 518 prt('Unsupported ABI: ' + abi) 519 sys.exit(1) 520 shell_user = lines[2] 521 if api_level < 29 or args.sideload: # 29: Android Q. 522 tracebox_bin = args.sideload_path 523 if tracebox_bin is None: 524 tracebox_bin = get_perfetto_prebuilt( 525 TRACEBOX_MANIFEST, arch='android-' + arch) 526 perfetto_cmd = '/data/local/tmp/tracebox' 527 exit_code = adb('push', '--sync', tracebox_bin, perfetto_cmd).wait() 528 exit_code |= adb('shell', 'chmod 755 ' + perfetto_cmd).wait() 529 if exit_code != 0: 530 prt('ADB push failed', ANSI.RED) 531 sys.exit(1) 532 device_dir = '/data/local/tmp/' 533 if shell_user != 'root' and not args.user: 534 # Run as root if possible as that will give access to more tracing 535 # capabilities. Non-root still works, but some ftrace events might not be 536 # available. 537 adb('root').wait() 538 539 tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M') 540 fname = '%s-%s.pftrace' % (tstamp, os.urandom(3).hex()) 541 device_file = device_dir + fname 542 543 cmd = [perfetto_cmd, '--background'] 544 if not args.bin: 545 cmd.append('--txt') 546 547 if args.no_guardrails: 548 cmd.append('--no-guardrails') 549 550 if args.reporter_api: 551 # Remove all old reporter files to avoid polluting the file we will extract 552 # later. 553 adb('shell', 554 'rm /sdcard/Android/data/android.perfetto.cts.reporter/files/*').wait() 555 cmd.append('--upload') 556 else: 557 cmd.extend(['-o', device_file]) 558 559 on_device_config = None 560 on_host_config = None 561 if args.config is not None: 562 cmd += ['-c', '-'] 563 if api_level < 24: 564 # adb shell does not redirect stdin. Push the config on a temporary file 565 # on the device. 566 mktmp = adb( 567 'shell', 568 'mktemp', 569 '--tmpdir', 570 '/data/local/tmp', 571 stdout=subprocess.PIPE) 572 on_device_config = mktmp.communicate()[0].decode().strip().strip() 573 if mktmp.returncode != 0: 574 prt('Failed to create config on device', ANSI.RED) 575 sys.exit(1) 576 exit_code = adb('push', '--sync', args.config, on_device_config).wait() 577 if exit_code != 0: 578 prt('Failed to push config on device', ANSI.RED) 579 sys.exit(1) 580 cmd = ['cat', on_device_config, '|'] + cmd 581 else: 582 on_host_config = args.config 583 else: 584 cmd += ['-t', args.time, '-b', args.buffer] 585 for app in args.app: 586 cmd += ['--app', '\'' + app + '\''] 587 cmd += args.events 588 589 # Work out the output file or directory. 590 if args.out.endswith('/') or os.path.isdir(args.out): 591 host_dir = args.out 592 host_file = os.path.join(args.out, fname) 593 else: 594 host_file = args.out 595 host_dir = os.path.dirname(host_file) 596 if host_dir == '': 597 host_dir = '.' 598 host_file = './' + host_file 599 if not os.path.exists(host_dir): 600 shutil.os.makedirs(host_dir) 601 602 with open(on_host_config or os.devnull, 'rb') as f: 603 print('Running ' + ' '.join(cmd)) 604 proc = adb('shell', *cmd, stdin=f, stdout=subprocess.PIPE) 605 proc_out = proc.communicate()[0].decode().strip() 606 if on_device_config is not None: 607 adb('shell', 'rm', on_device_config).wait() 608 # On older versions of Android (x86_64 emulator running API 22) the output 609 # looks like: 610 # WARNING: linker: /data/local/tmp/tracebox: unused DT entry: ... 611 # WARNING: ... (other 2 WARNING: linker: lines) 612 # 1234 <-- The actual pid we want. 613 match = re.search(r'^(\d+)$', proc_out, re.M) 614 if match is None: 615 prt('Failed to read the pid from perfetto --background', ANSI.RED) 616 prt(proc_out) 617 sys.exit(1) 618 bg_pid = match.group(1) 619 exit_code = proc.wait() 620 621 if exit_code != 0: 622 prt('Perfetto invocation failed', ANSI.RED) 623 sys.exit(1) 624 625 prt('Trace started. Press CTRL+C to stop', ANSI.BLACK + ANSI.BG_BLUE) 626 logcat = adb('logcat', '-v', 'brief', '-s', 'perfetto', '-b', 'main', '-T', 627 '1') 628 629 ctrl_c_count = 0 630 adb_failure_count = 0 631 while ctrl_c_count < 2: 632 try: 633 # On older Android devices adbd doesn't propagate the exit code. Hence 634 # the RUN/TERM parts. 635 poll = adb( 636 'shell', 637 'test -d /proc/%s && echo RUN || echo TERM' % bg_pid, 638 stdout=subprocess.PIPE) 639 poll_res = poll.communicate()[0].decode().strip() 640 if poll_res == 'TERM': 641 break # Process terminated 642 if poll_res == 'RUN': 643 # The 'perfetto' cmdline client is still running. If previously we had 644 # an ADB error, tell the user now it's all right again. 645 if adb_failure_count > 0: 646 adb_failure_count = 0 647 prt('ADB connection re-established, the trace is still ongoing', 648 ANSI.BLUE) 649 time.sleep(0.5) 650 continue 651 # Some ADB error happened. This can happen when tracing soon after boot, 652 # before logging in, when adb gets restarted. 653 adb_failure_count += 1 654 if adb_failure_count >= MAX_ADB_FAILURES: 655 prt('Too many unrecoverable ADB failures, bailing out', ANSI.RED) 656 sys.exit(1) 657 time.sleep(2) 658 except KeyboardInterrupt: 659 sig = 'TERM' if ctrl_c_count == 0 else 'KILL' 660 ctrl_c_count += 1 661 prt('Stopping the trace (SIG%s)' % sig, ANSI.BLACK + ANSI.BG_YELLOW) 662 adb('shell', 'kill -%s %s' % (sig, bg_pid)).wait() 663 664 logcat.kill() 665 logcat.wait() 666 667 if args.reporter_api: 668 prt('Waiting a few seconds to allow reporter to copy trace') 669 time.sleep(5) 670 671 ret = adb( 672 'shell', 673 'cp /sdcard/Android/data/android.perfetto.cts.reporter/files/* ' + 674 device_file).wait() 675 if ret != 0: 676 prt('Failed to extract reporter trace', ANSI.RED) 677 sys.exit(1) 678 679 prt('\n') 680 prt('Pulling into %s' % host_file, ANSI.BOLD) 681 adb('pull', device_file, host_file).wait() 682 adb('shell', 'rm -f ' + device_file).wait() 683 684 if not args.no_open: 685 prt('\n') 686 prt('Opening the trace (%s) in the browser' % host_file) 687 open_browser = not args.no_open_browser 688 open_trace_in_browser(host_file, open_browser, args.origin) 689 690 691def prt(msg, colors=ANSI.END): 692 print(colors + msg + ANSI.END) 693 694 695def find_adb(): 696 """ Locate the "right" adb path 697 698 If adb is in the PATH use that (likely what the user wants) otherwise use the 699 hermetic one in our SDK copy. 700 """ 701 global adb_path 702 for path in ['adb', HERMETIC_ADB_PATH]: 703 try: 704 subprocess.call([path, '--version'], stdout=devnull, stderr=devnull) 705 adb_path = path 706 break 707 except OSError: 708 continue 709 if adb_path is None: 710 sdk_url = 'https://developer.android.com/studio/releases/platform-tools' 711 prt('Could not find a suitable adb binary in the PATH. ', ANSI.RED) 712 prt('You can download adb from %s' % sdk_url, ANSI.RED) 713 sys.exit(1) 714 715 716def open_trace_in_browser(path, open_browser, origin): 717 # We reuse the HTTP+RPC port because it's the only one allowed by the CSP. 718 PORT = 9001 719 path = os.path.abspath(path) 720 os.chdir(os.path.dirname(path)) 721 fname = os.path.basename(path) 722 socketserver.TCPServer.allow_reuse_address = True 723 with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd: 724 address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}&referrer=record_android_trace' 725 if open_browser: 726 webbrowser.open_new_tab(address) 727 else: 728 print(f'Open URL in browser: {address}') 729 730 httpd.expected_fname = fname 731 httpd.fname_get_completed = None 732 httpd.allow_origin = origin 733 while httpd.fname_get_completed is None: 734 httpd.handle_request() 735 736 737def adb(*args, stdin=devnull, stdout=None): 738 cmd = [adb_path, *args] 739 setpgrp = None 740 if os.name != 'nt': 741 # On Linux/Mac, start a new process group so all child processes are killed 742 # on exit. Unsupported on Windows. 743 setpgrp = lambda: os.setpgrp() 744 proc = subprocess.Popen(cmd, stdin=stdin, stdout=stdout, preexec_fn=setpgrp) 745 procs.append(proc) 746 return proc 747 748 749def kill_all_subprocs_on_exit(): 750 for p in [p for p in procs if p.poll() is None]: 751 p.kill() 752 753 754def check_hash(file_name, sha_value): 755 with open(file_name, 'rb') as fd: 756 file_hash = hashlib.sha1(fd.read()).hexdigest() 757 return file_hash == sha_value 758 759 760if __name__ == '__main__': 761 sys.exit(main()) 762