1 # Cpu task migration overview toy
3 # Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com>
5 # perf script event handlers have been generated by perf script -g python
7 # This software is distributed under the terms of the GNU General
8 # Public License ("GPL") version 2 as published by the Free Software
10 from __future__
import print_function
15 from collections
import defaultdict
17 from UserList
import UserList
19 # Python 3: UserList moved to the collections package
20 from collections
import UserList
22 sys
.path
.append(os
.environ
['PERF_EXEC_PATH'] + \
23 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
24 sys
.path
.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace')
26 from perf_trace_context
import *
28 from SchedGui
import *
31 threads
= { 0 : "idle"}
34 return "%s:%d" % (threads
[pid
], pid
)
36 class RunqueueEventUnknown
:
44 class RunqueueEventSleep
:
49 def __init__(self
, sleeper
):
50 self
.sleeper
= sleeper
53 return "%s gone to sleep" % thread_name(self
.sleeper
)
55 class RunqueueEventWakeup
:
58 return (0xff, 0xff, 0)
60 def __init__(self
, wakee
):
64 return "%s woke up" % thread_name(self
.wakee
)
66 class RunqueueEventFork
:
71 def __init__(self
, child
):
75 return "new forked task %s" % thread_name(self
.child
)
77 class RunqueueMigrateIn
:
80 return (0, 0xf0, 0xff)
82 def __init__(self
, new
):
86 return "task migrated in %s" % thread_name(self
.new
)
88 class RunqueueMigrateOut
:
91 return (0xff, 0, 0xff)
93 def __init__(self
, old
):
97 return "task migrated out %s" % thread_name(self
.old
)
99 class RunqueueSnapshot
:
100 def __init__(self
, tasks
= [0], event
= RunqueueEventUnknown()):
101 self
.tasks
= tuple(tasks
)
104 def sched_switch(self
, prev
, prev_state
, next
):
105 event
= RunqueueEventUnknown()
107 if taskState(prev_state
) == "R" and next
in self
.tasks \
108 and prev
in self
.tasks
:
111 if taskState(prev_state
) != "R":
112 event
= RunqueueEventSleep(prev
)
114 next_tasks
= list(self
.tasks
[:])
115 if prev
in self
.tasks
:
116 if taskState(prev_state
) != "R":
117 next_tasks
.remove(prev
)
118 elif taskState(prev_state
) == "R":
119 next_tasks
.append(prev
)
121 if next
not in next_tasks
:
122 next_tasks
.append(next
)
124 return RunqueueSnapshot(next_tasks
, event
)
126 def migrate_out(self
, old
):
127 if old
not in self
.tasks
:
129 next_tasks
= [task
for task
in self
.tasks
if task
!= old
]
131 return RunqueueSnapshot(next_tasks
, RunqueueMigrateOut(old
))
133 def __migrate_in(self
, new
, event
):
134 if new
in self
.tasks
:
137 next_tasks
= self
.tasks
[:] + tuple([new
])
139 return RunqueueSnapshot(next_tasks
, event
)
141 def migrate_in(self
, new
):
142 return self
.__migrate
_in
(new
, RunqueueMigrateIn(new
))
144 def wake_up(self
, new
):
145 return self
.__migrate
_in
(new
, RunqueueEventWakeup(new
))
147 def wake_up_new(self
, new
):
148 return self
.__migrate
_in
(new
, RunqueueEventFork(new
))
151 """ Provide the number of tasks on the runqueue.
153 return len(self
.tasks
) - 1
156 ret
= self
.tasks
.__repr
__()
157 ret
+= self
.origin_tostring()
162 def __init__(self
, start
, prev
):
166 # cpus that triggered the event
169 self
.total_load
= prev
.total_load
170 self
.rqs
= prev
.rqs
.copy()
172 self
.rqs
= defaultdict(RunqueueSnapshot
)
175 def __update_total_load(self
, old_rq
, new_rq
):
176 diff
= new_rq
.load() - old_rq
.load()
177 self
.total_load
+= diff
179 def sched_switch(self
, ts_list
, prev
, prev_state
, next
, cpu
):
180 old_rq
= self
.prev
.rqs
[cpu
]
181 new_rq
= old_rq
.sched_switch(prev
, prev_state
, next
)
186 self
.rqs
[cpu
] = new_rq
187 self
.__update
_total
_load
(old_rq
, new_rq
)
189 self
.event_cpus
= [cpu
]
191 def migrate(self
, ts_list
, new
, old_cpu
, new_cpu
):
192 if old_cpu
== new_cpu
:
194 old_rq
= self
.prev
.rqs
[old_cpu
]
195 out_rq
= old_rq
.migrate_out(new
)
196 self
.rqs
[old_cpu
] = out_rq
197 self
.__update
_total
_load
(old_rq
, out_rq
)
199 new_rq
= self
.prev
.rqs
[new_cpu
]
200 in_rq
= new_rq
.migrate_in(new
)
201 self
.rqs
[new_cpu
] = in_rq
202 self
.__update
_total
_load
(new_rq
, in_rq
)
206 if old_rq
is not out_rq
:
207 self
.event_cpus
.append(old_cpu
)
208 self
.event_cpus
.append(new_cpu
)
210 def wake_up(self
, ts_list
, pid
, cpu
, fork
):
211 old_rq
= self
.prev
.rqs
[cpu
]
213 new_rq
= old_rq
.wake_up_new(pid
)
215 new_rq
= old_rq
.wake_up(pid
)
219 self
.rqs
[cpu
] = new_rq
220 self
.__update
_total
_load
(old_rq
, new_rq
)
222 self
.event_cpus
= [cpu
]
226 return TimeSlice(t
, self
)
228 class TimeSliceList(UserList
):
229 def __init__(self
, arg
= []):
232 def get_time_slice(self
, ts
):
233 if len(self
.data
) == 0:
234 slice = TimeSlice(ts
, TimeSlice(-1, None))
236 slice = self
.data
[-1].next(ts
)
239 def find_time_slice(self
, ts
):
245 if start
== end
or start
== end
- 1:
248 i
= (end
+ start
) / 2
249 if self
.data
[i
].start
<= ts
and self
.data
[i
].end
>= ts
:
254 if self
.data
[i
].end
< ts
:
257 elif self
.data
[i
].start
> ts
:
262 def set_root_win(self
, win
):
265 def mouse_down(self
, cpu
, t
):
266 idx
= self
.find_time_slice(t
)
272 raw
= "CPU: %d\n" % cpu
273 raw
+= "Last event : %s\n" % rq
.event
.__repr
__()
274 raw
+= "Timestamp : %d.%06d\n" % (ts
.start
/ (10 ** 9), (ts
.start
% (10 ** 9)) / 1000)
275 raw
+= "Duration : %6d us\n" % ((ts
.end
- ts
.start
) / (10 ** 6))
276 raw
+= "Load = %d\n" % rq
.load()
278 raw
+= "%s \n" % thread_name(t
)
280 self
.root_win
.update_summary(raw
)
282 def update_rectangle_cpu(self
, slice, cpu
):
285 if slice.total_load
!= 0:
286 load_rate
= rq
.load() / float(slice.total_load
)
290 red_power
= int(0xff - (0xff * load_rate
))
291 color
= (0xff, red_power
, red_power
)
295 if cpu
in slice.event_cpus
:
296 top_color
= rq
.event
.color()
298 self
.root_win
.paint_rectangle_zone(cpu
, color
, top_color
, slice.start
, slice.end
)
300 def fill_zone(self
, start
, end
):
301 i
= self
.find_time_slice(start
)
305 for i
in range(i
, len(self
.data
)):
306 timeslice
= self
.data
[i
]
307 if timeslice
.start
> end
:
310 for cpu
in timeslice
.rqs
:
311 self
.update_rectangle_cpu(timeslice
, cpu
)
314 if len(self
.data
) == 0:
317 return (self
.data
[0].start
, self
.data
[-1].end
)
319 def nr_rectangles(self
):
320 last_ts
= self
.data
[-1]
322 for cpu
in last_ts
.rqs
:
328 class SchedEventProxy
:
330 self
.current_tsk
= defaultdict(lambda : -1)
331 self
.timeslices
= TimeSliceList()
333 def sched_switch(self
, headers
, prev_comm
, prev_pid
, prev_prio
, prev_state
,
334 next_comm
, next_pid
, next_prio
):
335 """ Ensure the task we sched out this cpu is really the one
336 we logged. Otherwise we may have missed traces """
338 on_cpu_task
= self
.current_tsk
[headers
.cpu
]
340 if on_cpu_task
!= -1 and on_cpu_task
!= prev_pid
:
341 print("Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \
342 headers
.ts_format(), headers
.cpu
, prev_comm
, prev_pid
, next_comm
, next_pid
)
344 threads
[prev_pid
] = prev_comm
345 threads
[next_pid
] = next_comm
346 self
.current_tsk
[headers
.cpu
] = next_pid
348 ts
= self
.timeslices
.get_time_slice(headers
.ts())
349 ts
.sched_switch(self
.timeslices
, prev_pid
, prev_state
, next_pid
, headers
.cpu
)
351 def migrate(self
, headers
, pid
, prio
, orig_cpu
, dest_cpu
):
352 ts
= self
.timeslices
.get_time_slice(headers
.ts())
353 ts
.migrate(self
.timeslices
, pid
, orig_cpu
, dest_cpu
)
355 def wake_up(self
, headers
, comm
, pid
, success
, target_cpu
, fork
):
358 ts
= self
.timeslices
.get_time_slice(headers
.ts())
359 ts
.wake_up(self
.timeslices
, pid
, target_cpu
, fork
)
364 parser
= SchedEventProxy()
368 timeslices
= parser
.timeslices
369 frame
= RootFrame(timeslices
, "Migration")
372 def sched__sched_stat_runtime(event_name
, context
, common_cpu
,
373 common_secs
, common_nsecs
, common_pid
, common_comm
,
374 common_callchain
, comm
, pid
, runtime
, vruntime
):
377 def sched__sched_stat_iowait(event_name
, context
, common_cpu
,
378 common_secs
, common_nsecs
, common_pid
, common_comm
,
379 common_callchain
, comm
, pid
, delay
):
382 def sched__sched_stat_sleep(event_name
, context
, common_cpu
,
383 common_secs
, common_nsecs
, common_pid
, common_comm
,
384 common_callchain
, comm
, pid
, delay
):
387 def sched__sched_stat_wait(event_name
, context
, common_cpu
,
388 common_secs
, common_nsecs
, common_pid
, common_comm
,
389 common_callchain
, comm
, pid
, delay
):
392 def sched__sched_process_fork(event_name
, context
, common_cpu
,
393 common_secs
, common_nsecs
, common_pid
, common_comm
,
394 common_callchain
, parent_comm
, parent_pid
, child_comm
, child_pid
):
397 def sched__sched_process_wait(event_name
, context
, common_cpu
,
398 common_secs
, common_nsecs
, common_pid
, common_comm
,
399 common_callchain
, comm
, pid
, prio
):
402 def sched__sched_process_exit(event_name
, context
, common_cpu
,
403 common_secs
, common_nsecs
, common_pid
, common_comm
,
404 common_callchain
, comm
, pid
, prio
):
407 def sched__sched_process_free(event_name
, context
, common_cpu
,
408 common_secs
, common_nsecs
, common_pid
, common_comm
,
409 common_callchain
, comm
, pid
, prio
):
412 def sched__sched_migrate_task(event_name
, context
, common_cpu
,
413 common_secs
, common_nsecs
, common_pid
, common_comm
,
414 common_callchain
, comm
, pid
, prio
, orig_cpu
,
416 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
417 common_pid
, common_comm
, common_callchain
)
418 parser
.migrate(headers
, pid
, prio
, orig_cpu
, dest_cpu
)
420 def sched__sched_switch(event_name
, context
, common_cpu
,
421 common_secs
, common_nsecs
, common_pid
, common_comm
, common_callchain
,
422 prev_comm
, prev_pid
, prev_prio
, prev_state
,
423 next_comm
, next_pid
, next_prio
):
425 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
426 common_pid
, common_comm
, common_callchain
)
427 parser
.sched_switch(headers
, prev_comm
, prev_pid
, prev_prio
, prev_state
,
428 next_comm
, next_pid
, next_prio
)
430 def sched__sched_wakeup_new(event_name
, context
, common_cpu
,
431 common_secs
, common_nsecs
, common_pid
, common_comm
,
432 common_callchain
, comm
, pid
, prio
, success
,
434 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
435 common_pid
, common_comm
, common_callchain
)
436 parser
.wake_up(headers
, comm
, pid
, success
, target_cpu
, 1)
438 def sched__sched_wakeup(event_name
, context
, common_cpu
,
439 common_secs
, common_nsecs
, common_pid
, common_comm
,
440 common_callchain
, comm
, pid
, prio
, success
,
442 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
443 common_pid
, common_comm
, common_callchain
)
444 parser
.wake_up(headers
, comm
, pid
, success
, target_cpu
, 0)
446 def sched__sched_wait_task(event_name
, context
, common_cpu
,
447 common_secs
, common_nsecs
, common_pid
, common_comm
,
448 common_callchain
, comm
, pid
, prio
):
451 def sched__sched_kthread_stop_ret(event_name
, context
, common_cpu
,
452 common_secs
, common_nsecs
, common_pid
, common_comm
,
453 common_callchain
, ret
):
456 def sched__sched_kthread_stop(event_name
, context
, common_cpu
,
457 common_secs
, common_nsecs
, common_pid
, common_comm
,
458 common_callchain
, comm
, pid
):
461 def trace_unhandled(event_name
, context
, event_fields_dict
):