]> bbs.cooldavid.org Git - net-next-2.6.git/blame - tools/perf/scripts/python/sched-migration.py
perf, sched migration: Librarize the GUI class
[net-next-2.6.git] / tools / perf / scripts / python / sched-migration.py
CommitLineData
880d22f2
FW
1#!/usr/bin/python
2#
3# Cpu task migration overview toy
4#
5# Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com>
6#
7# perf trace event handlers have been generated by perf trace -g python
8#
df92b408
FW
9# This software is distributed under the terms of the GNU General
10# Public License ("GPL") version 2 as published by the Free Software
11# Foundation.
880d22f2
FW
12
13
880d22f2
FW
14import os
15import sys
16
17from collections import defaultdict
18from UserList import UserList
19
20sys.path.append(os.environ['PERF_EXEC_PATH'] + \
21 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
df92b408 22sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace')
880d22f2
FW
23
24from perf_trace_context import *
25from Core import *
df92b408 26from SchedGui import *
880d22f2
FW
27
28
29threads = { 0 : "idle"}
30
31def thread_name(pid):
32 return "%s:%d" % (threads[pid], pid)
33
34class EventHeaders:
35 def __init__(self, common_cpu, common_secs, common_nsecs,
36 common_pid, common_comm):
37 self.cpu = common_cpu
38 self.secs = common_secs
39 self.nsecs = common_nsecs
40 self.pid = common_pid
41 self.comm = common_comm
42
43 def ts(self):
44 return (self.secs * (10 ** 9)) + self.nsecs
45
46 def ts_format(self):
47 return "%d.%d" % (self.secs, int(self.nsecs / 1000))
48
49
50def taskState(state):
51 states = {
52 0 : "R",
53 1 : "S",
54 2 : "D",
55 64: "DEAD"
56 }
57
58 if state not in states:
207f90fc 59 return "Unknown"
880d22f2
FW
60
61 return states[state]
62
63
64class RunqueueEventUnknown:
65 @staticmethod
66 def color():
67 return None
68
69 def __repr__(self):
70 return "unknown"
71
72class RunqueueEventSleep:
73 @staticmethod
74 def color():
75 return (0, 0, 0xff)
76
77 def __init__(self, sleeper):
78 self.sleeper = sleeper
79
80 def __repr__(self):
81 return "%s gone to sleep" % thread_name(self.sleeper)
82
83class RunqueueEventWakeup:
84 @staticmethod
85 def color():
86 return (0xff, 0xff, 0)
87
88 def __init__(self, wakee):
89 self.wakee = wakee
90
91 def __repr__(self):
92 return "%s woke up" % thread_name(self.wakee)
93
94class RunqueueEventFork:
95 @staticmethod
96 def color():
97 return (0, 0xff, 0)
98
99 def __init__(self, child):
100 self.child = child
101
102 def __repr__(self):
103 return "new forked task %s" % thread_name(self.child)
104
105class RunqueueMigrateIn:
106 @staticmethod
107 def color():
108 return (0, 0xf0, 0xff)
109
110 def __init__(self, new):
111 self.new = new
112
113 def __repr__(self):
114 return "task migrated in %s" % thread_name(self.new)
115
116class RunqueueMigrateOut:
117 @staticmethod
118 def color():
119 return (0xff, 0, 0xff)
120
121 def __init__(self, old):
122 self.old = old
123
124 def __repr__(self):
125 return "task migrated out %s" % thread_name(self.old)
126
127class RunqueueSnapshot:
128 def __init__(self, tasks = [0], event = RunqueueEventUnknown()):
129 self.tasks = tuple(tasks)
130 self.event = event
131
132 def sched_switch(self, prev, prev_state, next):
133 event = RunqueueEventUnknown()
134
135 if taskState(prev_state) == "R" and next in self.tasks \
136 and prev in self.tasks:
137 return self
138
139 if taskState(prev_state) != "R":
140 event = RunqueueEventSleep(prev)
141
142 next_tasks = list(self.tasks[:])
143 if prev in self.tasks:
144 if taskState(prev_state) != "R":
145 next_tasks.remove(prev)
146 elif taskState(prev_state) == "R":
147 next_tasks.append(prev)
148
149 if next not in next_tasks:
150 next_tasks.append(next)
151
152 return RunqueueSnapshot(next_tasks, event)
153
154 def migrate_out(self, old):
155 if old not in self.tasks:
156 return self
157 next_tasks = [task for task in self.tasks if task != old]
158
159 return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old))
160
161 def __migrate_in(self, new, event):
162 if new in self.tasks:
163 self.event = event
164 return self
165 next_tasks = self.tasks[:] + tuple([new])
166
167 return RunqueueSnapshot(next_tasks, event)
168
169 def migrate_in(self, new):
170 return self.__migrate_in(new, RunqueueMigrateIn(new))
171
172 def wake_up(self, new):
173 return self.__migrate_in(new, RunqueueEventWakeup(new))
174
175 def wake_up_new(self, new):
176 return self.__migrate_in(new, RunqueueEventFork(new))
177
178 def load(self):
179 """ Provide the number of tasks on the runqueue.
180 Don't count idle"""
181 return len(self.tasks) - 1
182
183 def __repr__(self):
184 ret = self.tasks.__repr__()
185 ret += self.origin_tostring()
186
187 return ret
188
189class TimeSlice:
190 def __init__(self, start, prev):
191 self.start = start
192 self.prev = prev
193 self.end = start
194 # cpus that triggered the event
195 self.event_cpus = []
196 if prev is not None:
197 self.total_load = prev.total_load
198 self.rqs = prev.rqs.copy()
199 else:
200 self.rqs = defaultdict(RunqueueSnapshot)
201 self.total_load = 0
202
203 def __update_total_load(self, old_rq, new_rq):
204 diff = new_rq.load() - old_rq.load()
205 self.total_load += diff
206
207 def sched_switch(self, ts_list, prev, prev_state, next, cpu):
208 old_rq = self.prev.rqs[cpu]
209 new_rq = old_rq.sched_switch(prev, prev_state, next)
210
211 if old_rq is new_rq:
212 return
213
214 self.rqs[cpu] = new_rq
215 self.__update_total_load(old_rq, new_rq)
216 ts_list.append(self)
217 self.event_cpus = [cpu]
218
219 def migrate(self, ts_list, new, old_cpu, new_cpu):
220 if old_cpu == new_cpu:
221 return
222 old_rq = self.prev.rqs[old_cpu]
223 out_rq = old_rq.migrate_out(new)
224 self.rqs[old_cpu] = out_rq
225 self.__update_total_load(old_rq, out_rq)
226
227 new_rq = self.prev.rqs[new_cpu]
228 in_rq = new_rq.migrate_in(new)
229 self.rqs[new_cpu] = in_rq
230 self.__update_total_load(new_rq, in_rq)
231
232 ts_list.append(self)
749e5074
FW
233
234 if old_rq is not out_rq:
235 self.event_cpus.append(old_cpu)
236 self.event_cpus.append(new_cpu)
880d22f2
FW
237
238 def wake_up(self, ts_list, pid, cpu, fork):
239 old_rq = self.prev.rqs[cpu]
240 if fork:
241 new_rq = old_rq.wake_up_new(pid)
242 else:
243 new_rq = old_rq.wake_up(pid)
244
245 if new_rq is old_rq:
246 return
247 self.rqs[cpu] = new_rq
248 self.__update_total_load(old_rq, new_rq)
249 ts_list.append(self)
250 self.event_cpus = [cpu]
251
252 def next(self, t):
253 self.end = t
254 return TimeSlice(t, self)
255
256class TimeSliceList(UserList):
257 def __init__(self, arg = []):
258 self.data = arg
259
260 def get_time_slice(self, ts):
261 if len(self.data) == 0:
262 slice = TimeSlice(ts, TimeSlice(-1, None))
263 else:
264 slice = self.data[-1].next(ts)
265 return slice
266
267 def find_time_slice(self, ts):
268 start = 0
269 end = len(self.data)
270 found = -1
271 searching = True
272 while searching:
273 if start == end or start == end - 1:
274 searching = False
275
276 i = (end + start) / 2
277 if self.data[i].start <= ts and self.data[i].end >= ts:
278 found = i
279 end = i
280 continue
281
282 if self.data[i].end < ts:
283 start = i
284
285 elif self.data[i].start > ts:
286 end = i
287
288 return found
289
699b6d92
FW
290 def set_root_win(self, win):
291 self.root_win = win
292
293 def mouse_down(self, cpu, t):
294 idx = self.find_time_slice(t)
295 if idx == -1:
296 return
297
298 ts = self[idx]
299 rq = ts.rqs[cpu]
300 raw = "CPU: %d\n" % cpu
301 raw += "Last event : %s\n" % rq.event.__repr__()
302 raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000)
303 raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6))
304 raw += "Load = %d\n" % rq.load()
305 for t in rq.tasks:
306 raw += "%s \n" % thread_name(t)
307
308 self.root_win.update_summary(raw)
309
310 def update_rectangle_cpu(self, slice, cpu):
311 rq = slice.rqs[cpu]
312
313 if slice.total_load != 0:
314 load_rate = rq.load() / float(slice.total_load)
315 else:
316 load_rate = 0
317
318 red_power = int(0xff - (0xff * load_rate))
319 color = (0xff, red_power, red_power)
320
321 top_color = None
322
323 if cpu in slice.event_cpus:
324 top_color = rq.event.color()
325
326 self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end)
327
328 def fill_zone(self, start, end):
329 i = self.find_time_slice(start)
330 if i == -1:
331 return
332
333 for i in xrange(i, len(self.data)):
334 timeslice = self.data[i]
335 if timeslice.start > end:
336 return
337
338 for cpu in timeslice.rqs:
339 self.update_rectangle_cpu(timeslice, cpu)
340
880d22f2
FW
341 def interval(self):
342 if len(self.data) == 0:
343 return (0, 0)
344
345 return (self.data[0].start, self.data[-1].end)
346
699b6d92 347 def nr_rectangles(self):
70d815a3
FW
348 last_ts = self.data[-1]
349 max_cpu = 0
350 for cpu in last_ts.rqs:
351 if cpu > max_cpu:
352 max_cpu = cpu
353 return max_cpu
354
880d22f2
FW
355
356class SchedEventProxy:
357 def __init__(self):
358 self.current_tsk = defaultdict(lambda : -1)
359 self.timeslices = TimeSliceList()
360
361 def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state,
362 next_comm, next_pid, next_prio):
363 """ Ensure the task we sched out this cpu is really the one
364 we logged. Otherwise we may have missed traces """
365
366 on_cpu_task = self.current_tsk[headers.cpu]
367
368 if on_cpu_task != -1 and on_cpu_task != prev_pid:
369 print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \
370 (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid)
371
372 threads[prev_pid] = prev_comm
373 threads[next_pid] = next_comm
374 self.current_tsk[headers.cpu] = next_pid
375
376 ts = self.timeslices.get_time_slice(headers.ts())
377 ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu)
378
379 def migrate(self, headers, pid, prio, orig_cpu, dest_cpu):
380 ts = self.timeslices.get_time_slice(headers.ts())
381 ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu)
382
383 def wake_up(self, headers, comm, pid, success, target_cpu, fork):
384 if success == 0:
385 return
386 ts = self.timeslices.get_time_slice(headers.ts())
387 ts.wake_up(self.timeslices, pid, target_cpu, fork)
388
389
390def trace_begin():
391 global parser
392 parser = SchedEventProxy()
393
394def trace_end():
395 app = wx.App(False)
396 timeslices = parser.timeslices
699b6d92 397 frame = RootFrame(timeslices, "Migration")
880d22f2
FW
398 app.MainLoop()
399
400def sched__sched_stat_runtime(event_name, context, common_cpu,
401 common_secs, common_nsecs, common_pid, common_comm,
402 comm, pid, runtime, vruntime):
403 pass
404
405def sched__sched_stat_iowait(event_name, context, common_cpu,
406 common_secs, common_nsecs, common_pid, common_comm,
407 comm, pid, delay):
408 pass
409
410def sched__sched_stat_sleep(event_name, context, common_cpu,
411 common_secs, common_nsecs, common_pid, common_comm,
412 comm, pid, delay):
413 pass
414
415def sched__sched_stat_wait(event_name, context, common_cpu,
416 common_secs, common_nsecs, common_pid, common_comm,
417 comm, pid, delay):
418 pass
419
420def sched__sched_process_fork(event_name, context, common_cpu,
421 common_secs, common_nsecs, common_pid, common_comm,
422 parent_comm, parent_pid, child_comm, child_pid):
423 pass
424
425def sched__sched_process_wait(event_name, context, common_cpu,
426 common_secs, common_nsecs, common_pid, common_comm,
427 comm, pid, prio):
428 pass
429
430def sched__sched_process_exit(event_name, context, common_cpu,
431 common_secs, common_nsecs, common_pid, common_comm,
432 comm, pid, prio):
433 pass
434
435def sched__sched_process_free(event_name, context, common_cpu,
436 common_secs, common_nsecs, common_pid, common_comm,
437 comm, pid, prio):
438 pass
439
440def sched__sched_migrate_task(event_name, context, common_cpu,
441 common_secs, common_nsecs, common_pid, common_comm,
442 comm, pid, prio, orig_cpu,
443 dest_cpu):
444 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
445 common_pid, common_comm)
446 parser.migrate(headers, pid, prio, orig_cpu, dest_cpu)
447
448def sched__sched_switch(event_name, context, common_cpu,
449 common_secs, common_nsecs, common_pid, common_comm,
450 prev_comm, prev_pid, prev_prio, prev_state,
451 next_comm, next_pid, next_prio):
452
453 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
454 common_pid, common_comm)
455 parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state,
456 next_comm, next_pid, next_prio)
457
458def sched__sched_wakeup_new(event_name, context, common_cpu,
459 common_secs, common_nsecs, common_pid, common_comm,
460 comm, pid, prio, success,
461 target_cpu):
462 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
463 common_pid, common_comm)
464 parser.wake_up(headers, comm, pid, success, target_cpu, 1)
465
466def sched__sched_wakeup(event_name, context, common_cpu,
467 common_secs, common_nsecs, common_pid, common_comm,
468 comm, pid, prio, success,
469 target_cpu):
470 headers = EventHeaders(common_cpu, common_secs, common_nsecs,
471 common_pid, common_comm)
472 parser.wake_up(headers, comm, pid, success, target_cpu, 0)
473
474def sched__sched_wait_task(event_name, context, common_cpu,
475 common_secs, common_nsecs, common_pid, common_comm,
476 comm, pid, prio):
477 pass
478
479def sched__sched_kthread_stop_ret(event_name, context, common_cpu,
480 common_secs, common_nsecs, common_pid, common_comm,
481 ret):
482 pass
483
484def sched__sched_kthread_stop(event_name, context, common_cpu,
485 common_secs, common_nsecs, common_pid, common_comm,
486 comm, pid):
487 pass
488
489def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
490 common_pid, common_comm):
491 pass