mirror of
https://github.com/MobileGL-Dev/MobileGlues.git
synced 2025-08-03 07:26:07 -04:00
feat(perfetto): integration with perfetto
This commit is contained in:
parent
d5ca8baeb3
commit
2f8fd01051
18
mg.pbtx
Normal file
18
mg.pbtx
Normal file
@ -0,0 +1,18 @@
|
||||
buffers: {
|
||||
size_kb: 522240
|
||||
fill_policy: RING_BUFFER
|
||||
}
|
||||
data_sources {
|
||||
config {
|
||||
name: "track_event"
|
||||
track_event_config {
|
||||
enabled_categories: "glcalls"
|
||||
disabled_categories: "*"
|
||||
}
|
||||
}
|
||||
}
|
||||
duration_ms: 1800000
|
||||
flush_period_ms: 30000
|
||||
incremental_state_config {
|
||||
clear_period_ms: 5000
|
||||
}
|
794
record_android_trace.py
Normal file
794
record_android_trace.py
Normal file
@ -0,0 +1,794 @@
|
||||
#!/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.
|
||||
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
import atexit
|
||||
import argparse
|
||||
import datetime
|
||||
import hashlib
|
||||
import http.server
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import socketserver
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import webbrowser
|
||||
|
||||
|
||||
# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
|
||||
# This file has been generated by: tools/roll-prebuilts v48.1
|
||||
TRACEBOX_MANIFEST = [{
|
||||
'arch':
|
||||
'mac-amd64',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
1613864,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/mac-amd64/tracebox',
|
||||
'sha256':
|
||||
'dfb1a3affe905d2e7d1f82bc4dda46b1fda6db054d60ae87c3215dd529b77fee',
|
||||
'platform':
|
||||
'darwin',
|
||||
'machine': ['x86_64']
|
||||
}, {
|
||||
'arch':
|
||||
'mac-arm64',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
1492184,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/mac-arm64/tracebox',
|
||||
'sha256':
|
||||
'4a492a629dd1f13f3146c4b8267c0b163afba8cef1d49e0c00c48bb727496066',
|
||||
'platform':
|
||||
'darwin',
|
||||
'machine': ['arm64']
|
||||
}, {
|
||||
'arch':
|
||||
'linux-amd64',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
2380040,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/linux-amd64/tracebox',
|
||||
'sha256':
|
||||
'd70b284e8c28858fd539ae61ca59764d7f9fd6232073c304926e892fe75e692a',
|
||||
'platform':
|
||||
'linux',
|
||||
'machine': ['x86_64']
|
||||
}, {
|
||||
'arch':
|
||||
'linux-arm',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
1450708,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/linux-arm/tracebox',
|
||||
'sha256':
|
||||
'178fa6a1a9bc80f72d81938d40fe201c25c595ffaff7e030d59c2af09dfcc06c',
|
||||
'platform':
|
||||
'linux',
|
||||
'machine': ['armv6l', 'armv7l', 'armv8l']
|
||||
}, {
|
||||
'arch':
|
||||
'linux-arm64',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
2269816,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/linux-arm64/tracebox',
|
||||
'sha256':
|
||||
'42c64f9807756aaa08a2bfa13e9e4828c193a6b90ba1329408873c3ebf5adf3f',
|
||||
'platform':
|
||||
'linux',
|
||||
'machine': ['aarch64']
|
||||
}, {
|
||||
'arch':
|
||||
'android-arm',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
1333336,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-arm/tracebox',
|
||||
'sha256':
|
||||
'93a78d2c42e3c00f117e2f155326383f69c891281ed693a39d87b8cb54ca4e19'
|
||||
}, {
|
||||
'arch':
|
||||
'android-arm64',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
2115984,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-arm64/tracebox',
|
||||
'sha256':
|
||||
'508248a9e47ab605fd742efb700391d7267b68b586199a93e13e6ca14b72fe3d'
|
||||
}, {
|
||||
'arch':
|
||||
'android-x86',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
2302960,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-x86/tracebox',
|
||||
'sha256':
|
||||
'63d20a69c4e0c291329d7917e640fa0d4f146c344e79988e87393b1431d594b1'
|
||||
}, {
|
||||
'arch':
|
||||
'android-x64',
|
||||
'file_name':
|
||||
'tracebox',
|
||||
'file_size':
|
||||
2147880,
|
||||
'url':
|
||||
'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-x64/tracebox',
|
||||
'sha256':
|
||||
'c0ea1d5fd6d020e4c2b45d4d45cdd0c44ae63cd755d69260a3e5d2bacd3cbd6a'
|
||||
}]
|
||||
|
||||
# ----- 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 random
|
||||
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
|
||||
~/.local/share/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: # The file doesn't exist or the SHA256 doesn't match.
|
||||
# Use a unique random file to guard against concurrent executions.
|
||||
# See https://github.com/google/perfetto/issues/786 .
|
||||
tmp_path = '%s.%d.tmp' % (bin_path, random.randint(0, 100000))
|
||||
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.replace(tmp_path, bin_path)
|
||||
with open(tmp_path, 'w') as f:
|
||||
f.write(sha256)
|
||||
os.replace(tmp_path, sha256_path)
|
||||
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 = 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', self.server.allow_origin)
|
||||
self.send_header('Cache-Control', 'no-cache')
|
||||
super().end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
if self.path != '/' + self.server.expected_fname:
|
||||
self.send_error(404, "File not found")
|
||||
return
|
||||
|
||||
self.server.fname_get_completed = True
|
||||
super().do_GET()
|
||||
|
||||
def do_POST(self):
|
||||
self.send_error(404, "File not found")
|
||||
|
||||
|
||||
def setup_arguments():
|
||||
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 or serve the trace'
|
||||
parser.add_argument('-n', '--no-open', action='store_true', help=help)
|
||||
|
||||
help = 'Don\'t open in browser, but still serve trace (good for remote use)'
|
||||
parser.add_argument('--no-open-browser', action='store_true', help=help)
|
||||
|
||||
help = 'The web address used to open trace files'
|
||||
parser.add_argument('--origin', default='https://ui.perfetto.dev', 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 = 'Ignores any tracing guardrails which might be used'
|
||||
parser.add_argument('--no-guardrails', action='store_true', 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)
|
||||
|
||||
help = 'Parse input from --config as binary proto (default: parse as text)'
|
||||
grp.add_argument('--bin', action='store_true', help=help)
|
||||
|
||||
help = ('Pass the trace through the trace reporter API. Only works when '
|
||||
'using the full trace config (-c) with the reporter package name '
|
||||
"'android.perfetto.cts.reporter' and the reporter class name "
|
||||
"'android.perfetto.cts.reporter.PerfettoReportService' with the "
|
||||
'reporter installed on the device (see '
|
||||
'tools/install_test_reporter_app.py).')
|
||||
grp.add_argument('--reporter-api', action='store_true', 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)
|
||||
|
||||
if args.reporter_api and not args.config:
|
||||
prt('Must pass --config when using --reporter-api', ANSI.RED)
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
class SignalException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
raise SignalException('Received signal ' + str(sig))
|
||||
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
|
||||
def start_trace(args, print_log=True):
|
||||
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']
|
||||
if not args.bin:
|
||||
cmd.append('--txt')
|
||||
|
||||
if args.no_guardrails:
|
||||
cmd.append('--no-guardrails')
|
||||
|
||||
if args.reporter_api:
|
||||
# Remove all old reporter files to avoid polluting the file we will extract
|
||||
# later.
|
||||
adb('shell',
|
||||
'rm /sdcard/Android/data/android.perfetto.cts.reporter/files/*').wait()
|
||||
cmd.append('--upload')
|
||||
else:
|
||||
cmd.extend(['-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
|
||||
|
||||
# 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:
|
||||
if print_log:
|
||||
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)
|
||||
log_level = "-v"
|
||||
if not print_log:
|
||||
log_level = "-e"
|
||||
logcat = adb('logcat', log_level, '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, SignalException):
|
||||
sig = 'TERM' if ctrl_c_count == 0 else 'KILL'
|
||||
ctrl_c_count += 1
|
||||
if print_log:
|
||||
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()
|
||||
|
||||
if args.reporter_api:
|
||||
if print_log:
|
||||
prt('Waiting a few seconds to allow reporter to copy trace')
|
||||
time.sleep(5)
|
||||
|
||||
ret = adb(
|
||||
'shell',
|
||||
'cp /sdcard/Android/data/android.perfetto.cts.reporter/files/* ' +
|
||||
device_file).wait()
|
||||
if ret != 0:
|
||||
prt('Failed to extract reporter trace', ANSI.RED)
|
||||
sys.exit(1)
|
||||
|
||||
if print_log:
|
||||
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:
|
||||
if print_log:
|
||||
prt('\n')
|
||||
prt('Opening the trace (%s) in the browser' % host_file)
|
||||
open_browser = not args.no_open_browser
|
||||
open_trace_in_browser(host_file, open_browser, args.origin)
|
||||
|
||||
return host_file
|
||||
|
||||
|
||||
def main():
|
||||
args = setup_arguments()
|
||||
start_trace(args)
|
||||
|
||||
|
||||
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, open_browser, origin):
|
||||
# We reuse the HTTP+RPC port because it's the only one allowed by the CSP.
|
||||
PORT = 9001
|
||||
path = os.path.abspath(path)
|
||||
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:
|
||||
address = f'{origin}/#!/?url=http://127.0.0.1:{PORT}/{fname}&referrer=record_android_trace'
|
||||
if open_browser:
|
||||
webbrowser.open_new_tab(address)
|
||||
else:
|
||||
print(f'Open URL in browser: {address}')
|
||||
|
||||
httpd.expected_fname = fname
|
||||
httpd.fname_get_completed = None
|
||||
httpd.allow_origin = origin
|
||||
while httpd.fname_get_completed is None:
|
||||
httpd.handle_request()
|
||||
|
||||
|
||||
def adb(*args, stdin=devnull, stdout=None, stderr=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, stderr=stderr, 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())
|
@ -25,7 +25,15 @@ const char *glEnumToString(GLenum e);
|
||||
#define LOG_E(...) {}
|
||||
#define LOG_F(...) {}
|
||||
#else
|
||||
#define LOG() if(DEBUG||GLOBAL_DEBUG) {__android_log_print(ANDROID_LOG_DEBUG, RENDERERNAME, "Use function: %s", __FUNCTION__);printf("Use function: %s\n", __FUNCTION__);write_log("Use function: %s\n", __FUNCTION__);}
|
||||
#if PROFILING
|
||||
#define LOG() \
|
||||
perfetto::StaticString _FUNC_NAME_ = __func__; \
|
||||
TRACE_EVENT("glcalls", _FUNC_NAME_);
|
||||
#else
|
||||
#define LOG() \
|
||||
if(DEBUG||GLOBAL_DEBUG) {__android_log_print(ANDROID_LOG_DEBUG, RENDERERNAME, "Use function: %s", __FUNCTION__);printf("Use function: %s\n", __FUNCTION__);write_log("Use function: %s\n", __FUNCTION__);}
|
||||
#endif
|
||||
|
||||
#define LOG_D(...) if(DEBUG||GLOBAL_DEBUG) {__android_log_print(ANDROID_LOG_DEBUG, RENDERERNAME, __VA_ARGS__);printf(__VA_ARGS__);printf("\n");write_log(__VA_ARGS__);}
|
||||
#define LOG_W(...) if(DEBUG||GLOBAL_DEBUG) {__android_log_print(ANDROID_LOG_WARN, RENDERERNAME, __VA_ARGS__);printf(__VA_ARGS__);printf("\n");write_log(__VA_ARGS__);}
|
||||
#define LOG_E(...) if(DEBUG||GLOBAL_DEBUG) {__android_log_print(ANDROID_LOG_ERROR, RENDERERNAME, __VA_ARGS__);printf(__VA_ARGS__);printf("\n");write_log(__VA_ARGS__);}
|
||||
|
@ -15,14 +15,14 @@
|
||||
#include "egl/egl.h"
|
||||
#include "egl/loader.h"
|
||||
|
||||
#if PROFILING
|
||||
#include <perfetto.h>
|
||||
|
||||
PERFETTO_DEFINE_CATEGORIES(
|
||||
perfetto::Category("GLCalls")
|
||||
perfetto::Category("glcalls")
|
||||
.SetDescription("Calls from OpenGL"),
|
||||
perfetto::Category("Internal")
|
||||
perfetto::Category("internal")
|
||||
.SetDescription("Internal calls"));
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -16,10 +16,6 @@
|
||||
|
||||
#define DEBUG 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
__attribute__((used)) const char* copyright = "Copyright (C) 2025 Swung0x48, BZLZHH, Tungsten. All rights reserved. Logo artwork kindly provided by Aou156.";
|
||||
|
||||
extern char* (*MesaConvertShader)(const char *src, unsigned int type, unsigned int glsl, unsigned int essl);
|
||||
@ -54,6 +50,20 @@ void show_copyright() {
|
||||
LOG_V(" %s", copyright);
|
||||
}
|
||||
|
||||
#if PROFILING
|
||||
|
||||
PERFETTO_TRACK_EVENT_STATIC_STORAGE();
|
||||
|
||||
void init_perfetto() {
|
||||
perfetto::TracingInitArgs args;
|
||||
|
||||
args.backends |= perfetto::kSystemBackend;
|
||||
|
||||
perfetto::Tracing::Initialize(args);
|
||||
perfetto::TrackEvent::Register();
|
||||
}
|
||||
#endif
|
||||
|
||||
void proc_init() {
|
||||
init_config();
|
||||
|
||||
@ -71,9 +81,9 @@ void proc_init() {
|
||||
|
||||
init_libshaderconv();
|
||||
|
||||
#if PROFILING
|
||||
init_perfetto();
|
||||
#endif
|
||||
|
||||
g_initialized = 1;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user