3 # Cpu task migration overview toy
5 # Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com>
7 # perf script event handlers have been generated by perf script -g python
9 # This software is distributed under the terms of the GNU General
10 # Public License ("GPL") version 2 as published by the Free Software
17 from collections
import defaultdict
18 from UserList
import UserList
20 sys
.path
.append(os
.environ
['PERF_EXEC_PATH'] + \
21 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
22 sys
.path
.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace')
24 from perf_trace_context
import *
26 from SchedGui
import *
29 threads
= { 0 : "idle"}
32 return "%s:%d" % (threads
[pid
], pid
)
34 class RunqueueEventUnknown
:
42 class RunqueueEventSleep
:
47 def __init__(self
, sleeper
):
48 self
.sleeper
= sleeper
51 return "%s gone to sleep" % thread_name(self
.sleeper
)
53 class RunqueueEventWakeup
:
56 return (0xff, 0xff, 0)
58 def __init__(self
, wakee
):
62 return "%s woke up" % thread_name(self
.wakee
)
64 class RunqueueEventFork
:
69 def __init__(self
, child
):
73 return "new forked task %s" % thread_name(self
.child
)
75 class RunqueueMigrateIn
:
78 return (0, 0xf0, 0xff)
80 def __init__(self
, new
):
84 return "task migrated in %s" % thread_name(self
.new
)
86 class RunqueueMigrateOut
:
89 return (0xff, 0, 0xff)
91 def __init__(self
, old
):
95 return "task migrated out %s" % thread_name(self
.old
)
97 class RunqueueSnapshot
:
98 def __init__(self
, tasks
= [0], event
= RunqueueEventUnknown()):
99 self
.tasks
= tuple(tasks
)
102 def sched_switch(self
, prev
, prev_state
, next
):
103 event
= RunqueueEventUnknown()
105 if taskState(prev_state
) == "R" and next
in self
.tasks \
106 and prev
in self
.tasks
:
109 if taskState(prev_state
) != "R":
110 event
= RunqueueEventSleep(prev
)
112 next_tasks
= list(self
.tasks
[:])
113 if prev
in self
.tasks
:
114 if taskState(prev_state
) != "R":
115 next_tasks
.remove(prev
)
116 elif taskState(prev_state
) == "R":
117 next_tasks
.append(prev
)
119 if next
not in next_tasks
:
120 next_tasks
.append(next
)
122 return RunqueueSnapshot(next_tasks
, event
)
124 def migrate_out(self
, old
):
125 if old
not in self
.tasks
:
127 next_tasks
= [task
for task
in self
.tasks
if task
!= old
]
129 return RunqueueSnapshot(next_tasks
, RunqueueMigrateOut(old
))
131 def __migrate_in(self
, new
, event
):
132 if new
in self
.tasks
:
135 next_tasks
= self
.tasks
[:] + tuple([new
])
137 return RunqueueSnapshot(next_tasks
, event
)
139 def migrate_in(self
, new
):
140 return self
.__migrate
_in
(new
, RunqueueMigrateIn(new
))
142 def wake_up(self
, new
):
143 return self
.__migrate
_in
(new
, RunqueueEventWakeup(new
))
145 def wake_up_new(self
, new
):
146 return self
.__migrate
_in
(new
, RunqueueEventFork(new
))
149 """ Provide the number of tasks on the runqueue.
151 return len(self
.tasks
) - 1
154 ret
= self
.tasks
.__repr
__()
155 ret
+= self
.origin_tostring()
160 def __init__(self
, start
, prev
):
164 # cpus that triggered the event
167 self
.total_load
= prev
.total_load
168 self
.rqs
= prev
.rqs
.copy()
170 self
.rqs
= defaultdict(RunqueueSnapshot
)
173 def __update_total_load(self
, old_rq
, new_rq
):
174 diff
= new_rq
.load() - old_rq
.load()
175 self
.total_load
+= diff
177 def sched_switch(self
, ts_list
, prev
, prev_state
, next
, cpu
):
178 old_rq
= self
.prev
.rqs
[cpu
]
179 new_rq
= old_rq
.sched_switch(prev
, prev_state
, next
)
184 self
.rqs
[cpu
] = new_rq
185 self
.__update
_total
_load
(old_rq
, new_rq
)
187 self
.event_cpus
= [cpu
]
189 def migrate(self
, ts_list
, new
, old_cpu
, new_cpu
):
190 if old_cpu
== new_cpu
:
192 old_rq
= self
.prev
.rqs
[old_cpu
]
193 out_rq
= old_rq
.migrate_out(new
)
194 self
.rqs
[old_cpu
] = out_rq
195 self
.__update
_total
_load
(old_rq
, out_rq
)
197 new_rq
= self
.prev
.rqs
[new_cpu
]
198 in_rq
= new_rq
.migrate_in(new
)
199 self
.rqs
[new_cpu
] = in_rq
200 self
.__update
_total
_load
(new_rq
, in_rq
)
204 if old_rq
is not out_rq
:
205 self
.event_cpus
.append(old_cpu
)
206 self
.event_cpus
.append(new_cpu
)
208 def wake_up(self
, ts_list
, pid
, cpu
, fork
):
209 old_rq
= self
.prev
.rqs
[cpu
]
211 new_rq
= old_rq
.wake_up_new(pid
)
213 new_rq
= old_rq
.wake_up(pid
)
217 self
.rqs
[cpu
] = new_rq
218 self
.__update
_total
_load
(old_rq
, new_rq
)
220 self
.event_cpus
= [cpu
]
224 return TimeSlice(t
, self
)
226 class TimeSliceList(UserList
):
227 def __init__(self
, arg
= []):
230 def get_time_slice(self
, ts
):
231 if len(self
.data
) == 0:
232 slice = TimeSlice(ts
, TimeSlice(-1, None))
234 slice = self
.data
[-1].next(ts
)
237 def find_time_slice(self
, ts
):
243 if start
== end
or start
== end
- 1:
246 i
= (end
+ start
) / 2
247 if self
.data
[i
].start
<= ts
and self
.data
[i
].end
>= ts
:
252 if self
.data
[i
].end
< ts
:
255 elif self
.data
[i
].start
> ts
:
260 def set_root_win(self
, win
):
263 def mouse_down(self
, cpu
, t
):
264 idx
= self
.find_time_slice(t
)
270 raw
= "CPU: %d\n" % cpu
271 raw
+= "Last event : %s\n" % rq
.event
.__repr
__()
272 raw
+= "Timestamp : %d.%06d\n" % (ts
.start
/ (10 ** 9), (ts
.start
% (10 ** 9)) / 1000)
273 raw
+= "Duration : %6d us\n" % ((ts
.end
- ts
.start
) / (10 ** 6))
274 raw
+= "Load = %d\n" % rq
.load()
276 raw
+= "%s \n" % thread_name(t
)
278 self
.root_win
.update_summary(raw
)
280 def update_rectangle_cpu(self
, slice, cpu
):
283 if slice.total_load
!= 0:
284 load_rate
= rq
.load() / float(slice.total_load
)
288 red_power
= int(0xff - (0xff * load_rate
))
289 color
= (0xff, red_power
, red_power
)
293 if cpu
in slice.event_cpus
:
294 top_color
= rq
.event
.color()
296 self
.root_win
.paint_rectangle_zone(cpu
, color
, top_color
, slice.start
, slice.end
)
298 def fill_zone(self
, start
, end
):
299 i
= self
.find_time_slice(start
)
303 for i
in xrange(i
, len(self
.data
)):
304 timeslice
= self
.data
[i
]
305 if timeslice
.start
> end
:
308 for cpu
in timeslice
.rqs
:
309 self
.update_rectangle_cpu(timeslice
, cpu
)
312 if len(self
.data
) == 0:
315 return (self
.data
[0].start
, self
.data
[-1].end
)
317 def nr_rectangles(self
):
318 last_ts
= self
.data
[-1]
320 for cpu
in last_ts
.rqs
:
326 class SchedEventProxy
:
328 self
.current_tsk
= defaultdict(lambda : -1)
329 self
.timeslices
= TimeSliceList()
331 def sched_switch(self
, headers
, prev_comm
, prev_pid
, prev_prio
, prev_state
,
332 next_comm
, next_pid
, next_prio
):
333 """ Ensure the task we sched out this cpu is really the one
334 we logged. Otherwise we may have missed traces """
336 on_cpu_task
= self
.current_tsk
[headers
.cpu
]
338 if on_cpu_task
!= -1 and on_cpu_task
!= prev_pid
:
339 print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \
340 (headers
.ts_format(), headers
.cpu
, prev_comm
, prev_pid
, next_comm
, next_pid
)
342 threads
[prev_pid
] = prev_comm
343 threads
[next_pid
] = next_comm
344 self
.current_tsk
[headers
.cpu
] = next_pid
346 ts
= self
.timeslices
.get_time_slice(headers
.ts())
347 ts
.sched_switch(self
.timeslices
, prev_pid
, prev_state
, next_pid
, headers
.cpu
)
349 def migrate(self
, headers
, pid
, prio
, orig_cpu
, dest_cpu
):
350 ts
= self
.timeslices
.get_time_slice(headers
.ts())
351 ts
.migrate(self
.timeslices
, pid
, orig_cpu
, dest_cpu
)
353 def wake_up(self
, headers
, comm
, pid
, success
, target_cpu
, fork
):
356 ts
= self
.timeslices
.get_time_slice(headers
.ts())
357 ts
.wake_up(self
.timeslices
, pid
, target_cpu
, fork
)
362 parser
= SchedEventProxy()
366 timeslices
= parser
.timeslices
367 frame
= RootFrame(timeslices
, "Migration")
370 def sched__sched_stat_runtime(event_name
, context
, common_cpu
,
371 common_secs
, common_nsecs
, common_pid
, common_comm
,
372 comm
, pid
, runtime
, vruntime
):
375 def sched__sched_stat_iowait(event_name
, context
, common_cpu
,
376 common_secs
, common_nsecs
, common_pid
, common_comm
,
380 def sched__sched_stat_sleep(event_name
, context
, common_cpu
,
381 common_secs
, common_nsecs
, common_pid
, common_comm
,
385 def sched__sched_stat_wait(event_name
, context
, common_cpu
,
386 common_secs
, common_nsecs
, common_pid
, common_comm
,
390 def sched__sched_process_fork(event_name
, context
, common_cpu
,
391 common_secs
, common_nsecs
, common_pid
, common_comm
,
392 parent_comm
, parent_pid
, child_comm
, child_pid
):
395 def sched__sched_process_wait(event_name
, context
, common_cpu
,
396 common_secs
, common_nsecs
, common_pid
, common_comm
,
400 def sched__sched_process_exit(event_name
, context
, common_cpu
,
401 common_secs
, common_nsecs
, common_pid
, common_comm
,
405 def sched__sched_process_free(event_name
, context
, common_cpu
,
406 common_secs
, common_nsecs
, common_pid
, common_comm
,
410 def sched__sched_migrate_task(event_name
, context
, common_cpu
,
411 common_secs
, common_nsecs
, common_pid
, common_comm
,
412 comm
, pid
, prio
, orig_cpu
,
414 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
415 common_pid
, common_comm
)
416 parser
.migrate(headers
, pid
, prio
, orig_cpu
, dest_cpu
)
418 def sched__sched_switch(event_name
, context
, common_cpu
,
419 common_secs
, common_nsecs
, common_pid
, common_comm
,
420 prev_comm
, prev_pid
, prev_prio
, prev_state
,
421 next_comm
, next_pid
, next_prio
):
423 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
424 common_pid
, common_comm
)
425 parser
.sched_switch(headers
, prev_comm
, prev_pid
, prev_prio
, prev_state
,
426 next_comm
, next_pid
, next_prio
)
428 def sched__sched_wakeup_new(event_name
, context
, common_cpu
,
429 common_secs
, common_nsecs
, common_pid
, common_comm
,
430 comm
, pid
, prio
, success
,
432 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
433 common_pid
, common_comm
)
434 parser
.wake_up(headers
, comm
, pid
, success
, target_cpu
, 1)
436 def sched__sched_wakeup(event_name
, context
, common_cpu
,
437 common_secs
, common_nsecs
, common_pid
, common_comm
,
438 comm
, pid
, prio
, success
,
440 headers
= EventHeaders(common_cpu
, common_secs
, common_nsecs
,
441 common_pid
, common_comm
)
442 parser
.wake_up(headers
, comm
, pid
, success
, target_cpu
, 0)
444 def sched__sched_wait_task(event_name
, context
, common_cpu
,
445 common_secs
, common_nsecs
, common_pid
, common_comm
,
449 def sched__sched_kthread_stop_ret(event_name
, context
, common_cpu
,
450 common_secs
, common_nsecs
, common_pid
, common_comm
,
454 def sched__sched_kthread_stop(event_name
, context
, common_cpu
,
455 common_secs
, common_nsecs
, common_pid
, common_comm
,
459 def trace_unhandled(event_name
, context
, common_cpu
, common_secs
, common_nsecs
,
460 common_pid
, common_comm
):