3 # The MIT License (MIT)
5 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
6 # 2015 - Antoine Busque <abusque@efficios.com>
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
15 # The above copyright notice and this permission notice shall be included in
16 # all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 from .analysis
import Analysis
29 class Cputop(Analysis
):
30 def __init__(self
, state
):
32 'sched_migrate_task': self
._process
_sched
_migrate
_task
,
33 'sched_switch_per_cpu': self
._process
_sched
_switch
_per
_cpu
,
34 'sched_switch_per_tid': self
._process
_sched
_switch
_per
_tid
38 self
._state
.register_notification_cbs(notification_cbs
)
43 def process_event(self
, ev
):
46 def reset(self
, timestamp
):
47 for cpu_id
in self
.cpus
:
48 self
.cpus
[cpu_id
].reset(timestamp
)
51 self
.tids
[tid
].reset(timestamp
)
53 def compute_stats(self
, start_ts
, end_ts
):
54 """Compute usage stats relative to a certain time range
56 For each CPU and process tracked by the analysis, we set its
57 usage_percent attribute, which represents the percentage of
58 usage time for the given CPU or process relative to the full
59 duration of the time range. Do note that we need to know the
60 timestamps and not just the duration, because if a CPU or a
61 process is currently busy, we use the end timestamp to add
62 the partial results of the currently running task to the usage
66 start_ts (int): start of time range (nanoseconds from unix
68 end_ts (int): end of time range (nanoseconds from unix epoch)
70 duration
= end_ts
- start_ts
72 for cpu_id
in self
.cpus
:
73 cpu
= self
.cpus
[cpu_id
]
74 if cpu
.current_task_start_ts
is not None:
75 cpu
.total_usage_time
+= end_ts
- cpu
.current_task_start_ts
77 cpu
.compute_stats(duration
)
81 if proc
.last_sched_ts
is not None:
82 proc
.total_cpu_time
+= end_ts
- proc
.last_sched_ts
84 proc
.compute_stats(duration
)
86 def _process_sched_switch_per_cpu(self
, **kwargs
):
87 timestamp
= kwargs
['timestamp']
88 cpu_id
= kwargs
['cpu_id']
89 next_tid
= kwargs
['next_tid']
91 if cpu_id
not in self
.cpus
:
92 self
.cpus
[cpu_id
] = CpuUsageStats(cpu_id
)
94 cpu
= self
.cpus
[cpu_id
]
95 if cpu
.current_task_start_ts
is not None:
96 cpu
.total_usage_time
+= timestamp
- cpu
.current_task_start_ts
99 cpu
.current_task_start_ts
= None
101 cpu
.current_task_start_ts
= timestamp
103 def _process_sched_switch_per_tid(self
, **kwargs
):
104 timestamp
= kwargs
['timestamp']
105 prev_tid
= kwargs
['prev_tid']
106 next_tid
= kwargs
['next_tid']
107 next_comm
= kwargs
['next_comm']
109 if prev_tid
in self
.tids
:
110 prev_proc
= self
.tids
[prev_tid
]
111 if prev_proc
.last_sched_ts
is not None:
112 prev_proc
.total_cpu_time
+= timestamp
- prev_proc
.last_sched_ts
113 prev_proc
.last_sched_ts
= None
115 # Don't account for swapper process
119 if next_tid
not in self
.tids
:
120 self
.tids
[next_tid
] = ProcessCpuStats(next_tid
, next_comm
)
122 next_proc
= self
.tids
[next_tid
]
123 next_proc
.last_sched_ts
= timestamp
125 def _process_sched_migrate_task(self
, **kwargs
):
126 proc
= kwargs
['proc']
128 if tid
not in self
.tids
:
129 self
.tids
[tid
] = ProcessCpuStats
.new_from_process(proc
)
131 self
.tids
[tid
].migrate_count
+= 1
134 def event_count(self
):
135 return self
._ev
_count
138 class CpuUsageStats():
139 def __init__(self
, cpu_id
):
141 # Usage time and start timestamp are in nanoseconds (ns)
142 self
.total_usage_time
= 0
143 self
.current_task_start_ts
= None
144 self
.usage_percent
= None
146 def compute_stats(self
, duration
):
147 self
.usage_percent
= self
.total_usage_time
* 100 / duration
149 def reset(self
, timestamp
):
150 self
.total_usage_time
= 0
151 self
.usage_percent
= None
152 if self
.current_task_start_ts
is not None:
153 self
.current_task_start_ts
= timestamp
156 class ProcessCpuStats():
157 def __init__(self
, tid
, comm
):
160 # CPU Time and timestamp in nanoseconds (ns)
161 self
.total_cpu_time
= 0
162 self
.last_sched_ts
= None
163 self
.migrate_count
= 0
164 self
.usage_percent
= None
167 def new_from_process(cls
, proc
):
168 return cls(proc
.tid
, proc
.comm
)
170 def compute_stats(self
, duration
):
171 self
.usage_percent
= self
.total_cpu_time
* 100 / duration
173 def reset(self
, timestamp
):
174 self
.total_cpu_time
= 0
175 self
.migrate_count
= 0
176 self
.usage_percent
= None
177 if self
.last_sched_ts
is not None:
178 self
.last_sched_ts
= timestamp
This page took 0.037499 seconds and 5 git commands to generate.