1#!/usr/bin/python 2# 3# Cpu task migration overview toy 4# 5# Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com> 6# 7# perf script event handlers have been generated by perf script -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 RunqueueEventUnknown: 35 @staticmethod 36 def color(): 37 return None 38 39 def __repr__(self): 40 return "unknown" 41 42class RunqueueEventSleep: 43 @staticmethod 44 def color(): 45 return (0, 0, 0xff) 46 47 def __init__(self, sleeper): 48 self.sleeper = sleeper 49 50 def __repr__(self): 51 return "%s gone to sleep" % thread_name(self.sleeper) 52 53class RunqueueEventWakeup: 54 @staticmethod 55 def color(): 56 return (0xff, 0xff, 0) 57 58 def __init__(self, wakee): 59 self.wakee = wakee 60 61 def __repr__(self): 62 return "%s woke up" % thread_name(self.wakee) 63 64class RunqueueEventFork: 65 @staticmethod 66 def color(): 67 return (0, 0xff, 0) 68 69 def __init__(self, child): 70 self.child = child 71 72 def __repr__(self): 73 return "new forked task %s" % thread_name(self.child) 74 75class RunqueueMigrateIn: 76 @staticmethod 77 def color(): 78 return (0, 0xf0, 0xff) 79 80 def __init__(self, new): 81 self.new = new 82 83 def __repr__(self): 84 return "task migrated in %s" % thread_name(self.new) 85 86class RunqueueMigrateOut: 87 @staticmethod 88 def color(): 89 return (0xff, 0, 0xff) 90 91 def __init__(self, old): 92 self.old = old 93 94 def __repr__(self): 95 return "task migrated out %s" % thread_name(self.old) 96 97class RunqueueSnapshot: 98 def __init__(self, tasks = [0], event = RunqueueEventUnknown()): 99 self.tasks = tuple(tasks) 100 self.event = event 101 102 def sched_switch(self, prev, prev_state, next): 103 event = RunqueueEventUnknown() 104 105 if taskState(prev_state) == "R" and next in self.tasks \ 106 and prev in self.tasks: 107 return self 108 109 if taskState(prev_state) != "R": 110 event = RunqueueEventSleep(prev) 111 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) 118 119 if next not in next_tasks: 120 next_tasks.append(next) 121 122 return RunqueueSnapshot(next_tasks, event) 123 124 def migrate_out(self, old): 125 if old not in self.tasks: 126 return self 127 next_tasks = [task for task in self.tasks if task != old] 128 129 return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) 130 131 def __migrate_in(self, new, event): 132 if new in self.tasks: 133 self.event = event 134 return self 135 next_tasks = self.tasks[:] + tuple([new]) 136 137 return RunqueueSnapshot(next_tasks, event) 138 139 def migrate_in(self, new): 140 return self.__migrate_in(new, RunqueueMigrateIn(new)) 141 142 def wake_up(self, new): 143 return self.__migrate_in(new, RunqueueEventWakeup(new)) 144 145 def wake_up_new(self, new): 146 return self.__migrate_in(new, RunqueueEventFork(new)) 147 148 def load(self): 149 """ Provide the number of tasks on the runqueue. 150 Don't count idle""" 151 return len(self.tasks) - 1 152 153 def __repr__(self): 154 ret = self.tasks.__repr__() 155 ret += self.origin_tostring() 156 157 return ret 158 159class TimeSlice: 160 def __init__(self, start, prev): 161 self.start = start 162 self.prev = prev 163 self.end = start 164 # cpus that triggered the event 165 self.event_cpus = [] 166 if prev is not None: 167 self.total_load = prev.total_load 168 self.rqs = prev.rqs.copy() 169 else: 170 self.rqs = defaultdict(RunqueueSnapshot) 171 self.total_load = 0 172 173 def __update_total_load(self, old_rq, new_rq): 174 diff = new_rq.load() - old_rq.load() 175 self.total_load += diff 176 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) 180 181 if old_rq is new_rq: 182 return 183 184 self.rqs[cpu] = new_rq 185 self.__update_total_load(old_rq, new_rq) 186 ts_list.append(self) 187 self.event_cpus = [cpu] 188 189 def migrate(self, ts_list, new, old_cpu, new_cpu): 190 if old_cpu == new_cpu: 191 return 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) 196 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) 201 202 ts_list.append(self) 203 204 if old_rq is not out_rq: 205 self.event_cpus.append(old_cpu) 206 self.event_cpus.append(new_cpu) 207 208 def wake_up(self, ts_list, pid, cpu, fork): 209 old_rq = self.prev.rqs[cpu] 210 if fork: 211 new_rq = old_rq.wake_up_new(pid) 212 else: 213 new_rq = old_rq.wake_up(pid) 214 215 if new_rq is old_rq: 216 return 217 self.rqs[cpu] = new_rq 218 self.__update_total_load(old_rq, new_rq) 219 ts_list.append(self) 220 self.event_cpus = [cpu] 221 222 def next(self, t): 223 self.end = t 224 return TimeSlice(t, self) 225 226class TimeSliceList(UserList): 227 def __init__(self, arg = []): 228 self.data = arg 229 230 def get_time_slice(self, ts): 231 if len(self.data) == 0: 232 slice = TimeSlice(ts, TimeSlice(-1, None)) 233 else: 234 slice = self.data[-1].next(ts) 235 return slice 236 237 def find_time_slice(self, ts): 238 start = 0 239 end = len(self.data) 240 found = -1 241 searching = True 242 while searching: 243 if start == end or start == end - 1: 244 searching = False 245 246 i = (end + start) / 2 247 if self.data[i].start <= ts and self.data[i].end >= ts: 248 found = i 249 end = i 250 continue 251 252 if self.data[i].end < ts: 253 start = i 254 255 elif self.data[i].start > ts: 256 end = i 257 258 return found 259 260 def set_root_win(self, win): 261 self.root_win = win 262 263 def mouse_down(self, cpu, t): 264 idx = self.find_time_slice(t) 265 if idx == -1: 266 return 267 268 ts = self[idx] 269 rq = ts.rqs[cpu] 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() 275 for t in rq.tasks: 276 raw += "%s \n" % thread_name(t) 277 278 self.root_win.update_summary(raw) 279 280 def update_rectangle_cpu(self, slice, cpu): 281 rq = slice.rqs[cpu] 282 283 if slice.total_load != 0: 284 load_rate = rq.load() / float(slice.total_load) 285 else: 286 load_rate = 0 287 288 red_power = int(0xff - (0xff * load_rate)) 289 color = (0xff, red_power, red_power) 290 291 top_color = None 292 293 if cpu in slice.event_cpus: 294 top_color = rq.event.color() 295 296 self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end) 297 298 def fill_zone(self, start, end): 299 i = self.find_time_slice(start) 300 if i == -1: 301 return 302 303 for i in xrange(i, len(self.data)): 304 timeslice = self.data[i] 305 if timeslice.start > end: 306 return 307 308 for cpu in timeslice.rqs: 309 self.update_rectangle_cpu(timeslice, cpu) 310 311 def interval(self): 312 if len(self.data) == 0: 313 return (0, 0) 314 315 return (self.data[0].start, self.data[-1].end) 316 317 def nr_rectangles(self): 318 last_ts = self.data[-1] 319 max_cpu = 0 320 for cpu in last_ts.rqs: 321 if cpu > max_cpu: 322 max_cpu = cpu 323 return max_cpu 324 325 326class SchedEventProxy: 327 def __init__(self): 328 self.current_tsk = defaultdict(lambda : -1) 329 self.timeslices = TimeSliceList() 330 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 """ 335 336 on_cpu_task = self.current_tsk[headers.cpu] 337 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) 341 342 threads[prev_pid] = prev_comm 343 threads[next_pid] = next_comm 344 self.current_tsk[headers.cpu] = next_pid 345 346 ts = self.timeslices.get_time_slice(headers.ts()) 347 ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu) 348 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) 352 353 def wake_up(self, headers, comm, pid, success, target_cpu, fork): 354 if success == 0: 355 return 356 ts = self.timeslices.get_time_slice(headers.ts()) 357 ts.wake_up(self.timeslices, pid, target_cpu, fork) 358 359 360def trace_begin(): 361 global parser 362 parser = SchedEventProxy() 363 364def trace_end(): 365 app = wx.App(False) 366 timeslices = parser.timeslices 367 frame = RootFrame(timeslices, "Migration") 368 app.MainLoop() 369 370def sched__sched_stat_runtime(event_name, context, common_cpu, 371 common_secs, common_nsecs, common_pid, common_comm, 372 common_callchain, comm, pid, runtime, vruntime): 373 pass 374 375def sched__sched_stat_iowait(event_name, context, common_cpu, 376 common_secs, common_nsecs, common_pid, common_comm, 377 common_callchain, comm, pid, delay): 378 pass 379 380def sched__sched_stat_sleep(event_name, context, common_cpu, 381 common_secs, common_nsecs, common_pid, common_comm, 382 common_callchain, comm, pid, delay): 383 pass 384 385def sched__sched_stat_wait(event_name, context, common_cpu, 386 common_secs, common_nsecs, common_pid, common_comm, 387 common_callchain, comm, pid, delay): 388 pass 389 390def sched__sched_process_fork(event_name, context, common_cpu, 391 common_secs, common_nsecs, common_pid, common_comm, 392 common_callchain, parent_comm, parent_pid, child_comm, child_pid): 393 pass 394 395def sched__sched_process_wait(event_name, context, common_cpu, 396 common_secs, common_nsecs, common_pid, common_comm, 397 common_callchain, comm, pid, prio): 398 pass 399 400def sched__sched_process_exit(event_name, context, common_cpu, 401 common_secs, common_nsecs, common_pid, common_comm, 402 common_callchain, comm, pid, prio): 403 pass 404 405def sched__sched_process_free(event_name, context, common_cpu, 406 common_secs, common_nsecs, common_pid, common_comm, 407 common_callchain, comm, pid, prio): 408 pass 409 410def sched__sched_migrate_task(event_name, context, common_cpu, 411 common_secs, common_nsecs, common_pid, common_comm, 412 common_callchain, comm, pid, prio, orig_cpu, 413 dest_cpu): 414 headers = EventHeaders(common_cpu, common_secs, common_nsecs, 415 common_pid, common_comm, common_callchain) 416 parser.migrate(headers, pid, prio, orig_cpu, dest_cpu) 417 418def sched__sched_switch(event_name, context, common_cpu, 419 common_secs, common_nsecs, common_pid, common_comm, common_callchain, 420 prev_comm, prev_pid, prev_prio, prev_state, 421 next_comm, next_pid, next_prio): 422 423 headers = EventHeaders(common_cpu, common_secs, common_nsecs, 424 common_pid, common_comm, common_callchain) 425 parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state, 426 next_comm, next_pid, next_prio) 427 428def sched__sched_wakeup_new(event_name, context, common_cpu, 429 common_secs, common_nsecs, common_pid, common_comm, 430 common_callchain, comm, pid, prio, success, 431 target_cpu): 432 headers = EventHeaders(common_cpu, common_secs, common_nsecs, 433 common_pid, common_comm, common_callchain) 434 parser.wake_up(headers, comm, pid, success, target_cpu, 1) 435 436def sched__sched_wakeup(event_name, context, common_cpu, 437 common_secs, common_nsecs, common_pid, common_comm, 438 common_callchain, comm, pid, prio, success, 439 target_cpu): 440 headers = EventHeaders(common_cpu, common_secs, common_nsecs, 441 common_pid, common_comm, common_callchain) 442 parser.wake_up(headers, comm, pid, success, target_cpu, 0) 443 444def sched__sched_wait_task(event_name, context, common_cpu, 445 common_secs, common_nsecs, common_pid, common_comm, 446 common_callchain, comm, pid, prio): 447 pass 448 449def sched__sched_kthread_stop_ret(event_name, context, common_cpu, 450 common_secs, common_nsecs, common_pid, common_comm, 451 common_callchain, ret): 452 pass 453 454def sched__sched_kthread_stop(event_name, context, common_cpu, 455 common_secs, common_nsecs, common_pid, common_comm, 456 common_callchain, comm, pid): 457 pass 458 459def trace_unhandled(event_name, context, event_fields_dict): 460 pass 461