| Index: tools/telemetry/telemetry/internal/platform/profiler/perf_profiler.py
|
| diff --git a/tools/telemetry/telemetry/internal/platform/profiler/perf_profiler.py b/tools/telemetry/telemetry/internal/platform/profiler/perf_profiler.py
|
| deleted file mode 100644
|
| index 9218ae8c8b86efd3104daad6ce2ee133807d20f5..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/telemetry/internal/platform/profiler/perf_profiler.py
|
| +++ /dev/null
|
| @@ -1,257 +0,0 @@
|
| -# Copyright 2013 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 logging
|
| -import os
|
| -import re
|
| -import signal
|
| -import subprocess
|
| -import sys
|
| -import tempfile
|
| -
|
| -from devil.android import device_errors # pylint: disable=import-error
|
| -
|
| -from telemetry.internal.util import binary_manager
|
| -from telemetry.core import platform
|
| -from telemetry.internal.platform import profiler
|
| -from telemetry.internal.platform.profiler import android_profiling_helper
|
| -
|
| -from devil.android.perf import perf_control # pylint: disable=import-error
|
| -
|
| -
|
| -_PERF_OPTIONS = [
|
| - # Sample across all processes and CPUs to so that the current CPU gets
|
| - # recorded to each sample.
|
| - '--all-cpus',
|
| - # In perf 3.13 --call-graph requires an argument, so use the -g short-hand
|
| - # which does not.
|
| - '-g',
|
| - # Record raw samples to get CPU information.
|
| - '--raw-samples',
|
| - # 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(['/usr/bin/sudo', 'cp', zero.name, kptr_file])
|
| -
|
| -
|
| -def _InstallPerfHost():
|
| - perfhost_name = android_profiling_helper.GetPerfhostName()
|
| - host = platform.GetHostPlatform()
|
| - if not host.CanLaunchApplication(perfhost_name):
|
| - host.InstallApplication(perfhost_name)
|
| - return binary_manager.FetchPath(perfhost_name, 'x86_64', 'linux')
|
| -
|
| -
|
| -class _SingleProcessPerfProfiler(object):
|
| - """An internal class for using perf for a given process.
|
| -
|
| - On android, this profiler uses pre-built binaries from AOSP.
|
| - See more details in prebuilt/android/README.txt.
|
| - """
|
| - def __init__(self, pid, output_file, browser_backend, platform_backend,
|
| - 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._perf_binary = perf_binary
|
| - self._perfhost_binary = perfhost_binary
|
| - cmd_prefix = []
|
| - perf_args = ['record', '--pid', str(pid)]
|
| - if self._is_android:
|
| - cmd_prefix = ['adb', '-s', browser_backend.device.adb.GetDeviceSerial(),
|
| - '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
|
| - browser_backend.device.RunShellCommand(
|
| - 'mkdir -p ' + os.path.dirname(self._device_output_file))
|
| - browser_backend.device.RunShellCommand(
|
| - 'rm -f ' + self._device_output_file)
|
| - else:
|
| - cmd_prefix = [perf_binary]
|
| - 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):
|
| - if ('renderer' in self._output_file and
|
| - not self._is_android and
|
| - not self._platform_backend.GetCommandLine(self._pid)):
|
| - logging.warning('Renderer was swapped out during profiling. '
|
| - 'To collect a full profile rerun with '
|
| - '"--extra-browser-args=--single-process"')
|
| - if self._is_android:
|
| - device = self._browser_backend.device
|
| - try:
|
| - binary_name = os.path.basename(self._perf_binary)
|
| - device.KillAll(binary_name, signum=signal.SIGINT, blocking=True,
|
| - quiet=True)
|
| - except device_errors.CommandFailedError:
|
| - logging.warning('The perf process could not be killed on the device.')
|
| - self._proc.send_signal(signal.SIGINT)
|
| - exit_code = self._proc.wait()
|
| - try:
|
| - if exit_code == 128:
|
| - raise Exception(
|
| - """perf failed with exit code 128.
|
| -Try rerunning this script under sudo or setting
|
| -/proc/sys/kernel/perf_event_paranoid to "-1".\nOutput:\n%s""" %
|
| - self._GetStdOut())
|
| - elif exit_code not in (0, -2):
|
| - raise Exception(
|
| - 'perf failed with exit code %d. Output:\n%s' % (exit_code,
|
| - self._GetStdOut()))
|
| - finally:
|
| - self._tmp_output_file.close()
|
| - cmd = '%s report -n -i %s' % (_NicePath(self._perfhost_binary),
|
| - self._output_file)
|
| - if self._is_android:
|
| - device = self._browser_backend.device
|
| - try:
|
| - device.PullFile(self._device_output_file, self._output_file)
|
| - except:
|
| - logging.exception('New exception caused by DeviceUtils conversion')
|
| - raise
|
| - required_libs = \
|
| - android_profiling_helper.GetRequiredLibrariesForPerfProfile(
|
| - self._output_file)
|
| - symfs_root = os.path.dirname(self._output_file)
|
| - kallsyms = android_profiling_helper.CreateSymFs(device,
|
| - symfs_root,
|
| - required_libs,
|
| - use_symlinks=True)
|
| - cmd += ' --symfs %s --kallsyms %s' % (symfs_root, kallsyms)
|
| - 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
|
| - return self._output_file
|
| -
|
| - def _GetStdOut(self):
|
| - self._tmp_output_file.flush()
|
| - try:
|
| - with open(self._tmp_output_file.name) as f:
|
| - return f.read()
|
| - except IOError:
|
| - return ''
|
| -
|
| -
|
| -class PerfProfiler(profiler.Profiler):
|
| -
|
| - def __init__(self, browser_backend, platform_backend, output_path, state):
|
| - super(PerfProfiler, self).__init__(
|
| - browser_backend, platform_backend, output_path, state)
|
| - process_output_file_map = self._GetProcessOutputFileMap()
|
| - self._process_profilers = []
|
| - self._perf_control = None
|
| -
|
| - perf_binary = perfhost_binary = _InstallPerfHost()
|
| - try:
|
| - if platform_backend.GetOSName() == 'android':
|
| - device = browser_backend.device
|
| - perf_binary = android_profiling_helper.PrepareDeviceForPerf(device)
|
| - self._perf_control = perf_control.PerfControl(device)
|
| - self._perf_control.SetPerfProfilingMode()
|
| - 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._perf_control:
|
| - self._perf_control.SetDefaultPerfMode()
|
| - raise
|
| -
|
| - @classmethod
|
| - def name(cls):
|
| - return 'perf'
|
| -
|
| - @classmethod
|
| - def is_supported(cls, browser_type):
|
| - if sys.platform != 'linux2':
|
| - return False
|
| - if platform.GetHostPlatform().GetOSName() == 'chromeos':
|
| - return False
|
| - return True
|
| -
|
| - @classmethod
|
| - def CustomizeBrowserOptions(cls, browser_type, options):
|
| - options.AppendExtraBrowserArgs([
|
| - '--no-sandbox',
|
| - '--allow-sandbox-debugging',
|
| - ])
|
| -
|
| - def CollectProfile(self):
|
| - if self._perf_control:
|
| - self._perf_control.SetDefaultPerfMode()
|
| - output_files = []
|
| - for single_process in self._process_profilers:
|
| - output_files.append(single_process.CollectProfile())
|
| - return output_files
|
| -
|
| - @classmethod
|
| - def GetTopSamples(cls, file_name, number):
|
| - """Parses the perf generated profile in |file_name| and returns a
|
| - {function: period} dict of the |number| hottests functions.
|
| - """
|
| - assert os.path.exists(file_name)
|
| - with open(os.devnull, 'w') as devnull:
|
| - _InstallPerfHost()
|
| - report = subprocess.Popen(
|
| - [android_profiling_helper.GetPerfhostName(),
|
| - 'report', '--show-total-period', '-U', '-t', '^', '-i', file_name],
|
| - stdout=subprocess.PIPE, stderr=devnull).communicate()[0]
|
| - period_by_function = {}
|
| - for line in report.split('\n'):
|
| - if not line or line.startswith('#'):
|
| - continue
|
| - fields = line.split('^')
|
| - if len(fields) != 5:
|
| - continue
|
| - period = int(fields[1])
|
| - function = fields[4].partition(' ')[2]
|
| - function = re.sub('<.*>', '', function) # Strip template params.
|
| - function = re.sub('[(].*[)]', '', function) # Strip function params.
|
| - period_by_function[function] = period
|
| - if len(period_by_function) == number:
|
| - break
|
| - return period_by_function
|
|
|