Index: build/android/chrome_profiler/perf_controller.py |
diff --git a/build/android/chrome_profiler/perf_controller.py b/build/android/chrome_profiler/perf_controller.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..175a8fd806a598dde99f3cbcfb62d87127910fc0 |
--- /dev/null |
+++ b/build/android/chrome_profiler/perf_controller.py |
@@ -0,0 +1,142 @@ |
+# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import os |
+import subprocess |
+import sys |
+import tempfile |
+ |
+from chrome_profiler import controllers |
+ |
+from pylib import android_commands |
+from pylib import constants |
+ |
+sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, |
+ 'tools', |
+ 'telemetry')) |
+# pylint: disable=F0401 |
+from telemetry.core.platform.profiler import android_profiling_helper |
+from telemetry.util import support_binaries |
+ |
+ |
+_PERF_OPTIONS = [ |
+ '--all-cpus', |
+ '--call-graph', |
+ '--realtime', '80', |
+ '--raw-samples', |
+ '--freq', '2000', |
+] |
+ |
+ |
+class _PerfProfilerInstance(object): |
+ def __init__(self, device, perf_binary, categories): |
+ self._device = device |
+ self._output_file = android_commands.DeviceTempFile( |
+ self._device.old_interface, prefix='perf_output') |
+ self._log_file = tempfile.TemporaryFile() |
+ |
+ device_param = (['-s', self._device.old_interface.GetDevice()] |
+ if self._device.old_interface.GetDevice() else []) |
+ cmd = ['adb'] + device_param + \ |
+ ['shell', perf_binary, 'record', |
+ '--output', self._output_file.name] + _PERF_OPTIONS |
+ if categories: |
+ cmd += ['--event', ','.join(categories)] |
+ self._perf_process = subprocess.Popen(cmd, |
+ stdout=self._log_file, |
+ stderr=subprocess.STDOUT) |
+ |
+ def Wait(self): |
+ self._perf_process.wait() |
+ |
+ def _FailWithLog(self, msg): |
+ self._log_file.seek(0) |
+ log = self._log_file.read() |
+ raise RuntimeError('%s. Log output:\n%s' % (msg, log)) |
+ |
+ def PullResult(self, output_path): |
+ if not self._device.old_interface.FileExistsOnDevice( |
+ self._output_file.name): |
+ self._FailWithLog('Perf recorded no data') |
+ |
+ perf_profile = os.path.join(output_path, |
+ os.path.basename(self._output_file.name)) |
+ self._device.old_interface.PullFileFromDevice(self._output_file.name, |
+ perf_profile) |
+ if not os.stat(perf_profile).st_size: |
+ os.remove(perf_profile) |
+ self._FailWithLog('Perf recorded a zero-sized file') |
Dominik Grewe
2014/05/28 17:09:43
Can we also somehow check if there are any samples
Sami
2014/06/02 17:56:38
Great point, we should complain if the perf trace
|
+ |
+ self._log_file.close() |
+ self._output_file.close() |
+ return perf_profile |
+ |
+ |
+class PerfProfilerController(controllers.BaseController): |
+ def __init__(self, device, categories): |
+ controllers.BaseController.__init__(self) |
+ self._device = device |
+ self._categories = categories |
+ self._perf_binary = self._PrepareDevice(device) |
+ self._perf_instance = None |
+ |
+ def __repr__(self): |
+ return 'perf profile' |
+ |
+ @staticmethod |
+ def _PrepareDevice(device): |
+ if not 'BUILDTYPE' in os.environ: |
+ os.environ['BUILDTYPE'] = 'Release' |
+ return android_profiling_helper.PrepareDeviceForPerf(device) |
+ |
+ @classmethod |
+ def GetCategories(cls, device): |
+ perf_binary = cls._PrepareDevice(device) |
+ return device.old_interface.RunShellCommand('%s list' % perf_binary) |
+ |
+ def StartTracing(self, _): |
+ self._perf_instance = _PerfProfilerInstance(self._device, |
+ self._perf_binary, |
+ self._categories) |
+ |
+ def StopTracing(self): |
+ if not self._perf_instance: |
+ return |
+ perf_pids = self._device.old_interface.ExtractPid('perf') |
Dominik Grewe
2014/05/28 17:09:43
Why not ask the perf profiler instance directly fo
Sami
2014/06/02 17:56:38
Good idea, that's much cleaner.
|
+ self._device.old_interface.RunShellCommand( |
+ 'kill -SIGINT ' + ' '.join(perf_pids)) |
+ self._perf_instance.Wait() |
+ |
+ def PullTrace(self): |
+ symfs_dir = os.path.join(tempfile.gettempdir(), |
+ os.path.expandvars('$USER-perf-symfs')) |
+ if not os.path.exists(symfs_dir): |
+ os.makedirs(symfs_dir) |
+ required_libs = set() |
+ |
+ # Download the recorded perf profile. |
+ perf_profile = self._perf_instance.PullResult(symfs_dir) |
+ required_libs = \ |
+ android_profiling_helper.GetRequiredLibrariesForPerfProfile( |
+ perf_profile) |
+ |
+ # Build a symfs with all the necessary libraries. |
+ kallsyms = android_profiling_helper.CreateSymFs(self._device, |
+ symfs_dir, |
+ required_libs, |
+ use_symlinks=False) |
+ # Convert the perf profile into JSON. |
+ perfhost_path = \ |
+ os.path.abspath(support_binaries.FindPath('perfhost', 'linux')) |
+ perf_script_path = \ |
+ os.path.join(constants.DIR_SOURCE_ROOT, |
+ 'tools', 'telemetry', 'telemetry', 'core', 'platform', |
+ 'profiler', 'perf_vis', 'perf_to_tracing.py') |
+ json_file_name = os.path.basename(perf_profile) |
+ with open(json_file_name, 'w') as json_file: |
+ cmd = [perfhost_path, 'script', '-s', perf_script_path, |
+ '-i', perf_profile, '--symfs', symfs_dir, '--kallsyms', |
+ kallsyms] |
+ subprocess.call(cmd, stdout=json_file) |
+ return json_file_name |