Merge pull request #31 from abusque/staging
[deliverable/lttng-analyses.git] / lttnganalyses / core / cputop.py
CommitLineData
4ed24f86
JD
1# The MIT License (MIT)
2#
a3fa57c0 3# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
b4156aa2 4# 2015 - Antoine Busque <abusque@efficios.com>
4ed24f86
JD
5#
6# Permission is hereby granted, free of charge, to any person obtaining a copy
7# of this software and associated documentation files (the "Software"), to deal
8# in the Software without restriction, including without limitation the rights
9# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10# copies of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be included in
14# all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22# SOFTWARE.
23
ff833baa 24from . import stats
bd3cd7c5
JD
25from .analysis import Analysis
26
27
28class Cputop(Analysis):
b6d9132b 29 def __init__(self, state, conf):
b4156aa2
AB
30 notification_cbs = {
31 'sched_migrate_task': self._process_sched_migrate_task,
32 'sched_switch_per_cpu': self._process_sched_switch_per_cpu,
ff833baa
AB
33 'sched_switch_per_tid': self._process_sched_switch_per_tid,
34 'prio_changed': self._process_prio_changed,
b4156aa2
AB
35 }
36
b6d9132b 37 super().__init__(state, conf)
b4156aa2 38 self._state.register_notification_cbs(notification_cbs)
b6d9132b 39
bd3cd7c5 40 self._ev_count = 0
b4156aa2
AB
41 self.cpus = {}
42 self.tids = {}
bd3cd7c5
JD
43
44 def process_event(self, ev):
b6d9132b 45 super().process_event(ev)
bd3cd7c5
JD
46 self._ev_count += 1
47
b6d9132b 48 def reset(self):
ff833baa
AB
49 for cpu_stats in self.cpus.values():
50 cpu_stats.reset()
a234193d 51 if cpu_stats.current_task_start_ts is not None:
ff833baa 52 cpu_stats.current_task_start_ts = self._last_event_ts
b4156aa2 53
ff833baa
AB
54 for proc_stats in self.tids.values():
55 proc_stats.reset()
56 if proc_stats.last_sched_ts is not None:
57 proc_stats.last_sched_ts = self._last_event_ts
b6d9132b
AB
58
59 def _end_period_cb(self):
60 self._compute_stats()
b4156aa2 61
b6d9132b 62 def _compute_stats(self):
b4156aa2
AB
63 """Compute usage stats relative to a certain time range
64
65 For each CPU and process tracked by the analysis, we set its
66 usage_percent attribute, which represents the percentage of
67 usage time for the given CPU or process relative to the full
68 duration of the time range. Do note that we need to know the
69 timestamps and not just the duration, because if a CPU or a
70 process is currently busy, we use the end timestamp to add
71 the partial results of the currently running task to the usage
72 stats.
b4156aa2 73 """
b6d9132b 74 duration = self._last_event_ts - self._period_start_ts
b4156aa2
AB
75
76 for cpu_id in self.cpus:
77 cpu = self.cpus[cpu_id]
78 if cpu.current_task_start_ts is not None:
b6d9132b
AB
79 cpu.total_usage_time += self._last_event_ts - \
80 cpu.current_task_start_ts
b4156aa2
AB
81
82 cpu.compute_stats(duration)
83
84 for tid in self.tids:
85 proc = self.tids[tid]
86 if proc.last_sched_ts is not None:
b6d9132b
AB
87 proc.total_cpu_time += self._last_event_ts - \
88 proc.last_sched_ts
b4156aa2
AB
89
90 proc.compute_stats(duration)
91
92 def _process_sched_switch_per_cpu(self, **kwargs):
93 timestamp = kwargs['timestamp']
94 cpu_id = kwargs['cpu_id']
a621ba35 95 wakee_proc = kwargs['wakee_proc']
b4156aa2 96
a621ba35
AB
97 if not self._filter_cpu(cpu_id):
98 return
99
b4156aa2
AB
100 if cpu_id not in self.cpus:
101 self.cpus[cpu_id] = CpuUsageStats(cpu_id)
102
103 cpu = self.cpus[cpu_id]
104 if cpu.current_task_start_ts is not None:
105 cpu.total_usage_time += timestamp - cpu.current_task_start_ts
106
a621ba35 107 if not self._filter_process(wakee_proc):
b4156aa2
AB
108 cpu.current_task_start_ts = None
109 else:
110 cpu.current_task_start_ts = timestamp
111
112 def _process_sched_switch_per_tid(self, **kwargs):
a621ba35
AB
113 cpu_id = kwargs['cpu_id']
114 wakee_proc = kwargs['wakee_proc']
b4156aa2
AB
115 timestamp = kwargs['timestamp']
116 prev_tid = kwargs['prev_tid']
117 next_tid = kwargs['next_tid']
118 next_comm = kwargs['next_comm']
119
a621ba35
AB
120 if not self._filter_cpu(cpu_id):
121 return
122
b4156aa2
AB
123 if prev_tid in self.tids:
124 prev_proc = self.tids[prev_tid]
125 if prev_proc.last_sched_ts is not None:
126 prev_proc.total_cpu_time += timestamp - prev_proc.last_sched_ts
127 prev_proc.last_sched_ts = None
128
a621ba35
AB
129 # Only filter on wakee_proc after finalizing the prev_proc
130 # accounting
131 if not self._filter_process(wakee_proc):
b4156aa2
AB
132 return
133
134 if next_tid not in self.tids:
ff833baa
AB
135 self.tids[next_tid] = ProcessCpuStats(None, next_tid, next_comm)
136 self.tids[next_tid].update_prio(timestamp, wakee_proc.prio)
b4156aa2
AB
137
138 next_proc = self.tids[next_tid]
139 next_proc.last_sched_ts = timestamp
ff833baa 140
b4156aa2
AB
141
142 def _process_sched_migrate_task(self, **kwargs):
a621ba35 143 cpu_id = kwargs['cpu_id']
b4156aa2
AB
144 proc = kwargs['proc']
145 tid = proc.tid
a621ba35
AB
146
147 if not self._filter_process(proc):
148 return
149 if not self._filter_cpu(cpu_id):
150 return
151
b4156aa2
AB
152 if tid not in self.tids:
153 self.tids[tid] = ProcessCpuStats.new_from_process(proc)
154
155 self.tids[tid].migrate_count += 1
b5db5706 156
ff833baa
AB
157 def _process_prio_changed(self, **kwargs):
158 timestamp = kwargs['timestamp']
159 prio = kwargs['prio']
160 tid = kwargs['tid']
161
162 if tid not in self.tids:
163 return
164
165 self.tids[tid].update_prio(timestamp, prio)
166
43b66dd6
AB
167 def _filter_process(self, proc):
168 # Exclude swapper
169 if proc.tid == 0:
170 return False
171
172 return super()._filter_process(proc)
173
bd3cd7c5
JD
174 @property
175 def event_count(self):
176 return self._ev_count
b4156aa2
AB
177
178
179class CpuUsageStats():
180 def __init__(self, cpu_id):
181 self.cpu_id = cpu_id
182 # Usage time and start timestamp are in nanoseconds (ns)
183 self.total_usage_time = 0
184 self.current_task_start_ts = None
185 self.usage_percent = None
186
187 def compute_stats(self, duration):
88e6acc6
AB
188 if duration != 0:
189 self.usage_percent = self.total_usage_time * 100 / duration
190 else:
191 self.usage_percent = 0
b4156aa2 192
ff833baa 193 def reset(self):
b4156aa2
AB
194 self.total_usage_time = 0
195 self.usage_percent = None
b4156aa2
AB
196
197
ff833baa
AB
198class ProcessCpuStats(stats.Process):
199 def __init__(self, pid, tid, comm):
200 super().__init__(pid, tid, comm)
201
b4156aa2
AB
202 # CPU Time and timestamp in nanoseconds (ns)
203 self.total_cpu_time = 0
204 self.last_sched_ts = None
205 self.migrate_count = 0
206 self.usage_percent = None
207
b4156aa2 208 def compute_stats(self, duration):
88e6acc6
AB
209 if duration != 0:
210 self.usage_percent = self.total_cpu_time * 100 / duration
211 else:
212 self.usage_percent = 0
b4156aa2 213
ff833baa
AB
214 def reset(self):
215 super().reset()
b4156aa2
AB
216 self.total_cpu_time = 0
217 self.migrate_count = 0
218 self.usage_percent = None
This page took 0.033002 seconds and 5 git commands to generate.