| 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 = {}
|
|
|