Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1842)

Side by Side Diff: systrace/profile_chrome/perf_controller.py

Issue 1726783002: Copy adb_profile_chrome to catapult (Closed) Base URL: https://github.com/catapult-project/catapult.git@master
Patch Set: add OWNERS file to bin files Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « systrace/profile_chrome/main.py ('k') | systrace/profile_chrome/perf_controller_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 signal
8 import subprocess
9 import sys
10 import tempfile
11
12 from devil.android import device_temp_file
13 from devil.android.perf import perf_control
14
15 from profile_chrome import controllers
16 from profile_chrome import ui
17
18 _CATAPULT_DIR = os.path.join(
19 os.path.dirname(os.path.abspath(__file__)), '..', '..')
20 sys.path.append(os.path.join(_CATAPULT_DIR, 'telemetry'))
21 try:
22 # pylint: disable=F0401
23 from telemetry.internal.platform.profiler import android_profiling_helper
24 from telemetry.internal.util import binary_manager
25 except ImportError:
26 android_profiling_helper = None
27 binary_manager = None
28
29
30 _PERF_OPTIONS = [
31 # Sample across all processes and CPUs to so that the current CPU gets
32 # recorded to each sample.
33 '--all-cpus',
34 # In perf 3.13 --call-graph requires an argument, so use the -g short-hand
35 # which does not.
36 '-g',
37 # Increase priority to avoid dropping samples. Requires root.
38 '--realtime', '80',
39 # Record raw samples to get CPU information.
40 '--raw-samples',
41 # Increase sampling frequency for better coverage.
42 '--freq', '2000',
43 ]
44
45
46 class _PerfProfiler(object):
47 def __init__(self, device, perf_binary, categories):
48 self._device = device
49 self._output_file = device_temp_file.DeviceTempFile(
50 self._device.adb, prefix='perf_output')
51 self._log_file = tempfile.TemporaryFile()
52
53 # TODO(jbudorick) Look at providing a way to unhandroll this once the
54 # adb rewrite has fully landed.
55 device_param = (['-s', str(self._device)] if str(self._device) else [])
56 cmd = ['adb'] + device_param + \
57 ['shell', perf_binary, 'record',
58 '--output', self._output_file.name] + _PERF_OPTIONS
59 if categories:
60 cmd += ['--event', ','.join(categories)]
61 self._perf_control = perf_control.PerfControl(self._device)
62 self._perf_control.SetPerfProfilingMode()
63 self._perf_process = subprocess.Popen(cmd,
64 stdout=self._log_file,
65 stderr=subprocess.STDOUT)
66
67 def SignalAndWait(self):
68 self._device.KillAll('perf', signum=signal.SIGINT)
69 self._perf_process.wait()
70 self._perf_control.SetDefaultPerfMode()
71
72 def _FailWithLog(self, msg):
73 self._log_file.seek(0)
74 log = self._log_file.read()
75 raise RuntimeError('%s. Log output:\n%s' % (msg, log))
76
77 def PullResult(self, output_path):
78 if not self._device.FileExists(self._output_file.name):
79 self._FailWithLog('Perf recorded no data')
80
81 perf_profile = os.path.join(output_path,
82 os.path.basename(self._output_file.name))
83 self._device.PullFile(self._output_file.name, perf_profile)
84 if not os.stat(perf_profile).st_size:
85 os.remove(perf_profile)
86 self._FailWithLog('Perf recorded a zero-sized file')
87
88 self._log_file.close()
89 self._output_file.close()
90 return perf_profile
91
92
93 class PerfProfilerController(controllers.BaseController):
94 def __init__(self, device, categories):
95 controllers.BaseController.__init__(self)
96 self._device = device
97 self._categories = categories
98 self._perf_binary = self._PrepareDevice(device)
99 self._perf_instance = None
100
101 def __repr__(self):
102 return 'perf profile'
103
104 @staticmethod
105 def IsSupported():
106 return bool(android_profiling_helper)
107
108 @staticmethod
109 def _PrepareDevice(device):
110 if not 'BUILDTYPE' in os.environ:
111 os.environ['BUILDTYPE'] = 'Release'
112 if binary_manager.NeedsInit():
113 binary_manager.InitDependencyManager(None)
114 return android_profiling_helper.PrepareDeviceForPerf(device)
115
116 @classmethod
117 def GetCategories(cls, device):
118 perf_binary = cls._PrepareDevice(device)
119 return device.RunShellCommand('%s list' % perf_binary)
120
121 def StartTracing(self, _):
122 self._perf_instance = _PerfProfiler(self._device,
123 self._perf_binary,
124 self._categories)
125
126 def StopTracing(self):
127 if not self._perf_instance:
128 return
129 self._perf_instance.SignalAndWait()
130
131 @staticmethod
132 def _GetInteractivePerfCommand(perfhost_path, perf_profile, symfs_dir,
133 required_libs, kallsyms):
134 cmd = '%s report -n -i %s --symfs %s --kallsyms %s' % (
135 os.path.relpath(perfhost_path, '.'), perf_profile, symfs_dir, kallsyms)
136 for lib in required_libs:
137 lib = os.path.join(symfs_dir, lib[1:])
138 if not os.path.exists(lib):
139 continue
140 objdump_path = android_profiling_helper.GetToolchainBinaryPath(
141 lib, 'objdump')
142 if objdump_path:
143 cmd += ' --objdump %s' % os.path.relpath(objdump_path, '.')
144 break
145 return cmd
146
147 def PullTrace(self):
148 symfs_dir = os.path.join(tempfile.gettempdir(),
149 os.path.expandvars('$USER-perf-symfs'))
150 if not os.path.exists(symfs_dir):
151 os.makedirs(symfs_dir)
152 required_libs = set()
153
154 # Download the recorded perf profile.
155 perf_profile = self._perf_instance.PullResult(symfs_dir)
156 required_libs = \
157 android_profiling_helper.GetRequiredLibrariesForPerfProfile(
158 perf_profile)
159 if not required_libs:
160 logging.warning('No libraries required by perf trace. Most likely there '
161 'are no samples in the trace.')
162
163 # Build a symfs with all the necessary libraries.
164 kallsyms = android_profiling_helper.CreateSymFs(self._device,
165 symfs_dir,
166 required_libs,
167 use_symlinks=False)
168 perfhost_path = binary_manager.FetchPath(
169 android_profiling_helper.GetPerfhostName(), 'x86_64', 'linux')
170
171 ui.PrintMessage('\nNote: to view the profile in perf, run:')
172 ui.PrintMessage(' ' + self._GetInteractivePerfCommand(perfhost_path,
173 perf_profile, symfs_dir, required_libs, kallsyms))
174
175 # Convert the perf profile into JSON.
176 perf_script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
177 'third_party', 'perf_to_tracing.py')
178 json_file_name = os.path.basename(perf_profile)
179 with open(os.devnull, 'w') as dev_null, \
180 open(json_file_name, 'w') as json_file:
181 cmd = [perfhost_path, 'script', '-s', perf_script_path, '-i',
182 perf_profile, '--symfs', symfs_dir, '--kallsyms', kallsyms]
183 if subprocess.call(cmd, stdout=json_file, stderr=dev_null):
184 logging.warning('Perf data to JSON conversion failed. The result will '
185 'not contain any perf samples. You can still view the '
186 'perf data manually as shown above.')
187 return None
188
189 return json_file_name
OLDNEW
« no previous file with comments | « systrace/profile_chrome/main.py ('k') | systrace/profile_chrome/perf_controller_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698