tools: python amalgamation for tools/tracebox etc
Before this change:
the business logic for tools/{tracebox, record_android_trace, ...}
was in the file itself and the manifest was replaced in-place
when invoking tools/roll-prebuilts.
This still made it impossible to share code between tools
without copy/pasting.
With this change:
- Move the business logic to python/xxx
- Add an amalgamator that follows includes. Only the form
'from perfetto.xxx import yyy' is supported.
- Keep the amalgamated files in tools/traceconv
No code sharing / major refactorings are made by this change.
They can happen as a follow-up though
Change-Id: I7420387881e6ef1e109abae6380dde7c06ac1b27
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 901f812..5307e5a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -76,6 +76,7 @@
results += RunAndReportIfLong(CheckBannedCpp, input, output)
results += RunAndReportIfLong(CheckSqlMetrics, input, output)
results += RunAndReportIfLong(CheckTestData, input, output)
+ results += RunAndReportIfLong(CheckAmalgamatedPythonTools, input, output)
return results
@@ -291,3 +292,21 @@
)
]
return []
+
+
+def CheckAmalgamatedPythonTools(input_api, output_api):
+ tool = 'tools/gen_amalgamated_python_tools'
+
+ # If no GN files were modified, bail out.
+ def build_file_filter(x):
+ return input_api.FilterSourceFile(x, files_to_check=('python/.*$', tool))
+
+ if not input_api.AffectedSourceFiles(build_file_filter):
+ return []
+ if subprocess.call([tool, '--check-only']):
+ return [
+ output_api.PresubmitError(
+ 'amalgamated python tools/ are out of date. ' + 'Run ' + tool +
+ ' to update them.')
+ ]
+ return []
diff --git a/python/perfetto/common/repo_utils.py b/python/perfetto/common/repo_utils.py
new file mode 100644
index 0000000..0a84030
--- /dev/null
+++ b/python/perfetto/common/repo_utils.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def repo_root():
+ """ Finds the repo root by traversing up the hierarchy
+
+ This is for use in scripts that get amalgamated, where _file_ can be either
+ python/perfetto/... or tools/amalgamated_tool.
+ """
+ path = os.path.dirname(os.path.abspath(__file__)) # amalgamator:nocheck
+ last_dir = ''
+ while path and path != last_dir:
+ if os.path.exists(os.path.join(path, 'perfetto.rc')):
+ return path
+ last_dir = path
+ path = os.path.dirname(path)
+ return None
+
+
+def repo_dir(rel_path):
+ return os.path.join(repo_root() or '', rel_path)
diff --git a/python/perfetto/prebuilts/manifests/trace_processor_shell.py b/python/perfetto/prebuilts/manifests/trace_processor_shell.py
new file mode 100755
index 0000000..1c2ebd4
--- /dev/null
+++ b/python/perfetto/prebuilts/manifests/trace_processor_shell.py
@@ -0,0 +1,130 @@
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACE_PROCESSOR_SHELL_MANIFEST = [{
+ 'arch':
+ 'mac-amd64',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 7991568,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/trace_processor_shell',
+ 'sha256':
+ '4704eb8c51946ae288b74ad9f4386019a2b146c310c7717aeeaada73d6eef8fc',
+ 'platform':
+ 'darwin',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'mac-arm64',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 6757608,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/trace_processor_shell',
+ 'sha256':
+ '2aa7ddebedb6d5cf6300b3dc3e6ea8e294f645f67c2dbd727d497ab4a17d6599',
+ 'platform':
+ 'darwin',
+ 'machine': ['arm64']
+}, {
+ 'arch':
+ 'linux-amd64',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 8568248,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/trace_processor_shell',
+ 'sha256':
+ '67deabb7e215cc66d3694c0e0367f441cd48a3456d2dd5877bab6eb07117887f',
+ 'platform':
+ 'linux',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'linux-arm',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 6279736,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/trace_processor_shell',
+ 'sha256':
+ '172ac15b010b1e9a28e1efb8991e2e9195dc732969b33e04ebd5b5f801a99218',
+ 'platform':
+ 'linux',
+ 'machine': ['armv6l', 'armv7l', 'armv8l']
+}, {
+ 'arch':
+ 'linux-arm64',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 7703256,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/trace_processor_shell',
+ 'sha256':
+ 'd70d621ca9aedff0b29a24984ff896e7069cadee607ada1ccfca58634e1774a1',
+ 'platform':
+ 'linux',
+ 'machine': ['aarch64']
+}, {
+ 'arch':
+ 'android-arm',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 5357880,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/trace_processor_shell',
+ 'sha256':
+ '75285ac8964e93890b936bfacd2679ee67ae4cd029be74e675253f92746f9904'
+}, {
+ 'arch':
+ 'android-arm64',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 6940832,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/trace_processor_shell',
+ 'sha256':
+ '75118859c9a1b2ed2bcffc664eb98884dab26a9d2ad7788d05d90ef22b24a8d5'
+}, {
+ 'arch':
+ 'android-x86',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 7856396,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/trace_processor_shell',
+ 'sha256':
+ 'c3d4da7aa12a2eedd59df98b5c8143658f39ebd9a4ba3c8180be33ab3484e824'
+}, {
+ 'arch':
+ 'android-x64',
+ 'file_name':
+ 'trace_processor_shell',
+ 'file_size':
+ 8194240,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/trace_processor_shell',
+ 'sha256':
+ '105c70a3908bf6997b428ee7f74bf7ee8a48907b860480c87e62a62cf92e1a8a'
+}, {
+ 'arch':
+ 'windows-amd64',
+ 'file_name':
+ 'trace_processor_shell.exe',
+ 'file_size':
+ 7857152,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/trace_processor_shell.exe',
+ 'sha256':
+ 'eda1e38cb7490a99ba31285a977a3a564c53f25eae2b301152b2578de62fcc5d',
+ 'platform':
+ 'win32',
+ 'machine': ['amd64']
+}]
diff --git a/python/perfetto/prebuilts/manifests/tracebox.py b/python/perfetto/prebuilts/manifests/tracebox.py
new file mode 100755
index 0000000..46779cb
--- /dev/null
+++ b/python/perfetto/prebuilts/manifests/tracebox.py
@@ -0,0 +1,116 @@
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACEBOX_MANIFEST = [{
+ 'arch':
+ 'mac-amd64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1399080,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/tracebox',
+ 'sha256':
+ '05674f9872a14bf92d4215a180c883ab54c58376d7e08b95e3f83d03efdeba21',
+ 'platform':
+ 'darwin',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'mac-arm64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1292504,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/tracebox',
+ 'sha256':
+ '48008e6aeb7680c4f31b1147d5c26a504368a696c793aec1895bb5fb1f597f64',
+ 'platform':
+ 'darwin',
+ 'machine': ['arm64']
+}, {
+ 'arch':
+ 'linux-amd64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 2243632,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/tracebox',
+ 'sha256':
+ '12c87ba1d03e39d4c07792e09f0be9e240677cec2d9bc1638b0a676bc664d724',
+ 'platform':
+ 'linux',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'linux-arm',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1315252,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/tracebox',
+ 'sha256':
+ '3d026fe982e90fb5bc410528f210458f9bcafea30d3e1b9728772fd32148ee9a',
+ 'platform':
+ 'linux',
+ 'machine': ['armv6l', 'armv7l', 'armv8l']
+}, {
+ 'arch':
+ 'linux-arm64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 2089160,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/tracebox',
+ 'sha256':
+ '9f524f943a1c12dcb6b273e78d5c46952d4a7528514639cd2537686d5c530c89',
+ 'platform':
+ 'linux',
+ 'machine': ['aarch64']
+}, {
+ 'arch':
+ 'android-arm',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1099732,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/tracebox',
+ 'sha256':
+ '53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
+}, {
+ 'arch':
+ 'android-arm64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1653416,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/tracebox',
+ 'sha256':
+ 'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
+}, {
+ 'arch':
+ 'android-x86',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1677228,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/tracebox',
+ 'sha256':
+ '8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
+}, {
+ 'arch':
+ 'android-x64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1911464,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/tracebox',
+ 'sha256':
+ '8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
+}]
diff --git a/python/perfetto/prebuilts/manifests/traceconv.py b/python/perfetto/prebuilts/manifests/traceconv.py
new file mode 100755
index 0000000..3b7334a
--- /dev/null
+++ b/python/perfetto/prebuilts/manifests/traceconv.py
@@ -0,0 +1,130 @@
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACECONV_MANIFEST = [{
+ 'arch':
+ 'mac-amd64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7181712,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+ 'sha256':
+ '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+ 'platform':
+ 'darwin',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'mac-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6025176,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+ 'sha256':
+ '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+ 'platform':
+ 'darwin',
+ 'machine': ['arm64']
+}, {
+ 'arch':
+ 'linux-amd64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7668600,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+ 'sha256':
+ '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+ 'platform':
+ 'linux',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'linux-arm',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 5827680,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+ 'sha256':
+ 'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+ 'platform':
+ 'linux',
+ 'machine': ['armv6l', 'armv7l', 'armv8l']
+}, {
+ 'arch':
+ 'linux-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6876384,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+ 'sha256':
+ '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+ 'platform':
+ 'linux',
+ 'machine': ['aarch64']
+}, {
+ 'arch':
+ 'android-arm',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 4881820,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+ 'sha256':
+ '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+}, {
+ 'arch':
+ 'android-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6222592,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+ 'sha256':
+ '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+}, {
+ 'arch':
+ 'android-x86',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7089524,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+ 'sha256':
+ '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+}, {
+ 'arch':
+ 'android-x64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7316248,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+ 'sha256':
+ '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+}, {
+ 'arch':
+ 'windows-amd64',
+ 'file_name':
+ 'traceconv.exe',
+ 'file_size':
+ 6850048,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+ 'sha256':
+ '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+ 'platform':
+ 'win32',
+ 'machine': ['amd64']
+}]
diff --git a/python/perfetto/prebuilts/perfetto_prebuilts.py b/python/perfetto/prebuilts/perfetto_prebuilts.py
new file mode 100644
index 0000000..723e796
--- /dev/null
+++ b/python/perfetto/prebuilts/perfetto_prebuilts.py
@@ -0,0 +1,123 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
+ """ Downloads the prebuilt, if necessary, and returns its path on disk. """
+ plat = sys.platform.lower()
+ machine = platform.machine().lower()
+ manifest_entry = None
+ for entry in manifest:
+ # If the caller overrides the arch, just match that (for Android prebuilts).
+ if arch:
+ if entry.get('arch') == arch:
+ manifest_entry = entry
+ break
+ continue
+ # Otherwise guess the local machine arch.
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
+ manifest_entry = entry
+ break
+ if manifest_entry is None:
+ if soft_fail:
+ return None
+ raise Exception(
+ ('No prebuilts available for %s-%s\n' % (plat, machine)) +
+ 'See https://perfetto.dev/docs/contributing/build-instructions')
+
+ return download_or_get_cached(
+ file_name=manifest_entry['file_name'],
+ url=manifest_entry['url'],
+ sha256=manifest_entry['sha256'])
+
+
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
diff --git a/python/tools/cpu_profile.py b/python/tools/cpu_profile.py
new file mode 100644
index 0000000..4c33f2b
--- /dev/null
+++ b/python/tools/cpu_profile.py
@@ -0,0 +1,454 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Runs tracing with CPU profiling enabled, and symbolizes traces if requested.
+
+For usage instructions, please see:
+https://perfetto.dev/docs/quickstart/callstack-sampling
+
+Adapted in large part from `heap_profile`.
+"""
+
+import argparse
+import os
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+import textwrap
+import time
+import uuid
+
+from perfetto.prebuilts.manifests.traceconv import *
+from perfetto.prebuilts.perfetto_prebuilts import *
+
+# Used for creating directories, etc.
+UUID = str(uuid.uuid4())[-6:]
+
+# See `sigint_handler` below.
+IS_INTERRUPTED = False
+
+
+def sigint_handler(signal, frame):
+ """Useful for cleanly interrupting tracing."""
+ global IS_INTERRUPTED
+ IS_INTERRUPTED = True
+
+
+def exit_with_no_profile():
+ sys.exit("No profiles generated.")
+
+
+def exit_with_bug_report(error):
+ sys.exit(
+ "{}\n\n If this is unexpected, please consider filing a bug at: \n"
+ "https://perfetto.dev/docs/contributing/getting-started#bugs.".format(
+ error))
+
+
+def adb_check_output(command):
+ """Runs an `adb` command and returns its output."""
+ try:
+ return subprocess.check_output(command).decode('utf-8')
+ except FileNotFoundError:
+ sys.exit("`adb` not found: Is it installed or on PATH?")
+ except subprocess.CalledProcessError as error:
+ sys.exit("`adb` error: Are any (or multiple) devices connected?\n"
+ "If multiple devices are connected, please select one by "
+ "setting `ANDROID_SERIAL=device_id`.\n"
+ "{}".format(error))
+ except Exception as error:
+ exit_with_bug_report(error)
+
+
+def parse_and_validate_args():
+ """Parses, validates, and returns command-line arguments for this script."""
+ DESCRIPTION = """Runs tracing with CPU profiling enabled, and symbolizes
+ traces if requested.
+
+ For usage instructions, please see:
+ https://perfetto.dev/docs/quickstart/cpu-profiling
+ """
+ parser = argparse.ArgumentParser(description=DESCRIPTION)
+ parser.add_argument(
+ "-f",
+ "--frequency",
+ help="Sampling frequency (Hz). "
+ "Default: 100 Hz.",
+ metavar="FREQUENCY",
+ type=int,
+ default=100)
+ parser.add_argument(
+ "-d",
+ "--duration",
+ help="Duration of profile (ms). 0 to run until interrupted. "
+ "Default: until interrupted by user.",
+ metavar="DURATION",
+ type=int,
+ default=0)
+ parser.add_argument(
+ "-n",
+ "--name",
+ help="Comma-separated list of names of processes to be profiled.",
+ metavar="NAMES",
+ default=None)
+ parser.add_argument(
+ "-p",
+ "--partial-matching",
+ help="If set, enables \"partial matching\" on the strings in --names/-n."
+ "Processes that are already running when profiling is started, and whose "
+ "names include any of the values in --names/-n as substrings will be profiled.",
+ action="store_true")
+ parser.add_argument(
+ "-c",
+ "--config",
+ help="A custom configuration file, if any, to be used for profiling. "
+ "If provided, --frequency/-f, --duration/-d, and --name/-n are not used.",
+ metavar="CONFIG",
+ default=None)
+ parser.add_argument(
+ "--print-config",
+ action="store_true",
+ help="Print config instead of running. For debugging.")
+ parser.add_argument(
+ "-o",
+ "--output",
+ help="Output directory for recorded trace.",
+ metavar="DIRECTORY",
+ default=None)
+
+ args = parser.parse_args()
+ if args.config is not None and args.name is not None:
+ sys.exit("--name/-n should not be provided when --config/-c is provided.")
+ elif args.config is None and args.name is None:
+ sys.exit("One of --names/-n or --config/-c is required.")
+
+ return args
+
+
+def get_matching_processes(args, names_to_match):
+ """Returns a list of currently-running processes whose names match `names_to_match`.
+
+ Args:
+ args: The command-line arguments provided to this script.
+ names_to_match: The list of process names provided by the user.
+ """
+ # Returns names as they are.
+ if not args.partial_matching:
+ return names_to_match
+
+ # Attempt to match names to names of currently running processes.
+ PS_PROCESS_OFFSET = 8
+ matching_processes = []
+ for line in adb_check_output(['adb', 'shell', 'ps', '-A']).splitlines():
+ line_split = line.split()
+ if len(line_split) <= PS_PROCESS_OFFSET:
+ continue
+ process = line_split[PS_PROCESS_OFFSET]
+ for name in names_to_match:
+ if name in process:
+ matching_processes.append(process)
+ break
+
+ return matching_processes
+
+
+def get_perfetto_config(args):
+ """Returns a Perfetto config with CPU profiling enabled for the selected processes.
+
+ Args:
+ args: The command-line arguments provided to this script.
+ """
+ if args.config is not None:
+ try:
+ with open(args.config, 'r') as config_file:
+ return config_file.read()
+ except IOError as error:
+ sys.exit("Unable to read config file: {}".format(error))
+
+ CONFIG_INDENT = ' '
+ CONFIG = textwrap.dedent('''\
+ buffers {{
+ size_kb: 2048
+ }}
+
+ buffers {{
+ size_kb: 63488
+ }}
+
+ data_sources {{
+ config {{
+ name: "linux.process_stats"
+ target_buffer: 0
+ process_stats_config {{
+ proc_stats_poll_ms: 100
+ }}
+ }}
+ }}
+
+ data_sources {{
+ config {{
+ name: "linux.perf"
+ target_buffer: 1
+ perf_event_config {{
+ all_cpus: true
+ sampling_frequency: {frequency}
+ {target_config}
+ }}
+ }}
+ }}
+
+ duration_ms: {duration}
+ write_into_file: true
+ flush_timeout_ms: 30000
+ flush_period_ms: 604800000
+ ''')
+
+ matching_processes = []
+ if args.name is not None:
+ names_to_match = [name.strip() for name in args.name.split(',')]
+ matching_processes = get_matching_processes(args, names_to_match)
+
+ if not matching_processes:
+ sys.exit("No running processes matched for profiling.")
+
+ target_config = "\n".join(
+ [f'{CONFIG_INDENT}target_cmdline: "{p}"' for p in matching_processes])
+
+ if not args.print_config:
+ print("Configured profiling for these processes:\n")
+ for matching_process in matching_processes:
+ print(matching_process)
+ print()
+
+ config = CONFIG.format(
+ frequency=args.frequency,
+ duration=args.duration,
+ target_config=target_config)
+
+ return config
+
+
+def release_or_newer(release):
+ """Returns whether a new enough Android release is being used."""
+ SDK = {'R': 30}
+ sdk = int(
+ adb_check_output(
+ ['adb', 'shell', 'getprop', 'ro.system.build.version.sdk']).strip())
+ if sdk >= SDK[release]:
+ return True
+
+ codename = adb_check_output(
+ ['adb', 'shell', 'getprop', 'ro.build.version.codename']).strip()
+ return codename == release
+
+
+def get_and_prepare_profile_target(args):
+ """Returns the target where the trace/profile will be output. Creates a new directory if necessary.
+
+ Args:
+ args: The command-line arguments provided to this script.
+ """
+ profile_target = os.path.join(tempfile.gettempdir(), UUID)
+ if args.output is not None:
+ profile_target = args.output
+ else:
+ os.makedirs(profile_target, exist_ok=True)
+ if not os.path.isdir(profile_target):
+ sys.exit("Output directory {} not found.".format(profile_target))
+ if os.listdir(profile_target):
+ sys.exit("Output directory {} not empty.".format(profile_target))
+
+ return profile_target
+
+
+def record_trace(config, profile_target):
+ """Runs Perfetto with the provided configuration to record a trace.
+
+ Args:
+ config: The Perfetto config to be used for tracing/profiling.
+ profile_target: The directory where the recorded trace is output.
+ """
+ NULL = open(os.devnull)
+ NO_OUT = {
+ 'stdout': NULL,
+ 'stderr': NULL,
+ }
+ if not release_or_newer('R'):
+ sys.exit("This tool requires Android R+ to run.")
+ profile_device_path = '/data/misc/perfetto-traces/profile-' + UUID
+ perfetto_command = ('CONFIG=\'{}\'; echo ${{CONFIG}} | '
+ 'perfetto --txt -c - -o {} -d')
+ try:
+ perfetto_pid = int(
+ adb_check_output([
+ 'adb', 'exec-out',
+ perfetto_command.format(config, profile_device_path)
+ ]).strip())
+ except ValueError as error:
+ sys.exit("Unable to start profiling: {}".format(error))
+
+ print("Profiling active. Press Ctrl+C to terminate.")
+
+ old_handler = signal.signal(signal.SIGINT, sigint_handler)
+
+ perfetto_alive = True
+ while perfetto_alive and not IS_INTERRUPTED:
+ perfetto_alive = subprocess.call(
+ ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)], **NO_OUT) == 0
+ time.sleep(0.25)
+
+ print("Finishing profiling and symbolization...")
+
+ if IS_INTERRUPTED:
+ adb_check_output(['adb', 'shell', 'kill', '-INT', str(perfetto_pid)])
+
+ # Restore old handler.
+ signal.signal(signal.SIGINT, old_handler)
+
+ while perfetto_alive:
+ perfetto_alive = subprocess.call(
+ ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
+ time.sleep(0.25)
+
+ profile_host_path = os.path.join(profile_target, 'raw-trace')
+ adb_check_output(['adb', 'pull', profile_device_path, profile_host_path])
+ adb_check_output(['adb', 'shell', 'rm', profile_device_path])
+
+
+def get_traceconv():
+ """Sets up and returns the path to `traceconv`."""
+ try:
+ traceconv = get_perfetto_prebuilt(TRACECONV_MANIFEST, soft_fail=True)
+ except Exception as error:
+ exit_with_bug_report(error)
+ if traceconv is None:
+ exit_with_bug_report(
+ "Unable to download `traceconv` for symbolizing profiles.")
+
+ return traceconv
+
+
+def concatenate_files(files_to_concatenate, output_file):
+ """Concatenates files.
+
+ Args:
+ files_to_concatenate: Paths for input files to concatenate.
+ output_file: Path to the resultant output file.
+ """
+ with open(output_file, 'wb') as output:
+ for file in files_to_concatenate:
+ with open(file, 'rb') as input:
+ shutil.copyfileobj(input, output)
+
+
+def symbolize_trace(traceconv, profile_target):
+ """Attempts symbolization of the recorded trace/profile, if symbols are available.
+
+ Args:
+ traceconv: The path to the `traceconv` binary used for symbolization.
+ profile_target: The directory where the recorded trace was output.
+
+ Returns:
+ The path to the symbolized trace file if symbolization was completed,
+ and the original trace file, if it was not.
+ """
+ binary_path = os.getenv('PERFETTO_BINARY_PATH')
+ trace_file = os.path.join(profile_target, 'raw-trace')
+ files_to_concatenate = [trace_file]
+
+ if binary_path is not None:
+ try:
+ with open(os.path.join(profile_target, 'symbols'), 'w') as symbols_file:
+ return_code = subprocess.call([traceconv, 'symbolize', trace_file],
+ env=dict(
+ os.environ,
+ PERFETTO_BINARY_PATH=binary_path),
+ stdout=symbols_file)
+ except IOError as error:
+ sys.exit("Unable to write symbols to disk: {}".format(error))
+ if return_code == 0:
+ files_to_concatenate.append(os.path.join(profile_target, 'symbols'))
+ else:
+ print("Failed to symbolize. Continuing without symbols.", file=sys.stderr)
+
+ if len(files_to_concatenate) > 1:
+ trace_file = os.path.join(profile_target, 'symbolized-trace')
+ try:
+ concatenate_files(files_to_concatenate, trace_file)
+ except Exception as error:
+ sys.exit("Unable to write symbolized profile to disk: {}".format(error))
+
+ return trace_file
+
+
+def generate_pprof_profiles(traceconv, trace_file):
+ """Generates pprof profiles from the recorded trace.
+
+ Args:
+ traceconv: The path to the `traceconv` binary used for generating profiles.
+ trace_file: The oath to the recorded and potentially symbolized trace file.
+
+ Returns:
+ The directory where pprof profiles are output.
+ """
+ try:
+ traceconv_output = subprocess.check_output(
+ [traceconv, 'profile', '--perf', trace_file])
+ except Exception as error:
+ exit_with_bug_report(
+ "Unable to extract profiles from trace: {}".format(error))
+
+ profiles_output_directory = None
+ for word in traceconv_output.decode('utf-8').split():
+ if 'perf_profile-' in word:
+ profiles_output_directory = word
+ if profiles_output_directory is None:
+ exit_with_no_profile()
+ return profiles_output_directory
+
+
+def copy_profiles_to_destination(profile_target, profile_path):
+ """Copies recorded profiles to `profile_target` from `profile_path`."""
+ profile_files = os.listdir(profile_path)
+ if not profile_files:
+ exit_with_no_profile()
+
+ try:
+ for profile_file in profile_files:
+ shutil.copy(os.path.join(profile_path, profile_file), profile_target)
+ except Exception as error:
+ sys.exit("Unable to copy profiles to {}: {}".format(profile_target, error))
+
+ print("Wrote profiles to {}".format(profile_target))
+
+
+def main(argv):
+ args = parse_and_validate_args()
+ profile_target = get_and_prepare_profile_target(args)
+ trace_config = get_perfetto_config(args)
+ if args.print_config:
+ print(trace_config)
+ return 0
+ record_trace(trace_config, profile_target)
+ traceconv = get_traceconv()
+ trace_file = symbolize_trace(traceconv, profile_target)
+ copy_profiles_to_destination(profile_target,
+ generate_pprof_profiles(traceconv, trace_file))
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/python/tools/heap_profile.py b/python/tools/heap_profile.py
new file mode 100644
index 0000000..38325de
--- /dev/null
+++ b/python/tools/heap_profile.py
@@ -0,0 +1,578 @@
+#!/usr/bin/env python3
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import atexit
+import os
+import shutil
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+import uuid
+
+from perfetto.prebuilts.manifests.traceconv import *
+from perfetto.prebuilts.perfetto_prebuilts import *
+
+NULL = open(os.devnull)
+NOOUT = {
+ 'stdout': NULL,
+ 'stderr': NULL,
+}
+
+UUID = str(uuid.uuid4())[-6:]
+
+PACKAGES_LIST_CFG = '''data_sources {
+ config {
+ name: "android.packages_list"
+ }
+}
+'''
+
+CFG_INDENT = ' '
+CFG = '''buffers {{
+ size_kb: 63488
+}}
+
+data_sources {{
+ config {{
+ name: "android.heapprofd"
+ heapprofd_config {{
+ shmem_size_bytes: {shmem_size}
+ sampling_interval_bytes: {interval}
+{target_cfg}
+ }}
+ }}
+}}
+
+duration_ms: {duration}
+write_into_file: true
+flush_timeout_ms: 30000
+flush_period_ms: 604800000
+'''
+
+# flush_period_ms of 1 week to suppress trace_processor_shell warning.
+
+CONTINUOUS_DUMP = """
+ continuous_dump_config {{
+ dump_phase_ms: 0
+ dump_interval_ms: {dump_interval}
+ }}
+"""
+
+PROFILE_LOCAL_PATH = os.path.join(tempfile.gettempdir(), UUID)
+
+IS_INTERRUPTED = False
+
+
+def sigint_handler(sig, frame):
+ global IS_INTERRUPTED
+ IS_INTERRUPTED = True
+
+
+def print_no_profile_error():
+ print("No profiles generated", file=sys.stderr)
+ print(
+ "If this is unexpected, check "
+ "https://perfetto.dev/docs/data-sources/native-heap-profiler#troubleshooting.",
+ file=sys.stderr)
+
+
+def known_issues_url(number):
+ return ('https://perfetto.dev/docs/data-sources/native-heap-profiler'
+ '#known-issues-android{}'.format(number))
+
+
+KNOWN_ISSUES = {
+ '10': known_issues_url(10),
+ 'Q': known_issues_url(10),
+ '11': known_issues_url(11),
+ 'R': known_issues_url(11),
+}
+
+
+def maybe_known_issues():
+ release_or_codename = subprocess.check_output(
+ ['adb', 'shell', 'getprop',
+ 'ro.build.version.release_or_codename']).decode('utf-8').strip()
+ return KNOWN_ISSUES.get(release_or_codename, None)
+
+
+SDK = {
+ 'R': 30,
+}
+
+
+def release_or_newer(release):
+ sdk = int(
+ subprocess.check_output(
+ ['adb', 'shell', 'getprop',
+ 'ro.system.build.version.sdk']).decode('utf-8').strip())
+ if sdk >= SDK[release]:
+ return True
+ codename = subprocess.check_output(
+ ['adb', 'shell', 'getprop',
+ 'ro.build.version.codename']).decode('utf-8').strip()
+ return codename == release
+
+
+ORDER = ['-n', '-p', '-i', '-o']
+
+
+def arg_order(action):
+ result = len(ORDER)
+ for opt in action.option_strings:
+ if opt in ORDER:
+ result = min(ORDER.index(opt), result)
+ return result, action.option_strings[0].strip('-')
+
+
+def print_options(parser):
+ for action in sorted(parser._actions, key=arg_order):
+ if action.help is argparse.SUPPRESS:
+ continue
+ opts = ', '.join('`' + x + '`' for x in action.option_strings)
+ metavar = '' if action.metavar is None else ' _' + action.metavar + '_'
+ print('{}{}'.format(opts, metavar))
+ print(': {}'.format(action.help))
+ print()
+
+
+def main(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-i",
+ "--interval",
+ help="Sampling interval. "
+ "Default 4096 (4KiB)",
+ type=int,
+ default=4096)
+ parser.add_argument(
+ "-d",
+ "--duration",
+ help="Duration of profile (ms). 0 to run until interrupted. "
+ "Default: until interrupted by user.",
+ type=int,
+ default=0)
+ # This flag is a no-op now. We never start heapprofd explicitly using system
+ # properties.
+ parser.add_argument(
+ "--no-start", help="Do not start heapprofd.", action='store_true')
+ parser.add_argument(
+ "-p",
+ "--pid",
+ help="Comma-separated list of PIDs to "
+ "profile.",
+ metavar="PIDS")
+ parser.add_argument(
+ "-n",
+ "--name",
+ help="Comma-separated list of process "
+ "names to profile.",
+ metavar="NAMES")
+ parser.add_argument(
+ "-c",
+ "--continuous-dump",
+ help="Dump interval in ms. 0 to disable continuous dump.",
+ type=int,
+ default=0)
+ parser.add_argument(
+ "--heaps",
+ help="Comma-separated list of heaps to collect, e.g: malloc,art. "
+ "Requires Android 12.",
+ metavar="HEAPS")
+ parser.add_argument(
+ "--all-heaps",
+ action="store_true",
+ help="Collect allocations from all heaps registered by target.")
+ parser.add_argument(
+ "--no-android-tree-symbolization",
+ action="store_true",
+ help="Do not symbolize using currently lunched target in the "
+ "Android tree.")
+ parser.add_argument(
+ "--disable-selinux",
+ action="store_true",
+ help="Disable SELinux enforcement for duration of "
+ "profile.")
+ parser.add_argument(
+ "--no-versions",
+ action="store_true",
+ help="Do not get version information about APKs.")
+ parser.add_argument(
+ "--no-running",
+ action="store_true",
+ help="Do not target already running processes. Requires Android 11.")
+ parser.add_argument(
+ "--no-startup",
+ action="store_true",
+ help="Do not target processes that start during "
+ "the profile. Requires Android 11.")
+ parser.add_argument(
+ "--shmem-size",
+ help="Size of buffer between client and "
+ "heapprofd. Default 8MiB. Needs to be a power of two "
+ "multiple of 4096, at least 8192.",
+ type=int,
+ default=8 * 1048576)
+ parser.add_argument(
+ "--block-client",
+ help="When buffer is full, block the "
+ "client to wait for buffer space. Use with caution as "
+ "this can significantly slow down the client. "
+ "This is the default",
+ action="store_true")
+ parser.add_argument(
+ "--block-client-timeout",
+ help="If --block-client is given, do not block any allocation for "
+ "longer than this timeout (us).",
+ type=int)
+ parser.add_argument(
+ "--no-block-client",
+ help="When buffer is full, stop the "
+ "profile early.",
+ action="store_true")
+ parser.add_argument(
+ "--idle-allocations",
+ help="Keep track of how many "
+ "bytes were unused since the last dump, per "
+ "callstack",
+ action="store_true")
+ parser.add_argument(
+ "--dump-at-max",
+ help="Dump the maximum memory usage "
+ "rather than at the time of the dump.",
+ action="store_true")
+ parser.add_argument(
+ "--disable-fork-teardown",
+ help="Do not tear down client in forks. This can be useful for programs "
+ "that use vfork. Android 11+ only.",
+ action="store_true")
+ parser.add_argument(
+ "--simpleperf",
+ action="store_true",
+ help="Get simpleperf profile of heapprofd. This is "
+ "only for heapprofd development.")
+ parser.add_argument(
+ "--traceconv-binary", help="Path to local trace to text. For debugging.")
+ parser.add_argument(
+ "--print-config",
+ action="store_true",
+ help="Print config instead of running. For debugging.")
+ parser.add_argument(
+ "-o",
+ "--output",
+ help="Output directory.",
+ metavar="DIRECTORY",
+ default=None)
+ parser.add_argument(
+ "--print-options", action="store_true", help=argparse.SUPPRESS)
+
+ args = parser.parse_args()
+ if args.print_options:
+ print_options(parser)
+ return 0
+ fail = False
+ if args.block_client and args.no_block_client:
+ print(
+ "FATAL: Both block-client and no-block-client given.", file=sys.stderr)
+ fail = True
+ if args.pid is None and args.name is None:
+ print("FATAL: Neither PID nor NAME given.", file=sys.stderr)
+ fail = True
+ if args.duration is None:
+ print("FATAL: No duration given.", file=sys.stderr)
+ fail = True
+ if args.interval is None:
+ print("FATAL: No interval given.", file=sys.stderr)
+ fail = True
+ if args.shmem_size % 4096:
+ print("FATAL: shmem-size is not a multiple of 4096.", file=sys.stderr)
+ fail = True
+ if args.shmem_size < 8192:
+ print("FATAL: shmem-size is less than 8192.", file=sys.stderr)
+ fail = True
+ if args.shmem_size & (args.shmem_size - 1):
+ print("FATAL: shmem-size is not a power of two.", file=sys.stderr)
+ fail = True
+
+ target_cfg = ""
+ if not args.no_block_client:
+ target_cfg += CFG_INDENT + "block_client: true\n"
+ if args.block_client_timeout:
+ target_cfg += (
+ CFG_INDENT +
+ "block_client_timeout_us: %s\n" % args.block_client_timeout)
+ if args.no_startup:
+ target_cfg += CFG_INDENT + "no_startup: true\n"
+ if args.no_running:
+ target_cfg += CFG_INDENT + "no_running: true\n"
+ if args.dump_at_max:
+ target_cfg += CFG_INDENT + "dump_at_max: true\n"
+ if args.disable_fork_teardown:
+ target_cfg += CFG_INDENT + "disable_fork_teardown: true\n"
+ if args.all_heaps:
+ target_cfg += CFG_INDENT + "all_heaps: true\n"
+ if args.pid:
+ for pid in args.pid.split(','):
+ try:
+ pid = int(pid)
+ except ValueError:
+ print("FATAL: invalid PID %s" % pid, file=sys.stderr)
+ fail = True
+ target_cfg += CFG_INDENT + 'pid: {}\n'.format(pid)
+ if args.name:
+ for name in args.name.split(','):
+ target_cfg += CFG_INDENT + 'process_cmdline: "{}"\n'.format(name)
+ if args.heaps:
+ for heap in args.heaps.split(','):
+ target_cfg += CFG_INDENT + 'heaps: "{}"\n'.format(heap)
+
+ if fail:
+ parser.print_help()
+ return 1
+
+ traceconv_binary = args.traceconv_binary
+
+ if args.continuous_dump:
+ target_cfg += CONTINUOUS_DUMP.format(dump_interval=args.continuous_dump)
+ cfg = CFG.format(
+ interval=args.interval,
+ duration=args.duration,
+ target_cfg=target_cfg,
+ shmem_size=args.shmem_size)
+ if not args.no_versions:
+ cfg += PACKAGES_LIST_CFG
+
+ if args.print_config:
+ print(cfg)
+ return 0
+
+ # Do this AFTER print_config so we do not download traceconv only to
+ # print out the config.
+ if traceconv_binary is None:
+ traceconv_binary = get_perfetto_prebuilt(TRACECONV_MANIFEST, soft_fail=True)
+
+ known_issues = maybe_known_issues()
+ if known_issues:
+ print('If you are experiencing problems, please see the known issues for '
+ 'your release: {}.'.format(known_issues))
+
+ # TODO(fmayer): Maybe feature detect whether we can remove traces instead of
+ # this.
+ uuid_trace = release_or_newer('R')
+ if uuid_trace:
+ profile_device_path = '/data/misc/perfetto-traces/profile-' + UUID
+ else:
+ user = subprocess.check_output(['adb', 'shell',
+ 'whoami']).decode('utf-8').strip()
+ profile_device_path = '/data/misc/perfetto-traces/profile-' + user
+
+ perfetto_cmd = ('CFG=\'{cfg}\'; echo ${{CFG}} | '
+ 'perfetto --txt -c - -o ' + profile_device_path + ' -d')
+
+ if args.disable_selinux:
+ enforcing = subprocess.check_output(['adb', 'shell', 'getenforce'])
+ atexit.register(
+ subprocess.check_call,
+ ['adb', 'shell', 'su root setenforce %s' % enforcing])
+ subprocess.check_call(['adb', 'shell', 'su root setenforce 0'])
+
+ if args.simpleperf:
+ subprocess.check_call([
+ 'adb', 'shell', 'mkdir -p /data/local/tmp/heapprofd_profile && '
+ 'cd /data/local/tmp/heapprofd_profile &&'
+ '(nohup simpleperf record -g -p $(pidof heapprofd) 2>&1 &) '
+ '> /dev/null'
+ ])
+
+ profile_target = PROFILE_LOCAL_PATH
+ if args.output is not None:
+ profile_target = args.output
+ else:
+ os.mkdir(profile_target)
+
+ if not os.path.isdir(profile_target):
+ print(
+ "Output directory {} not found".format(profile_target), file=sys.stderr)
+ return 1
+
+ if os.listdir(profile_target):
+ print(
+ "Output directory {} not empty".format(profile_target), file=sys.stderr)
+ return 1
+
+ perfetto_pid = subprocess.check_output(
+ ['adb', 'exec-out', perfetto_cmd.format(cfg=cfg)]).strip()
+ try:
+ perfetto_pid = int(perfetto_pid.strip())
+ except ValueError:
+ print("Failed to invoke perfetto: {}".format(perfetto_pid), file=sys.stderr)
+ return 1
+
+ old_handler = signal.signal(signal.SIGINT, sigint_handler)
+ print("Profiling active. Press Ctrl+C to terminate.")
+ print("You may disconnect your device.")
+ print()
+ exists = True
+ device_connected = True
+ while not device_connected or (exists and not IS_INTERRUPTED):
+ exists = subprocess.call(
+ ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)], **NOOUT) == 0
+ device_connected = subprocess.call(['adb', 'shell', 'true'], **NOOUT) == 0
+ time.sleep(1)
+ print("Waiting for profiler shutdown...")
+ signal.signal(signal.SIGINT, old_handler)
+ if IS_INTERRUPTED:
+ # Not check_call because it could have existed in the meantime.
+ subprocess.call(['adb', 'shell', 'kill', '-INT', str(perfetto_pid)])
+ if args.simpleperf:
+ subprocess.check_call(['adb', 'shell', 'killall', '-INT', 'simpleperf'])
+ print("Waiting for simpleperf to exit.")
+ while subprocess.call(
+ ['adb', 'shell', '[ -f /proc/$(pidof simpleperf)/exe ]'], **NOOUT) == 0:
+ time.sleep(1)
+ subprocess.check_call(
+ ['adb', 'pull', '/data/local/tmp/heapprofd_profile', profile_target])
+ print("Pulled simpleperf profile to " + profile_target +
+ "/heapprofd_profile")
+
+ # Wait for perfetto cmd to return.
+ while exists:
+ exists = subprocess.call(
+ ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
+ time.sleep(1)
+
+ profile_host_path = os.path.join(profile_target, 'raw-trace')
+ subprocess.check_call(['adb', 'pull', profile_device_path, profile_host_path],
+ stdout=NULL)
+ if uuid_trace:
+ subprocess.check_call(['adb', 'shell', 'rm', profile_device_path],
+ stdout=NULL)
+
+ if traceconv_binary is None:
+ print('Wrote profile to {}'.format(profile_host_path))
+ print(
+ 'This file can be opened using the Perfetto UI, https://ui.perfetto.dev'
+ )
+ return 0
+
+ binary_path = os.getenv('PERFETTO_BINARY_PATH')
+ if not args.no_android_tree_symbolization:
+ product_out = os.getenv('ANDROID_PRODUCT_OUT')
+ if product_out:
+ product_out_symbols = product_out + '/symbols'
+ else:
+ product_out_symbols = None
+
+ if binary_path is None:
+ binary_path = product_out_symbols
+ elif product_out_symbols is not None:
+ binary_path += ":" + product_out_symbols
+
+ trace_file = os.path.join(profile_target, 'raw-trace')
+ concat_files = [trace_file]
+
+ if binary_path is not None:
+ with open(os.path.join(profile_target, 'symbols'), 'w') as fd:
+ ret = subprocess.call([
+ traceconv_binary, 'symbolize',
+ os.path.join(profile_target, 'raw-trace')
+ ],
+ env=dict(
+ os.environ, PERFETTO_BINARY_PATH=binary_path),
+ stdout=fd)
+ if ret == 0:
+ concat_files.append(os.path.join(profile_target, 'symbols'))
+ else:
+ print("Failed to symbolize. Continuing without symbols.", file=sys.stderr)
+
+ proguard_map = os.getenv('PERFETTO_PROGUARD_MAP')
+ if proguard_map is not None:
+ with open(os.path.join(profile_target, 'deobfuscation-packets'), 'w') as fd:
+ ret = subprocess.call([
+ traceconv_binary, 'deobfuscate',
+ os.path.join(profile_target, 'raw-trace')
+ ],
+ env=dict(
+ os.environ, PERFETTO_PROGUARD_MAP=proguard_map),
+ stdout=fd)
+ if ret == 0:
+ concat_files.append(os.path.join(profile_target, 'deobfuscation-packets'))
+ else:
+ print(
+ "Failed to deobfuscate. Continuing without deobfuscated.",
+ file=sys.stderr)
+
+ if len(concat_files) > 1:
+ with open(os.path.join(profile_target, 'symbolized-trace'), 'wb') as out:
+ for fn in concat_files:
+ with open(fn, 'rb') as inp:
+ while True:
+ buf = inp.read(4096)
+ if not buf:
+ break
+ out.write(buf)
+ trace_file = os.path.join(profile_target, 'symbolized-trace')
+
+ traceconv_output = subprocess.check_output(
+ [traceconv_binary, 'profile', trace_file])
+ profile_path = None
+ for word in traceconv_output.decode('utf-8').split():
+ if 'heap_profile-' in word:
+ profile_path = word
+ if profile_path is None:
+ print_no_profile_error()
+ return 1
+
+ profile_files = os.listdir(profile_path)
+ if not profile_files:
+ print_no_profile_error()
+ return 1
+
+ for profile_file in profile_files:
+ shutil.copy(os.path.join(profile_path, profile_file), profile_target)
+
+ symlink_path = None
+ if not sys.platform.startswith('win'):
+ subprocess.check_call(
+ ['gzip'] + [os.path.join(profile_target, x) for x in profile_files])
+ if args.output is None:
+ symlink_path = os.path.join(
+ os.path.dirname(profile_target), "heap_profile-latest")
+ if os.path.lexists(symlink_path):
+ os.unlink(symlink_path)
+ os.symlink(profile_target, symlink_path)
+
+ if symlink_path is not None:
+ print("Wrote profiles to {} (symlink {})".format(profile_target,
+ symlink_path))
+ else:
+ print("Wrote profiles to {}".format(profile_target))
+
+ print("The raw-trace file can be viewed using https://ui.perfetto.dev.")
+ print("The heap_dump.* files can be viewed using pprof/ (Googlers only) " +
+ "or https://www.speedscope.app/.")
+ print("The two above are equivalent. The raw-trace contains the union of " +
+ "all the heap dumps.")
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/python/tools/record_android_trace.py b/python/tools/record_android_trace.py
new file mode 100644
index 0000000..ed62c7e
--- /dev/null
+++ b/python/tools/record_android_trace.py
@@ -0,0 +1,409 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import atexit
+import argparse
+import datetime
+import hashlib
+import http.server
+import os
+import re
+import shutil
+import socketserver
+import subprocess
+import sys
+import time
+import webbrowser
+
+from perfetto.prebuilts.manifests.tracebox import *
+from perfetto.prebuilts.perfetto_prebuilts import *
+from perfetto.common.repo_utils import *
+
+# This is not required. It's only used as a fallback if no adb is found on the
+# PATH. It's fine if it doesn't exist so this script can be copied elsewhere.
+HERMETIC_ADB_PATH = repo_dir('/buildtools/android_sdk/platform-tools/adb')
+
+# Translates the Android ro.product.cpu.abi into the GN's target_cpu.
+ABI_TO_ARCH = {
+ 'armeabi-v7a': 'arm',
+ 'arm64-v8a': 'arm64',
+ 'x86': 'x86',
+ 'x86_64': 'x64',
+}
+
+MAX_ADB_FAILURES = 15 # 2 seconds between retries, 30 seconds total.
+
+devnull = open(os.devnull, 'rb')
+adb_path = None
+procs = []
+
+
+class ANSI:
+ END = '\033[0m'
+ BOLD = '\033[1m'
+ RED = '\033[91m'
+ BLACK = '\033[30m'
+ BLUE = '\033[94m'
+ BG_YELLOW = '\033[43m'
+ BG_BLUE = '\033[44m'
+
+
+# HTTP Server used to open the trace in the browser.
+class HttpHandler(http.server.SimpleHTTPRequestHandler):
+
+ def end_headers(self):
+ self.send_header('Access-Control-Allow-Origin', '*')
+ return super().end_headers()
+
+ def do_GET(self):
+ self.server.last_request = self.path
+ return super().do_GET()
+
+ def do_POST(self):
+ self.send_error(404, "File not found")
+
+
+def main():
+ atexit.register(kill_all_subprocs_on_exit)
+ default_out_dir_str = '~/traces/'
+ default_out_dir = os.path.expanduser(default_out_dir_str)
+
+ examples = '\n'.join([
+ ANSI.BOLD + 'Examples' + ANSI.END, ' -t 10s -b 32mb sched gfx wm -a*',
+ ' -t 5s sched/sched_switch raw_syscalls/sys_enter raw_syscalls/sys_exit',
+ ' -c /path/to/full-textual-trace.config', '',
+ ANSI.BOLD + 'Long traces' + ANSI.END,
+ 'If you want to record a hours long trace and stream it into a file ',
+ 'you need to pass a full trace config and set write_into_file = true.',
+ 'See https://perfetto.dev/docs/concepts/config#long-traces .'
+ ])
+ parser = argparse.ArgumentParser(
+ epilog=examples, formatter_class=argparse.RawTextHelpFormatter)
+
+ help = 'Output file or directory (default: %s)' % default_out_dir_str
+ parser.add_argument('-o', '--out', default=default_out_dir, help=help)
+
+ help = 'Don\'t open in the browser'
+ parser.add_argument('-n', '--no-open', action='store_true', help=help)
+
+ help = 'Force the use of the sideloaded binaries rather than system daemons'
+ parser.add_argument('--sideload', action='store_true', help=help)
+
+ help = ('Sideload the given binary rather than downloading it. ' +
+ 'Implies --sideload')
+ parser.add_argument('--sideload-path', default=None, help=help)
+
+ help = 'Don\'t run `adb root` run as user (only when sideloading)'
+ parser.add_argument('-u', '--user', action='store_true', help=help)
+
+ help = 'Specify the ADB device serial'
+ parser.add_argument('--serial', '-s', default=None, help=help)
+
+ grp = parser.add_argument_group(
+ 'Short options: (only when not using -c/--config)')
+
+ help = 'Trace duration N[s,m,h] (default: trace until stopped)'
+ grp.add_argument('-t', '--time', default='0s', help=help)
+
+ help = 'Ring buffer size N[mb,gb] (default: 32mb)'
+ grp.add_argument('-b', '--buffer', default='32mb', help=help)
+
+ help = ('Android (atrace) app names. Can be specified multiple times.\n-a*' +
+ 'for all apps (without space between a and * or bash will expand it)')
+ grp.add_argument(
+ '-a',
+ '--app',
+ metavar='com.myapp',
+ action='append',
+ default=[],
+ help=help)
+
+ help = 'sched, gfx, am, wm (see --list)'
+ grp.add_argument('events', metavar='Atrace events', nargs='*', help=help)
+
+ help = 'sched/sched_switch kmem/kmem (see --list-ftrace)'
+ grp.add_argument('_', metavar='Ftrace events', nargs='*', help=help)
+
+ help = 'Lists all the categories available'
+ grp.add_argument('--list', action='store_true', help=help)
+
+ help = 'Lists all the ftrace events available'
+ grp.add_argument('--list-ftrace', action='store_true', help=help)
+
+ section = ('Full trace config (only when not using short options)')
+ grp = parser.add_argument_group(section)
+
+ help = 'Can be generated with https://ui.perfetto.dev/#!/record'
+ grp.add_argument('-c', '--config', default=None, help=help)
+
+ args = parser.parse_args()
+ args.sideload = args.sideload or args.sideload_path is not None
+
+ if args.serial:
+ os.environ["ANDROID_SERIAL"] = args.serial
+
+ find_adb()
+
+ if args.list:
+ adb('shell', 'atrace', '--list_categories').wait()
+ sys.exit(0)
+
+ if args.list_ftrace:
+ adb('shell', 'cat /d/tracing/available_events | tr : /').wait()
+ sys.exit(0)
+
+ if args.config is not None and not os.path.exists(args.config):
+ prt('Config file not found: %s' % args.config, ANSI.RED)
+ sys.exit(1)
+
+ if len(args.events) == 0 and args.config is None:
+ prt('Must either pass short options (e.g. -t 10s sched) or a --config file',
+ ANSI.RED)
+ parser.print_help()
+ sys.exit(1)
+
+ if args.config is None and args.events and os.path.exists(args.events[0]):
+ prt(('The passed event name "%s" is a local file. ' % args.events[0] +
+ 'Did you mean to pass -c / --config ?'), ANSI.RED)
+ sys.exit(1)
+
+ perfetto_cmd = 'perfetto'
+ device_dir = '/data/misc/perfetto-traces/'
+
+ # Check the version of android. If too old (< Q) sideload tracebox. Also use
+ # use /data/local/tmp as /data/misc/perfetto-traces was introduced only later.
+ probe_cmd = 'getprop ro.build.version.sdk; getprop ro.product.cpu.abi; whoami'
+ probe = adb('shell', probe_cmd, stdout=subprocess.PIPE)
+ lines = probe.communicate()[0].decode().strip().split('\n')
+ lines = [x.strip() for x in lines] # To strip \r(s) on Windows.
+ if probe.returncode != 0:
+ prt('ADB connection failed', ANSI.RED)
+ sys.exit(1)
+ api_level = int(lines[0])
+ abi = lines[1]
+ arch = ABI_TO_ARCH.get(abi)
+ if arch is None:
+ prt('Unsupported ABI: ' + abi)
+ sys.exit(1)
+ shell_user = lines[2]
+ if api_level < 29 or args.sideload: # 29: Android Q.
+ tracebox_bin = args.sideload_path
+ if tracebox_bin is None:
+ tracebox_bin = get_perfetto_prebuilt(
+ TRACEBOX_MANIFEST, arch='android-' + arch)
+ perfetto_cmd = '/data/local/tmp/tracebox'
+ exit_code = adb('push', '--sync', tracebox_bin, perfetto_cmd).wait()
+ exit_code |= adb('shell', 'chmod 755 ' + perfetto_cmd).wait()
+ if exit_code != 0:
+ prt('ADB push failed', ANSI.RED)
+ sys.exit(1)
+ device_dir = '/data/local/tmp/'
+ if shell_user != 'root' and not args.user:
+ # Run as root if possible as that will give access to more tracing
+ # capabilities. Non-root still works, but some ftrace events might not be
+ # available.
+ adb('root').wait()
+
+ tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M')
+ fname = '%s-%s.pftrace' % (tstamp, os.urandom(3).hex())
+ device_file = device_dir + fname
+
+ cmd = [perfetto_cmd, '--background', '--txt', '-o', device_file]
+ on_device_config = None
+ on_host_config = None
+ if args.config is not None:
+ cmd += ['-c', '-']
+ if api_level < 24:
+ # adb shell does not redirect stdin. Push the config on a temporary file
+ # on the device.
+ mktmp = adb(
+ 'shell',
+ 'mktemp',
+ '--tmpdir',
+ '/data/local/tmp',
+ stdout=subprocess.PIPE)
+ on_device_config = mktmp.communicate()[0].decode().strip().strip()
+ if mktmp.returncode != 0:
+ prt('Failed to create config on device', ANSI.RED)
+ sys.exit(1)
+ exit_code = adb('push', '--sync', args.config, on_device_config).wait()
+ if exit_code != 0:
+ prt('Failed to push config on device', ANSI.RED)
+ sys.exit(1)
+ cmd = ['cat', on_device_config, '|'] + cmd
+ else:
+ on_host_config = args.config
+ else:
+ cmd += ['-t', args.time, '-b', args.buffer]
+ for app in args.app:
+ cmd += ['--app', '\'' + app + '\'']
+ cmd += args.events
+
+ # Perfetto will error out with a proper message if both a config file and
+ # short options are specified. No need to replicate that logic.
+
+ # Work out the output file or directory.
+ if args.out.endswith('/') or os.path.isdir(args.out):
+ host_dir = args.out
+ host_file = os.path.join(args.out, fname)
+ else:
+ host_file = args.out
+ host_dir = os.path.dirname(host_file)
+ if host_dir == '':
+ host_dir = '.'
+ host_file = './' + host_file
+ if not os.path.exists(host_dir):
+ shutil.os.makedirs(host_dir)
+
+ with open(on_host_config or os.devnull, 'rb') as f:
+ print('Running ' + ' '.join(cmd))
+ proc = adb('shell', *cmd, stdin=f, stdout=subprocess.PIPE)
+ proc_out = proc.communicate()[0].decode().strip()
+ if on_device_config is not None:
+ adb('shell', 'rm', on_device_config).wait()
+ # On older versions of Android (x86_64 emulator running API 22) the output
+ # looks like:
+ # WARNING: linker: /data/local/tmp/tracebox: unused DT entry: ...
+ # WARNING: ... (other 2 WARNING: linker: lines)
+ # 1234 <-- The actual pid we want.
+ match = re.search(r'^(\d+)$', proc_out, re.M)
+ if match is None:
+ prt('Failed to read the pid from perfetto --background', ANSI.RED)
+ prt(proc_out)
+ sys.exit(1)
+ bg_pid = match.group(1)
+ exit_code = proc.wait()
+
+ if exit_code != 0:
+ prt('Perfetto invocation failed', ANSI.RED)
+ sys.exit(1)
+
+ prt('Trace started. Press CTRL+C to stop', ANSI.BLACK + ANSI.BG_BLUE)
+ logcat = adb('logcat', '-v', 'brief', '-s', 'perfetto', '-b', 'main', '-T',
+ '1')
+
+ ctrl_c_count = 0
+ adb_failure_count = 0
+ while ctrl_c_count < 2:
+ try:
+ # On older Android devices adbd doesn't propagate the exit code. Hence
+ # the RUN/TERM parts.
+ poll = adb(
+ 'shell',
+ 'test -d /proc/%s && echo RUN || echo TERM' % bg_pid,
+ stdout=subprocess.PIPE)
+ poll_res = poll.communicate()[0].decode().strip()
+ if poll_res == 'TERM':
+ break # Process terminated
+ if poll_res == 'RUN':
+ # The 'perfetto' cmdline client is still running. If previously we had
+ # an ADB error, tell the user now it's all right again.
+ if adb_failure_count > 0:
+ adb_failure_count = 0
+ prt('ADB connection re-established, the trace is still ongoing',
+ ANSI.BLUE)
+ time.sleep(0.5)
+ continue
+ # Some ADB error happened. This can happen when tracing soon after boot,
+ # before logging in, when adb gets restarted.
+ adb_failure_count += 1
+ if adb_failure_count >= MAX_ADB_FAILURES:
+ prt('Too many unrecoverable ADB failures, bailing out', ANSI.RED)
+ sys.exit(1)
+ time.sleep(2)
+ except KeyboardInterrupt:
+ sig = 'TERM' if ctrl_c_count == 0 else 'KILL'
+ ctrl_c_count += 1
+ prt('Stopping the trace (SIG%s)' % sig, ANSI.BLACK + ANSI.BG_YELLOW)
+ adb('shell', 'kill -%s %s' % (sig, bg_pid)).wait()
+
+ logcat.kill()
+ logcat.wait()
+
+ prt('\n')
+ prt('Pulling into %s' % host_file, ANSI.BOLD)
+ adb('pull', device_file, host_file).wait()
+ adb('shell', 'rm -f ' + device_file).wait()
+
+ if not args.no_open:
+ prt('\n')
+ prt('Opening the trace (%s) in the browser' % host_file)
+ open_trace_in_browser(host_file)
+
+
+def prt(msg, colors=ANSI.END):
+ print(colors + msg + ANSI.END)
+
+
+def find_adb():
+ """ Locate the "right" adb path
+
+ If adb is in the PATH use that (likely what the user wants) otherwise use the
+ hermetic one in our SDK copy.
+ """
+ global adb_path
+ for path in ['adb', HERMETIC_ADB_PATH]:
+ try:
+ subprocess.call([path, '--version'], stdout=devnull, stderr=devnull)
+ adb_path = path
+ break
+ except OSError:
+ continue
+ if adb_path is None:
+ sdk_url = 'https://developer.android.com/studio/releases/platform-tools'
+ prt('Could not find a suitable adb binary in the PATH. ', ANSI.RED)
+ prt('You can download adb from %s' % sdk_url, ANSI.RED)
+ sys.exit(1)
+
+
+def open_trace_in_browser(path):
+ # We reuse the HTTP+RPC port because it's the only one allowed by the CSP.
+ PORT = 9001
+ os.chdir(os.path.dirname(path))
+ fname = os.path.basename(path)
+ socketserver.TCPServer.allow_reuse_address = True
+ with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
+ webbrowser.open_new_tab(
+ 'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
+ (PORT, fname))
+ while httpd.__dict__.get('last_request') != '/' + fname:
+ httpd.handle_request()
+
+
+def adb(*args, stdin=devnull, stdout=None):
+ cmd = [adb_path, *args]
+ setpgrp = None
+ if os.name != 'nt':
+ # On Linux/Mac, start a new process group so all child processes are killed
+ # on exit. Unsupported on Windows.
+ setpgrp = lambda: os.setpgrp()
+ proc = subprocess.Popen(cmd, stdin=stdin, stdout=stdout, preexec_fn=setpgrp)
+ procs.append(proc)
+ return proc
+
+
+def kill_all_subprocs_on_exit():
+ for p in [p for p in procs if p.poll() is None]:
+ p.kill()
+
+
+def check_hash(file_name, sha_value):
+ with open(file_name, 'rb') as fd:
+ file_hash = hashlib.sha1(fd.read()).hexdigest()
+ return file_hash == sha_value
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/python/tools/trace_processor.py b/python/tools/trace_processor.py
new file mode 100644
index 0000000..12d63e4
--- /dev/null
+++ b/python/tools/trace_processor.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file should do the same thing when being invoked in any of these ways:
+# ./trace_processor
+# python trace_processor
+# bash trace_processor
+# cat ./trace_processor | bash
+# cat ./trace_processor | python -
+
+BASH_FALLBACK=""" "
+exec python3 - "$@" <<'#'EOF
+#""" # yapf: disable
+
+from perfetto.prebuilts.manifests.trace_processor_shell import *
+from perfetto.prebuilts.perfetto_prebuilts import *
+
+if __name__ == '__main__':
+ run_perfetto_prebuilt(TRACE_PROCESSOR_SHELL_MANIFEST)
+
+#EOF
diff --git a/python/tools/tracebox.py b/python/tools/tracebox.py
new file mode 100644
index 0000000..5ca2898
--- /dev/null
+++ b/python/tools/tracebox.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file should do the same thing when being invoked in any of these ways:
+# ./tracebox
+# python tracebox
+# bash tracebox
+# cat ./tracebox | bash
+# cat ./tracebox | python -
+
+BASH_FALLBACK=""" "
+exec python3 - "$@" <<'#'EOF
+#""" # yapf: disable
+
+from perfetto.prebuilts.manifests.tracebox import *
+from perfetto.prebuilts.perfetto_prebuilts import *
+
+if __name__ == '__main__':
+ run_perfetto_prebuilt(TRACEBOX_MANIFEST)
+
+#EOF
diff --git a/python/tools/traceconv.py b/python/tools/traceconv.py
new file mode 100644
index 0000000..4a42baa
--- /dev/null
+++ b/python/tools/traceconv.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file should do the same thing when being invoked in any of these ways:
+# ./traceconv
+# python traceconv
+# bash traceconv
+# cat ./traceconv | bash
+# cat ./traceconv | python -
+
+BASH_FALLBACK=""" "
+exec python3 - "$@" <<'#'EOF
+#""" # yapf: disable
+
+from perfetto.prebuilts.manifests.traceconv import *
+from perfetto.prebuilts.perfetto_prebuilts import *
+
+if __name__ == '__main__':
+ run_perfetto_prebuilt(TRACECONV_MANIFEST)
+
+#EOF
diff --git a/tools/cpu_profile b/tools/cpu_profile
index 38fb15f..46cedbd 100755
--- a/tools/cpu_profile
+++ b/tools/cpu_profile
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-
# Copyright (C) 2022 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +14,10 @@
# limitations under the License.
"""Runs tracing with CPU profiling enabled, and symbolizes traces if requested.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
For usage instructions, please see:
https://perfetto.dev/docs/quickstart/callstack-sampling
@@ -32,6 +35,268 @@
import time
import uuid
+
+# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACECONV_MANIFEST = [{
+ 'arch':
+ 'mac-amd64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7181712,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+ 'sha256':
+ '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+ 'platform':
+ 'darwin',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'mac-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6025176,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+ 'sha256':
+ '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+ 'platform':
+ 'darwin',
+ 'machine': ['arm64']
+}, {
+ 'arch':
+ 'linux-amd64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7668600,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+ 'sha256':
+ '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+ 'platform':
+ 'linux',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'linux-arm',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 5827680,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+ 'sha256':
+ 'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+ 'platform':
+ 'linux',
+ 'machine': ['armv6l', 'armv7l', 'armv8l']
+}, {
+ 'arch':
+ 'linux-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6876384,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+ 'sha256':
+ '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+ 'platform':
+ 'linux',
+ 'machine': ['aarch64']
+}, {
+ 'arch':
+ 'android-arm',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 4881820,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+ 'sha256':
+ '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+}, {
+ 'arch':
+ 'android-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6222592,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+ 'sha256':
+ '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+}, {
+ 'arch':
+ 'android-x86',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7089524,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+ 'sha256':
+ '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+}, {
+ 'arch':
+ 'android-x64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7316248,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+ 'sha256':
+ '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+}, {
+ 'arch':
+ 'windows-amd64',
+ 'file_name':
+ 'traceconv.exe',
+ 'file_size':
+ 6850048,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+ 'sha256':
+ '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+ 'platform':
+ 'win32',
+ 'machine': ['amd64']
+}]
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/traceconv.py
+
+# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
+ """ Downloads the prebuilt, if necessary, and returns its path on disk. """
+ plat = sys.platform.lower()
+ machine = platform.machine().lower()
+ manifest_entry = None
+ for entry in manifest:
+ # If the caller overrides the arch, just match that (for Android prebuilts).
+ if arch:
+ if entry.get('arch') == arch:
+ manifest_entry = entry
+ break
+ continue
+ # Otherwise guess the local machine arch.
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
+ manifest_entry = entry
+ break
+ if manifest_entry is None:
+ if soft_fail:
+ return None
+ raise Exception(
+ ('No prebuilts available for %s-%s\n' % (plat, machine)) +
+ 'See https://perfetto.dev/docs/contributing/build-instructions')
+
+ return download_or_get_cached(
+ file_name=manifest_entry['file_name'],
+ url=manifest_entry['url'],
+ sha256=manifest_entry['sha256'])
+
+
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
+
# Used for creating directories, etc.
UUID = str(uuid.uuid4())[-6:]
@@ -329,7 +594,7 @@
def get_traceconv():
"""Sets up and returns the path to `traceconv`."""
try:
- traceconv = get_perfetto_prebuilt('traceconv', soft_fail=True)
+ traceconv = get_perfetto_prebuilt(TRACECONV_MANIFEST, soft_fail=True)
except Exception as error:
exit_with_bug_report(error)
if traceconv is None:
@@ -448,233 +713,5 @@
return 0
-# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v28.0
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool':
- 'traceconv',
- 'arch':
- 'mac-amd64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7181712,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
- 'sha256':
- '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
- 'platform':
- 'darwin',
- 'machine': ['x86_64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'mac-arm64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 6025176,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
- 'sha256':
- '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
- 'platform':
- 'darwin',
- 'machine': ['arm64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'linux-amd64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7668600,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
- 'sha256':
- '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
- 'platform':
- 'linux',
- 'machine': ['x86_64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'linux-arm',
- 'file_name':
- 'traceconv',
- 'file_size':
- 5827680,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
- 'sha256':
- 'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
- 'platform':
- 'linux',
- 'machine': ['armv6l', 'armv7l', 'armv8l']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'linux-arm64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 6876384,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
- 'sha256':
- '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
- 'platform':
- 'linux',
- 'machine': ['aarch64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-arm',
- 'file_name':
- 'traceconv',
- 'file_size':
- 4881820,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
- 'sha256':
- '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-arm64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 6222592,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
- 'sha256':
- '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-x86',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7089524,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
- 'sha256':
- '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-x64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7316248,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
- 'sha256':
- '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'windows-amd64',
- 'file_name':
- 'traceconv.exe',
- 'file_size':
- 6850048,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
- 'sha256':
- '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
- 'platform':
- 'win32',
- 'machine': ['amd64']
-}]
-
-
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
- """ Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
- plat = sys.platform.lower()
- machine = platform.machine().lower()
- manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
- # If the caller overrides the arch, just match that (for Android prebuilts).
- if arch:
- if entry.get('arch') == arch:
- manifest_entry = entry
- break
- continue
- # Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
- manifest_entry = entry
- break
- if manifest_entry is None:
- if soft_fail:
- return None
- raise Exception(
- ('No prebuilts available for %s-%s\n' % (plat, machine)) +
- 'See https://perfetto.dev/docs/contributing/build-instructions')
-
- return download_or_get_cached(
- file_name=manifest_entry['file_name'],
- url=manifest_entry['url'],
- sha256=manifest_entry['sha256'])
-
-
-# END_SECTION_GENERATED_BY(roll-prebuilts)
-
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/tools/gen_all b/tools/gen_all
index bcf6bd3..ad2884f 100755
--- a/tools/gen_all
+++ b/tools/gen_all
@@ -58,6 +58,7 @@
call('gen_bazel', *check_only)
call('gen_android_bp', *check_only)
call('gen_merged_protos', *check_only)
+ call('gen_amalgamated_python_tools', *check_only)
call('ninja', '-C', out, 'protoc')
call('gen_binary_descriptors', '--protoc', protoc_path(out), *check_only)
diff --git a/tools/gen_amalgamated_python_tools b/tools/gen_amalgamated_python_tools
new file mode 100755
index 0000000..4b21069
--- /dev/null
+++ b/tools/gen_amalgamated_python_tools
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+import logging
+import re
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+AMALGAMATION_MAP = {
+ 'python/tools/record_android_trace.py': 'tools/record_android_trace',
+ 'python/tools/tracebox.py': 'tools/tracebox',
+ 'python/tools/traceconv.py': 'tools/traceconv',
+ 'python/tools/trace_processor.py': 'tools/trace_processor',
+ 'python/tools/cpu_profile.py': 'tools/cpu_profile',
+ 'python/tools/heap_profile.py': 'tools/heap_profile',
+}
+
+
+def amalgamate_file(fname, stack=None, done=None, in_progress=None):
+ stack = [] if stack is None else stack
+ done = set() if done is None else done
+ in_progress = set() if in_progress is None else in_progress
+ if fname in in_progress:
+ cycle = ' > '.join(stack + [fname])
+ logging.fatal('Cycle detected in %s', cycle)
+ sys.exit(1)
+ if fname in done:
+ return []
+ logging.debug('Processing %s', fname)
+ done.add(fname)
+ in_progress.add(fname)
+ with open(fname, encoding='utf-8') as f:
+ lines = f.readlines()
+ outlines = []
+ for line in lines:
+ if line.startswith('from perfetto') or line.startswith('import perfetto'):
+ if not re.match('from perfetto[.][.\w]+\s+import\s+[*]$', line):
+ logging.fatal('Error in %s on line \"%s\"', fname, line.rstrip())
+ logging.fatal('Only "from perfetto.foo import *" is supported in '
+ 'sources that are used in //tools and get amalgamated')
+ sys.exit(1)
+ pkg = line.split()[1]
+ fpath = os.path.join('python', pkg.replace('.', os.sep) + '.py')
+ outlines.append('\n# ----- Amalgamator: begin of %s\n' % fpath)
+ outlines += amalgamate_file(fpath, stack + [fname], done, in_progress)
+ outlines.append('\n# ----- Amalgamator: end of %s\n' % fpath)
+ elif '__file__' in line and not 'amalgamator:nocheck' in line:
+ logging.fatal('__file__ is not allowed in sources that get amalgamated.'
+ 'In %s on line \"%s\"', fname, line.rstrip())
+ sys.exit(1)
+
+ else:
+ outlines.append(line)
+ in_progress.remove(fname)
+ logging.debug('%s: %d lines', fname, len(outlines))
+ return outlines
+
+
+def amalgamate(src, dst, check_only=False):
+ lines = amalgamate_file(src)
+ banner = '''
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by %s
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+'''
+ lines.insert(lines.index('\n'), banner % os.path.relpath(__file__, ROOT_DIR))
+ new_content = ''.join(lines)
+
+ if check_only:
+ if not os.path.exists(dst):
+ return False
+ with open(dst, encoding='utf-8') as f:
+ return f.read() == new_content
+
+ logging.info('Amalgamating %s -> %s', src, dst)
+ with open(dst + '.tmp', 'w', encoding='utf-8') as f:
+ f.write(new_content)
+ os.chmod(dst + '.tmp', 0o755)
+ os.rename(dst + '.tmp', dst)
+ return True
+
+
+def main():
+ check_only = '--check-only' in sys.argv
+ logging.basicConfig(
+ format='%(levelname)-8s: %(message)s',
+ level=logging.DEBUG if '-v' in sys.argv else logging.INFO)
+ os.chdir(ROOT_DIR) # Make the execution cwd-independent.
+ success = True
+ for src, dst in AMALGAMATION_MAP.items():
+ success = success and amalgamate(src, dst, check_only)
+ return 0 if success else 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/get_perfetto_prebuilt.py b/tools/get_perfetto_prebuilt.py
deleted file mode 100644
index ad9248b..0000000
--- a/tools/get_perfetto_prebuilt.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-This source defines a self-contained function to fetch a perfetto prebuilt.
-
-This function is copy/pasted by //tools/roll-prebuilts in different places:
-- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
- wrappers around executables.
-- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
- some other hand-written python code.
-In both cases toll-prebuilts copies this source (together with a manifest) into
-a section annotated with "BEGIN_SECTION_GENERATED_BY(roll-prebuilts)" / END... .
-The automated-copy-paste is to keep those script hermetic, so people can just
-download and run them without checking out the repo.
-
-The manifest argument looks as follows in the generated files:
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool': 'traceconv',
- 'arch': 'mac-amd64',
- 'file_name': 'traceconv',
- 'file_size': 7087080,
- 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
- 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
- 'platform': 'darwin',
- 'machine': 'x86_64'
- },
- ...
-]
-
-The intended usage is:
-
-bin_path = get_perfetto_prebuilt('trace_processor_shell')
-subprocess.call(bin_path, ...)
-"""
-
-from logging import exception
-
-PERFETTO_PREBUILT_MANIFEST = []
-
-# COPIED_SECTION_START_MARKER
-
-
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
- """ Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
- plat = sys.platform.lower()
- machine = platform.machine().lower()
- manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
- # If the caller overrides the arch, just match that (for Android prebuilts).
- if arch:
- if entry.get('arch') == arch:
- manifest_entry = entry
- break
- continue
- # Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
- manifest_entry = entry
- break
- if manifest_entry is None:
- if soft_fail:
- return None
- raise Exception(
- ('No prebuilts available for %s-%s\n' % (plat, machine)) +
- 'See https://perfetto.dev/docs/contributing/build-instructions')
-
- return download_or_get_cached(
- file_name=manifest_entry['file_name'],
- url=manifest_entry['url'],
- sha256=manifest_entry['sha256'])
diff --git a/tools/heap_profile b/tools/heap_profile
index e2c5a0e..c952191 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,13 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import atexit
-import hashlib
import os
import shutil
import signal
@@ -30,6 +32,268 @@
import time
import uuid
+
+# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACECONV_MANIFEST = [{
+ 'arch':
+ 'mac-amd64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7181712,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+ 'sha256':
+ '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+ 'platform':
+ 'darwin',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'mac-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6025176,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+ 'sha256':
+ '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+ 'platform':
+ 'darwin',
+ 'machine': ['arm64']
+}, {
+ 'arch':
+ 'linux-amd64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7668600,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+ 'sha256':
+ '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+ 'platform':
+ 'linux',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'linux-arm',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 5827680,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+ 'sha256':
+ 'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+ 'platform':
+ 'linux',
+ 'machine': ['armv6l', 'armv7l', 'armv8l']
+}, {
+ 'arch':
+ 'linux-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6876384,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+ 'sha256':
+ '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+ 'platform':
+ 'linux',
+ 'machine': ['aarch64']
+}, {
+ 'arch':
+ 'android-arm',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 4881820,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+ 'sha256':
+ '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+}, {
+ 'arch':
+ 'android-arm64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 6222592,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+ 'sha256':
+ '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+}, {
+ 'arch':
+ 'android-x86',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7089524,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+ 'sha256':
+ '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+}, {
+ 'arch':
+ 'android-x64',
+ 'file_name':
+ 'traceconv',
+ 'file_size':
+ 7316248,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+ 'sha256':
+ '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+}, {
+ 'arch':
+ 'windows-amd64',
+ 'file_name':
+ 'traceconv.exe',
+ 'file_size':
+ 6850048,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+ 'sha256':
+ '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+ 'platform':
+ 'win32',
+ 'machine': ['amd64']
+}]
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/traceconv.py
+
+# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
+ """ Downloads the prebuilt, if necessary, and returns its path on disk. """
+ plat = sys.platform.lower()
+ machine = platform.machine().lower()
+ manifest_entry = None
+ for entry in manifest:
+ # If the caller overrides the arch, just match that (for Android prebuilts).
+ if arch:
+ if entry.get('arch') == arch:
+ manifest_entry = entry
+ break
+ continue
+ # Otherwise guess the local machine arch.
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
+ manifest_entry = entry
+ break
+ if manifest_entry is None:
+ if soft_fail:
+ return None
+ raise Exception(
+ ('No prebuilts available for %s-%s\n' % (plat, machine)) +
+ 'See https://perfetto.dev/docs/contributing/build-instructions')
+
+ return download_or_get_cached(
+ file_name=manifest_entry['file_name'],
+ url=manifest_entry['url'],
+ sha256=manifest_entry['sha256'])
+
+
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
+
NULL = open(os.devnull)
NOOUT = {
'stdout': NULL,
@@ -367,7 +631,7 @@
# Do this AFTER print_config so we do not download traceconv only to
# print out the config.
if traceconv_binary is None:
- traceconv_binary = get_perfetto_prebuilt('traceconv', soft_fail=True)
+ traceconv_binary = get_perfetto_prebuilt(TRACECONV_MANIFEST, soft_fail=True)
known_issues = maybe_known_issues()
if known_issues:
@@ -573,233 +837,5 @@
"all the heap dumps.")
-# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v28.0
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool':
- 'traceconv',
- 'arch':
- 'mac-amd64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7181712,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
- 'sha256':
- '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
- 'platform':
- 'darwin',
- 'machine': ['x86_64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'mac-arm64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 6025176,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
- 'sha256':
- '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
- 'platform':
- 'darwin',
- 'machine': ['arm64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'linux-amd64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7668600,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
- 'sha256':
- '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
- 'platform':
- 'linux',
- 'machine': ['x86_64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'linux-arm',
- 'file_name':
- 'traceconv',
- 'file_size':
- 5827680,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
- 'sha256':
- 'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
- 'platform':
- 'linux',
- 'machine': ['armv6l', 'armv7l', 'armv8l']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'linux-arm64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 6876384,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
- 'sha256':
- '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
- 'platform':
- 'linux',
- 'machine': ['aarch64']
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-arm',
- 'file_name':
- 'traceconv',
- 'file_size':
- 4881820,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
- 'sha256':
- '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-arm64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 6222592,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
- 'sha256':
- '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-x86',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7089524,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
- 'sha256':
- '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'android-x64',
- 'file_name':
- 'traceconv',
- 'file_size':
- 7316248,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
- 'sha256':
- '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
-}, {
- 'tool':
- 'traceconv',
- 'arch':
- 'windows-amd64',
- 'file_name':
- 'traceconv.exe',
- 'file_size':
- 6850048,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
- 'sha256':
- '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
- 'platform':
- 'win32',
- 'machine': ['amd64']
-}]
-
-
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
- """ Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
- plat = sys.platform.lower()
- machine = platform.machine().lower()
- manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
- # If the caller overrides the arch, just match that (for Android prebuilts).
- if arch:
- if entry.get('arch') == arch:
- manifest_entry = entry
- break
- continue
- # Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
- manifest_entry = entry
- break
- if manifest_entry is None:
- if soft_fail:
- return None
- raise Exception(
- ('No prebuilts available for %s-%s\n' % (plat, machine)) +
- 'See https://perfetto.dev/docs/contributing/build-instructions')
-
- return download_or_get_cached(
- file_name=manifest_entry['file_name'],
- url=manifest_entry['url'],
- sha256=manifest_entry['sha256'])
-
-
-# END_SECTION_GENERATED_BY(roll-prebuilts)
-
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/tools/record_android_trace b/tools/record_android_trace
index 35915c0..c507a2a 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
import atexit
import argparse
import datetime
@@ -24,15 +28,299 @@
import socketserver
import subprocess
import sys
-import tempfile
import time
import webbrowser
-ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACEBOX_MANIFEST = [{
+ 'arch':
+ 'mac-amd64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1399080,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/tracebox',
+ 'sha256':
+ '05674f9872a14bf92d4215a180c883ab54c58376d7e08b95e3f83d03efdeba21',
+ 'platform':
+ 'darwin',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'mac-arm64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1292504,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/tracebox',
+ 'sha256':
+ '48008e6aeb7680c4f31b1147d5c26a504368a696c793aec1895bb5fb1f597f64',
+ 'platform':
+ 'darwin',
+ 'machine': ['arm64']
+}, {
+ 'arch':
+ 'linux-amd64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 2243632,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/tracebox',
+ 'sha256':
+ '12c87ba1d03e39d4c07792e09f0be9e240677cec2d9bc1638b0a676bc664d724',
+ 'platform':
+ 'linux',
+ 'machine': ['x86_64']
+}, {
+ 'arch':
+ 'linux-arm',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1315252,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/tracebox',
+ 'sha256':
+ '3d026fe982e90fb5bc410528f210458f9bcafea30d3e1b9728772fd32148ee9a',
+ 'platform':
+ 'linux',
+ 'machine': ['armv6l', 'armv7l', 'armv8l']
+}, {
+ 'arch':
+ 'linux-arm64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 2089160,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/tracebox',
+ 'sha256':
+ '9f524f943a1c12dcb6b273e78d5c46952d4a7528514639cd2537686d5c530c89',
+ 'platform':
+ 'linux',
+ 'machine': ['aarch64']
+}, {
+ 'arch':
+ 'android-arm',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1099732,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/tracebox',
+ 'sha256':
+ '53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
+}, {
+ 'arch':
+ 'android-arm64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1653416,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/tracebox',
+ 'sha256':
+ 'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
+}, {
+ 'arch':
+ 'android-x86',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1677228,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/tracebox',
+ 'sha256':
+ '8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
+}, {
+ 'arch':
+ 'android-x64',
+ 'file_name':
+ 'tracebox',
+ 'file_size':
+ 1911464,
+ 'url':
+ 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/tracebox',
+ 'sha256':
+ '8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
+}]
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py
+
+# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
+ """ Downloads the prebuilt, if necessary, and returns its path on disk. """
+ plat = sys.platform.lower()
+ machine = platform.machine().lower()
+ manifest_entry = None
+ for entry in manifest:
+ # If the caller overrides the arch, just match that (for Android prebuilts).
+ if arch:
+ if entry.get('arch') == arch:
+ manifest_entry = entry
+ break
+ continue
+ # Otherwise guess the local machine arch.
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
+ manifest_entry = entry
+ break
+ if manifest_entry is None:
+ if soft_fail:
+ return None
+ raise Exception(
+ ('No prebuilts available for %s-%s\n' % (plat, machine)) +
+ 'See https://perfetto.dev/docs/contributing/build-instructions')
+
+ return download_or_get_cached(
+ file_name=manifest_entry['file_name'],
+ url=manifest_entry['url'],
+ sha256=manifest_entry['sha256'])
+
+
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
+
+# ----- Amalgamator: begin of python/perfetto/common/repo_utils.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def repo_root():
+ """ Finds the repo root by traversing up the hierarchy
+
+ This is for use in scripts that get amalgamated, where _file_ can be either
+ python/perfetto/... or tools/amalgamated_tool.
+ """
+ path = os.path.dirname(os.path.abspath(__file__)) # amalgamator:nocheck
+ last_dir = ''
+ while path and path != last_dir:
+ if os.path.exists(os.path.join(path, 'perfetto.rc')):
+ return path
+ last_dir = path
+ path = os.path.dirname(path)
+ return None
+
+
+def repo_dir(rel_path):
+ return os.path.join(repo_root() or '', rel_path)
+
+# ----- Amalgamator: end of python/perfetto/common/repo_utils.py
# This is not required. It's only used as a fallback if no adb is found on the
# PATH. It's fine if it doesn't exist so this script can be copied elsewhere.
-HERMETIC_ADB_PATH = ROOT_DIR + '/buildtools/android_sdk/platform-tools/adb'
+HERMETIC_ADB_PATH = repo_dir('/buildtools/android_sdk/platform-tools/adb')
# Translates the Android ro.product.cpu.abi into the GN's target_cpu.
ABI_TO_ARCH = {
@@ -200,7 +488,8 @@
if api_level < 29 or args.sideload: # 29: Android Q.
tracebox_bin = args.sideload_path
if tracebox_bin is None:
- tracebox_bin = get_perfetto_prebuilt('tracebox', arch='android-' + arch)
+ tracebox_bin = get_perfetto_prebuilt(
+ TRACEBOX_MANIFEST, arch='android-' + arch)
perfetto_cmd = '/data/local/tmp/tracebox'
exit_code = adb('push', '--sync', tracebox_bin, perfetto_cmd).wait()
exit_code |= adb('shell', 'chmod 755 ' + perfetto_cmd).wait()
@@ -403,217 +692,5 @@
return file_hash == sha_value
-# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v28.0
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool':
- 'tracebox',
- 'arch':
- 'mac-amd64',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1399080,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/tracebox',
- 'sha256':
- '05674f9872a14bf92d4215a180c883ab54c58376d7e08b95e3f83d03efdeba21',
- 'platform':
- 'darwin',
- 'machine': ['x86_64']
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'mac-arm64',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1292504,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/tracebox',
- 'sha256':
- '48008e6aeb7680c4f31b1147d5c26a504368a696c793aec1895bb5fb1f597f64',
- 'platform':
- 'darwin',
- 'machine': ['arm64']
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'linux-amd64',
- 'file_name':
- 'tracebox',
- 'file_size':
- 2243632,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/tracebox',
- 'sha256':
- '12c87ba1d03e39d4c07792e09f0be9e240677cec2d9bc1638b0a676bc664d724',
- 'platform':
- 'linux',
- 'machine': ['x86_64']
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'linux-arm',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1315252,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/tracebox',
- 'sha256':
- '3d026fe982e90fb5bc410528f210458f9bcafea30d3e1b9728772fd32148ee9a',
- 'platform':
- 'linux',
- 'machine': ['armv6l', 'armv7l', 'armv8l']
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'linux-arm64',
- 'file_name':
- 'tracebox',
- 'file_size':
- 2089160,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/tracebox',
- 'sha256':
- '9f524f943a1c12dcb6b273e78d5c46952d4a7528514639cd2537686d5c530c89',
- 'platform':
- 'linux',
- 'machine': ['aarch64']
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'android-arm',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1099732,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/tracebox',
- 'sha256':
- '53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'android-arm64',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1653416,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/tracebox',
- 'sha256':
- 'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'android-x86',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1677228,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/tracebox',
- 'sha256':
- '8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
-}, {
- 'tool':
- 'tracebox',
- 'arch':
- 'android-x64',
- 'file_name':
- 'tracebox',
- 'file_size':
- 1911464,
- 'url':
- 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/tracebox',
- 'sha256':
- '8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
-}]
-
-
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
- """ Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
- plat = sys.platform.lower()
- machine = platform.machine().lower()
- manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
- # If the caller overrides the arch, just match that (for Android prebuilts).
- if arch:
- if entry.get('arch') == arch:
- manifest_entry = entry
- break
- continue
- # Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
- manifest_entry = entry
- break
- if manifest_entry is None:
- if soft_fail:
- return None
- raise Exception(
- ('No prebuilts available for %s-%s\n' % (plat, machine)) +
- 'See https://perfetto.dev/docs/contributing/build-instructions')
-
- return download_or_get_cached(
- file_name=manifest_entry['file_name'],
- url=manifest_entry['url'],
- sha256=manifest_entry['sha256'])
-
-
-# END_SECTION_GENERATED_BY(roll-prebuilts)
-
if __name__ == '__main__':
sys.exit(main())
diff --git a/tools/roll-prebuilts b/tools/roll-prebuilts
index 50ef65d..08666ad 100755
--- a/tools/roll-prebuilts
+++ b/tools/roll-prebuilts
@@ -12,9 +12,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Updates the python scripts in tools/{trace_processor, traceconv, tracebox}
+"""Updates the python scripts in python/perfetto/prebuilts/manifests
-This script does the following, for each entry in SCRIPTS_TO_UPDATE:
+This script does the following, for each entry in MANIFESTS_TO_UPDATE:
- Downloads the artifact by the LUCI infrastructure, one for each arch.
- Computes the SHA-256 of each artifact.
- Generates a manifest with URL, SHA-256 and other details.
@@ -36,7 +36,7 @@
GCS_URL = 'https://commondatastorage.googleapis.com/perfetto-luci-artifacts'
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-TOOLS_DIR = os.path.join(ROOT_DIR, 'tools')
+MANIFESTS_DIR = os.path.join(ROOT_DIR, 'python/perfetto/prebuilts/manifests')
UNIX_ARCHS = [
'mac-amd64',
@@ -51,39 +51,16 @@
]
ALL_ARCHS = UNIX_ARCHS + ['windows-amd64']
-SCRIPTS_TO_UPDATE = [
- # Scripts using trace_processor_shell.
+MANIFESTS_TO_UPDATE = [
{
- 'script': 'trace_processor',
'tool': 'trace_processor_shell',
'archs': ALL_ARCHS
},
-
- # Scripts using traceconv.
{
- 'script': 'traceconv',
'tool': 'traceconv',
'archs': ALL_ARCHS
},
{
- 'script': 'heap_profile',
- 'tool': 'traceconv',
- 'archs': ALL_ARCHS
- },
- {
- 'script': 'cpu_profile',
- 'tool': 'traceconv',
- 'archs': ALL_ARCHS
- },
-
- # Scripts using tracebox.
- {
- 'script': 'tracebox',
- 'tool': 'tracebox',
- 'archs': UNIX_ARCHS
- },
- {
- 'script': 'record_android_trace',
'tool': 'tracebox',
'archs': UNIX_ARCHS
},
@@ -127,7 +104,6 @@
logging.info('Downloading %s', url)
data = subprocess.check_output(['curl', '-fsL', '-o', '-', url])
manifest = {
- 'tool': tool,
'arch': arch,
'file_name': file_name,
'file_size': len(data),
@@ -138,41 +114,23 @@
return manifest
-# Returns the section of get_perfetto_prebuilt.py which should be copy/pasted
-# in the various scripts.
-def read_get_perfetto_prebuilt_script():
- in_file = os.path.join(TOOLS_DIR, 'get_perfetto_prebuilt.py')
- with open(in_file, 'r') as f:
- contents = f.read()
- return contents.split('COPIED_SECTION_START_MARKER')[1]
-
-
-def update_script(git_revision, tool_name, script_name, archs):
+def update_manifest(git_revision, tool_name, archs):
with ThreadPoolExecutor(max_workers=8) as executor:
manifests = list(
executor.map(lambda arch: make_manifest(git_revision, tool_name, arch),
archs))
- out_file = os.path.join(TOOLS_DIR, script_name)
- with open(out_file) as f:
- script = f.read()
+ out_file = os.path.join(MANIFESTS_DIR, tool_name + '.py')
- begin_marker = '\n# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)\n'
- end_marker = '\n# END_SECTION_GENERATED_BY(roll-prebuilts)\n'
- before = script.partition(begin_marker)[0]
- after = script.partition(end_marker)[2]
-
- content = '# Revision: {git_revision}\n'
- content += 'PERFETTO_PREBUILT_MANIFEST = {manifests}\n'
- content += '{fn_body}\n'
+ content = '# This file has been generated by: {script} {git_revision}\n'
+ content += '{tool_uppercase}_MANIFEST = {manifests}\n'
content = content.format(
+ script=__file__,
+ tool_uppercase=tool_name.upper(),
git_revision=git_revision,
- manifests=str(manifests),
- fn_body=read_get_perfetto_prebuilt_script())
-
- script = before + begin_marker + content + end_marker + after
+ manifests=str(manifests))
with open(out_file + '.tmp', 'w') as f:
- f.write(script)
+ f.write(content)
subprocess.check_call(['yapf', '-i', out_file + '.tmp'])
os.rename(out_file + '.tmp', out_file)
os.chmod(out_file, 0o755)
@@ -188,9 +146,9 @@
args = parser.parse_args()
git_revision = args.version
- for spec in SCRIPTS_TO_UPDATE:
- logging.info('Updating %s', spec['script'])
- update_script(git_revision, spec['tool'], spec['script'], spec['archs'])
+ for spec in MANIFESTS_TO_UPDATE:
+ logging.info('Updating %s', spec['tool'])
+ update_manifest(git_revision, spec['tool'], spec['archs'])
if __name__ == '__main__':
diff --git a/tools/trace_processor b/tools/trace_processor
index 48d13b1..18354b2 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019 The Android Open Source Project
+# Copyright (C) 2021 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# This file should do the same thing when being invoked in any of these ways:
# ./trace_processor
# python trace_processor
@@ -20,17 +24,14 @@
# cat ./trace_processor | bash
# cat ./trace_processor | python -
-BASH_FALLBACK = """ "
+BASH_FALLBACK=""" "
exec python3 - "$@" <<'#'EOF
-#"""
+#""" # yapf: disable
-TOOL_NAME = 'trace_processor_shell'
-# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v28.0
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool':
- 'trace_processor_shell',
+# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/trace_processor_shell.py
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACE_PROCESSOR_SHELL_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
@@ -45,8 +46,6 @@
'darwin',
'machine': ['x86_64']
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'mac-arm64',
'file_name':
@@ -61,8 +60,6 @@
'darwin',
'machine': ['arm64']
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'linux-amd64',
'file_name':
@@ -77,8 +74,6 @@
'linux',
'machine': ['x86_64']
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'linux-arm',
'file_name':
@@ -93,8 +88,6 @@
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'linux-arm64',
'file_name':
@@ -109,8 +102,6 @@
'linux',
'machine': ['aarch64']
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'android-arm',
'file_name':
@@ -122,8 +113,6 @@
'sha256':
'75285ac8964e93890b936bfacd2679ee67ae4cd029be74e675253f92746f9904'
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'android-arm64',
'file_name':
@@ -135,8 +124,6 @@
'sha256':
'75118859c9a1b2ed2bcffc664eb98884dab26a9d2ad7788d05d90ef22b24a8d5'
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'android-x86',
'file_name':
@@ -148,8 +135,6 @@
'sha256':
'c3d4da7aa12a2eedd59df98b5c8143658f39ebd9a4ba3c8180be33ab3484e824'
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'android-x64',
'file_name':
@@ -161,8 +146,6 @@
'sha256':
'105c70a3908bf6997b428ee7f74bf7ee8a48907b860480c87e62a62cf92e1a8a'
}, {
- 'tool':
- 'trace_processor_shell',
'arch':
'windows-amd64',
'file_name':
@@ -178,56 +161,104 @@
'machine': ['amd64']
}]
+# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/trace_processor_shell.py
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
+# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
""" Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
plat = sys.platform.lower()
machine = platform.machine().lower()
manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
+ for entry in manifest:
# If the caller overrides the arch, just match that (for Android prebuilts).
if arch:
if entry.get('arch') == arch:
@@ -235,8 +266,7 @@
break
continue
# Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
manifest_entry = entry
break
if manifest_entry is None:
@@ -252,13 +282,15 @@
sha256=manifest_entry['sha256'])
-# END_SECTION_GENERATED_BY(roll-prebuilts)
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
if __name__ == '__main__':
- import sys, subprocess, os
- bin_path = get_perfetto_prebuilt(TOOL_NAME)
- if sys.platform.lower() == 'win32':
- exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
- os.execv(bin_path, [bin_path] + sys.argv[1:])
+ run_perfetto_prebuilt(TRACE_PROCESSOR_SHELL_MANIFEST)
#EOF
diff --git a/tools/tracebox b/tools/tracebox
index 1d834cf..89eebea 100755
--- a/tools/tracebox
+++ b/tools/tracebox
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# This file should do the same thing when being invoked in any of these ways:
# ./tracebox
# python tracebox
@@ -20,17 +24,14 @@
# cat ./tracebox | bash
# cat ./tracebox | python -
-BASH_FALLBACK = """ "
+BASH_FALLBACK=""" "
exec python3 - "$@" <<'#'EOF
-#"""
+#""" # yapf: disable
-TOOL_NAME = 'tracebox'
-# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v28.0
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool':
- 'tracebox',
+# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACEBOX_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
@@ -45,8 +46,6 @@
'darwin',
'machine': ['x86_64']
}, {
- 'tool':
- 'tracebox',
'arch':
'mac-arm64',
'file_name':
@@ -61,8 +60,6 @@
'darwin',
'machine': ['arm64']
}, {
- 'tool':
- 'tracebox',
'arch':
'linux-amd64',
'file_name':
@@ -77,8 +74,6 @@
'linux',
'machine': ['x86_64']
}, {
- 'tool':
- 'tracebox',
'arch':
'linux-arm',
'file_name':
@@ -93,8 +88,6 @@
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
}, {
- 'tool':
- 'tracebox',
'arch':
'linux-arm64',
'file_name':
@@ -109,8 +102,6 @@
'linux',
'machine': ['aarch64']
}, {
- 'tool':
- 'tracebox',
'arch':
'android-arm',
'file_name':
@@ -122,8 +113,6 @@
'sha256':
'53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
}, {
- 'tool':
- 'tracebox',
'arch':
'android-arm64',
'file_name':
@@ -135,8 +124,6 @@
'sha256':
'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
}, {
- 'tool':
- 'tracebox',
'arch':
'android-x86',
'file_name':
@@ -148,8 +135,6 @@
'sha256':
'8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
}, {
- 'tool':
- 'tracebox',
'arch':
'android-x64',
'file_name':
@@ -162,56 +147,104 @@
'8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
}]
+# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
+# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
""" Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
plat = sys.platform.lower()
machine = platform.machine().lower()
manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
+ for entry in manifest:
# If the caller overrides the arch, just match that (for Android prebuilts).
if arch:
if entry.get('arch') == arch:
@@ -219,8 +252,7 @@
break
continue
# Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
manifest_entry = entry
break
if manifest_entry is None:
@@ -236,13 +268,15 @@
sha256=manifest_entry['sha256'])
-# END_SECTION_GENERATED_BY(roll-prebuilts)
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
if __name__ == '__main__':
- import sys, subprocess, os
- bin_path = get_perfetto_prebuilt(TOOL_NAME)
- if sys.platform.lower() == 'win32':
- exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
- os.execv(bin_path, [bin_path] + sys.argv[1:])
+ run_perfetto_prebuilt(TRACEBOX_MANIFEST)
#EOF
diff --git a/tools/traceconv b/tools/traceconv
index 0572002..db6699b 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -13,6 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# This file should do the same thing when being invoked in any of these ways:
# ./traceconv
# python traceconv
@@ -20,17 +24,14 @@
# cat ./traceconv | bash
# cat ./traceconv | python -
-BASH_FALLBACK = """ "
+BASH_FALLBACK=""" "
exec python3 - "$@" <<'#'EOF
-#"""
+#""" # yapf: disable
-TOOL_NAME = 'traceconv'
-# BEGIN_SECTION_GENERATED_BY(roll-prebuilts)
-# Revision: v28.0
-PERFETTO_PREBUILT_MANIFEST = [{
- 'tool':
- 'traceconv',
+# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
+# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+TRACECONV_MANIFEST = [{
'arch':
'mac-amd64',
'file_name':
@@ -45,8 +46,6 @@
'darwin',
'machine': ['x86_64']
}, {
- 'tool':
- 'traceconv',
'arch':
'mac-arm64',
'file_name':
@@ -61,8 +60,6 @@
'darwin',
'machine': ['arm64']
}, {
- 'tool':
- 'traceconv',
'arch':
'linux-amd64',
'file_name':
@@ -77,8 +74,6 @@
'linux',
'machine': ['x86_64']
}, {
- 'tool':
- 'traceconv',
'arch':
'linux-arm',
'file_name':
@@ -93,8 +88,6 @@
'linux',
'machine': ['armv6l', 'armv7l', 'armv8l']
}, {
- 'tool':
- 'traceconv',
'arch':
'linux-arm64',
'file_name':
@@ -109,8 +102,6 @@
'linux',
'machine': ['aarch64']
}, {
- 'tool':
- 'traceconv',
'arch':
'android-arm',
'file_name':
@@ -122,8 +113,6 @@
'sha256':
'73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
}, {
- 'tool':
- 'traceconv',
'arch':
'android-arm64',
'file_name':
@@ -135,8 +124,6 @@
'sha256':
'72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
}, {
- 'tool':
- 'traceconv',
'arch':
'android-x86',
'file_name':
@@ -148,8 +135,6 @@
'sha256':
'689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
}, {
- 'tool':
- 'traceconv',
'arch':
'android-x64',
'file_name':
@@ -161,8 +146,6 @@
'sha256':
'785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
}, {
- 'tool':
- 'traceconv',
'arch':
'windows-amd64',
'file_name':
@@ -178,56 +161,104 @@
'machine': ['amd64']
}]
+# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/traceconv.py
-# DO NOT EDIT. If you wish to make edits to this code, you need to change only
-# //tools/get_perfetto_prebuilt.py and run /tools/roll-prebuilts to regenerate
-# all the others scripts this is embedded into.
-def get_perfetto_prebuilt(tool_name, soft_fail=False, arch=None):
+# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Functions to fetch pre-pinned Perfetto prebuilts.
+
+This function is used in different places:
+- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
+ wrappers around executables.
+- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
+ some other hand-written python code.
+
+The manifest argument looks as follows:
+TRACECONV_MANIFEST = [
+ {
+ 'arch': 'mac-amd64',
+ 'file_name': 'traceconv',
+ 'file_size': 7087080,
+ 'url': https://commondatastorage.googleapis.com/.../trace_to_text',
+ 'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
+ 'platform': 'darwin',
+ 'machine': 'x86_64'
+ },
+ ...
+]
+
+The intended usage is:
+
+ from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
+ bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
+ subprocess.call(bin_path, ...)
+"""
+
+import hashlib
+import os
+import platform
+import subprocess
+import sys
+
+
+def download_or_get_cached(file_name, url, sha256):
+ """ Downloads a prebuilt or returns a cached version
+
+ The first time this is invoked, it downloads the |url| and caches it into
+ ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
+ cached version.
+ """
+ dir = os.path.join(
+ os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
+ os.makedirs(dir, exist_ok=True)
+ bin_path = os.path.join(dir, file_name)
+ sha256_path = os.path.join(dir, file_name + '.sha256')
+ needs_download = True
+
+ # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
+ # download is cached into file_name.sha256, just check if that matches.
+ if os.path.exists(bin_path) and os.path.exists(sha256_path):
+ with open(sha256_path, 'rb') as f:
+ digest = f.read().decode()
+ if digest == sha256:
+ needs_download = False
+
+ if needs_download:
+ # Either the filed doesn't exist or the SHA256 doesn't match.
+ tmp_path = bin_path + '.tmp'
+ print('Downloading ' + url)
+ subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
+ with open(tmp_path, 'rb') as fd:
+ actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
+ if actual_sha256 != sha256:
+ raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
+ (url, actual_sha256, sha256))
+ os.chmod(tmp_path, 0o755)
+ os.rename(tmp_path, bin_path)
+ with open(sha256_path, 'w') as f:
+ f.write(sha256)
+ return bin_path
+
+
+def get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
""" Downloads the prebuilt, if necessary, and returns its path on disk. """
-
- # The first time this is invoked, it downloads the |url| and caches it into
- # ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
- # cached version.
- def download_or_get_cached(file_name, url, sha256):
- import os, hashlib, subprocess
- dir = os.path.join(
- os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
- os.makedirs(dir, exist_ok=True)
- bin_path = os.path.join(dir, file_name)
- sha256_path = os.path.join(dir, file_name + '.sha256')
- needs_download = True
-
- # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
- # download is cached into file_name.sha256, just check if that matches.
- if os.path.exists(bin_path) and os.path.exists(sha256_path):
- with open(sha256_path, 'rb') as f:
- digest = f.read().decode()
- if digest == sha256:
- needs_download = False
-
- if needs_download:
- # Either the filed doesn't exist or the SHA256 doesn't match.
- tmp_path = bin_path + '.tmp'
- print('Downloading ' + url)
- subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
- with open(tmp_path, 'rb') as fd:
- actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
- if actual_sha256 != sha256:
- raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
- (url, actual_sha256, sha256))
- os.chmod(tmp_path, 0o755)
- os.rename(tmp_path, bin_path)
- with open(sha256_path, 'w') as f:
- f.write(sha256)
- return bin_path
- # --- end of download_or_get_cached() ---
-
- # --- get_perfetto_prebuilt() function starts here. ---
- import os, platform, sys
plat = sys.platform.lower()
machine = platform.machine().lower()
manifest_entry = None
- for entry in PERFETTO_PREBUILT_MANIFEST:
+ for entry in manifest:
# If the caller overrides the arch, just match that (for Android prebuilts).
if arch:
if entry.get('arch') == arch:
@@ -235,8 +266,7 @@
break
continue
# Otherwise guess the local machine arch.
- if entry.get('tool') == tool_name and entry.get(
- 'platform') == plat and machine in entry.get('machine', []):
+ if entry.get('platform') == plat and machine in entry.get('machine', []):
manifest_entry = entry
break
if manifest_entry is None:
@@ -252,13 +282,15 @@
sha256=manifest_entry['sha256'])
-# END_SECTION_GENERATED_BY(roll-prebuilts)
+def run_perfetto_prebuilt(manifest):
+ bin_path = get_perfetto_prebuilt(manifest)
+ if sys.platform.lower() == 'win32':
+ sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
+ os.execv(bin_path, [bin_path] + sys.argv[1:])
+
+# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
if __name__ == '__main__':
- import sys, subprocess, os
- bin_path = get_perfetto_prebuilt(TOOL_NAME)
- if sys.platform.lower() == 'win32':
- exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
- os.execv(bin_path, [bin_path] + sys.argv[1:])
+ run_perfetto_prebuilt(TRACECONV_MANIFEST)
#EOF