xref: /aosp_15_r20/external/angle/build/android/pylib/utils/simpleperf.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2018 The Chromium Authors
2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
4*8975f5c5SAndroid Build Coastguard Worker
5*8975f5c5SAndroid Build Coastguard Workerimport contextlib
6*8975f5c5SAndroid Build Coastguard Workerimport logging
7*8975f5c5SAndroid Build Coastguard Workerimport os
8*8975f5c5SAndroid Build Coastguard Workerimport shutil
9*8975f5c5SAndroid Build Coastguard Workerimport subprocess
10*8975f5c5SAndroid Build Coastguard Workerimport sys
11*8975f5c5SAndroid Build Coastguard Workerimport tempfile
12*8975f5c5SAndroid Build Coastguard Worker
13*8975f5c5SAndroid Build Coastguard Workerfrom devil import devil_env
14*8975f5c5SAndroid Build Coastguard Workerfrom devil.android import device_signal, device_errors
15*8975f5c5SAndroid Build Coastguard Workerfrom devil.android.sdk import version_codes
16*8975f5c5SAndroid Build Coastguard Workerfrom pylib import constants
17*8975f5c5SAndroid Build Coastguard Worker
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard Workerdef _ProcessType(proc):
20*8975f5c5SAndroid Build Coastguard Worker  _, _, suffix = proc.name.partition(':')
21*8975f5c5SAndroid Build Coastguard Worker  if not suffix:
22*8975f5c5SAndroid Build Coastguard Worker    return 'browser'
23*8975f5c5SAndroid Build Coastguard Worker  if suffix.startswith('sandboxed_process'):
24*8975f5c5SAndroid Build Coastguard Worker    return 'renderer'
25*8975f5c5SAndroid Build Coastguard Worker  if suffix.startswith('privileged_process'):
26*8975f5c5SAndroid Build Coastguard Worker    return 'gpu'
27*8975f5c5SAndroid Build Coastguard Worker  return None
28*8975f5c5SAndroid Build Coastguard Worker
29*8975f5c5SAndroid Build Coastguard Worker
30*8975f5c5SAndroid Build Coastguard Workerdef _GetSpecifiedPID(device, package_name, process_specifier):
31*8975f5c5SAndroid Build Coastguard Worker  if process_specifier is None:
32*8975f5c5SAndroid Build Coastguard Worker    return None
33*8975f5c5SAndroid Build Coastguard Worker
34*8975f5c5SAndroid Build Coastguard Worker  # Check for numeric PID
35*8975f5c5SAndroid Build Coastguard Worker  try:
36*8975f5c5SAndroid Build Coastguard Worker    pid = int(process_specifier)
37*8975f5c5SAndroid Build Coastguard Worker    return pid
38*8975f5c5SAndroid Build Coastguard Worker  except ValueError:
39*8975f5c5SAndroid Build Coastguard Worker    pass
40*8975f5c5SAndroid Build Coastguard Worker
41*8975f5c5SAndroid Build Coastguard Worker  # Check for exact process name; can be any of these formats:
42*8975f5c5SAndroid Build Coastguard Worker  #   <package>:<process name>, i.e. 'org.chromium.chrome:sandboxed_process0'
43*8975f5c5SAndroid Build Coastguard Worker  #   :<process name>, i.e. ':sandboxed_process0'
44*8975f5c5SAndroid Build Coastguard Worker  #   <process name>, i.e. 'sandboxed_process0'
45*8975f5c5SAndroid Build Coastguard Worker  full_process_name = process_specifier
46*8975f5c5SAndroid Build Coastguard Worker  if process_specifier.startswith(':'):
47*8975f5c5SAndroid Build Coastguard Worker    full_process_name = package_name + process_specifier
48*8975f5c5SAndroid Build Coastguard Worker  elif ':' not in process_specifier:
49*8975f5c5SAndroid Build Coastguard Worker    full_process_name = '%s:%s' % (package_name, process_specifier)
50*8975f5c5SAndroid Build Coastguard Worker  matching_processes = device.ListProcesses(full_process_name)
51*8975f5c5SAndroid Build Coastguard Worker  if len(matching_processes) == 1:
52*8975f5c5SAndroid Build Coastguard Worker    return matching_processes[0].pid
53*8975f5c5SAndroid Build Coastguard Worker  if len(matching_processes) > 1:
54*8975f5c5SAndroid Build Coastguard Worker    raise RuntimeError('Found %d processes with name "%s".' % (
55*8975f5c5SAndroid Build Coastguard Worker        len(matching_processes), process_specifier))
56*8975f5c5SAndroid Build Coastguard Worker
57*8975f5c5SAndroid Build Coastguard Worker  # Check for process type (i.e. 'renderer')
58*8975f5c5SAndroid Build Coastguard Worker  package_processes = device.ListProcesses(package_name)
59*8975f5c5SAndroid Build Coastguard Worker  matching_processes = [p for p in package_processes if (
60*8975f5c5SAndroid Build Coastguard Worker      _ProcessType(p) == process_specifier)]
61*8975f5c5SAndroid Build Coastguard Worker  if process_specifier == 'renderer' and len(matching_processes) > 1:
62*8975f5c5SAndroid Build Coastguard Worker    raise RuntimeError('Found %d renderer processes; please re-run with only '
63*8975f5c5SAndroid Build Coastguard Worker                       'one open tab.' % len(matching_processes))
64*8975f5c5SAndroid Build Coastguard Worker  if len(matching_processes) != 1:
65*8975f5c5SAndroid Build Coastguard Worker    raise RuntimeError('Found %d processes of type "%s".' % (
66*8975f5c5SAndroid Build Coastguard Worker        len(matching_processes), process_specifier))
67*8975f5c5SAndroid Build Coastguard Worker  return matching_processes[0].pid
68*8975f5c5SAndroid Build Coastguard Worker
69*8975f5c5SAndroid Build Coastguard Worker
70*8975f5c5SAndroid Build Coastguard Workerdef _ThreadsForProcess(device, pid):
71*8975f5c5SAndroid Build Coastguard Worker  # The thread list output format for 'ps' is the same regardless of version.
72*8975f5c5SAndroid Build Coastguard Worker  # Here's the column headers, and a sample line for a thread belonging to
73*8975f5c5SAndroid Build Coastguard Worker  # pid 12345 (note that the last few columns are not aligned with headers):
74*8975f5c5SAndroid Build Coastguard Worker  #
75*8975f5c5SAndroid Build Coastguard Worker  # USER        PID   TID  PPID     VSZ    RSS WCHAN            ADDR S CMD
76*8975f5c5SAndroid Build Coastguard Worker  # u0_i101   12345 24680   567 1357902  97531 futex_wait_queue_me e85acd9c S \
77*8975f5c5SAndroid Build Coastguard Worker  #     CrRendererMain
78*8975f5c5SAndroid Build Coastguard Worker  if device.build_version_sdk >= version_codes.OREO:
79*8975f5c5SAndroid Build Coastguard Worker    pid_regex = (
80*8975f5c5SAndroid Build Coastguard Worker        r'^[[:graph:]]\{1,\}[[:blank:]]\{1,\}%d[[:blank:]]\{1,\}' % pid)
81*8975f5c5SAndroid Build Coastguard Worker    ps_cmd = "ps -T -e | grep '%s'" % pid_regex
82*8975f5c5SAndroid Build Coastguard Worker    ps_output_lines = device.RunShellCommand(
83*8975f5c5SAndroid Build Coastguard Worker        ps_cmd, shell=True, check_return=True)
84*8975f5c5SAndroid Build Coastguard Worker  else:
85*8975f5c5SAndroid Build Coastguard Worker    ps_cmd = ['ps', '-p', str(pid), '-t']
86*8975f5c5SAndroid Build Coastguard Worker    ps_output_lines = device.RunShellCommand(ps_cmd, check_return=True)
87*8975f5c5SAndroid Build Coastguard Worker  result = []
88*8975f5c5SAndroid Build Coastguard Worker  for l in ps_output_lines:
89*8975f5c5SAndroid Build Coastguard Worker    fields = l.split()
90*8975f5c5SAndroid Build Coastguard Worker    # fields[2] is tid, fields[-1] is thread name. Output may include an entry
91*8975f5c5SAndroid Build Coastguard Worker    # for the process itself with tid=pid; omit that one.
92*8975f5c5SAndroid Build Coastguard Worker    if fields[2] == str(pid):
93*8975f5c5SAndroid Build Coastguard Worker      continue
94*8975f5c5SAndroid Build Coastguard Worker    result.append((int(fields[2]), fields[-1]))
95*8975f5c5SAndroid Build Coastguard Worker  return result
96*8975f5c5SAndroid Build Coastguard Worker
97*8975f5c5SAndroid Build Coastguard Worker
98*8975f5c5SAndroid Build Coastguard Workerdef _ThreadType(thread_name):
99*8975f5c5SAndroid Build Coastguard Worker  if not thread_name:
100*8975f5c5SAndroid Build Coastguard Worker    return 'unknown'
101*8975f5c5SAndroid Build Coastguard Worker  if (thread_name.startswith('Chrome_ChildIO') or
102*8975f5c5SAndroid Build Coastguard Worker      thread_name.startswith('Chrome_IO')):
103*8975f5c5SAndroid Build Coastguard Worker    return 'io'
104*8975f5c5SAndroid Build Coastguard Worker  if thread_name.startswith('Compositor'):
105*8975f5c5SAndroid Build Coastguard Worker    return 'compositor'
106*8975f5c5SAndroid Build Coastguard Worker  if (thread_name.startswith('ChildProcessMai') or
107*8975f5c5SAndroid Build Coastguard Worker      thread_name.startswith('CrGpuMain') or
108*8975f5c5SAndroid Build Coastguard Worker      thread_name.startswith('CrRendererMain')):
109*8975f5c5SAndroid Build Coastguard Worker    return 'main'
110*8975f5c5SAndroid Build Coastguard Worker  if thread_name.startswith('RenderThread'):
111*8975f5c5SAndroid Build Coastguard Worker    return 'render'
112*8975f5c5SAndroid Build Coastguard Worker  raise ValueError('got no matching thread_name')
113*8975f5c5SAndroid Build Coastguard Worker
114*8975f5c5SAndroid Build Coastguard Worker
115*8975f5c5SAndroid Build Coastguard Workerdef _GetSpecifiedTID(device, pid, thread_specifier):
116*8975f5c5SAndroid Build Coastguard Worker  if thread_specifier is None:
117*8975f5c5SAndroid Build Coastguard Worker    return None
118*8975f5c5SAndroid Build Coastguard Worker
119*8975f5c5SAndroid Build Coastguard Worker  # Check for numeric TID
120*8975f5c5SAndroid Build Coastguard Worker  try:
121*8975f5c5SAndroid Build Coastguard Worker    tid = int(thread_specifier)
122*8975f5c5SAndroid Build Coastguard Worker    return tid
123*8975f5c5SAndroid Build Coastguard Worker  except ValueError:
124*8975f5c5SAndroid Build Coastguard Worker    pass
125*8975f5c5SAndroid Build Coastguard Worker
126*8975f5c5SAndroid Build Coastguard Worker  # Check for thread type
127*8975f5c5SAndroid Build Coastguard Worker  if pid is not None:
128*8975f5c5SAndroid Build Coastguard Worker    matching_threads = [t for t in _ThreadsForProcess(device, pid) if (
129*8975f5c5SAndroid Build Coastguard Worker        _ThreadType(t[1]) == thread_specifier)]
130*8975f5c5SAndroid Build Coastguard Worker    if len(matching_threads) != 1:
131*8975f5c5SAndroid Build Coastguard Worker      raise RuntimeError('Found %d threads of type "%s".' % (
132*8975f5c5SAndroid Build Coastguard Worker          len(matching_threads), thread_specifier))
133*8975f5c5SAndroid Build Coastguard Worker    return matching_threads[0][0]
134*8975f5c5SAndroid Build Coastguard Worker
135*8975f5c5SAndroid Build Coastguard Worker  return None
136*8975f5c5SAndroid Build Coastguard Worker
137*8975f5c5SAndroid Build Coastguard Worker
138*8975f5c5SAndroid Build Coastguard Workerdef PrepareDevice(device):
139*8975f5c5SAndroid Build Coastguard Worker  if device.build_version_sdk < version_codes.NOUGAT:
140*8975f5c5SAndroid Build Coastguard Worker    raise RuntimeError('Simpleperf profiling is only supported on Android N '
141*8975f5c5SAndroid Build Coastguard Worker                       'and later.')
142*8975f5c5SAndroid Build Coastguard Worker
143*8975f5c5SAndroid Build Coastguard Worker  # Necessary for profiling
144*8975f5c5SAndroid Build Coastguard Worker  # https://android-review.googlesource.com/c/platform/system/sepolicy/+/234400
145*8975f5c5SAndroid Build Coastguard Worker  device.SetProp('security.perf_harden', '0')
146*8975f5c5SAndroid Build Coastguard Worker
147*8975f5c5SAndroid Build Coastguard Worker
148*8975f5c5SAndroid Build Coastguard Workerdef InstallSimpleperf(device, package_name):
149*8975f5c5SAndroid Build Coastguard Worker  package_arch = device.GetPackageArchitecture(package_name) or 'armeabi-v7a'
150*8975f5c5SAndroid Build Coastguard Worker  host_simpleperf_path = devil_env.config.LocalPath('simpleperf', package_arch)
151*8975f5c5SAndroid Build Coastguard Worker  if not host_simpleperf_path:
152*8975f5c5SAndroid Build Coastguard Worker    raise Exception('Could not get path to simpleperf executable on host.')
153*8975f5c5SAndroid Build Coastguard Worker  device_simpleperf_path = '/'.join(
154*8975f5c5SAndroid Build Coastguard Worker      ('/data/local/tmp/profilers', package_arch, 'simpleperf'))
155*8975f5c5SAndroid Build Coastguard Worker  device.PushChangedFiles([(host_simpleperf_path, device_simpleperf_path)])
156*8975f5c5SAndroid Build Coastguard Worker  return device_simpleperf_path
157*8975f5c5SAndroid Build Coastguard Worker
158*8975f5c5SAndroid Build Coastguard Worker
159*8975f5c5SAndroid Build Coastguard Worker@contextlib.contextmanager
160*8975f5c5SAndroid Build Coastguard Workerdef RunSimpleperf(device, device_simpleperf_path, package_name,
161*8975f5c5SAndroid Build Coastguard Worker                  process_specifier, thread_specifier, events,
162*8975f5c5SAndroid Build Coastguard Worker                  profiler_args, host_out_path):
163*8975f5c5SAndroid Build Coastguard Worker  pid = _GetSpecifiedPID(device, package_name, process_specifier)
164*8975f5c5SAndroid Build Coastguard Worker  tid = _GetSpecifiedTID(device, pid, thread_specifier)
165*8975f5c5SAndroid Build Coastguard Worker  if pid is None and tid is None:
166*8975f5c5SAndroid Build Coastguard Worker    raise RuntimeError('Could not find specified process/thread running on '
167*8975f5c5SAndroid Build Coastguard Worker                       'device. Make sure the apk is already running before '
168*8975f5c5SAndroid Build Coastguard Worker                       'attempting to profile.')
169*8975f5c5SAndroid Build Coastguard Worker  profiler_args = list(profiler_args)
170*8975f5c5SAndroid Build Coastguard Worker  if profiler_args and profiler_args[0] == 'record':
171*8975f5c5SAndroid Build Coastguard Worker    profiler_args.pop(0)
172*8975f5c5SAndroid Build Coastguard Worker  profiler_args.extend(('-e', events))
173*8975f5c5SAndroid Build Coastguard Worker  if '--call-graph' not in profiler_args and '-g' not in profiler_args:
174*8975f5c5SAndroid Build Coastguard Worker    profiler_args.append('-g')
175*8975f5c5SAndroid Build Coastguard Worker  if '-f' not in profiler_args:
176*8975f5c5SAndroid Build Coastguard Worker    profiler_args.extend(('-f', '1000'))
177*8975f5c5SAndroid Build Coastguard Worker
178*8975f5c5SAndroid Build Coastguard Worker  device_out_path = '/data/local/tmp/perf.data'
179*8975f5c5SAndroid Build Coastguard Worker  should_remove_device_out_path = True
180*8975f5c5SAndroid Build Coastguard Worker  if '-o' in profiler_args:
181*8975f5c5SAndroid Build Coastguard Worker    device_out_path = profiler_args[profiler_args.index('-o') + 1]
182*8975f5c5SAndroid Build Coastguard Worker    should_remove_device_out_path = False
183*8975f5c5SAndroid Build Coastguard Worker  else:
184*8975f5c5SAndroid Build Coastguard Worker    profiler_args.extend(('-o', device_out_path))
185*8975f5c5SAndroid Build Coastguard Worker
186*8975f5c5SAndroid Build Coastguard Worker  # Remove the default output to avoid confusion if simpleperf opts not
187*8975f5c5SAndroid Build Coastguard Worker  # to update the file.
188*8975f5c5SAndroid Build Coastguard Worker  file_exists = True
189*8975f5c5SAndroid Build Coastguard Worker  try:
190*8975f5c5SAndroid Build Coastguard Worker      device.adb.Shell('readlink -e ' + device_out_path)
191*8975f5c5SAndroid Build Coastguard Worker  except device_errors.AdbCommandFailedError:
192*8975f5c5SAndroid Build Coastguard Worker    file_exists = False
193*8975f5c5SAndroid Build Coastguard Worker  if file_exists:
194*8975f5c5SAndroid Build Coastguard Worker    logging.warning('%s output file already exists on device', device_out_path)
195*8975f5c5SAndroid Build Coastguard Worker    if not should_remove_device_out_path:
196*8975f5c5SAndroid Build Coastguard Worker      raise RuntimeError('Specified output file \'{}\' already exists, not '
197*8975f5c5SAndroid Build Coastguard Worker                         'continuing'.format(device_out_path))
198*8975f5c5SAndroid Build Coastguard Worker  device.adb.Shell('rm -f ' + device_out_path)
199*8975f5c5SAndroid Build Coastguard Worker
200*8975f5c5SAndroid Build Coastguard Worker  if tid:
201*8975f5c5SAndroid Build Coastguard Worker    profiler_args.extend(('-t', str(tid)))
202*8975f5c5SAndroid Build Coastguard Worker  else:
203*8975f5c5SAndroid Build Coastguard Worker    profiler_args.extend(('-p', str(pid)))
204*8975f5c5SAndroid Build Coastguard Worker
205*8975f5c5SAndroid Build Coastguard Worker  adb_shell_simpleperf_process = device.adb.StartShell(
206*8975f5c5SAndroid Build Coastguard Worker      [device_simpleperf_path, 'record'] + profiler_args)
207*8975f5c5SAndroid Build Coastguard Worker
208*8975f5c5SAndroid Build Coastguard Worker  completed = False
209*8975f5c5SAndroid Build Coastguard Worker  try:
210*8975f5c5SAndroid Build Coastguard Worker    yield
211*8975f5c5SAndroid Build Coastguard Worker    completed = True
212*8975f5c5SAndroid Build Coastguard Worker
213*8975f5c5SAndroid Build Coastguard Worker  finally:
214*8975f5c5SAndroid Build Coastguard Worker    device.KillAll('simpleperf', signum=device_signal.SIGINT, blocking=True,
215*8975f5c5SAndroid Build Coastguard Worker                   quiet=True)
216*8975f5c5SAndroid Build Coastguard Worker    if completed:
217*8975f5c5SAndroid Build Coastguard Worker      adb_shell_simpleperf_process.wait()
218*8975f5c5SAndroid Build Coastguard Worker      ret = adb_shell_simpleperf_process.returncode
219*8975f5c5SAndroid Build Coastguard Worker      if ret == 0:
220*8975f5c5SAndroid Build Coastguard Worker        # Successfully gathered a profile
221*8975f5c5SAndroid Build Coastguard Worker        device.PullFile(device_out_path, host_out_path)
222*8975f5c5SAndroid Build Coastguard Worker      else:
223*8975f5c5SAndroid Build Coastguard Worker        logging.warning(
224*8975f5c5SAndroid Build Coastguard Worker            'simpleperf exited unusually, expected exit 0, got %d', ret
225*8975f5c5SAndroid Build Coastguard Worker        )
226*8975f5c5SAndroid Build Coastguard Worker        stdout, stderr = adb_shell_simpleperf_process.communicate()
227*8975f5c5SAndroid Build Coastguard Worker        logging.info('stdout: \'%s\', stderr: \'%s\'', stdout, stderr)
228*8975f5c5SAndroid Build Coastguard Worker        raise RuntimeError('simpleperf exited with unexpected code {} '
229*8975f5c5SAndroid Build Coastguard Worker                           '(run with -vv for full stdout/stderr)'.format(ret))
230*8975f5c5SAndroid Build Coastguard Worker
231*8975f5c5SAndroid Build Coastguard Worker
232*8975f5c5SAndroid Build Coastguard Workerdef ConvertSimpleperfToPprof(simpleperf_out_path, build_directory,
233*8975f5c5SAndroid Build Coastguard Worker                             pprof_out_path):
234*8975f5c5SAndroid Build Coastguard Worker  # The simpleperf scripts require the unstripped libs to be installed in the
235*8975f5c5SAndroid Build Coastguard Worker  # same directory structure as the libs on the device. Much of the logic here
236*8975f5c5SAndroid Build Coastguard Worker  # is just figuring out and creating the necessary directory structure, and
237*8975f5c5SAndroid Build Coastguard Worker  # symlinking the unstripped shared libs.
238*8975f5c5SAndroid Build Coastguard Worker
239*8975f5c5SAndroid Build Coastguard Worker  # Get the set of libs that we can symbolize
240*8975f5c5SAndroid Build Coastguard Worker  unstripped_lib_dir = os.path.join(build_directory, 'lib.unstripped')
241*8975f5c5SAndroid Build Coastguard Worker  unstripped_libs = set(
242*8975f5c5SAndroid Build Coastguard Worker      f for f in os.listdir(unstripped_lib_dir) if f.endswith('.so'))
243*8975f5c5SAndroid Build Coastguard Worker
244*8975f5c5SAndroid Build Coastguard Worker  # report.py will show the directory structure above the shared libs;
245*8975f5c5SAndroid Build Coastguard Worker  # that is the directory structure we need to recreate on the host.
246*8975f5c5SAndroid Build Coastguard Worker  script_dir = devil_env.config.LocalPath('simpleperf_scripts')
247*8975f5c5SAndroid Build Coastguard Worker  report_path = os.path.join(script_dir, 'report.py')
248*8975f5c5SAndroid Build Coastguard Worker  report_cmd = [sys.executable, report_path, '-i', simpleperf_out_path]
249*8975f5c5SAndroid Build Coastguard Worker  device_lib_path = None
250*8975f5c5SAndroid Build Coastguard Worker  output = subprocess.check_output(report_cmd, stderr=subprocess.STDOUT)
251*8975f5c5SAndroid Build Coastguard Worker  if isinstance(output, bytes):
252*8975f5c5SAndroid Build Coastguard Worker    output = output.decode()
253*8975f5c5SAndroid Build Coastguard Worker  for line in output.splitlines():
254*8975f5c5SAndroid Build Coastguard Worker    fields = line.split()
255*8975f5c5SAndroid Build Coastguard Worker    if len(fields) < 5:
256*8975f5c5SAndroid Build Coastguard Worker      continue
257*8975f5c5SAndroid Build Coastguard Worker    shlib_path = fields[4]
258*8975f5c5SAndroid Build Coastguard Worker    shlib_dirname, shlib_basename = shlib_path.rpartition('/')[::2]
259*8975f5c5SAndroid Build Coastguard Worker    if shlib_basename in unstripped_libs:
260*8975f5c5SAndroid Build Coastguard Worker      device_lib_path = shlib_dirname
261*8975f5c5SAndroid Build Coastguard Worker      break
262*8975f5c5SAndroid Build Coastguard Worker  if not device_lib_path:
263*8975f5c5SAndroid Build Coastguard Worker    raise RuntimeError('No chrome-related symbols in profiling data in %s. '
264*8975f5c5SAndroid Build Coastguard Worker                       'Either the process was idle for the entire profiling '
265*8975f5c5SAndroid Build Coastguard Worker                       'period, or something went very wrong (and you should '
266*8975f5c5SAndroid Build Coastguard Worker                       'file a bug at crbug.com/new with component '
267*8975f5c5SAndroid Build Coastguard Worker                       'Speed>Tracing, and assign it to [email protected]).'
268*8975f5c5SAndroid Build Coastguard Worker                       % simpleperf_out_path)
269*8975f5c5SAndroid Build Coastguard Worker
270*8975f5c5SAndroid Build Coastguard Worker  # Recreate the directory structure locally, and symlink unstripped libs.
271*8975f5c5SAndroid Build Coastguard Worker  processing_dir = tempfile.mkdtemp()
272*8975f5c5SAndroid Build Coastguard Worker  try:
273*8975f5c5SAndroid Build Coastguard Worker    processing_lib_dir = os.path.join(
274*8975f5c5SAndroid Build Coastguard Worker        processing_dir, 'binary_cache', device_lib_path.lstrip('/'))
275*8975f5c5SAndroid Build Coastguard Worker    os.makedirs(processing_lib_dir)
276*8975f5c5SAndroid Build Coastguard Worker    for lib in unstripped_libs:
277*8975f5c5SAndroid Build Coastguard Worker      unstripped_lib_path = os.path.join(unstripped_lib_dir, lib)
278*8975f5c5SAndroid Build Coastguard Worker      processing_lib_path = os.path.join(processing_lib_dir, lib)
279*8975f5c5SAndroid Build Coastguard Worker      os.symlink(unstripped_lib_path, processing_lib_path)
280*8975f5c5SAndroid Build Coastguard Worker
281*8975f5c5SAndroid Build Coastguard Worker    # Run the script to annotate symbols and convert from simpleperf format to
282*8975f5c5SAndroid Build Coastguard Worker    # pprof format.
283*8975f5c5SAndroid Build Coastguard Worker    pprof_converter_script = os.path.join(
284*8975f5c5SAndroid Build Coastguard Worker        script_dir, 'pprof_proto_generator.py')
285*8975f5c5SAndroid Build Coastguard Worker    pprof_converter_cmd = [
286*8975f5c5SAndroid Build Coastguard Worker        sys.executable, pprof_converter_script, '-i', simpleperf_out_path, '-o',
287*8975f5c5SAndroid Build Coastguard Worker        os.path.abspath(pprof_out_path), '--ndk_path',
288*8975f5c5SAndroid Build Coastguard Worker        constants.ANDROID_NDK_ROOT
289*8975f5c5SAndroid Build Coastguard Worker    ]
290*8975f5c5SAndroid Build Coastguard Worker    subprocess.check_output(pprof_converter_cmd, stderr=subprocess.STDOUT,
291*8975f5c5SAndroid Build Coastguard Worker                            cwd=processing_dir)
292*8975f5c5SAndroid Build Coastguard Worker  finally:
293*8975f5c5SAndroid Build Coastguard Worker    shutil.rmtree(processing_dir, ignore_errors=True)
294