Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 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. |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 15 | |
| 16 | import atexit |
| 17 | import argparse |
| 18 | import datetime |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 19 | import hashlib |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 20 | import http.server |
| 21 | import os |
Primiano Tucci | 59f08d5 | 2021-10-19 17:28:36 +0100 | [diff] [blame] | 22 | import re |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 23 | import shutil |
| 24 | import socketserver |
| 25 | import subprocess |
| 26 | import sys |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 27 | import tempfile |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 28 | import time |
| 29 | import webbrowser |
| 30 | |
| 31 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 32 | |
| 33 | # This is not required. It's only used as a fallback if no adb is found on the |
| 34 | # PATH. It's fine if it doesn't exist so this script can be copied elsewhere. |
| 35 | HERMETIC_ADB_PATH = ROOT_DIR + '/buildtools/android_sdk/platform-tools/adb' |
| 36 | |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 37 | # Translates the Android ro.product.cpu.abi into the GN's target_cpu. |
| 38 | ABI_TO_ARCH = { |
| 39 | 'armeabi-v7a': 'arm', |
| 40 | 'arm64-v8a': 'arm64', |
| 41 | 'x86': 'x86', |
| 42 | 'x86_64': 'x64', |
| 43 | } |
| 44 | |
Primiano Tucci | 8e3e1bb | 2021-06-01 20:05:38 +0100 | [diff] [blame] | 45 | MAX_ADB_FAILURES = 15 # 2 seconds between retries, 30 seconds total. |
| 46 | |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 47 | devnull = open(os.devnull, 'rb') |
| 48 | adb_path = None |
| 49 | procs = [] |
| 50 | |
| 51 | |
| 52 | class ANSI: |
| 53 | END = '\033[0m' |
| 54 | BOLD = '\033[1m' |
| 55 | RED = '\033[91m' |
| 56 | BLACK = '\033[30m' |
| 57 | BLUE = '\033[94m' |
| 58 | BG_YELLOW = '\033[43m' |
| 59 | BG_BLUE = '\033[44m' |
| 60 | |
| 61 | |
| 62 | # HTTP Server used to open the trace in the browser. |
| 63 | class HttpHandler(http.server.SimpleHTTPRequestHandler): |
| 64 | |
| 65 | def end_headers(self): |
| 66 | self.send_header('Access-Control-Allow-Origin', '*') |
| 67 | return super().end_headers() |
| 68 | |
| 69 | def do_GET(self): |
| 70 | self.server.last_request = self.path |
| 71 | return super().do_GET() |
| 72 | |
| 73 | def do_POST(self): |
| 74 | self.send_error(404, "File not found") |
| 75 | |
| 76 | |
| 77 | def main(): |
| 78 | atexit.register(kill_all_subprocs_on_exit) |
| 79 | default_out_dir_str = '~/traces/' |
| 80 | default_out_dir = os.path.expanduser(default_out_dir_str) |
| 81 | |
| 82 | examples = '\n'.join([ |
Primiano Tucci | ddf9165 | 2021-05-21 22:59:41 +0100 | [diff] [blame] | 83 | ANSI.BOLD + 'Examples' + ANSI.END, ' -t 10s -b 32mb sched gfx wm -a*', |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 84 | ' -t 5s sched/sched_switch raw_syscalls/sys_enter raw_syscalls/sys_exit', |
| 85 | ' -c /path/to/full-textual-trace.config', '', |
| 86 | ANSI.BOLD + 'Long traces' + ANSI.END, |
| 87 | 'If you want to record a hours long trace and stream it into a file ', |
| 88 | 'you need to pass a full trace config and set write_into_file = true.', |
| 89 | 'See https://perfetto.dev/docs/concepts/config#long-traces .' |
| 90 | ]) |
| 91 | parser = argparse.ArgumentParser( |
| 92 | epilog=examples, formatter_class=argparse.RawTextHelpFormatter) |
| 93 | |
| 94 | help = 'Output file or directory (default: %s)' % default_out_dir_str |
| 95 | parser.add_argument('-o', '--out', default=default_out_dir, help=help) |
| 96 | |
| 97 | help = 'Don\'t open in the browser' |
| 98 | parser.add_argument('-n', '--no-open', action='store_true', help=help) |
| 99 | |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 100 | help = 'Force the use of the sideloaded binaries rather than system daemons' |
| 101 | parser.add_argument('--sideload', action='store_true', help=help) |
| 102 | |
Hector Dearman | 762c18f | 2021-05-24 11:10:52 +0100 | [diff] [blame] | 103 | help = ('Sideload the given binary rather than downloading it. ' + |
Primiano Tucci | ddf9165 | 2021-05-21 22:59:41 +0100 | [diff] [blame] | 104 | 'Implies --sideload') |
| 105 | parser.add_argument('--sideload-path', default=None, help=help) |
| 106 | |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 107 | help = 'Don\'t run `adb root` run as user (only when sideloading)' |
| 108 | parser.add_argument('-u', '--user', action='store_true', help=help) |
| 109 | |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 110 | grp = parser.add_argument_group( |
| 111 | 'Short options: (only when not using -c/--config)') |
| 112 | |
| 113 | help = 'Trace duration N[s,m,h] (default: trace until stopped)' |
| 114 | grp.add_argument('-t', '--time', default='0s', help=help) |
| 115 | |
| 116 | help = 'Ring buffer size N[mb,gb] (default: 32mb)' |
| 117 | grp.add_argument('-b', '--buffer', default='32mb', help=help) |
| 118 | |
Primiano Tucci | ddf9165 | 2021-05-21 22:59:41 +0100 | [diff] [blame] | 119 | help = ('Android (atrace) app names. Can be specified multiple times.\n-a*' + |
| 120 | 'for all apps (without space between a and * or bash will expand it)') |
Lalit Maganti | 484b674 | 2021-05-04 13:38:35 +0100 | [diff] [blame] | 121 | grp.add_argument( |
| 122 | '-a', |
| 123 | '--app', |
Primiano Tucci | ddf9165 | 2021-05-21 22:59:41 +0100 | [diff] [blame] | 124 | metavar='com.myapp', |
Lalit Maganti | 484b674 | 2021-05-04 13:38:35 +0100 | [diff] [blame] | 125 | action='append', |
| 126 | default=[], |
| 127 | help=help) |
| 128 | |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 129 | help = 'sched, gfx, am, wm (see --list)' |
| 130 | grp.add_argument('events', metavar='Atrace events', nargs='*', help=help) |
| 131 | |
| 132 | help = 'sched/sched_switch kmem/kmem (see --list-ftrace)' |
| 133 | grp.add_argument('_', metavar='Ftrace events', nargs='*', help=help) |
| 134 | |
| 135 | help = 'Lists all the categories available' |
| 136 | grp.add_argument('--list', action='store_true', help=help) |
| 137 | |
| 138 | help = 'Lists all the ftrace events available' |
| 139 | grp.add_argument('--list-ftrace', action='store_true', help=help) |
| 140 | |
| 141 | section = ('Full trace config (only when not using short options)') |
| 142 | grp = parser.add_argument_group(section) |
| 143 | |
| 144 | help = 'Can be generated with https://ui.perfetto.dev/#!/record' |
| 145 | grp.add_argument('-c', '--config', default=None, help=help) |
John | d8fa2bd | 2021-06-24 11:13:10 +0800 | [diff] [blame] | 146 | |
| 147 | help = 'Specify the ADB device serial' |
| 148 | grp.add_argument('--serial', '-s', default=None, help=help) |
| 149 | |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 150 | args = parser.parse_args() |
Primiano Tucci | ddf9165 | 2021-05-21 22:59:41 +0100 | [diff] [blame] | 151 | args.sideload = args.sideload or args.sideload_path is not None |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 152 | |
John | d8fa2bd | 2021-06-24 11:13:10 +0800 | [diff] [blame] | 153 | if args.serial: |
| 154 | os.environ["ANDROID_SERIAL"] = args.serial |
| 155 | |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 156 | find_adb() |
| 157 | |
| 158 | if args.list: |
| 159 | adb('shell', 'atrace', '--list_categories').wait() |
| 160 | sys.exit(0) |
| 161 | |
| 162 | if args.list_ftrace: |
| 163 | adb('shell', 'cat /d/tracing/available_events | tr : /').wait() |
| 164 | sys.exit(0) |
| 165 | |
| 166 | if args.config is not None and not os.path.exists(args.config): |
| 167 | prt('Config file not found: %s' % args.config, ANSI.RED) |
| 168 | sys.exit(1) |
| 169 | |
| 170 | if len(args.events) == 0 and args.config is None: |
| 171 | prt('Must either pass short options (e.g. -t 10s sched) or a --config file', |
| 172 | ANSI.RED) |
| 173 | parser.print_help() |
| 174 | sys.exit(1) |
| 175 | |
| 176 | if args.config is None and args.events and os.path.exists(args.events[0]): |
| 177 | prt(('The passed event name "%s" is a local file. ' % args.events[0] + |
| 178 | 'Did you mean to pass -c / --config ?'), ANSI.RED) |
| 179 | sys.exit(1) |
| 180 | |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 181 | perfetto_cmd = 'perfetto' |
| 182 | device_dir = '/data/misc/perfetto-traces/' |
| 183 | |
| 184 | # Check the version of android. If too old (< Q) sideload tracebox. Also use |
| 185 | # use /data/local/tmp as /data/misc/perfetto-traces was introduced only later. |
| 186 | probe_cmd = 'getprop ro.build.version.sdk; getprop ro.product.cpu.abi; whoami' |
| 187 | probe = adb('shell', probe_cmd, stdout=subprocess.PIPE) |
| 188 | lines = probe.communicate()[0].decode().strip().split('\n') |
| 189 | lines = [x.strip() for x in lines] # To strip \r(s) on Windows. |
| 190 | if probe.returncode != 0: |
| 191 | prt('ADB connection failed', ANSI.RED) |
| 192 | sys.exit(1) |
| 193 | api_level = int(lines[0]) |
| 194 | abi = lines[1] |
| 195 | arch = ABI_TO_ARCH.get(abi) |
| 196 | if arch is None: |
| 197 | prt('Unsupported ABI: ' + abi) |
| 198 | sys.exit(1) |
| 199 | shell_user = lines[2] |
| 200 | if api_level < 29 or args.sideload: # 29: Android Q. |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 201 | tracebox_bin = args.sideload_path |
| 202 | if tracebox_bin is None: |
| 203 | tracebox_bin = get_perfetto_prebuilt('tracebox', arch='android-' + arch) |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 204 | perfetto_cmd = '/data/local/tmp/tracebox' |
| 205 | exit_code = adb('push', '--sync', tracebox_bin, perfetto_cmd).wait() |
| 206 | exit_code |= adb('shell', 'chmod 755 ' + perfetto_cmd).wait() |
| 207 | if exit_code != 0: |
| 208 | prt('ADB push failed', ANSI.RED) |
| 209 | sys.exit(1) |
| 210 | device_dir = '/data/local/tmp/' |
| 211 | if shell_user != 'root' and not args.user: |
| 212 | # Run as root if possible as that will give access to more tracing |
| 213 | # capabilities. Non-root still works, but some ftrace events might not be |
| 214 | # available. |
| 215 | adb('root').wait() |
| 216 | |
| 217 | tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M') |
| 218 | fname = '%s-%s.pftrace' % (tstamp, os.urandom(3).hex()) |
| 219 | device_file = device_dir + fname |
| 220 | |
| 221 | cmd = [perfetto_cmd, '--background', '--txt', '-o', device_file] |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 222 | if args.config is not None: |
| 223 | cmd += ['-c', '-'] |
| 224 | else: |
Lalit Maganti | 484b674 | 2021-05-04 13:38:35 +0100 | [diff] [blame] | 225 | cmd += ['-t', args.time, '-b', args.buffer] |
| 226 | for app in args.app: |
Primiano Tucci | ddf9165 | 2021-05-21 22:59:41 +0100 | [diff] [blame] | 227 | cmd += ['--app', '\'' + app + '\''] |
Lalit Maganti | 484b674 | 2021-05-04 13:38:35 +0100 | [diff] [blame] | 228 | cmd += args.events |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 229 | |
| 230 | # Perfetto will error out with a proper message if both a config file and |
| 231 | # short options are specified. No need to replicate that logic. |
| 232 | |
| 233 | # Work out the output file or directory. |
| 234 | if args.out.endswith('/') or os.path.isdir(args.out): |
| 235 | host_dir = args.out |
| 236 | host_file = os.path.join(args.out, fname) |
| 237 | else: |
| 238 | host_file = args.out |
| 239 | host_dir = os.path.dirname(host_file) |
Primiano Tucci | 7c70b69 | 2021-02-16 13:58:39 +0100 | [diff] [blame] | 240 | if host_dir == '': |
| 241 | host_dir = '.' |
| 242 | host_file = './' + host_file |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 243 | if not os.path.exists(host_dir): |
| 244 | shutil.os.makedirs(host_dir) |
| 245 | |
| 246 | with open(args.config or os.devnull, 'rb') as f: |
| 247 | print('Running ' + ' '.join(cmd)) |
| 248 | proc = adb('shell', *cmd, stdin=f, stdout=subprocess.PIPE) |
Primiano Tucci | 59f08d5 | 2021-10-19 17:28:36 +0100 | [diff] [blame] | 249 | proc_out = proc.communicate()[0].decode().strip() |
| 250 | # On older versions of Android (x86_64 emulator running API 22) the output |
| 251 | # looks like: |
| 252 | # WARNING: linker: /data/local/tmp/tracebox: unused DT entry: ... |
| 253 | # WARNING: ... (other 2 WARNING: linker: lines) |
| 254 | # 1234 <-- The actual pid we want. |
| 255 | match = re.search(r'^(\d+)$', proc_out, re.M) |
| 256 | if match is None: |
| 257 | prt('Failed to read the pid from perfetto --background', ANSI.RED) |
| 258 | prt(proc_out) |
| 259 | sys.exit(1) |
| 260 | bg_pid = match.group(1) |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 261 | exit_code = proc.wait() |
| 262 | |
| 263 | if exit_code != 0: |
| 264 | prt('Perfetto invocation failed', ANSI.RED) |
| 265 | sys.exit(1) |
| 266 | |
| 267 | prt('Trace started. Press CTRL+C to stop', ANSI.BLACK + ANSI.BG_BLUE) |
| 268 | logcat = adb('logcat', '-v', 'brief', '-s', 'perfetto', '-b', 'main', '-T', |
| 269 | '1') |
| 270 | |
| 271 | ctrl_c_count = 0 |
Primiano Tucci | 8e3e1bb | 2021-06-01 20:05:38 +0100 | [diff] [blame] | 272 | adb_failure_count = 0 |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 273 | while ctrl_c_count < 2: |
| 274 | try: |
Primiano Tucci | 59f08d5 | 2021-10-19 17:28:36 +0100 | [diff] [blame] | 275 | # On older Android devices adbd doesn't propagate the exit code. Hence |
| 276 | # the RUN/TERM parts. |
| 277 | poll = adb( |
| 278 | 'shell', |
| 279 | 'test -d /proc/%s && echo RUN || echo TERM' % bg_pid, |
| 280 | stdout=subprocess.PIPE) |
| 281 | poll_res = poll.communicate()[0].decode().strip() |
| 282 | if poll_res == 'TERM': |
Primiano Tucci | 8e3e1bb | 2021-06-01 20:05:38 +0100 | [diff] [blame] | 283 | break # Process terminated |
Primiano Tucci | 59f08d5 | 2021-10-19 17:28:36 +0100 | [diff] [blame] | 284 | if poll_res == 'RUN': |
Primiano Tucci | 8e3e1bb | 2021-06-01 20:05:38 +0100 | [diff] [blame] | 285 | # The 'perfetto' cmdline client is still running. If previously we had |
| 286 | # an ADB error, tell the user now it's all right again. |
| 287 | if adb_failure_count > 0: |
| 288 | adb_failure_count = 0 |
| 289 | prt('ADB connection re-established, the trace is still ongoing', |
| 290 | ANSI.BLUE) |
| 291 | time.sleep(0.5) |
| 292 | continue |
| 293 | # Some ADB error happened. This can happen when tracing soon after boot, |
| 294 | # before logging in, when adb gets restarted. |
| 295 | adb_failure_count += 1 |
| 296 | if adb_failure_count >= MAX_ADB_FAILURES: |
| 297 | prt('Too many unrecoverable ADB failures, bailing out', ANSI.RED) |
| 298 | sys.exit(1) |
| 299 | time.sleep(2) |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 300 | except KeyboardInterrupt: |
| 301 | sig = 'TERM' if ctrl_c_count == 0 else 'KILL' |
| 302 | ctrl_c_count += 1 |
| 303 | prt('Stopping the trace (SIG%s)' % sig, ANSI.BLACK + ANSI.BG_YELLOW) |
Primiano Tucci | 8e3e1bb | 2021-06-01 20:05:38 +0100 | [diff] [blame] | 304 | adb('shell', 'kill -%s %s' % (sig, bg_pid)).wait() |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 305 | |
| 306 | logcat.kill() |
| 307 | logcat.wait() |
| 308 | |
| 309 | prt('\n') |
| 310 | prt('Pulling into %s' % host_file, ANSI.BOLD) |
| 311 | adb('pull', device_file, host_file).wait() |
Primiano Tucci | 407f407 | 2021-06-22 10:00:44 +0100 | [diff] [blame] | 312 | adb('shell', 'rm -f ' + device_file).wait() |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 313 | |
| 314 | if not args.no_open: |
| 315 | prt('\n') |
Primiano Tucci | 7c70b69 | 2021-02-16 13:58:39 +0100 | [diff] [blame] | 316 | prt('Opening the trace (%s) in the browser' % host_file) |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 317 | open_trace_in_browser(host_file) |
| 318 | |
| 319 | |
| 320 | def prt(msg, colors=ANSI.END): |
| 321 | print(colors + msg + ANSI.END) |
| 322 | |
| 323 | |
| 324 | def find_adb(): |
| 325 | """ Locate the "right" adb path |
| 326 | |
| 327 | If adb is in the PATH use that (likely what the user wants) otherwise use the |
| 328 | hermetic one in our SDK copy. |
| 329 | """ |
| 330 | global adb_path |
| 331 | for path in ['adb', HERMETIC_ADB_PATH]: |
| 332 | try: |
| 333 | subprocess.call([path, '--version'], stdout=devnull, stderr=devnull) |
| 334 | adb_path = path |
| 335 | break |
| 336 | except OSError: |
| 337 | continue |
| 338 | if adb_path is None: |
Primiano Tucci | fb43b84 | 2021-02-17 08:52:01 +0100 | [diff] [blame] | 339 | sdk_url = 'https://developer.android.com/studio/releases/platform-tools' |
| 340 | prt('Could not find a suitable adb binary in the PATH. ', ANSI.RED) |
| 341 | prt('You can download adb from %s' % sdk_url, ANSI.RED) |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 342 | sys.exit(1) |
| 343 | |
| 344 | |
| 345 | def open_trace_in_browser(path): |
| 346 | # We reuse the HTTP+RPC port because it's the only one allowed by the CSP. |
| 347 | PORT = 9001 |
| 348 | os.chdir(os.path.dirname(path)) |
| 349 | fname = os.path.basename(path) |
| 350 | socketserver.TCPServer.allow_reuse_address = True |
| 351 | with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd: |
| 352 | webbrowser.open_new_tab( |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 353 | 'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' % |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 354 | (PORT, fname)) |
| 355 | while httpd.__dict__.get('last_request') != '/' + fname: |
| 356 | httpd.handle_request() |
| 357 | |
| 358 | |
| 359 | def adb(*args, stdin=devnull, stdout=None): |
| 360 | cmd = [adb_path, *args] |
Primiano Tucci | fb43b84 | 2021-02-17 08:52:01 +0100 | [diff] [blame] | 361 | setpgrp = None |
| 362 | if os.name != 'nt': |
| 363 | # On Linux/Mac, start a new process group so all child processes are killed |
| 364 | # on exit. Unsupported on Windows. |
| 365 | setpgrp = lambda: os.setpgrp() |
| 366 | proc = subprocess.Popen(cmd, stdin=stdin, stdout=stdout, preexec_fn=setpgrp) |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 367 | procs.append(proc) |
| 368 | return proc |
| 369 | |
| 370 | |
| 371 | def kill_all_subprocs_on_exit(): |
| 372 | for p in [p for p in procs if p.poll() is None]: |
| 373 | p.kill() |
| 374 | |
| 375 | |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 376 | def check_hash(file_name, sha_value): |
| 377 | with open(file_name, 'rb') as fd: |
| 378 | file_hash = hashlib.sha1(fd.read()).hexdigest() |
| 379 | return file_hash == sha_value |
| 380 | |
| 381 | |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 382 | # BEGIN_SECTION_GENERATED_BY(roll-prebuilts) |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 383 | # Revision: v21.0 |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 384 | PERFETTO_PREBUILT_MANIFEST = [{ |
| 385 | 'tool': |
| 386 | 'tracebox', |
| 387 | 'arch': |
| 388 | 'android-arm', |
| 389 | 'file_name': |
| 390 | 'tracebox', |
| 391 | 'file_size': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 392 | 993356, |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 393 | 'url': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 394 | 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v21.0/android-arm/tracebox', |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 395 | 'sha256': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 396 | '86df4b8f210fc7fba88881db340182ecebbdecb9418a36a6c21d62863a77c1f6' |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 397 | }, { |
| 398 | 'tool': |
| 399 | 'tracebox', |
| 400 | 'arch': |
| 401 | 'android-arm64', |
| 402 | 'file_name': |
| 403 | 'tracebox', |
| 404 | 'file_size': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 405 | 1522496, |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 406 | 'url': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 407 | 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v21.0/android-arm64/tracebox', |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 408 | 'sha256': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 409 | '22f9a3c750c0ef60281a18a7bc7966427322f3a7d4bb8226bd2c8e37f22100e1' |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 410 | }, { |
| 411 | 'tool': |
| 412 | 'tracebox', |
| 413 | 'arch': |
| 414 | 'android-x86', |
| 415 | 'file_name': |
| 416 | 'tracebox', |
| 417 | 'file_size': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 418 | 1542172, |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 419 | 'url': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 420 | 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v21.0/android-x86/tracebox', |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 421 | 'sha256': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 422 | 'cbb1073c515848753d36d8bda81ce4ed97384c4cca90d6d84a294031d83724e5' |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 423 | }, { |
| 424 | 'tool': |
| 425 | 'tracebox', |
| 426 | 'arch': |
| 427 | 'android-x64', |
| 428 | 'file_name': |
| 429 | 'tracebox', |
| 430 | 'file_size': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 431 | 1780544, |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 432 | 'url': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 433 | 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v21.0/android-x64/tracebox', |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 434 | 'sha256': |
Primiano Tucci | 45646b6 | 2021-11-04 11:18:03 +0000 | [diff] [blame] | 435 | 'f01656ea00156e64a8cd2aa65c1a8814eebecea6280dc3eb14cfc7b7eefda776' |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 436 | }] |
Primiano Tucci | 35e311e | 2021-05-21 20:31:55 +0100 | [diff] [blame] | 437 | |
| 438 | |
Primiano Tucci | d3e40bd | 2021-08-03 10:52:39 +0100 | [diff] [blame] | 439 | # DO NOT EDIT. If you wish to make edits to this code, you need to change only |
| 440 | # //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate |
| 441 | # all the others scripts this is embedded into. |
| 442 | def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None): |
| 443 | """ Downloads the prebuilt, if necessary, and returns its path on disk. """ |
| 444 | |
| 445 | # The first time this is invoked, it downloads the |url| and caches it into |
| 446 | # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the |
| 447 | # cached version. |
| 448 | def download_or_get_cached(file_name, url, sha256): |
| 449 | import os, hashlib, subprocess |
| 450 | dir = os.path.join( |
| 451 | os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts') |
| 452 | os.makedirs(dir, exist_ok=True) |
| 453 | bin_path = os.path.join(dir, file_name) |
| 454 | sha256_path = os.path.join(dir, file_name + '.sha256') |
| 455 | needs_download = True |
| 456 | |
| 457 | # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last |
| 458 | # download is cached into file_name.sha256, just check if that matches. |
| 459 | if os.path.exists(bin_path) and os.path.exists(sha256_path): |
| 460 | with open(sha256_path, 'rb') as f: |
| 461 | digest = f.read().decode() |
| 462 | if digest == sha256: |
| 463 | needs_download = False |
| 464 | |
| 465 | if needs_download: |
| 466 | # Either the filed doesn't exist or the SHA256 doesn't match. |
| 467 | tmp_path = bin_path + '.tmp' |
| 468 | print('Downloading ' + url) |
| 469 | subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url]) |
| 470 | with open(tmp_path, 'rb') as fd: |
| 471 | actual_sha256 = hashlib.sha256(fd.read()).hexdigest() |
| 472 | if actual_sha256 != sha256: |
| 473 | raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' % |
| 474 | (url, actual_sha256, sha256)) |
| 475 | os.chmod(tmp_path, 0o755) |
| 476 | os.rename(tmp_path, bin_path) |
| 477 | with open(sha256_path, 'w') as f: |
| 478 | f.write(sha256) |
| 479 | return bin_path |
| 480 | # --- end of download_or_get_cached() --- |
| 481 | |
| 482 | # --- get_perfetto_prebuilt() function starts here. --- |
| 483 | import os, platform, sys |
| 484 | plat = sys.platform.lower() |
| 485 | machine = platform.machine().lower() |
| 486 | manifest_entry = None |
| 487 | for entry in PERFETTO_PREBUILT_MANIFEST: |
| 488 | # If the caller overrides the arch, just match that (for Android prebuilts). |
| 489 | if arch and entry.get('arch') == arch: |
| 490 | manifest_entry = entry |
| 491 | break |
| 492 | # Otherwise guess the local machine arch. |
| 493 | if entry.get('tool') == tool_name and entry.get( |
| 494 | 'platform') == plat and machine in entry.get('machine', []): |
| 495 | manifest_entry = entry |
| 496 | break |
| 497 | if manifest_entry is None: |
| 498 | if soft_fail: |
| 499 | return None |
| 500 | raise Exception( |
| 501 | ('No prebuilts available for %s-%s\n' % (plat, machine)) + |
| 502 | 'See https://perfetto.dev/docs/contributing/build-instructions') |
| 503 | |
| 504 | return download_or_get_cached( |
| 505 | file_name=manifest_entry['file_name'], |
| 506 | url=manifest_entry['url'], |
| 507 | sha256=manifest_entry['sha256']) |
| 508 | |
| 509 | |
| 510 | # END_SECTION_GENERATED_BY(roll-prebuilts) |
| 511 | |
Primiano Tucci | c2d4c6d | 2021-01-15 11:37:30 +0100 | [diff] [blame] | 512 | if __name__ == '__main__': |
| 513 | sys.exit(main()) |