1#!/usr/bin/python 2# 3# Cpu task migration overview toy 4# 5# Copyright (C) 2010 Frederic Weisbecker <[email protected]> 6# 7# perf trace event handlers have been generated by perf trace -g python 8# 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. 12 13 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') 22sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace') 23 24from perf_trace_context import * 25from Core import * 26from SchedGui import * 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: 59 return "Unknown" 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) 233 234 if old_rq is not out_rq: 235 self.event_cpus.append(old_cpu) 236 self.event_cpus.append(new_cpu) 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 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 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 347 def nr_rectangles(self): 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 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 397 frame = RootFrame(timeslices, "Migration") 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 492