146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)# found in the LICENSE file. 446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)import logging 646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)import os 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import signal 846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)import subprocess 946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)import sys 1046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)import tempfile 1146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from profile_chrome import controllers 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from profile_chrome import ui 1446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 1546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)from pylib import android_commands 1646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)from pylib import constants 17f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)from pylib.perf import perf_control 1846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 1946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 2046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 'tools', 2146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 'telemetry')) 2246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)try: 2346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # pylint: disable=F0401 2446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) from telemetry.core.platform.profiler import android_profiling_helper 2546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) from telemetry.util import support_binaries 2646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)except ImportError: 2746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) android_profiling_helper = None 2846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) support_binaries = None 2946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 3046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 3146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)_PERF_OPTIONS = [ 3246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # Sample across all processes and CPUs to so that the current CPU gets 3346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # recorded to each sample. 3446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) '--all-cpus', 3546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # In perf 3.13 --call-graph requires an argument, so use the -g short-hand 3646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # which does not. 3746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) '-g', 3846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # Increase priority to avoid dropping samples. Requires root. 3946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) '--realtime', '80', 4046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # Record raw samples to get CPU information. 4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) '--raw-samples', 4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # Increase sampling frequency for better coverage. 4346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) '--freq', '2000', 4446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)] 4546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 4646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 4746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)class _PerfProfiler(object): 4846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def __init__(self, device, perf_binary, categories): 4946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._device = device 5046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._output_file = android_commands.DeviceTempFile( 5146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._device.old_interface, prefix='perf_output') 5246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._log_file = tempfile.TemporaryFile() 5346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # TODO(jbudorick) Look at providing a way to unhandroll this once the 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # adb rewrite has fully landed. 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) device_param = (['-s', str(self._device)] if str(self._device) else []) 5746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) cmd = ['adb'] + device_param + \ 5846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) ['shell', perf_binary, 'record', 5946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) '--output', self._output_file.name] + _PERF_OPTIONS 6046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if categories: 6146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) cmd += ['--event', ','.join(categories)] 62f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) self._perf_control = perf_control.PerfControl(self._device) 636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) self._perf_control.SetPerfProfilingMode() 6446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_process = subprocess.Popen(cmd, 6546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) stdout=self._log_file, 6646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) stderr=subprocess.STDOUT) 6746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 6846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def SignalAndWait(self): 69116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._device.KillAll('perf', signum=signal.SIGINT) 7046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_process.wait() 716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) self._perf_control.SetDefaultPerfMode() 7246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 7346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def _FailWithLog(self, msg): 7446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._log_file.seek(0) 7546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) log = self._log_file.read() 7646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) raise RuntimeError('%s. Log output:\n%s' % (msg, log)) 7746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 7846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def PullResult(self, output_path): 79116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if not self._device.FileExists(self._output_file.name): 8046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._FailWithLog('Perf recorded no data') 8146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 8246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) perf_profile = os.path.join(output_path, 8346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) os.path.basename(self._output_file.name)) 84116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self._device.PullFile(self._output_file.name, perf_profile) 8546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if not os.stat(perf_profile).st_size: 8646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) os.remove(perf_profile) 8746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._FailWithLog('Perf recorded a zero-sized file') 8846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 8946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._log_file.close() 9046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._output_file.close() 9146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return perf_profile 9246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 9346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 9446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)class PerfProfilerController(controllers.BaseController): 9546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def __init__(self, device, categories): 9646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) controllers.BaseController.__init__(self) 9746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._device = device 9846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._categories = categories 9946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_binary = self._PrepareDevice(device) 10046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_instance = None 10146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 10246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def __repr__(self): 10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return 'perf profile' 10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 10546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) @staticmethod 10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def IsSupported(): 10746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return bool(android_profiling_helper) 10846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 10946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) @staticmethod 11046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def _PrepareDevice(device): 11146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if not 'BUILDTYPE' in os.environ: 11246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) os.environ['BUILDTYPE'] = 'Release' 11346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return android_profiling_helper.PrepareDeviceForPerf(device) 11446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 11546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) @classmethod 11646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def GetCategories(cls, device): 11746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) perf_binary = cls._PrepareDevice(device) 118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return device.RunShellCommand('%s list' % perf_binary) 11946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 12046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def StartTracing(self, _): 12146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_instance = _PerfProfiler(self._device, 12246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_binary, 12346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._categories) 12446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 12546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def StopTracing(self): 12646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if not self._perf_instance: 12746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return 12846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) self._perf_instance.SignalAndWait() 12946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) @staticmethod 131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) def _GetInteractivePerfCommand(perfhost_path, perf_profile, symfs_dir, 132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) required_libs, kallsyms): 133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) cmd = '%s report -n -i %s --symfs %s --kallsyms %s' % ( 134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) os.path.relpath(perfhost_path, '.'), perf_profile, symfs_dir, kallsyms) 135f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) for lib in required_libs: 136f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) lib = os.path.join(symfs_dir, lib[1:]) 137f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if not os.path.exists(lib): 138f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) continue 139f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) objdump_path = android_profiling_helper.GetToolchainBinaryPath( 140f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) lib, 'objdump') 141f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if objdump_path: 142f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) cmd += ' --objdump %s' % os.path.relpath(objdump_path, '.') 143f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) break 144f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return cmd 145f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 14646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) def PullTrace(self): 14746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) symfs_dir = os.path.join(tempfile.gettempdir(), 14846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) os.path.expandvars('$USER-perf-symfs')) 14946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if not os.path.exists(symfs_dir): 15046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) os.makedirs(symfs_dir) 15146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) required_libs = set() 15246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 15346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # Download the recorded perf profile. 15446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) perf_profile = self._perf_instance.PullResult(symfs_dir) 15546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) required_libs = \ 15646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) android_profiling_helper.GetRequiredLibrariesForPerfProfile( 15746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) perf_profile) 15846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if not required_libs: 15946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) logging.warning('No libraries required by perf trace. Most likely there ' 16046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 'are no samples in the trace.') 16146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 16246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) # Build a symfs with all the necessary libraries. 16346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) kallsyms = android_profiling_helper.CreateSymFs(self._device, 16446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) symfs_dir, 16546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) required_libs, 16646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) use_symlinks=False) 1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci perfhost_path = support_binaries.FindPath( 1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci android_profiling_helper.GetPerfhostName(), 'linux') 169f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) ui.PrintMessage('\nNote: to view the profile in perf, run:') 171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) ui.PrintMessage(' ' + self._GetInteractivePerfCommand(perfhost_path, 172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) perf_profile, symfs_dir, required_libs, kallsyms)) 173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) # Convert the perf profile into JSON. 1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) perf_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'third_party', 'perf_to_tracing.py') 17746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) json_file_name = os.path.basename(perf_profile) 17846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) with open(os.devnull, 'w') as dev_null, \ 17946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) open(json_file_name, 'w') as json_file: 18046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) cmd = [perfhost_path, 'script', '-s', perf_script_path, '-i', 18146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) perf_profile, '--symfs', symfs_dir, '--kallsyms', kallsyms] 182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if subprocess.call(cmd, stdout=json_file, stderr=dev_null): 183f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) logging.warning('Perf data to JSON conversion failed. The result will ' 184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 'not contain any perf samples. You can still view the ' 185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 'perf data manually as shown above.') 186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return None 187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 18846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return json_file_name 189