Index: tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py |
diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py |
index 4e0c9eae9f6bebe0731fb161f257c813be7b0921..f32eba30f7bd91b53618d5530afe6910b6b90786 100644 |
--- a/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py |
+++ b/tools/telemetry/telemetry/core/platform/profiler/perf_profiler.py |
@@ -10,9 +10,52 @@ import subprocess |
import sys |
import tempfile |
+from telemetry.core import platform |
from telemetry.core import util |
from telemetry.core.platform import profiler |
from telemetry.core.platform.profiler import android_profiling_helper |
+from telemetry.util import support_binaries |
+ |
+util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') |
+from pylib.perf import perf_control # pylint: disable=F0401 |
+ |
+ |
+_PERF_OPTIONS = [ |
+ # In perf 3.13 --call-graph requires an argument, so use the -g short-hand |
+ # which does not. |
+ '-g', |
+ # Increase sampling frequency for better coverage. |
+ '--freq', '2000', |
+] |
+ |
+_PERF_OPTIONS_ANDROID = [ |
+ # Increase priority to avoid dropping samples. Requires root. |
+ '--realtime', '80', |
+] |
+ |
+ |
+def _NicePath(path): |
+ rel_path = os.path.relpath(path, os.curdir) |
+ return rel_path if len(rel_path) < len(path) else path |
+ |
+ |
+def _PrepareHostForPerf(): |
+ kptr_file = '/proc/sys/kernel/kptr_restrict' |
+ with open(kptr_file) as f: |
+ if f.read().strip() != '0': |
+ logging.warning('Making kernel symbols unrestricted. You might have to ' |
+ 'enter your password for "sudo".') |
+ with tempfile.NamedTemporaryFile() as zero: |
+ zero.write('0') |
+ zero.flush() |
+ subprocess.call(['sudo', 'cp', zero.name, kptr_file]) |
+ |
+ |
+def _InstallPerfHost(): |
+ host = platform.GetHostPlatform() |
+ if not host.CanLaunchApplication('perfhost'): |
+ host.InstallApplication('perfhost') |
+ return support_binaries.FindPath('perfhost', host.GetOSName()) |
class _SingleProcessPerfProfiler(object): |
@@ -22,17 +65,20 @@ class _SingleProcessPerfProfiler(object): |
See more details in prebuilt/android/README.txt. |
""" |
def __init__(self, pid, output_file, browser_backend, platform_backend, |
- perf_binary): |
+ perf_binary, perfhost_binary): |
self._pid = pid |
self._browser_backend = browser_backend |
self._platform_backend = platform_backend |
self._output_file = output_file |
self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0) |
self._is_android = platform_backend.GetOSName() == 'android' |
+ self._perfhost_binary = perfhost_binary |
cmd_prefix = [] |
+ perf_args = ['record', '--pid', str(pid)] |
if self._is_android: |
cmd_prefix = ['adb', '-s', browser_backend.adb.device_serial(), 'shell', |
perf_binary] |
+ perf_args += _PERF_OPTIONS_ANDROID |
output_file = os.path.join('/sdcard', 'perf_profiles', |
os.path.basename(output_file)) |
self._device_output_file = output_file |
@@ -41,11 +87,8 @@ class _SingleProcessPerfProfiler(object): |
browser_backend.adb.RunShellCommand('rm -f ' + self._device_output_file) |
else: |
cmd_prefix = [perf_binary] |
- # In perf 3.13 --call-graph requires an argument, so use |
- # the -g short-hand which does not. |
- self._proc = subprocess.Popen(cmd_prefix + |
- ['record', '-g', |
- '--pid', str(pid), '--output', output_file], |
+ perf_args += ['--output', output_file] + _PERF_OPTIONS |
+ self._proc = subprocess.Popen(cmd_prefix + perf_args, |
stdout=self._tmp_output_file, stderr=subprocess.STDOUT) |
def CollectProfile(self): |
@@ -77,7 +120,8 @@ Try rerunning this script under sudo or setting |
self._GetStdOut())) |
finally: |
self._tmp_output_file.close() |
- cmd = 'perf report -n -i %s' % self._output_file |
+ cmd = '%s report -n -i %s' % (_NicePath(self._perfhost_binary), |
+ self._output_file) |
if self._is_android: |
device = self._browser_backend.adb.device() |
device.old_interface.Adb().Pull(self._device_output_file, |
@@ -91,13 +135,15 @@ Try rerunning this script under sudo or setting |
required_libs, |
use_symlinks=True) |
cmd += ' --symfs %s --kallsyms %s' % (symfs_root, kallsyms) |
- |
- objdump_path = os.path.join(os.environ.get('ANDROID_TOOLCHAIN', |
- '$ANDROID_TOOLCHAIN'), |
- 'arm-linux-androideabi-objdump') |
- print 'If you have recent version of perf (3.10+), append the following ' |
- print 'to see annotated source code (by pressing the \'a\' key): ' |
- print ' --objdump %s' % objdump_path |
+ for lib in required_libs: |
+ lib = os.path.join(symfs_root, lib[1:]) |
+ if not os.path.exists(lib): |
+ continue |
+ objdump_path = android_profiling_helper.GetToolchainBinaryPath( |
+ lib, 'objdump') |
+ if objdump_path: |
+ cmd += ' --objdump %s' % _NicePath(objdump_path) |
+ break |
print 'To view the profile, run:' |
print ' ', cmd |
@@ -119,17 +165,29 @@ class PerfProfiler(profiler.Profiler): |
browser_backend, platform_backend, output_path, state) |
process_output_file_map = self._GetProcessOutputFileMap() |
self._process_profilers = [] |
- perf_binary = 'perf' |
- if platform_backend.GetOSName() == 'android': |
- perf_binary = android_profiling_helper.PrepareDeviceForPerf( |
- browser_backend.adb.device()) |
+ self._is_android = platform_backend.GetOSName() == 'android' |
- for pid, output_file in process_output_file_map.iteritems(): |
- if 'zygote' in output_file: |
- continue |
- self._process_profilers.append( |
- _SingleProcessPerfProfiler( |
- pid, output_file, browser_backend, platform_backend, perf_binary)) |
+ perf_binary = perfhost_binary = _InstallPerfHost() |
+ try: |
+ if self._is_android: |
+ device = browser_backend.adb.device() |
+ perf_binary = android_profiling_helper.PrepareDeviceForPerf(device) |
+ self._perf_control = perf_control.PerfControl(device) |
+ self._perf_control.ForceAllCpusOnline(True) |
+ else: |
+ _PrepareHostForPerf() |
+ |
+ for pid, output_file in process_output_file_map.iteritems(): |
+ if 'zygote' in output_file: |
+ continue |
+ self._process_profilers.append( |
+ _SingleProcessPerfProfiler( |
+ pid, output_file, browser_backend, platform_backend, |
+ perf_binary, perfhost_binary)) |
+ except: |
+ if self._is_android: |
+ self._perf_control.ForceAllCpusOnline(False) |
+ raise |
@classmethod |
def name(cls): |
@@ -141,17 +199,7 @@ class PerfProfiler(profiler.Profiler): |
return False |
if browser_type.startswith('cros'): |
return False |
- return cls._CheckLinuxPerf() or browser_type.startswith('android') |
- |
- @classmethod |
- def _CheckLinuxPerf(cls): |
- try: |
- with open(os.devnull, 'w') as devnull: |
- return not subprocess.Popen(['perf', '--version'], |
- stderr=devnull, |
- stdout=devnull).wait() |
- except OSError: |
- return False |
+ return True |
@classmethod |
def CustomizeBrowserOptions(cls, browser_type, options): |
@@ -161,6 +209,8 @@ class PerfProfiler(profiler.Profiler): |
]) |
def CollectProfile(self): |
+ if self._is_android: |
+ self._perf_control.ForceAllCpusOnline(False) |
output_files = [] |
for single_process in self._process_profilers: |
output_files.append(single_process.CollectProfile()) |
@@ -173,8 +223,9 @@ class PerfProfiler(profiler.Profiler): |
""" |
assert os.path.exists(file_name) |
with open(os.devnull, 'w') as devnull: |
+ _InstallPerfHost() |
report = subprocess.Popen( |
- ['perf', 'report', '--show-total-period', '-U', '-t', '^', '-i', |
+ ['perfhost', 'report', '--show-total-period', '-U', '-t', '^', '-i', |
file_name], |
stdout=subprocess.PIPE, stderr=devnull).communicate()[0] |
period_by_function = {} |