OLD | NEW |
---|---|
(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 os | |
6 import subprocess | |
7 import sys | |
8 import tempfile | |
9 | |
10 from chrome_profiler import controllers | |
11 | |
12 from pylib import android_commands | |
13 from pylib import constants | |
14 | |
15 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, | |
16 'tools', | |
17 'telemetry')) | |
18 # pylint: disable=F0401 | |
19 from telemetry.core.platform.profiler import android_profiling_helper | |
20 from telemetry.util import support_binaries | |
21 | |
22 | |
23 _PERF_OPTIONS = [ | |
24 '--all-cpus', | |
25 '--call-graph', | |
26 '--realtime', '80', | |
27 '--raw-samples', | |
28 '--freq', '2000', | |
29 ] | |
30 | |
31 | |
32 class _PerfProfilerInstance(object): | |
33 def __init__(self, device, perf_binary, categories): | |
34 self._device = device | |
35 self._output_file = android_commands.DeviceTempFile( | |
36 self._device.old_interface, prefix='perf_output') | |
37 self._log_file = tempfile.TemporaryFile() | |
38 | |
39 device_param = (['-s', self._device.old_interface.GetDevice()] | |
40 if self._device.old_interface.GetDevice() else []) | |
41 cmd = ['adb'] + device_param + \ | |
42 ['shell', perf_binary, 'record', | |
43 '--output', self._output_file.name] + _PERF_OPTIONS | |
44 if categories: | |
45 cmd += ['--event', ','.join(categories)] | |
46 self._perf_process = subprocess.Popen(cmd, | |
47 stdout=self._log_file, | |
48 stderr=subprocess.STDOUT) | |
49 | |
50 def Wait(self): | |
51 self._perf_process.wait() | |
52 | |
53 def _FailWithLog(self, msg): | |
54 self._log_file.seek(0) | |
55 log = self._log_file.read() | |
56 raise RuntimeError('%s. Log output:\n%s' % (msg, log)) | |
57 | |
58 def PullResult(self, output_path): | |
59 if not self._device.old_interface.FileExistsOnDevice( | |
60 self._output_file.name): | |
61 self._FailWithLog('Perf recorded no data') | |
62 | |
63 perf_profile = os.path.join(output_path, | |
64 os.path.basename(self._output_file.name)) | |
65 self._device.old_interface.PullFileFromDevice(self._output_file.name, | |
66 perf_profile) | |
67 if not os.stat(perf_profile).st_size: | |
68 os.remove(perf_profile) | |
69 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
| |
70 | |
71 self._log_file.close() | |
72 self._output_file.close() | |
73 return perf_profile | |
74 | |
75 | |
76 class PerfProfilerController(controllers.BaseController): | |
77 def __init__(self, device, categories): | |
78 controllers.BaseController.__init__(self) | |
79 self._device = device | |
80 self._categories = categories | |
81 self._perf_binary = self._PrepareDevice(device) | |
82 self._perf_instance = None | |
83 | |
84 def __repr__(self): | |
85 return 'perf profile' | |
86 | |
87 @staticmethod | |
88 def _PrepareDevice(device): | |
89 if not 'BUILDTYPE' in os.environ: | |
90 os.environ['BUILDTYPE'] = 'Release' | |
91 return android_profiling_helper.PrepareDeviceForPerf(device) | |
92 | |
93 @classmethod | |
94 def GetCategories(cls, device): | |
95 perf_binary = cls._PrepareDevice(device) | |
96 return device.old_interface.RunShellCommand('%s list' % perf_binary) | |
97 | |
98 def StartTracing(self, _): | |
99 self._perf_instance = _PerfProfilerInstance(self._device, | |
100 self._perf_binary, | |
101 self._categories) | |
102 | |
103 def StopTracing(self): | |
104 if not self._perf_instance: | |
105 return | |
106 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.
| |
107 self._device.old_interface.RunShellCommand( | |
108 'kill -SIGINT ' + ' '.join(perf_pids)) | |
109 self._perf_instance.Wait() | |
110 | |
111 def PullTrace(self): | |
112 symfs_dir = os.path.join(tempfile.gettempdir(), | |
113 os.path.expandvars('$USER-perf-symfs')) | |
114 if not os.path.exists(symfs_dir): | |
115 os.makedirs(symfs_dir) | |
116 required_libs = set() | |
117 | |
118 # Download the recorded perf profile. | |
119 perf_profile = self._perf_instance.PullResult(symfs_dir) | |
120 required_libs = \ | |
121 android_profiling_helper.GetRequiredLibrariesForPerfProfile( | |
122 perf_profile) | |
123 | |
124 # Build a symfs with all the necessary libraries. | |
125 kallsyms = android_profiling_helper.CreateSymFs(self._device, | |
126 symfs_dir, | |
127 required_libs, | |
128 use_symlinks=False) | |
129 # Convert the perf profile into JSON. | |
130 perfhost_path = \ | |
131 os.path.abspath(support_binaries.FindPath('perfhost', 'linux')) | |
132 perf_script_path = \ | |
133 os.path.join(constants.DIR_SOURCE_ROOT, | |
134 'tools', 'telemetry', 'telemetry', 'core', 'platform', | |
135 'profiler', 'perf_vis', 'perf_to_tracing.py') | |
136 json_file_name = os.path.basename(perf_profile) | |
137 with open(json_file_name, 'w') as json_file: | |
138 cmd = [perfhost_path, 'script', '-s', perf_script_path, | |
139 '-i', perf_profile, '--symfs', symfs_dir, '--kallsyms', | |
140 kallsyms] | |
141 subprocess.call(cmd, stdout=json_file) | |
142 return json_file_name | |
OLD | NEW |