xref: /aosp_15_r20/external/perfetto/tools/cpu_profile (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker#
4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker#
8*6dbdd20aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker#
10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker"""Runs tracing with CPU profiling enabled, and symbolizes traces if requested.
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
18*6dbdd20aSAndroid Build Coastguard Worker# DO NOT EDIT. Auto-generated by tools/gen_amalgamated_python_tools
19*6dbdd20aSAndroid Build Coastguard Worker# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20*6dbdd20aSAndroid Build Coastguard Worker
21*6dbdd20aSAndroid Build Coastguard WorkerFor usage instructions, please see:
22*6dbdd20aSAndroid Build Coastguard Workerhttps://perfetto.dev/docs/quickstart/callstack-sampling
23*6dbdd20aSAndroid Build Coastguard Worker
24*6dbdd20aSAndroid Build Coastguard WorkerAdapted in large part from `heap_profile`.
25*6dbdd20aSAndroid Build Coastguard Worker"""
26*6dbdd20aSAndroid Build Coastguard Worker
27*6dbdd20aSAndroid Build Coastguard Workerimport argparse
28*6dbdd20aSAndroid Build Coastguard Workerimport os
29*6dbdd20aSAndroid Build Coastguard Workerimport shutil
30*6dbdd20aSAndroid Build Coastguard Workerimport signal
31*6dbdd20aSAndroid Build Coastguard Workerimport subprocess
32*6dbdd20aSAndroid Build Coastguard Workerimport sys
33*6dbdd20aSAndroid Build Coastguard Workerimport tempfile
34*6dbdd20aSAndroid Build Coastguard Workerimport textwrap
35*6dbdd20aSAndroid Build Coastguard Workerimport time
36*6dbdd20aSAndroid Build Coastguard Workerimport uuid
37*6dbdd20aSAndroid Build Coastguard Worker
38*6dbdd20aSAndroid Build Coastguard Worker
39*6dbdd20aSAndroid Build Coastguard Worker# ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
40*6dbdd20aSAndroid Build Coastguard Worker# This file has been generated by: tools/roll-prebuilts v48.1
41*6dbdd20aSAndroid Build Coastguard WorkerTRACECONV_MANIFEST = [{
42*6dbdd20aSAndroid Build Coastguard Worker    'arch':
43*6dbdd20aSAndroid Build Coastguard Worker        'mac-amd64',
44*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
45*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
46*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
47*6dbdd20aSAndroid Build Coastguard Worker        9041560,
48*6dbdd20aSAndroid Build Coastguard Worker    'url':
49*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/mac-amd64/traceconv',
50*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
51*6dbdd20aSAndroid Build Coastguard Worker        'cec2da5cb771a4812d0b2d15604d5023954d28e0af12e87313da2ab70d26b970',
52*6dbdd20aSAndroid Build Coastguard Worker    'platform':
53*6dbdd20aSAndroid Build Coastguard Worker        'darwin',
54*6dbdd20aSAndroid Build Coastguard Worker    'machine': ['x86_64']
55*6dbdd20aSAndroid Build Coastguard Worker}, {
56*6dbdd20aSAndroid Build Coastguard Worker    'arch':
57*6dbdd20aSAndroid Build Coastguard Worker        'mac-arm64',
58*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
59*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
60*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
61*6dbdd20aSAndroid Build Coastguard Worker        8375512,
62*6dbdd20aSAndroid Build Coastguard Worker    'url':
63*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/mac-arm64/traceconv',
64*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
65*6dbdd20aSAndroid Build Coastguard Worker        '64e200a58ea9c9f366e1071dd274d0023d1fd14043f75dbba3fe0cc138ff5fc7',
66*6dbdd20aSAndroid Build Coastguard Worker    'platform':
67*6dbdd20aSAndroid Build Coastguard Worker        'darwin',
68*6dbdd20aSAndroid Build Coastguard Worker    'machine': ['arm64']
69*6dbdd20aSAndroid Build Coastguard Worker}, {
70*6dbdd20aSAndroid Build Coastguard Worker    'arch':
71*6dbdd20aSAndroid Build Coastguard Worker        'linux-amd64',
72*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
73*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
74*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
75*6dbdd20aSAndroid Build Coastguard Worker        9134136,
76*6dbdd20aSAndroid Build Coastguard Worker    'url':
77*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/linux-amd64/traceconv',
78*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
79*6dbdd20aSAndroid Build Coastguard Worker        '87b87e1778367c1e3b99fc77439a28b4911125d2751f9909fd1b51f6bd60b6f4',
80*6dbdd20aSAndroid Build Coastguard Worker    'platform':
81*6dbdd20aSAndroid Build Coastguard Worker        'linux',
82*6dbdd20aSAndroid Build Coastguard Worker    'machine': ['x86_64']
83*6dbdd20aSAndroid Build Coastguard Worker}, {
84*6dbdd20aSAndroid Build Coastguard Worker    'arch':
85*6dbdd20aSAndroid Build Coastguard Worker        'linux-arm',
86*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
87*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
88*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
89*6dbdd20aSAndroid Build Coastguard Worker        6753020,
90*6dbdd20aSAndroid Build Coastguard Worker    'url':
91*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/linux-arm/traceconv',
92*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
93*6dbdd20aSAndroid Build Coastguard Worker        '804c4e13aca5798731056952d9cb0c6ee58795c03477c69514ccd39703060812',
94*6dbdd20aSAndroid Build Coastguard Worker    'platform':
95*6dbdd20aSAndroid Build Coastguard Worker        'linux',
96*6dbdd20aSAndroid Build Coastguard Worker    'machine': ['armv6l', 'armv7l', 'armv8l']
97*6dbdd20aSAndroid Build Coastguard Worker}, {
98*6dbdd20aSAndroid Build Coastguard Worker    'arch':
99*6dbdd20aSAndroid Build Coastguard Worker        'linux-arm64',
100*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
101*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
102*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
103*6dbdd20aSAndroid Build Coastguard Worker        8740064,
104*6dbdd20aSAndroid Build Coastguard Worker    'url':
105*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/linux-arm64/traceconv',
106*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
107*6dbdd20aSAndroid Build Coastguard Worker        '0d781886531d11e1d573a1ec5e06376ef139bb479eec38c16c8735821c35b895',
108*6dbdd20aSAndroid Build Coastguard Worker    'platform':
109*6dbdd20aSAndroid Build Coastguard Worker        'linux',
110*6dbdd20aSAndroid Build Coastguard Worker    'machine': ['aarch64']
111*6dbdd20aSAndroid Build Coastguard Worker}, {
112*6dbdd20aSAndroid Build Coastguard Worker    'arch':
113*6dbdd20aSAndroid Build Coastguard Worker        'android-arm',
114*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
115*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
116*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
117*6dbdd20aSAndroid Build Coastguard Worker        6792280,
118*6dbdd20aSAndroid Build Coastguard Worker    'url':
119*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-arm/traceconv',
120*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
121*6dbdd20aSAndroid Build Coastguard Worker        '7d91e4133184a3722a25488edd3692c5a195148eba56621014311d3f85d3fc15'
122*6dbdd20aSAndroid Build Coastguard Worker}, {
123*6dbdd20aSAndroid Build Coastguard Worker    'arch':
124*6dbdd20aSAndroid Build Coastguard Worker        'android-arm64',
125*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
126*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
127*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
128*6dbdd20aSAndroid Build Coastguard Worker        8677992,
129*6dbdd20aSAndroid Build Coastguard Worker    'url':
130*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-arm64/traceconv',
131*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
132*6dbdd20aSAndroid Build Coastguard Worker        'c03c4a901ed23f1e20a12c98ce4556353a62bddcd260fb4d797cd29ff6c49a05'
133*6dbdd20aSAndroid Build Coastguard Worker}, {
134*6dbdd20aSAndroid Build Coastguard Worker    'arch':
135*6dbdd20aSAndroid Build Coastguard Worker        'android-x86',
136*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
137*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
138*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
139*6dbdd20aSAndroid Build Coastguard Worker        9503704,
140*6dbdd20aSAndroid Build Coastguard Worker    'url':
141*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-x86/traceconv',
142*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
143*6dbdd20aSAndroid Build Coastguard Worker        '704e58a7249de56aadec64d4c0d83bab0821d2c4fd77114a9b71705ff4224539'
144*6dbdd20aSAndroid Build Coastguard Worker}, {
145*6dbdd20aSAndroid Build Coastguard Worker    'arch':
146*6dbdd20aSAndroid Build Coastguard Worker        'android-x64',
147*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
148*6dbdd20aSAndroid Build Coastguard Worker        'traceconv',
149*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
150*6dbdd20aSAndroid Build Coastguard Worker        8964488,
151*6dbdd20aSAndroid Build Coastguard Worker    'url':
152*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/android-x64/traceconv',
153*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
154*6dbdd20aSAndroid Build Coastguard Worker        'e4f07836fc2a5fb7cd997a9acc4183af7a06997d1e73aac71021af5114b921bc'
155*6dbdd20aSAndroid Build Coastguard Worker}, {
156*6dbdd20aSAndroid Build Coastguard Worker    'arch':
157*6dbdd20aSAndroid Build Coastguard Worker        'windows-amd64',
158*6dbdd20aSAndroid Build Coastguard Worker    'file_name':
159*6dbdd20aSAndroid Build Coastguard Worker        'traceconv.exe',
160*6dbdd20aSAndroid Build Coastguard Worker    'file_size':
161*6dbdd20aSAndroid Build Coastguard Worker        8763904,
162*6dbdd20aSAndroid Build Coastguard Worker    'url':
163*6dbdd20aSAndroid Build Coastguard Worker        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v48.1/windows-amd64/traceconv.exe',
164*6dbdd20aSAndroid Build Coastguard Worker    'sha256':
165*6dbdd20aSAndroid Build Coastguard Worker        '084670ac28ed59a9642782a30e051735c1b7474b8cd569b9bc94c305af68290e',
166*6dbdd20aSAndroid Build Coastguard Worker    'platform':
167*6dbdd20aSAndroid Build Coastguard Worker        'win32',
168*6dbdd20aSAndroid Build Coastguard Worker    'machine': ['amd64']
169*6dbdd20aSAndroid Build Coastguard Worker}]
170*6dbdd20aSAndroid Build Coastguard Worker
171*6dbdd20aSAndroid Build Coastguard Worker# ----- Amalgamator: end of python/perfetto/prebuilts/manifests/traceconv.py
172*6dbdd20aSAndroid Build Coastguard Worker
173*6dbdd20aSAndroid Build Coastguard Worker# ----- Amalgamator: begin of python/perfetto/prebuilts/perfetto_prebuilts.py
174*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project
175*6dbdd20aSAndroid Build Coastguard Worker#
176*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
177*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
178*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
179*6dbdd20aSAndroid Build Coastguard Worker#
180*6dbdd20aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
181*6dbdd20aSAndroid Build Coastguard Worker#
182*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
183*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
184*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
185*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
186*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License.
187*6dbdd20aSAndroid Build Coastguard Worker"""
188*6dbdd20aSAndroid Build Coastguard WorkerFunctions to fetch pre-pinned Perfetto prebuilts.
189*6dbdd20aSAndroid Build Coastguard Worker
190*6dbdd20aSAndroid Build Coastguard WorkerThis function is used in different places:
191*6dbdd20aSAndroid Build Coastguard Worker- Into the //tools/{trace_processor, traceconv} scripts, which are just plain
192*6dbdd20aSAndroid Build Coastguard Worker  wrappers around executables.
193*6dbdd20aSAndroid Build Coastguard Worker- Into the //tools/{heap_profiler, record_android_trace} scripts, which contain
194*6dbdd20aSAndroid Build Coastguard Worker  some other hand-written python code.
195*6dbdd20aSAndroid Build Coastguard Worker
196*6dbdd20aSAndroid Build Coastguard WorkerThe manifest argument looks as follows:
197*6dbdd20aSAndroid Build Coastguard WorkerTRACECONV_MANIFEST = [
198*6dbdd20aSAndroid Build Coastguard Worker  {
199*6dbdd20aSAndroid Build Coastguard Worker    'arch': 'mac-amd64',
200*6dbdd20aSAndroid Build Coastguard Worker    'file_name': 'traceconv',
201*6dbdd20aSAndroid Build Coastguard Worker    'file_size': 7087080,
202*6dbdd20aSAndroid Build Coastguard Worker    'url': https://commondatastorage.googleapis.com/.../trace_to_text',
203*6dbdd20aSAndroid Build Coastguard Worker    'sha256': 7d957c005b0dc130f5bd855d6cec27e060d38841b320d04840afc569f9087490',
204*6dbdd20aSAndroid Build Coastguard Worker    'platform': 'darwin',
205*6dbdd20aSAndroid Build Coastguard Worker    'machine': 'x86_64'
206*6dbdd20aSAndroid Build Coastguard Worker  },
207*6dbdd20aSAndroid Build Coastguard Worker  ...
208*6dbdd20aSAndroid Build Coastguard Worker]
209*6dbdd20aSAndroid Build Coastguard Worker
210*6dbdd20aSAndroid Build Coastguard WorkerThe intended usage is:
211*6dbdd20aSAndroid Build Coastguard Worker
212*6dbdd20aSAndroid Build Coastguard Worker  from perfetto.prebuilts.manifests.traceconv import TRACECONV_MANIFEST
213*6dbdd20aSAndroid Build Coastguard Worker  bin_path = get_perfetto_prebuilt(TRACECONV_MANIFEST)
214*6dbdd20aSAndroid Build Coastguard Worker  subprocess.call(bin_path, ...)
215*6dbdd20aSAndroid Build Coastguard Worker"""
216*6dbdd20aSAndroid Build Coastguard Worker
217*6dbdd20aSAndroid Build Coastguard Workerimport hashlib
218*6dbdd20aSAndroid Build Coastguard Workerimport os
219*6dbdd20aSAndroid Build Coastguard Workerimport platform
220*6dbdd20aSAndroid Build Coastguard Workerimport random
221*6dbdd20aSAndroid Build Coastguard Workerimport subprocess
222*6dbdd20aSAndroid Build Coastguard Workerimport sys
223*6dbdd20aSAndroid Build Coastguard Worker
224*6dbdd20aSAndroid Build Coastguard Worker
225*6dbdd20aSAndroid Build Coastguard Workerdef download_or_get_cached(file_name, url, sha256):
226*6dbdd20aSAndroid Build Coastguard Worker  """ Downloads a prebuilt or returns a cached version
227*6dbdd20aSAndroid Build Coastguard Worker
228*6dbdd20aSAndroid Build Coastguard Worker  The first time this is invoked, it downloads the |url| and caches it into
229*6dbdd20aSAndroid Build Coastguard Worker  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
230*6dbdd20aSAndroid Build Coastguard Worker  just runs the cached version.
231*6dbdd20aSAndroid Build Coastguard Worker  """
232*6dbdd20aSAndroid Build Coastguard Worker  dir = os.path.join(
233*6dbdd20aSAndroid Build Coastguard Worker      os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
234*6dbdd20aSAndroid Build Coastguard Worker  os.makedirs(dir, exist_ok=True)
235*6dbdd20aSAndroid Build Coastguard Worker  bin_path = os.path.join(dir, file_name)
236*6dbdd20aSAndroid Build Coastguard Worker  sha256_path = os.path.join(dir, file_name + '.sha256')
237*6dbdd20aSAndroid Build Coastguard Worker  needs_download = True
238*6dbdd20aSAndroid Build Coastguard Worker
239*6dbdd20aSAndroid Build Coastguard Worker  # Avoid recomputing the SHA-256 on each invocation. The SHA-256 of the last
240*6dbdd20aSAndroid Build Coastguard Worker  # download is cached into file_name.sha256, just check if that matches.
241*6dbdd20aSAndroid Build Coastguard Worker  if os.path.exists(bin_path) and os.path.exists(sha256_path):
242*6dbdd20aSAndroid Build Coastguard Worker    with open(sha256_path, 'rb') as f:
243*6dbdd20aSAndroid Build Coastguard Worker      digest = f.read().decode()
244*6dbdd20aSAndroid Build Coastguard Worker      if digest == sha256:
245*6dbdd20aSAndroid Build Coastguard Worker        needs_download = False
246*6dbdd20aSAndroid Build Coastguard Worker
247*6dbdd20aSAndroid Build Coastguard Worker  if needs_download:  # The file doesn't exist or the SHA256 doesn't match.
248*6dbdd20aSAndroid Build Coastguard Worker    # Use a unique random file to guard against concurrent executions.
249*6dbdd20aSAndroid Build Coastguard Worker    # See https://github.com/google/perfetto/issues/786 .
250*6dbdd20aSAndroid Build Coastguard Worker    tmp_path = '%s.%d.tmp' % (bin_path, random.randint(0, 100000))
251*6dbdd20aSAndroid Build Coastguard Worker    print('Downloading ' + url)
252*6dbdd20aSAndroid Build Coastguard Worker    subprocess.check_call(['curl', '-f', '-L', '-#', '-o', tmp_path, url])
253*6dbdd20aSAndroid Build Coastguard Worker    with open(tmp_path, 'rb') as fd:
254*6dbdd20aSAndroid Build Coastguard Worker      actual_sha256 = hashlib.sha256(fd.read()).hexdigest()
255*6dbdd20aSAndroid Build Coastguard Worker    if actual_sha256 != sha256:
256*6dbdd20aSAndroid Build Coastguard Worker      raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
257*6dbdd20aSAndroid Build Coastguard Worker                      (url, actual_sha256, sha256))
258*6dbdd20aSAndroid Build Coastguard Worker    os.chmod(tmp_path, 0o755)
259*6dbdd20aSAndroid Build Coastguard Worker    os.replace(tmp_path, bin_path)
260*6dbdd20aSAndroid Build Coastguard Worker    with open(tmp_path, 'w') as f:
261*6dbdd20aSAndroid Build Coastguard Worker      f.write(sha256)
262*6dbdd20aSAndroid Build Coastguard Worker    os.replace(tmp_path, sha256_path)
263*6dbdd20aSAndroid Build Coastguard Worker  return bin_path
264*6dbdd20aSAndroid Build Coastguard Worker
265*6dbdd20aSAndroid Build Coastguard Worker
266*6dbdd20aSAndroid Build Coastguard Workerdef get_perfetto_prebuilt(manifest, soft_fail=False, arch=None):
267*6dbdd20aSAndroid Build Coastguard Worker  """ Downloads the prebuilt, if necessary, and returns its path on disk. """
268*6dbdd20aSAndroid Build Coastguard Worker  plat = sys.platform.lower()
269*6dbdd20aSAndroid Build Coastguard Worker  machine = platform.machine().lower()
270*6dbdd20aSAndroid Build Coastguard Worker  manifest_entry = None
271*6dbdd20aSAndroid Build Coastguard Worker  for entry in manifest:
272*6dbdd20aSAndroid Build Coastguard Worker    # If the caller overrides the arch, just match that (for Android prebuilts).
273*6dbdd20aSAndroid Build Coastguard Worker    if arch:
274*6dbdd20aSAndroid Build Coastguard Worker      if entry.get('arch') == arch:
275*6dbdd20aSAndroid Build Coastguard Worker        manifest_entry = entry
276*6dbdd20aSAndroid Build Coastguard Worker        break
277*6dbdd20aSAndroid Build Coastguard Worker      continue
278*6dbdd20aSAndroid Build Coastguard Worker    # Otherwise guess the local machine arch.
279*6dbdd20aSAndroid Build Coastguard Worker    if entry.get('platform') == plat and machine in entry.get('machine', []):
280*6dbdd20aSAndroid Build Coastguard Worker      manifest_entry = entry
281*6dbdd20aSAndroid Build Coastguard Worker      break
282*6dbdd20aSAndroid Build Coastguard Worker  if manifest_entry is None:
283*6dbdd20aSAndroid Build Coastguard Worker    if soft_fail:
284*6dbdd20aSAndroid Build Coastguard Worker      return None
285*6dbdd20aSAndroid Build Coastguard Worker    raise Exception(
286*6dbdd20aSAndroid Build Coastguard Worker        ('No prebuilts available for %s-%s\n' % (plat, machine)) +
287*6dbdd20aSAndroid Build Coastguard Worker        'See https://perfetto.dev/docs/contributing/build-instructions')
288*6dbdd20aSAndroid Build Coastguard Worker
289*6dbdd20aSAndroid Build Coastguard Worker  return download_or_get_cached(
290*6dbdd20aSAndroid Build Coastguard Worker      file_name=manifest_entry['file_name'],
291*6dbdd20aSAndroid Build Coastguard Worker      url=manifest_entry['url'],
292*6dbdd20aSAndroid Build Coastguard Worker      sha256=manifest_entry['sha256'])
293*6dbdd20aSAndroid Build Coastguard Worker
294*6dbdd20aSAndroid Build Coastguard Worker
295*6dbdd20aSAndroid Build Coastguard Workerdef run_perfetto_prebuilt(manifest):
296*6dbdd20aSAndroid Build Coastguard Worker  bin_path = get_perfetto_prebuilt(manifest)
297*6dbdd20aSAndroid Build Coastguard Worker  if sys.platform.lower() == 'win32':
298*6dbdd20aSAndroid Build Coastguard Worker    sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
299*6dbdd20aSAndroid Build Coastguard Worker  os.execv(bin_path, [bin_path] + sys.argv[1:])
300*6dbdd20aSAndroid Build Coastguard Worker
301*6dbdd20aSAndroid Build Coastguard Worker# ----- Amalgamator: end of python/perfetto/prebuilts/perfetto_prebuilts.py
302*6dbdd20aSAndroid Build Coastguard Worker
303*6dbdd20aSAndroid Build Coastguard Worker# Used for creating directories, etc.
304*6dbdd20aSAndroid Build Coastguard WorkerUUID = str(uuid.uuid4())[-6:]
305*6dbdd20aSAndroid Build Coastguard Worker
306*6dbdd20aSAndroid Build Coastguard Worker# See `sigint_handler` below.
307*6dbdd20aSAndroid Build Coastguard WorkerIS_INTERRUPTED = False
308*6dbdd20aSAndroid Build Coastguard Worker
309*6dbdd20aSAndroid Build Coastguard Worker
310*6dbdd20aSAndroid Build Coastguard Workerdef sigint_handler(signal, frame):
311*6dbdd20aSAndroid Build Coastguard Worker  """Useful for cleanly interrupting tracing."""
312*6dbdd20aSAndroid Build Coastguard Worker  global IS_INTERRUPTED
313*6dbdd20aSAndroid Build Coastguard Worker  IS_INTERRUPTED = True
314*6dbdd20aSAndroid Build Coastguard Worker
315*6dbdd20aSAndroid Build Coastguard Worker
316*6dbdd20aSAndroid Build Coastguard Workerdef exit_with_no_profile():
317*6dbdd20aSAndroid Build Coastguard Worker  sys.exit("No profiles generated.")
318*6dbdd20aSAndroid Build Coastguard Worker
319*6dbdd20aSAndroid Build Coastguard Worker
320*6dbdd20aSAndroid Build Coastguard Workerdef exit_with_bug_report(error):
321*6dbdd20aSAndroid Build Coastguard Worker  sys.exit(
322*6dbdd20aSAndroid Build Coastguard Worker      "{}\n\n If this is unexpected, please consider filing a bug at: \n"
323*6dbdd20aSAndroid Build Coastguard Worker      "https://perfetto.dev/docs/contributing/getting-started#bugs.".format(
324*6dbdd20aSAndroid Build Coastguard Worker          error))
325*6dbdd20aSAndroid Build Coastguard Worker
326*6dbdd20aSAndroid Build Coastguard Worker
327*6dbdd20aSAndroid Build Coastguard Workerdef adb_check_output(command):
328*6dbdd20aSAndroid Build Coastguard Worker  """Runs an `adb` command and returns its output."""
329*6dbdd20aSAndroid Build Coastguard Worker  try:
330*6dbdd20aSAndroid Build Coastguard Worker    return subprocess.check_output(command).decode('utf-8')
331*6dbdd20aSAndroid Build Coastguard Worker  except FileNotFoundError:
332*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("`adb` not found: Is it installed or on PATH?")
333*6dbdd20aSAndroid Build Coastguard Worker  except subprocess.CalledProcessError as error:
334*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("`adb` error: Are any (or multiple) devices connected?\n"
335*6dbdd20aSAndroid Build Coastguard Worker             "If multiple devices are connected, please select one by "
336*6dbdd20aSAndroid Build Coastguard Worker             "setting `ANDROID_SERIAL=device_id`.\n"
337*6dbdd20aSAndroid Build Coastguard Worker             "{}".format(error))
338*6dbdd20aSAndroid Build Coastguard Worker  except Exception as error:
339*6dbdd20aSAndroid Build Coastguard Worker    exit_with_bug_report(error)
340*6dbdd20aSAndroid Build Coastguard Worker
341*6dbdd20aSAndroid Build Coastguard Worker
342*6dbdd20aSAndroid Build Coastguard Workerdef parse_and_validate_args():
343*6dbdd20aSAndroid Build Coastguard Worker  """Parses, validates, and returns command-line arguments for this script."""
344*6dbdd20aSAndroid Build Coastguard Worker  DESCRIPTION = """Runs tracing with CPU profiling enabled, and symbolizes
345*6dbdd20aSAndroid Build Coastguard Worker  traces if requested.
346*6dbdd20aSAndroid Build Coastguard Worker
347*6dbdd20aSAndroid Build Coastguard Worker  For usage instructions, please see:
348*6dbdd20aSAndroid Build Coastguard Worker  https://perfetto.dev/docs/quickstart/callstack-sampling
349*6dbdd20aSAndroid Build Coastguard Worker  """
350*6dbdd20aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description=DESCRIPTION)
351*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
352*6dbdd20aSAndroid Build Coastguard Worker      "-f",
353*6dbdd20aSAndroid Build Coastguard Worker      "--frequency",
354*6dbdd20aSAndroid Build Coastguard Worker      help="Sampling frequency (Hz). "
355*6dbdd20aSAndroid Build Coastguard Worker      "Default: 100 Hz.",
356*6dbdd20aSAndroid Build Coastguard Worker      metavar="FREQUENCY",
357*6dbdd20aSAndroid Build Coastguard Worker      type=int,
358*6dbdd20aSAndroid Build Coastguard Worker      default=100)
359*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
360*6dbdd20aSAndroid Build Coastguard Worker      "-d",
361*6dbdd20aSAndroid Build Coastguard Worker      "--duration",
362*6dbdd20aSAndroid Build Coastguard Worker      help="Duration of profile (ms). 0 to run until interrupted. "
363*6dbdd20aSAndroid Build Coastguard Worker      "Default: until interrupted by user.",
364*6dbdd20aSAndroid Build Coastguard Worker      metavar="DURATION",
365*6dbdd20aSAndroid Build Coastguard Worker      type=int,
366*6dbdd20aSAndroid Build Coastguard Worker      default=0)
367*6dbdd20aSAndroid Build Coastguard Worker  # Profiling using hardware counters.
368*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
369*6dbdd20aSAndroid Build Coastguard Worker      "-e",
370*6dbdd20aSAndroid Build Coastguard Worker      "--event",
371*6dbdd20aSAndroid Build Coastguard Worker      help="Use the specified hardware counter event for sampling.",
372*6dbdd20aSAndroid Build Coastguard Worker      metavar="EVENT",
373*6dbdd20aSAndroid Build Coastguard Worker      action="append",
374*6dbdd20aSAndroid Build Coastguard Worker      # See: '//perfetto/protos/perfetto/trace/perfetto_trace.proto'.
375*6dbdd20aSAndroid Build Coastguard Worker      choices=['HW_CPU_CYCLES', 'HW_INSTRUCTIONS', 'HW_CACHE_REFERENCES',
376*6dbdd20aSAndroid Build Coastguard Worker               'HW_CACHE_MISSES', 'HW_BRANCH_INSTRUCTIONS', 'HW_BRANCH_MISSES',
377*6dbdd20aSAndroid Build Coastguard Worker               'HW_BUS_CYCLES', 'HW_STALLED_CYCLES_FRONTEND',
378*6dbdd20aSAndroid Build Coastguard Worker               'HW_STALLED_CYCLES_BACKEND'],
379*6dbdd20aSAndroid Build Coastguard Worker      default=[])
380*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
381*6dbdd20aSAndroid Build Coastguard Worker      "-k",
382*6dbdd20aSAndroid Build Coastguard Worker      "--kernel-frames",
383*6dbdd20aSAndroid Build Coastguard Worker      help="Collect kernel frames.  Default: false.",
384*6dbdd20aSAndroid Build Coastguard Worker      action="store_true",
385*6dbdd20aSAndroid Build Coastguard Worker      default=False)
386*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
387*6dbdd20aSAndroid Build Coastguard Worker      "-n",
388*6dbdd20aSAndroid Build Coastguard Worker      "--name",
389*6dbdd20aSAndroid Build Coastguard Worker      help="Comma-separated list of names of processes to be profiled.",
390*6dbdd20aSAndroid Build Coastguard Worker      metavar="NAMES",
391*6dbdd20aSAndroid Build Coastguard Worker      default=None)
392*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
393*6dbdd20aSAndroid Build Coastguard Worker      "-p",
394*6dbdd20aSAndroid Build Coastguard Worker      "--partial-matching",
395*6dbdd20aSAndroid Build Coastguard Worker      help="If set, enables \"partial matching\" on the strings in --names/-n."
396*6dbdd20aSAndroid Build Coastguard Worker      "Processes that are already running when profiling is started, and whose "
397*6dbdd20aSAndroid Build Coastguard Worker      "names include any of the values in --names/-n as substrings will be "
398*6dbdd20aSAndroid Build Coastguard Worker      "profiled.",
399*6dbdd20aSAndroid Build Coastguard Worker      action="store_true")
400*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
401*6dbdd20aSAndroid Build Coastguard Worker      "-c",
402*6dbdd20aSAndroid Build Coastguard Worker      "--config",
403*6dbdd20aSAndroid Build Coastguard Worker      help="A custom configuration file, if any, to be used for profiling. "
404*6dbdd20aSAndroid Build Coastguard Worker      "If provided, --frequency/-f, --duration/-d, and --name/-n are not used.",
405*6dbdd20aSAndroid Build Coastguard Worker      metavar="CONFIG",
406*6dbdd20aSAndroid Build Coastguard Worker      default=None)
407*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
408*6dbdd20aSAndroid Build Coastguard Worker      "--no-annotations",
409*6dbdd20aSAndroid Build Coastguard Worker      help="Do not suffix the pprof function names with Android ART mode "
410*6dbdd20aSAndroid Build Coastguard Worker      "annotations such as [jit].",
411*6dbdd20aSAndroid Build Coastguard Worker      action="store_true")
412*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
413*6dbdd20aSAndroid Build Coastguard Worker      "--print-config",
414*6dbdd20aSAndroid Build Coastguard Worker      action="store_true",
415*6dbdd20aSAndroid Build Coastguard Worker      help="Print config instead of running. For debugging.")
416*6dbdd20aSAndroid Build Coastguard Worker  parser.add_argument(
417*6dbdd20aSAndroid Build Coastguard Worker      "-o",
418*6dbdd20aSAndroid Build Coastguard Worker      "--output",
419*6dbdd20aSAndroid Build Coastguard Worker      help="Output directory for recorded trace.",
420*6dbdd20aSAndroid Build Coastguard Worker      metavar="DIRECTORY",
421*6dbdd20aSAndroid Build Coastguard Worker      default=None)
422*6dbdd20aSAndroid Build Coastguard Worker
423*6dbdd20aSAndroid Build Coastguard Worker  args = parser.parse_args()
424*6dbdd20aSAndroid Build Coastguard Worker  if args.config is not None:
425*6dbdd20aSAndroid Build Coastguard Worker    if args.name is not None:
426*6dbdd20aSAndroid Build Coastguard Worker      sys.exit("--name/-n should not be specified with --config/-c.")
427*6dbdd20aSAndroid Build Coastguard Worker    elif args.event:
428*6dbdd20aSAndroid Build Coastguard Worker      sys.exit("-e/--event should not be specified with --config/-c.")
429*6dbdd20aSAndroid Build Coastguard Worker  elif args.config is None and args.name is None:
430*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("One of --names/-n or --config/-c is required.")
431*6dbdd20aSAndroid Build Coastguard Worker
432*6dbdd20aSAndroid Build Coastguard Worker  return args
433*6dbdd20aSAndroid Build Coastguard Worker
434*6dbdd20aSAndroid Build Coastguard Worker
435*6dbdd20aSAndroid Build Coastguard Workerdef get_matching_processes(args, names_to_match):
436*6dbdd20aSAndroid Build Coastguard Worker  """Returns a list of currently-running processes whose names match
437*6dbdd20aSAndroid Build Coastguard Worker  `names_to_match`.
438*6dbdd20aSAndroid Build Coastguard Worker
439*6dbdd20aSAndroid Build Coastguard Worker  Args:
440*6dbdd20aSAndroid Build Coastguard Worker    args: The command-line arguments provided to this script.
441*6dbdd20aSAndroid Build Coastguard Worker    names_to_match: The list of process names provided by the user.
442*6dbdd20aSAndroid Build Coastguard Worker  """
443*6dbdd20aSAndroid Build Coastguard Worker  # Returns names as they are.
444*6dbdd20aSAndroid Build Coastguard Worker  if not args.partial_matching:
445*6dbdd20aSAndroid Build Coastguard Worker    return names_to_match
446*6dbdd20aSAndroid Build Coastguard Worker
447*6dbdd20aSAndroid Build Coastguard Worker  # Attempt to match names to names of currently running processes.
448*6dbdd20aSAndroid Build Coastguard Worker  PS_PROCESS_OFFSET = 8
449*6dbdd20aSAndroid Build Coastguard Worker  matching_processes = []
450*6dbdd20aSAndroid Build Coastguard Worker  for line in adb_check_output(['adb', 'shell', 'ps', '-A']).splitlines():
451*6dbdd20aSAndroid Build Coastguard Worker    line_split = line.split()
452*6dbdd20aSAndroid Build Coastguard Worker    if len(line_split) <= PS_PROCESS_OFFSET:
453*6dbdd20aSAndroid Build Coastguard Worker      continue
454*6dbdd20aSAndroid Build Coastguard Worker    process = line_split[PS_PROCESS_OFFSET]
455*6dbdd20aSAndroid Build Coastguard Worker    for name in names_to_match:
456*6dbdd20aSAndroid Build Coastguard Worker      if name in process:
457*6dbdd20aSAndroid Build Coastguard Worker        matching_processes.append(process)
458*6dbdd20aSAndroid Build Coastguard Worker        break
459*6dbdd20aSAndroid Build Coastguard Worker
460*6dbdd20aSAndroid Build Coastguard Worker  return matching_processes
461*6dbdd20aSAndroid Build Coastguard Worker
462*6dbdd20aSAndroid Build Coastguard Worker
463*6dbdd20aSAndroid Build Coastguard Workerdef get_perfetto_config(args):
464*6dbdd20aSAndroid Build Coastguard Worker  """Returns a Perfetto config with CPU profiling enabled for the selected
465*6dbdd20aSAndroid Build Coastguard Worker  processes.
466*6dbdd20aSAndroid Build Coastguard Worker
467*6dbdd20aSAndroid Build Coastguard Worker  Args:
468*6dbdd20aSAndroid Build Coastguard Worker    args: The command-line arguments provided to this script.
469*6dbdd20aSAndroid Build Coastguard Worker  """
470*6dbdd20aSAndroid Build Coastguard Worker  if args.config is not None:
471*6dbdd20aSAndroid Build Coastguard Worker    try:
472*6dbdd20aSAndroid Build Coastguard Worker      with open(args.config, 'r') as config_file:
473*6dbdd20aSAndroid Build Coastguard Worker        return config_file.read()
474*6dbdd20aSAndroid Build Coastguard Worker    except IOError as error:
475*6dbdd20aSAndroid Build Coastguard Worker      sys.exit("Unable to read config file: {}".format(error))
476*6dbdd20aSAndroid Build Coastguard Worker
477*6dbdd20aSAndroid Build Coastguard Worker  CONFIG_INDENT = '          '
478*6dbdd20aSAndroid Build Coastguard Worker  CONFIG = textwrap.dedent('''\
479*6dbdd20aSAndroid Build Coastguard Worker  buffers {{
480*6dbdd20aSAndroid Build Coastguard Worker    size_kb: 2048
481*6dbdd20aSAndroid Build Coastguard Worker  }}
482*6dbdd20aSAndroid Build Coastguard Worker
483*6dbdd20aSAndroid Build Coastguard Worker  buffers {{
484*6dbdd20aSAndroid Build Coastguard Worker    size_kb: 63488
485*6dbdd20aSAndroid Build Coastguard Worker  }}
486*6dbdd20aSAndroid Build Coastguard Worker
487*6dbdd20aSAndroid Build Coastguard Worker  data_sources {{
488*6dbdd20aSAndroid Build Coastguard Worker    config {{
489*6dbdd20aSAndroid Build Coastguard Worker      name: "linux.process_stats"
490*6dbdd20aSAndroid Build Coastguard Worker      target_buffer: 0
491*6dbdd20aSAndroid Build Coastguard Worker      process_stats_config {{
492*6dbdd20aSAndroid Build Coastguard Worker        proc_stats_poll_ms: 100
493*6dbdd20aSAndroid Build Coastguard Worker      }}
494*6dbdd20aSAndroid Build Coastguard Worker    }}
495*6dbdd20aSAndroid Build Coastguard Worker  }}
496*6dbdd20aSAndroid Build Coastguard Worker
497*6dbdd20aSAndroid Build Coastguard Worker  duration_ms: {duration}
498*6dbdd20aSAndroid Build Coastguard Worker  write_into_file: true
499*6dbdd20aSAndroid Build Coastguard Worker  flush_timeout_ms: 30000
500*6dbdd20aSAndroid Build Coastguard Worker  flush_period_ms: 604800000
501*6dbdd20aSAndroid Build Coastguard Worker  ''')
502*6dbdd20aSAndroid Build Coastguard Worker
503*6dbdd20aSAndroid Build Coastguard Worker  matching_processes = []
504*6dbdd20aSAndroid Build Coastguard Worker  if args.name is not None:
505*6dbdd20aSAndroid Build Coastguard Worker    names_to_match = [name.strip() for name in args.name.split(',')]
506*6dbdd20aSAndroid Build Coastguard Worker    matching_processes = get_matching_processes(args, names_to_match)
507*6dbdd20aSAndroid Build Coastguard Worker
508*6dbdd20aSAndroid Build Coastguard Worker  if not matching_processes:
509*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("No running processes matched for profiling.")
510*6dbdd20aSAndroid Build Coastguard Worker
511*6dbdd20aSAndroid Build Coastguard Worker  target_config = "\n".join(
512*6dbdd20aSAndroid Build Coastguard Worker      [f'{CONFIG_INDENT}target_cmdline: "{p}"' for p in matching_processes])
513*6dbdd20aSAndroid Build Coastguard Worker
514*6dbdd20aSAndroid Build Coastguard Worker  events = args.event or ['SW_CPU_CLOCK']
515*6dbdd20aSAndroid Build Coastguard Worker  for event in events:
516*6dbdd20aSAndroid Build Coastguard Worker    CONFIG += (textwrap.dedent('''
517*6dbdd20aSAndroid Build Coastguard Worker    data_sources {{
518*6dbdd20aSAndroid Build Coastguard Worker      config {{
519*6dbdd20aSAndroid Build Coastguard Worker        name: "linux.perf"
520*6dbdd20aSAndroid Build Coastguard Worker        target_buffer: 1
521*6dbdd20aSAndroid Build Coastguard Worker        perf_event_config {{
522*6dbdd20aSAndroid Build Coastguard Worker          timebase {{
523*6dbdd20aSAndroid Build Coastguard Worker            counter: %s
524*6dbdd20aSAndroid Build Coastguard Worker            frequency: {frequency}
525*6dbdd20aSAndroid Build Coastguard Worker            timestamp_clock: PERF_CLOCK_MONOTONIC
526*6dbdd20aSAndroid Build Coastguard Worker          }}
527*6dbdd20aSAndroid Build Coastguard Worker          callstack_sampling {{
528*6dbdd20aSAndroid Build Coastguard Worker            scope {{
529*6dbdd20aSAndroid Build Coastguard Worker    {target_config}
530*6dbdd20aSAndroid Build Coastguard Worker            }}
531*6dbdd20aSAndroid Build Coastguard Worker            kernel_frames: {kernel_config}
532*6dbdd20aSAndroid Build Coastguard Worker          }}
533*6dbdd20aSAndroid Build Coastguard Worker        }}
534*6dbdd20aSAndroid Build Coastguard Worker      }}
535*6dbdd20aSAndroid Build Coastguard Worker    }}
536*6dbdd20aSAndroid Build Coastguard Worker    ''') % (event))
537*6dbdd20aSAndroid Build Coastguard Worker
538*6dbdd20aSAndroid Build Coastguard Worker  if args.kernel_frames:
539*6dbdd20aSAndroid Build Coastguard Worker    kernel_config = "true"
540*6dbdd20aSAndroid Build Coastguard Worker  else:
541*6dbdd20aSAndroid Build Coastguard Worker    kernel_config = "false"
542*6dbdd20aSAndroid Build Coastguard Worker
543*6dbdd20aSAndroid Build Coastguard Worker  if not args.print_config:
544*6dbdd20aSAndroid Build Coastguard Worker    print("Configured profiling for these processes:\n")
545*6dbdd20aSAndroid Build Coastguard Worker    for matching_process in matching_processes:
546*6dbdd20aSAndroid Build Coastguard Worker      print(matching_process)
547*6dbdd20aSAndroid Build Coastguard Worker    print()
548*6dbdd20aSAndroid Build Coastguard Worker
549*6dbdd20aSAndroid Build Coastguard Worker  config = CONFIG.format(
550*6dbdd20aSAndroid Build Coastguard Worker      frequency=args.frequency,
551*6dbdd20aSAndroid Build Coastguard Worker      duration=args.duration,
552*6dbdd20aSAndroid Build Coastguard Worker      target_config=target_config,
553*6dbdd20aSAndroid Build Coastguard Worker      kernel_config=kernel_config)
554*6dbdd20aSAndroid Build Coastguard Worker
555*6dbdd20aSAndroid Build Coastguard Worker  return config
556*6dbdd20aSAndroid Build Coastguard Worker
557*6dbdd20aSAndroid Build Coastguard Worker
558*6dbdd20aSAndroid Build Coastguard Workerdef release_or_newer(release):
559*6dbdd20aSAndroid Build Coastguard Worker  """Returns whether a new enough Android release is being used."""
560*6dbdd20aSAndroid Build Coastguard Worker  SDK = {'T': 33}
561*6dbdd20aSAndroid Build Coastguard Worker  sdk = int(
562*6dbdd20aSAndroid Build Coastguard Worker      adb_check_output(
563*6dbdd20aSAndroid Build Coastguard Worker          ['adb', 'shell', 'getprop', 'ro.system.build.version.sdk']).strip())
564*6dbdd20aSAndroid Build Coastguard Worker  if sdk >= SDK[release]:
565*6dbdd20aSAndroid Build Coastguard Worker    return True
566*6dbdd20aSAndroid Build Coastguard Worker
567*6dbdd20aSAndroid Build Coastguard Worker  codename = adb_check_output(
568*6dbdd20aSAndroid Build Coastguard Worker      ['adb', 'shell', 'getprop', 'ro.build.version.codename']).strip()
569*6dbdd20aSAndroid Build Coastguard Worker  return codename == release
570*6dbdd20aSAndroid Build Coastguard Worker
571*6dbdd20aSAndroid Build Coastguard Worker
572*6dbdd20aSAndroid Build Coastguard Workerdef get_and_prepare_profile_target(args):
573*6dbdd20aSAndroid Build Coastguard Worker  """Returns the target where the trace/profile will be output.  Creates a
574*6dbdd20aSAndroid Build Coastguard Worker  new directory if necessary.
575*6dbdd20aSAndroid Build Coastguard Worker
576*6dbdd20aSAndroid Build Coastguard Worker  Args:
577*6dbdd20aSAndroid Build Coastguard Worker    args: The command-line arguments provided to this script.
578*6dbdd20aSAndroid Build Coastguard Worker  """
579*6dbdd20aSAndroid Build Coastguard Worker  profile_target = os.path.join(tempfile.gettempdir(), UUID)
580*6dbdd20aSAndroid Build Coastguard Worker  if args.output is not None:
581*6dbdd20aSAndroid Build Coastguard Worker    profile_target = args.output
582*6dbdd20aSAndroid Build Coastguard Worker  else:
583*6dbdd20aSAndroid Build Coastguard Worker    os.makedirs(profile_target, exist_ok=True)
584*6dbdd20aSAndroid Build Coastguard Worker  if not os.path.isdir(profile_target):
585*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("Output directory {} not found.".format(profile_target))
586*6dbdd20aSAndroid Build Coastguard Worker  if os.listdir(profile_target):
587*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("Output directory {} not empty.".format(profile_target))
588*6dbdd20aSAndroid Build Coastguard Worker
589*6dbdd20aSAndroid Build Coastguard Worker  return profile_target
590*6dbdd20aSAndroid Build Coastguard Worker
591*6dbdd20aSAndroid Build Coastguard Worker
592*6dbdd20aSAndroid Build Coastguard Workerdef record_trace(config, profile_target):
593*6dbdd20aSAndroid Build Coastguard Worker  """Runs Perfetto with the provided configuration to record a trace.
594*6dbdd20aSAndroid Build Coastguard Worker
595*6dbdd20aSAndroid Build Coastguard Worker  Args:
596*6dbdd20aSAndroid Build Coastguard Worker    config: The Perfetto config to be used for tracing/profiling.
597*6dbdd20aSAndroid Build Coastguard Worker    profile_target: The directory where the recorded trace is output.
598*6dbdd20aSAndroid Build Coastguard Worker  """
599*6dbdd20aSAndroid Build Coastguard Worker  NULL = open(os.devnull)
600*6dbdd20aSAndroid Build Coastguard Worker  NO_OUT = {
601*6dbdd20aSAndroid Build Coastguard Worker      'stdout': NULL,
602*6dbdd20aSAndroid Build Coastguard Worker      'stderr': NULL,
603*6dbdd20aSAndroid Build Coastguard Worker  }
604*6dbdd20aSAndroid Build Coastguard Worker  if not release_or_newer('T'):
605*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("This tool requires Android T+ to run.")
606*6dbdd20aSAndroid Build Coastguard Worker
607*6dbdd20aSAndroid Build Coastguard Worker  # Push configuration to the device.
608*6dbdd20aSAndroid Build Coastguard Worker  tf = tempfile.NamedTemporaryFile()
609*6dbdd20aSAndroid Build Coastguard Worker  tf.file.write(config.encode('utf-8'))
610*6dbdd20aSAndroid Build Coastguard Worker  tf.file.flush()
611*6dbdd20aSAndroid Build Coastguard Worker  profile_config_path = '/data/misc/perfetto-configs/config-' + UUID
612*6dbdd20aSAndroid Build Coastguard Worker  adb_check_output(['adb', 'push', tf.name, profile_config_path])
613*6dbdd20aSAndroid Build Coastguard Worker  tf.close()
614*6dbdd20aSAndroid Build Coastguard Worker
615*6dbdd20aSAndroid Build Coastguard Worker
616*6dbdd20aSAndroid Build Coastguard Worker  profile_device_path = '/data/misc/perfetto-traces/profile-' + UUID
617*6dbdd20aSAndroid Build Coastguard Worker  perfetto_command = ('perfetto --txt -c {} -o {} -d')
618*6dbdd20aSAndroid Build Coastguard Worker  try:
619*6dbdd20aSAndroid Build Coastguard Worker    perfetto_pid = int(
620*6dbdd20aSAndroid Build Coastguard Worker        adb_check_output([
621*6dbdd20aSAndroid Build Coastguard Worker            'adb', 'exec-out',
622*6dbdd20aSAndroid Build Coastguard Worker            perfetto_command.format(profile_config_path, profile_device_path)
623*6dbdd20aSAndroid Build Coastguard Worker        ]).strip())
624*6dbdd20aSAndroid Build Coastguard Worker  except ValueError as error:
625*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("Unable to start profiling: {}".format(error))
626*6dbdd20aSAndroid Build Coastguard Worker
627*6dbdd20aSAndroid Build Coastguard Worker  print("Profiling active. Press Ctrl+C to terminate.")
628*6dbdd20aSAndroid Build Coastguard Worker
629*6dbdd20aSAndroid Build Coastguard Worker  old_handler = signal.signal(signal.SIGINT, sigint_handler)
630*6dbdd20aSAndroid Build Coastguard Worker
631*6dbdd20aSAndroid Build Coastguard Worker  perfetto_alive = True
632*6dbdd20aSAndroid Build Coastguard Worker  while perfetto_alive and not IS_INTERRUPTED:
633*6dbdd20aSAndroid Build Coastguard Worker    perfetto_alive = subprocess.call(
634*6dbdd20aSAndroid Build Coastguard Worker        ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)], **NO_OUT) == 0
635*6dbdd20aSAndroid Build Coastguard Worker    time.sleep(0.25)
636*6dbdd20aSAndroid Build Coastguard Worker
637*6dbdd20aSAndroid Build Coastguard Worker  print("Finishing profiling and symbolization...")
638*6dbdd20aSAndroid Build Coastguard Worker
639*6dbdd20aSAndroid Build Coastguard Worker  if IS_INTERRUPTED:
640*6dbdd20aSAndroid Build Coastguard Worker    adb_check_output(['adb', 'shell', 'kill', '-INT', str(perfetto_pid)])
641*6dbdd20aSAndroid Build Coastguard Worker
642*6dbdd20aSAndroid Build Coastguard Worker  # Restore old handler.
643*6dbdd20aSAndroid Build Coastguard Worker  signal.signal(signal.SIGINT, old_handler)
644*6dbdd20aSAndroid Build Coastguard Worker
645*6dbdd20aSAndroid Build Coastguard Worker  while perfetto_alive:
646*6dbdd20aSAndroid Build Coastguard Worker    perfetto_alive = subprocess.call(
647*6dbdd20aSAndroid Build Coastguard Worker        ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
648*6dbdd20aSAndroid Build Coastguard Worker    time.sleep(0.25)
649*6dbdd20aSAndroid Build Coastguard Worker
650*6dbdd20aSAndroid Build Coastguard Worker  profile_host_path = os.path.join(profile_target, 'raw-trace')
651*6dbdd20aSAndroid Build Coastguard Worker  adb_check_output(['adb', 'pull', profile_device_path, profile_host_path])
652*6dbdd20aSAndroid Build Coastguard Worker  adb_check_output(['adb', 'shell', 'rm', profile_config_path])
653*6dbdd20aSAndroid Build Coastguard Worker  adb_check_output(['adb', 'shell', 'rm', profile_device_path])
654*6dbdd20aSAndroid Build Coastguard Worker
655*6dbdd20aSAndroid Build Coastguard Worker
656*6dbdd20aSAndroid Build Coastguard Workerdef get_traceconv():
657*6dbdd20aSAndroid Build Coastguard Worker  """Sets up and returns the path to `traceconv`."""
658*6dbdd20aSAndroid Build Coastguard Worker  try:
659*6dbdd20aSAndroid Build Coastguard Worker    traceconv = get_perfetto_prebuilt(TRACECONV_MANIFEST, soft_fail=True)
660*6dbdd20aSAndroid Build Coastguard Worker  except Exception as error:
661*6dbdd20aSAndroid Build Coastguard Worker    exit_with_bug_report(error)
662*6dbdd20aSAndroid Build Coastguard Worker  if traceconv is None:
663*6dbdd20aSAndroid Build Coastguard Worker    exit_with_bug_report(
664*6dbdd20aSAndroid Build Coastguard Worker        "Unable to download `traceconv` for symbolizing profiles.")
665*6dbdd20aSAndroid Build Coastguard Worker
666*6dbdd20aSAndroid Build Coastguard Worker  return traceconv
667*6dbdd20aSAndroid Build Coastguard Worker
668*6dbdd20aSAndroid Build Coastguard Worker
669*6dbdd20aSAndroid Build Coastguard Workerdef concatenate_files(files_to_concatenate, output_file):
670*6dbdd20aSAndroid Build Coastguard Worker  """Concatenates files.
671*6dbdd20aSAndroid Build Coastguard Worker
672*6dbdd20aSAndroid Build Coastguard Worker  Args:
673*6dbdd20aSAndroid Build Coastguard Worker    files_to_concatenate: Paths for input files to concatenate.
674*6dbdd20aSAndroid Build Coastguard Worker    output_file: Path to the resultant output file.
675*6dbdd20aSAndroid Build Coastguard Worker  """
676*6dbdd20aSAndroid Build Coastguard Worker  with open(output_file, 'wb') as output:
677*6dbdd20aSAndroid Build Coastguard Worker    for file in files_to_concatenate:
678*6dbdd20aSAndroid Build Coastguard Worker      with open(file, 'rb') as input:
679*6dbdd20aSAndroid Build Coastguard Worker        shutil.copyfileobj(input, output)
680*6dbdd20aSAndroid Build Coastguard Worker
681*6dbdd20aSAndroid Build Coastguard Worker
682*6dbdd20aSAndroid Build Coastguard Workerdef symbolize_trace(traceconv, profile_target):
683*6dbdd20aSAndroid Build Coastguard Worker  """Attempts symbolization of the recorded trace/profile, if symbols are
684*6dbdd20aSAndroid Build Coastguard Worker  available.
685*6dbdd20aSAndroid Build Coastguard Worker
686*6dbdd20aSAndroid Build Coastguard Worker  Args:
687*6dbdd20aSAndroid Build Coastguard Worker    traceconv: The path to the `traceconv` binary used for symbolization.
688*6dbdd20aSAndroid Build Coastguard Worker    profile_target: The directory where the recorded trace was output.
689*6dbdd20aSAndroid Build Coastguard Worker
690*6dbdd20aSAndroid Build Coastguard Worker  Returns:
691*6dbdd20aSAndroid Build Coastguard Worker    The path to the symbolized trace file if symbolization was completed,
692*6dbdd20aSAndroid Build Coastguard Worker    and the original trace file, if it was not.
693*6dbdd20aSAndroid Build Coastguard Worker  """
694*6dbdd20aSAndroid Build Coastguard Worker  binary_path = os.getenv('PERFETTO_BINARY_PATH')
695*6dbdd20aSAndroid Build Coastguard Worker  trace_file = os.path.join(profile_target, 'raw-trace')
696*6dbdd20aSAndroid Build Coastguard Worker  files_to_concatenate = [trace_file]
697*6dbdd20aSAndroid Build Coastguard Worker
698*6dbdd20aSAndroid Build Coastguard Worker  if binary_path is not None:
699*6dbdd20aSAndroid Build Coastguard Worker    try:
700*6dbdd20aSAndroid Build Coastguard Worker      with open(os.path.join(profile_target, 'symbols'), 'w') as symbols_file:
701*6dbdd20aSAndroid Build Coastguard Worker        return_code = subprocess.call([traceconv, 'symbolize', trace_file],
702*6dbdd20aSAndroid Build Coastguard Worker                                      env=dict(
703*6dbdd20aSAndroid Build Coastguard Worker                                          os.environ,
704*6dbdd20aSAndroid Build Coastguard Worker                                          PERFETTO_BINARY_PATH=binary_path),
705*6dbdd20aSAndroid Build Coastguard Worker                                      stdout=symbols_file)
706*6dbdd20aSAndroid Build Coastguard Worker    except IOError as error:
707*6dbdd20aSAndroid Build Coastguard Worker      sys.exit("Unable to write symbols to disk: {}".format(error))
708*6dbdd20aSAndroid Build Coastguard Worker    if return_code == 0:
709*6dbdd20aSAndroid Build Coastguard Worker      files_to_concatenate.append(os.path.join(profile_target, 'symbols'))
710*6dbdd20aSAndroid Build Coastguard Worker    else:
711*6dbdd20aSAndroid Build Coastguard Worker      print("Failed to symbolize. Continuing without symbols.", file=sys.stderr)
712*6dbdd20aSAndroid Build Coastguard Worker
713*6dbdd20aSAndroid Build Coastguard Worker  if len(files_to_concatenate) > 1:
714*6dbdd20aSAndroid Build Coastguard Worker    trace_file = os.path.join(profile_target, 'symbolized-trace')
715*6dbdd20aSAndroid Build Coastguard Worker    try:
716*6dbdd20aSAndroid Build Coastguard Worker      concatenate_files(files_to_concatenate, trace_file)
717*6dbdd20aSAndroid Build Coastguard Worker    except Exception as error:
718*6dbdd20aSAndroid Build Coastguard Worker      sys.exit("Unable to write symbolized profile to disk: {}".format(error))
719*6dbdd20aSAndroid Build Coastguard Worker
720*6dbdd20aSAndroid Build Coastguard Worker  return trace_file
721*6dbdd20aSAndroid Build Coastguard Worker
722*6dbdd20aSAndroid Build Coastguard Worker
723*6dbdd20aSAndroid Build Coastguard Workerdef generate_pprof_profiles(traceconv, trace_file, args):
724*6dbdd20aSAndroid Build Coastguard Worker  """Generates pprof profiles from the recorded trace.
725*6dbdd20aSAndroid Build Coastguard Worker
726*6dbdd20aSAndroid Build Coastguard Worker  Args:
727*6dbdd20aSAndroid Build Coastguard Worker    traceconv: The path to the `traceconv` binary used for generating profiles.
728*6dbdd20aSAndroid Build Coastguard Worker    trace_file: The oath to the recorded and potentially symbolized trace file.
729*6dbdd20aSAndroid Build Coastguard Worker
730*6dbdd20aSAndroid Build Coastguard Worker  Returns:
731*6dbdd20aSAndroid Build Coastguard Worker    The directory where pprof profiles are output.
732*6dbdd20aSAndroid Build Coastguard Worker  """
733*6dbdd20aSAndroid Build Coastguard Worker  try:
734*6dbdd20aSAndroid Build Coastguard Worker    conversion_args = [traceconv, 'profile', '--perf'] + (
735*6dbdd20aSAndroid Build Coastguard Worker        ['--no-annotations'] if args.no_annotations else []) + [trace_file]
736*6dbdd20aSAndroid Build Coastguard Worker    traceconv_output = subprocess.check_output(conversion_args)
737*6dbdd20aSAndroid Build Coastguard Worker  except Exception as error:
738*6dbdd20aSAndroid Build Coastguard Worker    exit_with_bug_report(
739*6dbdd20aSAndroid Build Coastguard Worker        "Unable to extract profiles from trace: {}".format(error))
740*6dbdd20aSAndroid Build Coastguard Worker
741*6dbdd20aSAndroid Build Coastguard Worker  profiles_output_directory = None
742*6dbdd20aSAndroid Build Coastguard Worker  for word in traceconv_output.decode('utf-8').split():
743*6dbdd20aSAndroid Build Coastguard Worker    if 'perf_profile-' in word:
744*6dbdd20aSAndroid Build Coastguard Worker      profiles_output_directory = word
745*6dbdd20aSAndroid Build Coastguard Worker  if profiles_output_directory is None:
746*6dbdd20aSAndroid Build Coastguard Worker    exit_with_no_profile()
747*6dbdd20aSAndroid Build Coastguard Worker  return profiles_output_directory
748*6dbdd20aSAndroid Build Coastguard Worker
749*6dbdd20aSAndroid Build Coastguard Worker
750*6dbdd20aSAndroid Build Coastguard Workerdef copy_profiles_to_destination(profile_target, profile_path):
751*6dbdd20aSAndroid Build Coastguard Worker  """Copies recorded profiles to `profile_target` from `profile_path`."""
752*6dbdd20aSAndroid Build Coastguard Worker  profile_files = os.listdir(profile_path)
753*6dbdd20aSAndroid Build Coastguard Worker  if not profile_files:
754*6dbdd20aSAndroid Build Coastguard Worker    exit_with_no_profile()
755*6dbdd20aSAndroid Build Coastguard Worker
756*6dbdd20aSAndroid Build Coastguard Worker  try:
757*6dbdd20aSAndroid Build Coastguard Worker    for profile_file in profile_files:
758*6dbdd20aSAndroid Build Coastguard Worker      shutil.copy(os.path.join(profile_path, profile_file), profile_target)
759*6dbdd20aSAndroid Build Coastguard Worker  except Exception as error:
760*6dbdd20aSAndroid Build Coastguard Worker    sys.exit("Unable to copy profiles to {}: {}".format(profile_target, error))
761*6dbdd20aSAndroid Build Coastguard Worker
762*6dbdd20aSAndroid Build Coastguard Worker  print("Wrote profiles to {}".format(profile_target))
763*6dbdd20aSAndroid Build Coastguard Worker
764*6dbdd20aSAndroid Build Coastguard Worker
765*6dbdd20aSAndroid Build Coastguard Workerdef main(argv):
766*6dbdd20aSAndroid Build Coastguard Worker  args = parse_and_validate_args()
767*6dbdd20aSAndroid Build Coastguard Worker  profile_target = get_and_prepare_profile_target(args)
768*6dbdd20aSAndroid Build Coastguard Worker  trace_config = get_perfetto_config(args)
769*6dbdd20aSAndroid Build Coastguard Worker  if args.print_config:
770*6dbdd20aSAndroid Build Coastguard Worker    print(trace_config)
771*6dbdd20aSAndroid Build Coastguard Worker    return 0
772*6dbdd20aSAndroid Build Coastguard Worker  record_trace(trace_config, profile_target)
773*6dbdd20aSAndroid Build Coastguard Worker  traceconv = get_traceconv()
774*6dbdd20aSAndroid Build Coastguard Worker  trace_file = symbolize_trace(traceconv, profile_target)
775*6dbdd20aSAndroid Build Coastguard Worker  copy_profiles_to_destination(
776*6dbdd20aSAndroid Build Coastguard Worker      profile_target, generate_pprof_profiles(traceconv, trace_file, args))
777*6dbdd20aSAndroid Build Coastguard Worker  return 0
778*6dbdd20aSAndroid Build Coastguard Worker
779*6dbdd20aSAndroid Build Coastguard Worker
780*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__':
781*6dbdd20aSAndroid Build Coastguard Worker  sys.exit(main(sys.argv))
782