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

Side by Side Diff: telemetry/telemetry/internal/platform/tracing_agent/cpu_tracing_agent.py

Issue 2162963002: [polymer] Merge of master into polymer10-migration (Closed) Base URL: git@github.com:catapult-project/catapult.git@polymer10-migration
Patch Set: Merge polymer10-migration int polymer10-merge Created 4 years, 5 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
OLDNEW
(Empty)
1 # Copyright 2016 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 json
6 import os
7 try:
8 import psutil
9 except ImportError:
10 psutil = None
11 import subprocess
12 from threading import Timer
13
14 from py_trace_event import trace_time
15 from telemetry.internal.platform import tracing_agent
16 from telemetry.timeline import trace_data
17
18 DEFAULT_MIN_PCPU = 0.1
19
20 class ProcessCollector(object):
21
22 def __init__(self, min_pcpu):
23 self._min_pcpu = min_pcpu
24
25 def GetProcesses(self):
26 return NotImplemented
27
28 class UnixProcessCollector(ProcessCollector):
29
30 _SHELL_COMMAND = NotImplemented
31 _START_LINE_NUMBER = 1
32 _TOKEN_COUNT = 4
33 _TOKEN_MAP = {
34 'pCpu': 2,
35 'pid': 0,
36 'pMem': 3,
37 'command': 1
38 }
39
40 def __init__(self, min_pcpu, binary_output=False):
41 super(UnixProcessCollector, self).__init__(min_pcpu)
42 self._binary_output = binary_output
43
44 def _ParseLine(self, line):
45 """Parses a line from top output
46
47 Args:
48 line(str): a line from top output that contains the information about a
49 process.
50
51 Returns:
52 An dictionary with useful information about the process.
53 """
54 token_list = line.strip().split()
55 if len(token_list) != self._TOKEN_COUNT:
56 return None
57 return {attribute_name: token_list[index]
58 for attribute_name, index in self._TOKEN_MAP.items()}
59
60 def GetProcesses(self):
61 """Fetches the top processes returned by top command.
62
63 Returns:
64 A list of dictionaries, each representing one of the top processes.
65 """
66 if self._binary_output:
67 processes = subprocess.check_output(self._SHELL_COMMAND).decode(
68 'ascii').split('\n')
69 else:
70 processes = subprocess.check_output(self._SHELL_COMMAND).split('\n')
71 process_lines = processes[self._START_LINE_NUMBER:]
72 top_processes = []
73 for process_line in process_lines:
74 process = self._ParseLine(process_line)
75 if (not process) or (float(process['pCpu']) < self._min_pcpu):
76 continue
77 top_processes.append(process)
78 return top_processes
79
80
81 class WindowsProcessCollector(ProcessCollector):
82 """Class for collecting information about processes on Windows.
83
84 Windows does not have a fast and simple command to list processes, so psutil
85 package is used instead."""
86
87 def __init__(self, min_pcpu):
88 super(WindowsProcessCollector, self).__init__(min_pcpu)
89
90 def GetProcesses(self):
91 data = []
92 for p in psutil.process_iter():
93 try:
94 cpu_percent = p.get_cpu_percent(interval=0)
95 if cpu_percent >= self._min_pcpu:
96 data.append({
97 'pCpu': cpu_percent,
98 'pMem': p.get_memory_percent(),
99 'command': p.name,
100 'pid': p.pid
101 })
102 except psutil.Error:
103 pass
104 data = sorted(data, key=lambda d: d['pCpu'],
105 reverse=True)
106 return data
107
108
109 class LinuxProcessCollector(UnixProcessCollector):
110 """Class for collecting information about processes on Linux.
111
112 Example of Linux command output: '31887 com.app.Webkit 3.4 8.0'"""
113
114 _SHELL_COMMAND = ["ps", "axo", "pid,cmd,pcpu,pmem", "--sort=-pcpu"]
115
116
117 def __init__(self, min_pcpu):
118 super(LinuxProcessCollector, self).__init__(min_pcpu)
119
120
121 class MacProcessCollector(UnixProcessCollector):
122 """Class for collecting information about processes on Mac.
123
124 Example of Mac command output:
125 '31887 com.app.Webkit 3.4 8.0'"""
126
127 _SHELL_COMMAND = ['ps', '-arcwwwxo', 'pid command %cpu %mem']
128
129 def __init__(self, min_pcpu):
130 super(MacProcessCollector, self).__init__(min_pcpu, binary_output=True)
131
132
133 class CpuTracingAgent(tracing_agent.TracingAgent):
134
135 SNAPSHOT_FREQUENCY = 1.0
136
137 def __init__(self, platform_backend, min_pcpu=DEFAULT_MIN_PCPU):
138 super(CpuTracingAgent, self).__init__(platform_backend)
139 self._snapshot_ongoing = False
140 self._snapshots = []
141 os_name = platform_backend.GetOSName()
142 if os_name == 'win':
143 self._collector = WindowsProcessCollector(min_pcpu)
144 elif os_name == 'mac':
145 self._collector = MacProcessCollector(min_pcpu)
146 else:
147 self._collector = LinuxProcessCollector(min_pcpu)
148
149 @classmethod
150 def IsSupported(cls, platform_backend):
151 os_name = platform_backend.GetOSName()
152 return (os_name in ['mac', 'linux']) or (os_name == 'win' and psutil)
153
154 def StartAgentTracing(self, config, timeout):
155 assert not self._snapshot_ongoing, (
156 'Agent is already taking snapshots when tracing is started.')
157 if not config.enable_cpu_trace:
158 return False
159 self._snapshot_ongoing = True
160 self._KeepTakingSnapshots()
161 return True
162
163 def _KeepTakingSnapshots(self):
164 """Take CPU snapshots every SNAPSHOT_FREQUENCY seconds."""
165 if not self._snapshot_ongoing:
166 return
167 # Assume CpuTracingAgent shares the same clock domain as telemetry
168 self._snapshots.append((self._collector.GetProcesses(), trace_time.Now()))
169 Timer(self.SNAPSHOT_FREQUENCY, self._KeepTakingSnapshots).start()
170
171 def StopAgentTracing(self):
172 assert self._snapshot_ongoing, (
173 'Agent is not taking snapshots when tracing is stopped.')
174 self._snapshot_ongoing = False
175
176 def CollectAgentTraceData(self, trace_data_builder, timeout=None):
177 assert not self._snapshot_ongoing, (
178 'Agent is still taking snapshots when data is collected.')
179 self._snapshot_ongoing = False
180 data = json.dumps(self._FormatSnapshotsData())
181 trace_data_builder.SetTraceFor(trace_data.CPU_TRACE_DATA, data)
182
183 def _FormatSnapshotsData(self):
184 """Format raw data into Object Event specified in Trace Format document."""
185 pid = os.getpid()
186 return [{
187 'name': 'CPUSnapshots',
188 'ph': 'O',
189 'id': '0x1000',
190 'local': True,
191 'ts': timestamp,
192 'pid': pid,
193 'tid':None,
194 'args': {
195 'processes': snapshot
196 }
197 } for snapshot, timestamp in self._snapshots]
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698