Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import logging | |
| 6 import os | |
| 7 import subprocess | |
| 8 import sys | |
| 9 import tempfile | |
| 10 | |
| 11 from chrome_profiler import controllers | |
| 12 | |
| 13 from pylib import android_commands | |
| 14 from pylib import constants | |
| 15 | |
| 16 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, | |
| 17 'tools', | |
| 18 'telemetry')) | |
| 19 try: | |
| 20 # pylint: disable=F0401 | |
| 21 from telemetry.core.platform.profiler import android_profiling_helper | |
| 22 from telemetry.util import support_binaries | |
| 23 except ImportError: | |
| 24 android_profiling_helper = None | |
| 25 support_binaries = None | |
| 26 | |
| 27 | |
| 28 _PERF_OPTIONS = [ | |
| 29 '--all-cpus', | |
| 30 '--call-graph', | |
| 31 '--realtime', '80', | |
| 32 '--raw-samples', | |
| 33 '--freq', '2000', | |
| 34 ] | |
| 35 | |
| 36 | |
| 37 class _PerfProfilerInstance(object): | |
|
bulach
2014/06/04 10:34:12
nit: how about just _PerfProfiler? or _PerfProfile
Sami
2014/06/04 15:16:59
_PerfProfiler sgtm.
| |
| 38 def __init__(self, device, perf_binary, categories): | |
| 39 self._device = device | |
| 40 self._output_file = android_commands.DeviceTempFile( | |
| 41 self._device.old_interface, prefix='perf_output') | |
| 42 self._log_file = tempfile.TemporaryFile() | |
| 43 | |
| 44 device_param = (['-s', self._device.old_interface.GetDevice()] | |
| 45 if self._device.old_interface.GetDevice() else []) | |
| 46 cmd = ['adb'] + device_param + \ | |
| 47 ['shell', perf_binary, 'record', | |
| 48 '--output', self._output_file.name] + _PERF_OPTIONS | |
| 49 if categories: | |
| 50 cmd += ['--event', ','.join(categories)] | |
| 51 self._perf_process = subprocess.Popen(cmd, | |
| 52 stdout=self._log_file, | |
| 53 stderr=subprocess.STDOUT) | |
| 54 | |
| 55 def SignalAndWait(self): | |
| 56 perf_pids = self._device.old_interface.ExtractPid('perf') | |
| 57 self._device.old_interface.RunShellCommand( | |
| 58 'kill -SIGINT ' + ' '.join(perf_pids)) | |
| 59 self._perf_process.wait() | |
| 60 | |
| 61 def _FailWithLog(self, msg): | |
| 62 self._log_file.seek(0) | |
| 63 log = self._log_file.read() | |
| 64 raise RuntimeError('%s. Log output:\n%s' % (msg, log)) | |
| 65 | |
| 66 def PullResult(self, output_path): | |
| 67 if not self._device.old_interface.FileExistsOnDevice( | |
| 68 self._output_file.name): | |
| 69 self._FailWithLog('Perf recorded no data') | |
| 70 | |
| 71 perf_profile = os.path.join(output_path, | |
| 72 os.path.basename(self._output_file.name)) | |
| 73 self._device.old_interface.PullFileFromDevice(self._output_file.name, | |
| 74 perf_profile) | |
| 75 if not os.stat(perf_profile).st_size: | |
| 76 os.remove(perf_profile) | |
| 77 self._FailWithLog('Perf recorded a zero-sized file') | |
| 78 | |
| 79 self._log_file.close() | |
| 80 self._output_file.close() | |
| 81 return perf_profile | |
| 82 | |
| 83 | |
| 84 class PerfProfilerController(controllers.BaseController): | |
| 85 def __init__(self, device, categories): | |
| 86 controllers.BaseController.__init__(self) | |
| 87 self._device = device | |
| 88 self._categories = categories | |
| 89 self._perf_binary = self._PrepareDevice(device) | |
| 90 self._perf_instance = None | |
| 91 | |
| 92 def __repr__(self): | |
| 93 return 'perf profile' | |
| 94 | |
| 95 @staticmethod | |
| 96 def IsSupported(): | |
| 97 return bool(android_profiling_helper) | |
| 98 | |
| 99 @staticmethod | |
| 100 def _PrepareDevice(device): | |
| 101 if not 'BUILDTYPE' in os.environ: | |
| 102 os.environ['BUILDTYPE'] = 'Release' | |
| 103 return android_profiling_helper.PrepareDeviceForPerf(device) | |
| 104 | |
| 105 @classmethod | |
| 106 def GetCategories(cls, device): | |
| 107 perf_binary = cls._PrepareDevice(device) | |
| 108 return device.old_interface.RunShellCommand('%s list' % perf_binary) | |
| 109 | |
| 110 def StartTracing(self, _): | |
| 111 self._perf_instance = _PerfProfilerInstance(self._device, | |
| 112 self._perf_binary, | |
| 113 self._categories) | |
| 114 | |
| 115 def StopTracing(self): | |
| 116 if not self._perf_instance: | |
| 117 return | |
| 118 self._perf_instance.SignalAndWait() | |
| 119 | |
| 120 def PullTrace(self): | |
| 121 symfs_dir = os.path.join(tempfile.gettempdir(), | |
| 122 os.path.expandvars('$USER-perf-symfs')) | |
| 123 if not os.path.exists(symfs_dir): | |
| 124 os.makedirs(symfs_dir) | |
| 125 required_libs = set() | |
| 126 | |
| 127 # Download the recorded perf profile. | |
| 128 perf_profile = self._perf_instance.PullResult(symfs_dir) | |
| 129 required_libs = \ | |
| 130 android_profiling_helper.GetRequiredLibrariesForPerfProfile( | |
| 131 perf_profile) | |
| 132 if not required_libs: | |
| 133 logging.warning('No libraries required by perf trace. Most likely there ' | |
| 134 'are no samples in the trace.') | |
| 135 | |
| 136 # Build a symfs with all the necessary libraries. | |
| 137 kallsyms = android_profiling_helper.CreateSymFs(self._device, | |
| 138 symfs_dir, | |
| 139 required_libs, | |
| 140 use_symlinks=False) | |
| 141 # Convert the perf profile into JSON. | |
| 142 perfhost_path = \ | |
|
bulach
2014/06/04 10:34:12
nit: avoid \, something like:
perfhost_path = os.
Sami
2014/06/04 15:16:59
Well spotted, done. I just got used to having long
| |
| 143 os.path.abspath(support_binaries.FindPath('perfhost', 'linux')) | |
| 144 perf_script_path = \ | |
|
bulach
2014/06/04 10:34:12
ditto..
Sami
2014/06/04 15:16:59
Done.
| |
| 145 os.path.join(constants.DIR_SOURCE_ROOT, | |
| 146 'tools', 'telemetry', 'telemetry', 'core', 'platform', | |
| 147 'profiler', 'perf_vis', 'perf_to_tracing.py') | |
| 148 json_file_name = os.path.basename(perf_profile) | |
| 149 with open(os.devnull, 'w') as dev_null: | |
| 150 with open(json_file_name, 'w') as json_file: | |
|
bulach
2014/06/04 10:34:12
nit: I think we're on python2.7 everywhere, so mul
Sami
2014/06/04 15:16:59
Oh thanks, I forgot about that syntax!
| |
| 151 cmd = [perfhost_path, 'script', '-s', perf_script_path, | |
| 152 '-i', perf_profile, '--symfs', symfs_dir, '--kallsyms', | |
| 153 kallsyms] | |
| 154 subprocess.call(cmd, stdout=json_file, stderr=dev_null) | |
| 155 return json_file_name | |
| OLD | NEW |