blob: 6e32c4bfd4393c3eb70838cac5d9327224b2bc40 [file] [log] [blame]
Florian Mayerc8aa81c2021-04-19 15:16:15 +01001#!/usr/bin/env python3
Florian Mayer801349e2018-11-29 10:15:25 +00002# Copyright (C) 2017 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
Primiano Tucci11d94e12022-08-02 17:44:33 +010016# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
17# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
18# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
19
Florian Mayer801349e2018-11-29 10:15:25 +000020from __future__ import absolute_import
21from __future__ import division
22from __future__ import print_function
23
24import argparse
25import atexit
Florian Mayer801349e2018-11-29 10:15:25 +000026import os
Florian Mayer92c80d82019-09-25 14:00:01 +010027import shutil
Florian Mayer801349e2018-11-29 10:15:25 +000028import signal
29import subprocess
30import sys
31import tempfile
32import time
Florian Mayer15866392020-04-02 11:52:16 +020033import uuid
Florian Mayer801349e2018-11-29 10:15:25 +000034
Primiano Tucci11d94e12022-08-02 17:44:33 +010035
36# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
Hector Dearmane812c092023-05-02 17:45:42 +010037# This file has been generated by: /Users/hjd/src/perfetto/tools/roll-prebuilts v34.0
Primiano Tucci11d94e12022-08-02 17:44:33 +010038TRACECONV_MANIFEST = [{
39 'arch':
40 'mac-amd64',
41 'file_name':
42 'traceconv',
43 'file_size':
Hector Dearmane812c092023-05-02 17:45:42 +010044 7904536,
Primiano Tucci11d94e12022-08-02 17:44:33 +010045 'url':
Hector Dearmane812c092023-05-02 17:45:42 +010046 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-amd64/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +010047 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +010048 '037f84ac943f3f4d75447c668cc49c966fe3d85eca3a455c958b24fc6a9e314a',
Primiano Tucci11d94e12022-08-02 17:44:33 +010049 'platform':
50 'darwin',
51 'machine': ['x86_64']
52}, {
53 'arch':
54 'mac-arm64',
55 'file_name':
56 'traceconv',
57 'file_size':
Lalit Magantib0bb3642023-04-27 13:40:05 +010058 6554600,
Primiano Tucci11d94e12022-08-02 17:44:33 +010059 'url':
Hector Dearmane812c092023-05-02 17:45:42 +010060 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/mac-arm64/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +010061 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +010062 'eda545ef4fa37fdfa1b47ced7cbbe0aa3c0df9bd161cacd7c78e6c55aef98d20',
Primiano Tucci11d94e12022-08-02 17:44:33 +010063 'platform':
64 'darwin',
65 'machine': ['arm64']
66}, {
67 'arch':
68 'linux-amd64',
69 'file_name':
70 'traceconv',
71 'file_size':
Hector Dearmane812c092023-05-02 17:45:42 +010072 7664384,
Primiano Tucci11d94e12022-08-02 17:44:33 +010073 'url':
Hector Dearmane812c092023-05-02 17:45:42 +010074 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-amd64/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +010075 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +010076 '24285e6e0e873d393fa5a993bac18ec8e1ab5fae6f4e3453214e095ef36e4c45',
Primiano Tucci11d94e12022-08-02 17:44:33 +010077 'platform':
78 'linux',
79 'machine': ['x86_64']
80}, {
81 'arch':
82 'linux-arm',
83 'file_name':
84 'traceconv',
85 'file_size':
Hector Dearmane812c092023-05-02 17:45:42 +010086 5657944,
Primiano Tucci11d94e12022-08-02 17:44:33 +010087 'url':
Hector Dearmane812c092023-05-02 17:45:42 +010088 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +010089 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +010090 'c9af3d976f849fc75e96c2c552cb14fcc9eacce6fe7c45c4a8289080b0f66706',
Primiano Tucci11d94e12022-08-02 17:44:33 +010091 'platform':
92 'linux',
93 'machine': ['armv6l', 'armv7l', 'armv8l']
94}, {
95 'arch':
96 'linux-arm64',
97 'file_name':
98 'traceconv',
99 'file_size':
Hector Dearmane812c092023-05-02 17:45:42 +0100100 7184224,
Primiano Tucci11d94e12022-08-02 17:44:33 +0100101 'url':
Hector Dearmane812c092023-05-02 17:45:42 +0100102 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/linux-arm64/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100103 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +0100104 'c6dc936492d58a40cd8e0b58abc46bd479e0c1c387cd1ba29198a6c9b2000d7a',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100105 'platform':
106 'linux',
107 'machine': ['aarch64']
108}, {
109 'arch':
110 'android-arm',
111 'file_name':
112 'traceconv',
113 'file_size':
Lalit Magantib0bb3642023-04-27 13:40:05 +0100114 5325260,
Primiano Tucci11d94e12022-08-02 17:44:33 +0100115 'url':
Hector Dearmane812c092023-05-02 17:45:42 +0100116 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100117 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +0100118 '963267dcb58cdde9f61a952e5cb7f3557833209d3251e7fdcefc3b52db54f77b'
Primiano Tucci11d94e12022-08-02 17:44:33 +0100119}, {
120 'arch':
121 'android-arm64',
122 'file_name':
123 'traceconv',
124 'file_size':
Lalit Magantib0bb3642023-04-27 13:40:05 +0100125 6572688,
Primiano Tucci11d94e12022-08-02 17:44:33 +0100126 'url':
Hector Dearmane812c092023-05-02 17:45:42 +0100127 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-arm64/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100128 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +0100129 '87373c351fe5e947826cd957438cab8a37a352bf83b1cbbb15fe276eee9d873a'
Primiano Tucci11d94e12022-08-02 17:44:33 +0100130}, {
131 'arch':
132 'android-x86',
133 'file_name':
134 'traceconv',
135 'file_size':
Lalit Magantib0bb3642023-04-27 13:40:05 +0100136 7303588,
Primiano Tucci11d94e12022-08-02 17:44:33 +0100137 'url':
Hector Dearmane812c092023-05-02 17:45:42 +0100138 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x86/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100139 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +0100140 'dfc4e714963b5ed662d29d6028ffa69e67f8cd2f9a28223f715437a260fd456f'
Primiano Tucci11d94e12022-08-02 17:44:33 +0100141}, {
142 'arch':
143 'android-x64',
144 'file_name':
145 'traceconv',
146 'file_size':
Lalit Magantib0bb3642023-04-27 13:40:05 +0100147 7482056,
Primiano Tucci11d94e12022-08-02 17:44:33 +0100148 'url':
Hector Dearmane812c092023-05-02 17:45:42 +0100149 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/android-x64/traceconv',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100150 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +0100151 '79c666c629fcffd810635270b45e58b40ed253d22650f41550057e5d8f8c49a7'
Primiano Tucci11d94e12022-08-02 17:44:33 +0100152}, {
153 'arch':
154 'windows-amd64',
155 'file_name':
156 'traceconv.exe',
157 'file_size':
Hector Dearmane812c092023-05-02 17:45:42 +0100158 7072768,
Primiano Tucci11d94e12022-08-02 17:44:33 +0100159 'url':
Hector Dearmane812c092023-05-02 17:45:42 +0100160 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v34.0/windows-amd64/traceconv.exe',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100161 'sha256':
Hector Dearmane812c092023-05-02 17:45:42 +0100162 '40fac80fdeae443a924e160650c94629e6463c1fb5a4f04f4ef6e9e5e72a3965',
Primiano Tucci11d94e12022-08-02 17:44:33 +0100163 'platform':
164 'win32',
165 'machine': ['amd64']
166}]
167
168# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/traceconv.py
169
170# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
171# Copyright (C) 2021 The Android Open Source Project
172#
173# Licensed under the Apache License, Version 2.0 (the "License");
174# you may not use this file except in compliance with the License.
175# You may obtain a copy of the License at
176#
177# http://www.apache.org/licenses/LICENSE-2.0
178#
179# Unless required by applicable law or agreed to in writing, software
180# distributed under the License is distributed on an "AS IS" BASIS,
181# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
182# See the License for the specific language governing permissions and
183# limitations under the License.
184"""
185Functions to fetch pre-pinned Perfetto prebuilts.
186
187This function is used in different places:
188- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
189 wrappers around executables.
190- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
191 some other hand-written python code.
192
193The manifest argument looks as follows:
194TRACECONV_MANIFEST = [
195 {
196 'arch': 'mac-amd64',
197 'file_name': 'traceconv',
198 'file_size': 7087080,
199 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
200 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
201 'platform': 'darwin',
202 'machine': 'x86_64'
203 },
204 ...
205]
206
207The intended usage is:
208
209 from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
210 bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
211 subprocess.call(bin_path, ...)
212"""
213
214import hashlib
215import os
216import platform
217import subprocess
218import sys
219
220
221def download_or_get_cached(file_name, url, sha256):
222 """ Downloads a prebuilt or returns a cached version
223
224 The first time this is invoked, it downloads the |url| and caches it into
Joseph Koshy5efb8a12022-10-19 12:39:29 +0100225 ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
226 just runs the cached version.
Primiano Tucci11d94e12022-08-02 17:44:33 +0100227 """
228 dir = os.path.join(
229 os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
230 os.makedirs(dir, exist_ok=True)
231 bin_path = os.path.join(dir, file_name)
232 sha256_path = os.path.join(dir, file_name + '.sha256')
233 needs_download = True
234
235 # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
236 # download is cached into file_name.sha256, just check if that matches.
237 if os.path.exists(bin_path) and os.path.exists(sha256_path):
238 with open(sha256_path, 'rb') as f:
239 digest = f.read().decode()
240 if digest == sha256:
241 needs_download = False
242
243 if needs_download:
244 # Either the filed doesn't exist or the SHA256 doesn't match.
245 tmp_path = bin_path + '.tmp'
246 print('Downloading ' + url)
247 subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
248 with open(tmp_path, 'rb') as fd:
249 actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
250 if actual_sha256 != sha256:
251 raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
252 (url, actual_sha256, sha256))
253 os.chmod(tmp_path, 0o755)
Lalit Maganti1480b862022-10-27 15:02:54 +0100254 os.replace(tmp_path, bin_path)
Primiano Tucci11d94e12022-08-02 17:44:33 +0100255 with open(sha256_path, 'w') as f:
256 f.write(sha256)
257 return bin_path
258
259
260def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
261 """ Downloads the prebuilt, if necessary, and returns its path on disk. """
262 plat = sys.platform.lower()
263 machine = platform.machine().lower()
264 manifest_entry = None
265 for entry in manifest:
266 # If the caller overrides the arch, just match that (for Android prebuilts).
267 if arch:
268 if entry.get('arch') == arch:
269 manifest_entry = entry
270 break
271 continue
272 # Otherwise guess the local machine arch.
273 if entry.get('platform') == plat and machine in entry.get('machine', []):
274 manifest_entry = entry
275 break
276 if manifest_entry is None:
277 if soft_fail:
278 return None
279 raise Exception(
280 ('No prebuilts available for %s-%s\n' % (plat, machine)) +
281 'See https://perfetto.dev/docs/contributing/build-instructions')
282
283 return download_or_get_cached(
284 file_name=manifest_entry['file_name'],
285 url=manifest_entry['url'],
286 sha256=manifest_entry['sha256'])
287
288
289def run_perfetto_prebuilt(manifest):
290 bin_path = get_perfetto_prebuilt(manifest)
291 if sys.platform.lower() == 'win32':
292 sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
293 os.execv(bin_path, [bin_path] + sys.argv[1:])
294
295# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
296
Florian Mayerbd0a62a2019-04-10 11:09:21 +0100297NULL = open(os.devnull)
298NOOUT = {
Primiano Tucci834fdc72019-10-04 11:33:44 +0100299 'stdout': NULL,
300 'stderr': NULL,
Florian Mayerbd0a62a2019-04-10 11:09:21 +0100301}
302
Florian Mayer2007d5c2020-09-08 15:45:44 +0100303UUID = str(uuid.uuid4())[-6:]
Florian Mayerbd0a62a2019-04-10 11:09:21 +0100304
Primiano Tucci834fdc72019-10-04 11:33:44 +0100305PACKAGES_LIST_CFG = '''data_sources {
Florian Mayerc8b28692019-05-16 17:03:21 +0100306 config {
Florian Mayerfe4361d2019-05-14 11:54:00 +0100307 name: "android.packages_list"
Florian Mayerc8b28692019-05-16 17:03:21 +0100308 }
309}
Florian Mayerfe4361d2019-05-14 11:54:00 +0100310'''
311
Florian Mayera8ff9032020-03-04 11:31:48 -0800312CFG_INDENT = ' '
Primiano Tucci834fdc72019-10-04 11:33:44 +0100313CFG = '''buffers {{
Florian Mayer2214c2e2021-02-03 16:54:00 +0000314 size_kb: 63488
Florian Mayer801349e2018-11-29 10:15:25 +0000315}}
316
317data_sources {{
318 config {{
319 name: "android.heapprofd"
320 heapprofd_config {{
Florian Mayer91b3c6d2019-04-10 13:44:37 -0700321 shmem_size_bytes: {shmem_size}
Florian Mayer801349e2018-11-29 10:15:25 +0000322 sampling_interval_bytes: {interval}
323{target_cfg}
Florian Mayer801349e2018-11-29 10:15:25 +0000324 }}
325 }}
326}}
327
328duration_ms: {duration}
Florian Mayer2aab3162019-05-03 16:02:30 +0100329write_into_file: true
Florian Mayer82610462019-04-16 10:26:07 +0100330flush_timeout_ms: 30000
Florian Mayer96f607b2020-01-14 12:57:41 +0000331flush_period_ms: 604800000
Florian Mayer801349e2018-11-29 10:15:25 +0000332'''
333
Florian Mayer96f607b2020-01-14 12:57:41 +0000334# flush_period_ms of 1 week to suppress trace_processor_shell warning.
335
Florian Mayera8312c72019-01-31 13:50:22 +0000336CONTINUOUS_DUMP = """
337 continuous_dump_config {{
338 dump_phase_ms: 0
339 dump_interval_ms: {dump_interval}
340 }}
341"""
342
Florian Mayer2007d5c2020-09-08 15:45:44 +0100343PROFILE_LOCAL_PATH = os.path.join(tempfile.gettempdir(), UUID)
Florian Mayer15866392020-04-02 11:52:16 +0200344
Florian Mayer801349e2018-11-29 10:15:25 +0000345IS_INTERRUPTED = False
Primiano Tucci834fdc72019-10-04 11:33:44 +0100346
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100347
Florian Mayer801349e2018-11-29 10:15:25 +0000348def sigint_handler(sig, frame):
349 global IS_INTERRUPTED
350 IS_INTERRUPTED = True
351
352
Florian Mayer96f607b2020-01-14 12:57:41 +0000353def print_no_profile_error():
354 print("No profiles generated", file=sys.stderr)
355 print(
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100356 "If this is unexpected, check "
357 "https://perfetto.dev/docs/data-sources/native-heap-profiler#troubleshooting.",
358 file=sys.stderr)
359
Florian Mayer96f607b2020-01-14 12:57:41 +0000360
Florian Mayerc4de3912020-11-23 14:11:43 +0000361def known_issues_url(number):
362 return ('https://perfetto.dev/docs/data-sources/native-heap-profiler'
363 '#known-issues-android{}'.format(number))
364
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100365
Florian Mayerc4de3912020-11-23 14:11:43 +0000366KNOWN_ISSUES = {
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100367 '10': known_issues_url(10),
368 'Q': known_issues_url(10),
369 '11': known_issues_url(11),
370 'R': known_issues_url(11),
Florian Mayerc4de3912020-11-23 14:11:43 +0000371}
372
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100373
Florian Mayerc4de3912020-11-23 14:11:43 +0000374def maybe_known_issues():
375 release_or_codename = subprocess.check_output(
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100376 ['adb', 'shell', 'getprop',
377 'ro.build.version.release_or_codename']).decode('utf-8').strip()
Florian Mayerc4de3912020-11-23 14:11:43 +0000378 return KNOWN_ISSUES.get(release_or_codename, None)
379
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100380
Florian Mayer9a904742020-04-28 18:40:52 +0200381SDK = {
382 'R': 30,
383}
384
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100385
Florian Mayer9a904742020-04-28 18:40:52 +0200386def release_or_newer(release):
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100387 sdk = int(
388 subprocess.check_output(
389 ['adb', 'shell', 'getprop',
390 'ro.system.build.version.sdk']).decode('utf-8').strip())
Florian Mayer9a904742020-04-28 18:40:52 +0200391 if sdk >= SDK[release]:
392 return True
393 codename = subprocess.check_output(
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100394 ['adb', 'shell', 'getprop',
395 'ro.build.version.codename']).decode('utf-8').strip()
Florian Mayer9a904742020-04-28 18:40:52 +0200396 return codename == release
397
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100398
Florian Mayerbe1e4252021-05-26 15:49:21 +0100399ORDER = ['-n', '-p', '-i', '-o']
400
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100401
Florian Mayerbe1e4252021-05-26 15:49:21 +0100402def arg_order(action):
403 result = len(ORDER)
404 for opt in action.option_strings:
405 if opt in ORDER:
406 result = min(ORDER.index(opt), result)
407 return result, action.option_strings[0].strip('-')
408
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100409
Florian Mayerbe1e4252021-05-26 15:49:21 +0100410def print_options(parser):
411 for action in sorted(parser._actions, key=arg_order):
412 if action.help is argparse.SUPPRESS:
413 continue
414 opts = ', '.join('`' + x + '`' for x in action.option_strings)
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100415 metavar = '' if action.metavar is None else ' _' + action.metavar + '_'
Florian Mayerbe1e4252021-05-26 15:49:21 +0100416 print('{}{}'.format(opts, metavar))
417 print(': {}'.format(action.help))
418 print()
419
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100420
Florian Mayer801349e2018-11-29 10:15:25 +0000421def main(argv):
422 parser = argparse.ArgumentParser()
Primiano Tucci834fdc72019-10-04 11:33:44 +0100423 parser.add_argument(
424 "-i",
425 "--interval",
426 help="Sampling interval. "
427 "Default 4096 (4KiB)",
428 type=int,
429 default=4096)
430 parser.add_argument(
431 "-d",
432 "--duration",
Florian Mayer2ad38612020-08-06 10:35:31 +0100433 help="Duration of profile (ms). 0 to run until interrupted. "
434 "Default: until interrupted by user.",
Primiano Tucci834fdc72019-10-04 11:33:44 +0100435 type=int,
Florian Mayer2ad38612020-08-06 10:35:31 +0100436 default=0)
Florian Mayer19ec17e2020-06-10 16:09:12 +0200437 # This flag is a no-op now. We never start heapprofd explicitly using system
438 # properties.
Primiano Tucci834fdc72019-10-04 11:33:44 +0100439 parser.add_argument(
440 "--no-start", help="Do not start heapprofd.", action='store_true')
441 parser.add_argument(
442 "-p",
443 "--pid",
444 help="Comma-separated list of PIDs to "
445 "profile.",
446 metavar="PIDS")
447 parser.add_argument(
448 "-n",
449 "--name",
450 help="Comma-separated list of process "
451 "names to profile.",
452 metavar="NAMES")
453 parser.add_argument(
454 "-c",
455 "--continuous-dump",
456 help="Dump interval in ms. 0 to disable continuous dump.",
457 type=int,
458 default=0)
459 parser.add_argument(
Florian Mayer39ddaad2020-06-25 19:58:00 +0200460 "--heaps",
461 help="Comma-separated list of heaps to collect, e.g: malloc,art. "
462 "Requires Android 12.",
463 metavar="HEAPS")
Hector Dearman091a54e2020-08-03 11:17:13 +0100464 parser.add_argument(
Florian Mayered4b1c02020-07-28 16:56:30 +0100465 "--all-heaps",
466 action="store_true",
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100467 help="Collect allocations from all heaps registered by target.")
Florian Mayer39ddaad2020-06-25 19:58:00 +0200468 parser.add_argument(
Florian Mayercad84b72020-11-24 14:26:32 +0000469 "--no-android-tree-symbolization",
470 action="store_true",
471 help="Do not symbolize using currently lunched target in the "
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100472 "Android tree.")
Florian Mayercad84b72020-11-24 14:26:32 +0000473 parser.add_argument(
Primiano Tucci834fdc72019-10-04 11:33:44 +0100474 "--disable-selinux",
475 action="store_true",
476 help="Disable SELinux enforcement for duration of "
477 "profile.")
478 parser.add_argument(
479 "--no-versions",
480 action="store_true",
481 help="Do not get version information about APKs.")
482 parser.add_argument(
483 "--no-running",
484 action="store_true",
Florian Mayer615fbc92019-12-18 16:29:07 +0000485 help="Do not target already running processes. Requires Android 11.")
Primiano Tucci834fdc72019-10-04 11:33:44 +0100486 parser.add_argument(
487 "--no-startup",
488 action="store_true",
489 help="Do not target processes that start during "
Florian Mayer615fbc92019-12-18 16:29:07 +0000490 "the profile. Requires Android 11.")
Primiano Tucci834fdc72019-10-04 11:33:44 +0100491 parser.add_argument(
492 "--shmem-size",
493 help="Size of buffer between client and "
494 "heapprofd. Default 8MiB. Needs to be a power of two "
495 "multiple of 4096, at least 8192.",
496 type=int,
497 default=8 * 1048576)
498 parser.add_argument(
499 "--block-client",
500 help="When buffer is full, block the "
501 "client to wait for buffer space. Use with caution as "
502 "this can significantly slow down the client. "
503 "This is the default",
504 action="store_true")
505 parser.add_argument(
Florian Mayere17af212019-11-13 10:04:03 +0000506 "--block-client-timeout",
507 help="If --block-client is given, do not block any allocation for "
508 "longer than this timeout (us).",
509 type=int)
510 parser.add_argument(
Primiano Tucci834fdc72019-10-04 11:33:44 +0100511 "--no-block-client",
512 help="When buffer is full, stop the "
513 "profile early.",
514 action="store_true")
515 parser.add_argument(
516 "--idle-allocations",
517 help="Keep track of how many "
518 "bytes were unused since the last dump, per "
519 "callstack",
520 action="store_true")
521 parser.add_argument(
522 "--dump-at-max",
Florian Mayer2fcb7dd2020-04-27 15:26:47 +0200523 help="Dump the maximum memory usage "
Primiano Tucci834fdc72019-10-04 11:33:44 +0100524 "rather than at the time of the dump.",
525 action="store_true")
526 parser.add_argument(
Florian Mayerf9cd8532020-04-21 12:18:10 +0200527 "--disable-fork-teardown",
528 help="Do not tear down client in forks. This can be useful for programs "
529 "that use vfork. Android 11+ only.",
530 action="store_true")
531 parser.add_argument(
Primiano Tucci834fdc72019-10-04 11:33:44 +0100532 "--simpleperf",
533 action="store_true",
534 help="Get simpleperf profile of heapprofd. This is "
535 "only for heapprofd development.")
536 parser.add_argument(
Lalit Maganti9d035022022-06-13 17:40:31 +0100537 "--traceconv-binary", help="Path to local trace to text. For debugging.")
Primiano Tucci834fdc72019-10-04 11:33:44 +0100538 parser.add_argument(
Ryan Savitski4bb2b222022-11-17 18:08:03 +0000539 "--no-annotations",
540 help="Do not suffix the pprof function names with Android ART mode "
541 "annotations such as [jit].",
542 action="store_true")
543 parser.add_argument(
Primiano Tucci834fdc72019-10-04 11:33:44 +0100544 "--print-config",
545 action="store_true",
546 help="Print config instead of running. For debugging.")
Florian Mayere9a55c62020-04-14 16:39:34 +0200547 parser.add_argument(
548 "-o",
549 "--output",
550 help="Output directory.",
551 metavar="DIRECTORY",
552 default=None)
Florian Mayerbe1e4252021-05-26 15:49:21 +0100553 parser.add_argument(
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100554 "--print-options", action="store_true", help=argparse.SUPPRESS)
Florian Mayer0eee91b2019-05-10 10:36:16 +0100555
Florian Mayer801349e2018-11-29 10:15:25 +0000556 args = parser.parse_args()
Florian Mayerbe1e4252021-05-26 15:49:21 +0100557 if args.print_options:
558 print_options(parser)
559 return 0
Florian Mayer801349e2018-11-29 10:15:25 +0000560 fail = False
Florian Mayerf40dedd2019-07-19 13:08:48 +0100561 if args.block_client and args.no_block_client:
Primiano Tucci834fdc72019-10-04 11:33:44 +0100562 print(
563 "FATAL: Both block-client and no-block-client given.", file=sys.stderr)
Florian Mayerf40dedd2019-07-19 13:08:48 +0100564 fail = True
Florian Mayera774cb72019-04-29 14:20:43 +0100565 if args.pid is None and args.name is None:
566 print("FATAL: Neither PID nor NAME given.", file=sys.stderr)
Florian Mayer801349e2018-11-29 10:15:25 +0000567 fail = True
568 if args.duration is None:
569 print("FATAL: No duration given.", file=sys.stderr)
570 fail = True
571 if args.interval is None:
572 print("FATAL: No interval given.", file=sys.stderr)
573 fail = True
Florian Mayer91b3c6d2019-04-10 13:44:37 -0700574 if args.shmem_size % 4096:
575 print("FATAL: shmem-size is not a multiple of 4096.", file=sys.stderr)
576 fail = True
577 if args.shmem_size < 8192:
578 print("FATAL: shmem-size is less than 8192.", file=sys.stderr)
579 fail = True
580 if args.shmem_size & (args.shmem_size - 1):
581 print("FATAL: shmem-size is not a power of two.", file=sys.stderr)
582 fail = True
Florian Mayer0eee91b2019-05-10 10:36:16 +0100583
Florian Mayer801349e2018-11-29 10:15:25 +0000584 target_cfg = ""
Florian Mayerf40dedd2019-07-19 13:08:48 +0100585 if not args.no_block_client:
Florian Mayer2ad38612020-08-06 10:35:31 +0100586 target_cfg += CFG_INDENT + "block_client: true\n"
Florian Mayere17af212019-11-13 10:04:03 +0000587 if args.block_client_timeout:
Florian Mayer2ad38612020-08-06 10:35:31 +0100588 target_cfg += (
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100589 CFG_INDENT +
590 "block_client_timeout_us: %s\n" % args.block_client_timeout)
Florian Mayer400e4432019-05-29 11:53:20 +0100591 if args.no_startup:
Florian Mayer2ad38612020-08-06 10:35:31 +0100592 target_cfg += CFG_INDENT + "no_startup: true\n"
Florian Mayer400e4432019-05-29 11:53:20 +0100593 if args.no_running:
Florian Mayer2ad38612020-08-06 10:35:31 +0100594 target_cfg += CFG_INDENT + "no_running: true\n"
Florian Mayer8707d4d2019-07-16 11:17:46 +0100595 if args.dump_at_max:
Florian Mayer2ad38612020-08-06 10:35:31 +0100596 target_cfg += CFG_INDENT + "dump_at_max: true\n"
Florian Mayerf9cd8532020-04-21 12:18:10 +0200597 if args.disable_fork_teardown:
Florian Mayer2ad38612020-08-06 10:35:31 +0100598 target_cfg += CFG_INDENT + "disable_fork_teardown: true\n"
Florian Mayered4b1c02020-07-28 16:56:30 +0100599 if args.all_heaps:
Florian Mayer2ad38612020-08-06 10:35:31 +0100600 target_cfg += CFG_INDENT + "all_heaps: true\n"
Florian Mayer801349e2018-11-29 10:15:25 +0000601 if args.pid:
Florian Mayer0eee91b2019-05-10 10:36:16 +0100602 for pid in args.pid.split(','):
603 try:
604 pid = int(pid)
605 except ValueError:
606 print("FATAL: invalid PID %s" % pid, file=sys.stderr)
607 fail = True
Florian Mayer2ad38612020-08-06 10:35:31 +0100608 target_cfg += CFG_INDENT + 'pid: {}\n'.format(pid)
Florian Mayer801349e2018-11-29 10:15:25 +0000609 if args.name:
Florian Mayer0eee91b2019-05-10 10:36:16 +0100610 for name in args.name.split(','):
Florian Mayer2ad38612020-08-06 10:35:31 +0100611 target_cfg += CFG_INDENT + 'process_cmdline: "{}"\n'.format(name)
Florian Mayer39ddaad2020-06-25 19:58:00 +0200612 if args.heaps:
613 for heap in args.heaps.split(','):
Florian Mayer2ad38612020-08-06 10:35:31 +0100614 target_cfg += CFG_INDENT + 'heaps: "{}"\n'.format(heap)
Florian Mayer801349e2018-11-29 10:15:25 +0000615
Florian Mayer0eee91b2019-05-10 10:36:16 +0100616 if fail:
617 parser.print_help()
618 return 1
619
Hector Dearmana9545e52022-05-17 12:23:25 +0100620 traceconv_binary = args.traceconv_binary
Florian Mayer2ad38612020-08-06 10:35:31 +0100621
622 if args.continuous_dump:
623 target_cfg += CONTINUOUS_DUMP.format(dump_interval=args.continuous_dump)
624 cfg = CFG.format(
625 interval=args.interval,
626 duration=args.duration,
627 target_cfg=target_cfg,
628 shmem_size=args.shmem_size)
629 if not args.no_versions:
630 cfg += PACKAGES_LIST_CFG
631
632 if args.print_config:
633 print(cfg)
634 return 0
635
Hector Dearmana9545e52022-05-17 12:23:25 +0100636 # Do this AFTER print_config so we do not download traceconv only to
Florian Mayer2ad38612020-08-06 10:35:31 +0100637 # print out the config.
Hector Dearmana9545e52022-05-17 12:23:25 +0100638 if traceconv_binary is None:
Primiano Tucci11d94e12022-08-02 17:44:33 +0100639 traceconv_binary = get_perfetto_prebuilt(TRACECONV_MANIFEST, soft_fail=True)
Florian Mayer801349e2018-11-29 10:15:25 +0000640
Florian Mayerc4de3912020-11-23 14:11:43 +0000641 known_issues = maybe_known_issues()
642 if known_issues:
643 print('If you are experiencing problems, please see the known issues for '
644 'your release: {}.'.format(known_issues))
645
Florian Mayer230b9552020-07-10 22:11:24 +0100646 # TODO(fmayer): Maybe feature detect whether we can remove traces instead of
647 # this.
648 uuid_trace = release_or_newer('R')
649 if uuid_trace:
650 profile_device_path = '/data/misc/perfetto-traces/profile-' + UUID
651 else:
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100652 user = subprocess.check_output(['adb', 'shell',
653 'whoami']).decode('utf-8').strip()
Florian Mayer230b9552020-07-10 22:11:24 +0100654 profile_device_path = '/data/misc/perfetto-traces/profile-' + user
655
656 perfetto_cmd = ('CFG=\'{cfg}\'; echo ${{CFG}} | '
657 'perfetto --txt -c - -o ' + profile_device_path + ' -d')
658
Florian Mayer6ae95262018-12-06 16:10:29 +0000659 if args.disable_selinux:
Christopher Phlipotc7d0d3a2022-09-15 11:34:24 -0700660 enforcing = subprocess.check_output(['adb', 'shell',
661 'getenforce']).decode('utf-8').strip()
Primiano Tucci834fdc72019-10-04 11:33:44 +0100662 atexit.register(
663 subprocess.check_call,
Florian Mayer6ae95262018-12-06 16:10:29 +0000664 ['adb', 'shell', 'su root setenforce %s' % enforcing])
665 subprocess.check_call(['adb', 'shell', 'su root setenforce 0'])
Florian Mayer801349e2018-11-29 10:15:25 +0000666
Florian Mayer2b8a3b22019-05-02 18:35:38 +0100667 if args.simpleperf:
Primiano Tucci834fdc72019-10-04 11:33:44 +0100668 subprocess.check_call([
669 'adb', 'shell', 'mkdir -p /data/local/tmp/heapprofd_profile && '
670 'cd /data/local/tmp/heapprofd_profile &&'
671 '(nohup simpleperf record -g -p $(pidof heapprofd) 2>&1 &) '
672 '> /dev/null'
673 ])
Florian Mayer2b8a3b22019-05-02 18:35:38 +0100674
Florian Mayere9a55c62020-04-14 16:39:34 +0200675 profile_target = PROFILE_LOCAL_PATH
676 if args.output is not None:
677 profile_target = args.output
678 else:
679 os.mkdir(profile_target)
680
681 if not os.path.isdir(profile_target):
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100682 print(
683 "Output directory {} not found".format(profile_target), file=sys.stderr)
Florian Mayere9a55c62020-04-14 16:39:34 +0200684 return 1
685
686 if os.listdir(profile_target):
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100687 print(
688 "Output directory {} not empty".format(profile_target), file=sys.stderr)
Florian Mayere9a55c62020-04-14 16:39:34 +0200689 return 1
690
Florian Mayer801349e2018-11-29 10:15:25 +0000691 perfetto_pid = subprocess.check_output(
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100692 ['adb', 'exec-out', perfetto_cmd.format(cfg=cfg)]).strip()
Florian Mayer35647422019-03-07 16:28:10 +0000693 try:
Florian Mayer0bc32522020-04-28 16:35:55 +0200694 perfetto_pid = int(perfetto_pid.strip())
Florian Mayer35647422019-03-07 16:28:10 +0000695 except ValueError:
Primiano Tucci834fdc72019-10-04 11:33:44 +0100696 print("Failed to invoke perfetto: {}".format(perfetto_pid), file=sys.stderr)
Florian Mayer35647422019-03-07 16:28:10 +0000697 return 1
Florian Mayer801349e2018-11-29 10:15:25 +0000698
699 old_handler = signal.signal(signal.SIGINT, sigint_handler)
700 print("Profiling active. Press Ctrl+C to terminate.")
Florian Mayerbd0a62a2019-04-10 11:09:21 +0100701 print("You may disconnect your device.")
Florian Mayer96f607b2020-01-14 12:57:41 +0000702 print()
Florian Mayer801349e2018-11-29 10:15:25 +0000703 exists = True
Florian Mayerbd0a62a2019-04-10 11:09:21 +0100704 device_connected = True
705 while not device_connected or (exists and not IS_INTERRUPTED):
Florian Mayer801349e2018-11-29 10:15:25 +0000706 exists = subprocess.call(
Primiano Tucci834fdc72019-10-04 11:33:44 +0100707 ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)], **NOOUT) == 0
Florian Mayerbd0a62a2019-04-10 11:09:21 +0100708 device_connected = subprocess.call(['adb', 'shell', 'true'], **NOOUT) == 0
Florian Mayer801349e2018-11-29 10:15:25 +0000709 time.sleep(1)
Florian Mayer2007d5c2020-09-08 15:45:44 +0100710 print("Waiting for profiler shutdown...")
Florian Mayer801349e2018-11-29 10:15:25 +0000711 signal.signal(signal.SIGINT, old_handler)
712 if IS_INTERRUPTED:
713 # Not check_call because it could have existed in the meantime.
Florian Mayer0bc32522020-04-28 16:35:55 +0200714 subprocess.call(['adb', 'shell', 'kill', '-INT', str(perfetto_pid)])
Florian Mayer2b8a3b22019-05-02 18:35:38 +0100715 if args.simpleperf:
716 subprocess.check_call(['adb', 'shell', 'killall', '-INT', 'simpleperf'])
717 print("Waiting for simpleperf to exit.")
718 while subprocess.call(
Primiano Tucci834fdc72019-10-04 11:33:44 +0100719 ['adb', 'shell', '[ -f /proc/$(pidof simpleperf)/exe ]'], **NOOUT) == 0:
Florian Mayer2b8a3b22019-05-02 18:35:38 +0100720 time.sleep(1)
Primiano Tucci834fdc72019-10-04 11:33:44 +0100721 subprocess.check_call(
Florian Mayer3c57f3f2020-10-01 14:58:08 +0100722 ['adb', 'pull', '/data/local/tmp/heapprofd_profile', profile_target])
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100723 print("Pulled simpleperf profile to " + profile_target +
724 "/heapprofd_profile")
Florian Mayer801349e2018-11-29 10:15:25 +0000725
Florian Mayerddbe31e2018-11-30 14:49:30 +0000726 # Wait for perfetto cmd to return.
727 while exists:
728 exists = subprocess.call(
729 ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
730 time.sleep(1)
731
Florian Mayer2007d5c2020-09-08 15:45:44 +0100732 profile_host_path = os.path.join(profile_target, 'raw-trace')
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100733 subprocess.check_call(['adb', 'pull', profile_device_path, profile_host_path],
734 stdout=NULL)
Florian Mayer4fc59932020-04-28 17:19:55 +0200735 if uuid_trace:
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100736 subprocess.check_call(['adb', 'shell', 'rm', profile_device_path],
737 stdout=NULL)
Florian Mayer213c8d42019-12-18 17:10:34 +0000738
Hector Dearmana9545e52022-05-17 12:23:25 +0100739 if traceconv_binary is None:
Florian Mayer2007d5c2020-09-08 15:45:44 +0100740 print('Wrote profile to {}'.format(profile_host_path))
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100741 print(
742 'This file can be opened using the Perfetto UI, https://ui.perfetto.dev'
743 )
Florian Mayer2007d5c2020-09-08 15:45:44 +0100744 return 0
745
Florian Mayercad84b72020-11-24 14:26:32 +0000746 binary_path = os.getenv('PERFETTO_BINARY_PATH')
747 if not args.no_android_tree_symbolization:
748 product_out = os.getenv('ANDROID_PRODUCT_OUT')
749 if product_out:
750 product_out_symbols = product_out + '/symbols'
751 else:
752 product_out_symbols = None
753
754 if binary_path is None:
755 binary_path = product_out_symbols
756 elif product_out_symbols is not None:
757 binary_path += ":" + product_out_symbols
758
759 trace_file = os.path.join(profile_target, 'raw-trace')
Florian Mayeredc48102020-12-15 19:38:44 +0000760 concat_files = [trace_file]
Florian Mayercad84b72020-11-24 14:26:32 +0000761
762 if binary_path is not None:
763 with open(os.path.join(profile_target, 'symbols'), 'w') as fd:
764 ret = subprocess.call([
Hector Dearmana9545e52022-05-17 12:23:25 +0100765 traceconv_binary, 'symbolize',
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100766 os.path.join(profile_target, 'raw-trace')
767 ],
768 env=dict(
769 os.environ, PERFETTO_BINARY_PATH=binary_path),
770 stdout=fd)
Florian Mayercad84b72020-11-24 14:26:32 +0000771 if ret == 0:
Florian Mayeredc48102020-12-15 19:38:44 +0000772 concat_files.append(os.path.join(profile_target, 'symbols'))
Florian Mayercad84b72020-11-24 14:26:32 +0000773 else:
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100774 print("Failed to symbolize. Continuing without symbols.", file=sys.stderr)
Florian Mayercad84b72020-11-24 14:26:32 +0000775
Florian Mayeredc48102020-12-15 19:38:44 +0000776 proguard_map = os.getenv('PERFETTO_PROGUARD_MAP')
777 if proguard_map is not None:
778 with open(os.path.join(profile_target, 'deobfuscation-packets'), 'w') as fd:
779 ret = subprocess.call([
Hector Dearmana9545e52022-05-17 12:23:25 +0100780 traceconv_binary, 'deobfuscate',
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100781 os.path.join(profile_target, 'raw-trace')
782 ],
783 env=dict(
784 os.environ, PERFETTO_PROGUARD_MAP=proguard_map),
785 stdout=fd)
Florian Mayeredc48102020-12-15 19:38:44 +0000786 if ret == 0:
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100787 concat_files.append(os.path.join(profile_target, 'deobfuscation-packets'))
Florian Mayeredc48102020-12-15 19:38:44 +0000788 else:
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100789 print(
790 "Failed to deobfuscate. Continuing without deobfuscated.",
791 file=sys.stderr)
Florian Mayeredc48102020-12-15 19:38:44 +0000792
793 if len(concat_files) > 1:
Isaac Nickaein1ca57282021-03-15 16:10:59 +0000794 with open(os.path.join(profile_target, 'symbolized-trace'), 'wb') as out:
Florian Mayeredc48102020-12-15 19:38:44 +0000795 for fn in concat_files:
Isaac Nickaein1ca57282021-03-15 16:10:59 +0000796 with open(fn, 'rb') as inp:
Florian Mayeredc48102020-12-15 19:38:44 +0000797 while True:
798 buf = inp.read(4096)
799 if not buf:
800 break
801 out.write(buf)
802 trace_file = os.path.join(profile_target, 'symbolized-trace')
803
Ryan Savitski4bb2b222022-11-17 18:08:03 +0000804 conversion_args = [traceconv_binary, 'profile'] + (
805 ['--no-annotations'] if args.no_annotations else []) + [trace_file]
806 traceconv_output = subprocess.check_output(conversion_args)
Florian Mayer801349e2018-11-29 10:15:25 +0000807 profile_path = None
Hector Dearmana9545e52022-05-17 12:23:25 +0100808 for word in traceconv_output.decode('utf-8').split():
Florian Mayer801349e2018-11-29 10:15:25 +0000809 if 'heap_profile-' in word:
810 profile_path = word
811 if profile_path is None:
Florian Mayer88bca762020-07-28 14:42:15 +0100812 print_no_profile_error()
Florian Mayer801349e2018-11-29 10:15:25 +0000813 return 1
814
815 profile_files = os.listdir(profile_path)
816 if not profile_files:
Florian Mayer88bca762020-07-28 14:42:15 +0100817 print_no_profile_error()
Florian Mayer801349e2018-11-29 10:15:25 +0000818 return 1
819
Florian Mayere9a55c62020-04-14 16:39:34 +0200820 for profile_file in profile_files:
821 shutil.copy(os.path.join(profile_path, profile_file), profile_target)
822
Florian Mayere9a55c62020-04-14 16:39:34 +0200823 symlink_path = None
Primiano Tuccif94e4972021-08-03 11:35:45 +0100824 if not sys.platform.startswith('win'):
825 subprocess.check_call(
Lalit Maganti79c80192021-08-04 15:35:33 +0100826 ['gzip'] + [os.path.join(profile_target, x) for x in profile_files])
Primiano Tuccif94e4972021-08-03 11:35:45 +0100827 if args.output is None:
828 symlink_path = os.path.join(
829 os.path.dirname(profile_target), "heap_profile-latest")
830 if os.path.lexists(symlink_path):
831 os.unlink(symlink_path)
832 os.symlink(profile_target, symlink_path)
Florian Mayer82f43d12019-01-17 14:37:45 +0000833
Florian Mayere9a55c62020-04-14 16:39:34 +0200834 if symlink_path is not None:
Primiano Tuccid3e40bd2021-08-03 10:52:39 +0100835 print("Wrote profiles to {} (symlink {})".format(profile_target,
836 symlink_path))
Florian Mayere9a55c62020-04-14 16:39:34 +0200837 else:
838 print("Wrote profiles to {}".format(profile_target))
839
Primiano Tuccif94e4972021-08-03 11:35:45 +0100840 print("The raw-trace file can be viewed using https://ui.perfetto.dev.")
841 print("The heap_dump.* files can be viewed using pprof/ (Googlers only) " +
842 "or https://www.speedscope.app/.")
843 print("The two above are equivalent. The raw-trace contains the union of " +
844 "all the heap dumps.")
Florian Mayer801349e2018-11-29 10:15:25 +0000845
846
Florian Mayer801349e2018-11-29 10:15:25 +0000847if __name__ == '__main__':
848 sys.exit(main(sys.argv))