1# Cpu task migration overview toy 2# 3# Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com> 4# 5# perf script event handlers have been generated by perf script -g python 6# 7# This software is distributed under the terms of the GNU General 8# Public License ("GPL") version 2 as published by the Free Software 9# Foundation. 10from __future__ import print_function 11 12import os 13import sys 14 15from collections import defaultdict 16try: 17 from UserList import UserList 18except ImportError: 19 # Python 3: UserList moved to the collections package 20 from collections import UserList 21 22sys.path.append(os.environ['PERF_EXEC_PATH'] + \ 23 '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') 24sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace') 25 26from perf_trace_context import * 27from Core import * 28from SchedGui import * 29 30 31threads = { 0 : "idle"} 32 33def thread_name(pid): 34 return "%s:%d" % (threads[pid], pid) 35 36class RunqueueEventUnknown: 37 @staticmethod 38 def color(): 39 return None 40 41 def __repr__(self): 42 return "unknown" 43 44class RunqueueEventSleep: 45 @staticmethod 46 def color(): 47 return (0, 0, 0xff) 48 49 def __init__(self, sleeper): 50 self.sleeper = sleeper 51 52 def __repr__(self): 53 return "%s gone to sleep" % thread_name(self.sleeper) 54 55class RunqueueEventWakeup: 56 @staticmethod 57 def color(): 58 return (0xff, 0xff, 0) 59 60 def __init__(self, wakee): 61 self.wakee = wakee 62 63 def __repr__(self): 64 return "%s woke up" % thread_name(self.wakee) 65 66class RunqueueEventFork: 67 @staticmethod 68 def color(): 69 return (0, 0xff, 0) 70 71 def __init__(self, child): 72 self.child = child 73 74 def __repr__(self): 75 return "new forked task %s" % thread_name(self.child) 76 77class RunqueueMigrateIn: 78 @staticmethod 79 def color(): 80 return (0, 0xf0, 0xff) 81 82 def __init__(self, new): 83 self.new = new 84 85 def __repr__(self): 86 return "task migrated in %s" % thread_name(self.new) 87 88class RunqueueMigrateOut: 89 @staticmethod 90 def color(): 91 return (0xff, 0, 0xff) 92 93 def __init__(self, old): 94 self.old = old 95 96 def __repr__(self): 97 return "task migrated out %s" % thread_name(self.old) 98 99class RunqueueSnapshot: 100 def __init__(self, tasks = [0], event = RunqueueEventUnknown()): 101 self.tasks = tuple(tasks) 102 self.event = event 103 104 def sched_switch(self, prev, prev_state, next): 105 event = RunqueueEventUnknown() 106 107 if taskState(prev_state) == "R" and next in self.tasks \ 108 and prev in self.tasks: 109 return self 110 111 if taskState(prev_state) != "R": 112 event = RunqueueEventSleep(prev) 113 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) 120 121 if next not in next_tasks: 122 next_tasks.append(next) 123 124 return RunqueueSnapshot(next_tasks, event) 125 126 def migrate_out(self, old): 127 if old not in self.tasks: 128 return self 129 next_tasks = [task for task in self.tasks if task != old] 130 131 return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) 132 133 def __migrate_in(self, new, event): 134 if new in self.tasks: 135 self.event = event 136 return self 137 next_tasks = self.tasks[:] + tuple([new]) 138 139 return RunqueueSnapshot(next_tasks, event) 140 141 def migrate_in(self, new): 142 return self.__migrate_in(new, RunqueueMigrateIn(new)) 143 144 def wake_up(self, new): 145 return self.__migrate_in(new, RunqueueEventWakeup(new)) 146 147 def wake_up_new(self, new): 148 return self.__migrate_in(new, RunqueueEventFork(new)) 149 150 def load(self): 151 """ Provide the number of tasks on the runqueue. 152 Don't count idle""" 153 return len(self.tasks) - 1 154 155 def __repr__(self): 156 ret = self.tasks.__repr__() 157 ret += self.origin_tostring() 158 159 return ret 160 161class TimeSlice: 162 def __init__(self, start, prev): 163 self.start = start 164 self.prev = prev 165 self.end = start 166 # cpus that triggered the event 167 self.event_cpus = [] 168 if prev is not None: 169 self.total_load = prev.total_load 170 self.rqs = prev.rqs.copy() 171 else: 172 self.rqs = defaultdict(RunqueueSnapshot) 173 self.total_load = 0 174 175 def __update_total_load(self, old_rq, new_rq): 176 diff = new_rq.load() - old_rq.load() 177 self.total_load += diff 178 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) 182 183 if old_rq is new_rq: 184 return 185 186 self.rqs[cpu] = new_rq 187 self.__update_total_load(old_rq, new_rq) 188 ts_list.append(self) 189 self.event_cpus = [cpu] 190 191 def migrate(self, ts_list, new, old_cpu, new_cpu): 192 if old_cpu == new_cpu: 193 return 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) 198 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) 203 204 ts_list.append(self) 205 206 if old_rq is not out_rq: 207 self.event_cpus.append(old_cpu) 208 self.event_cpus.append(new_cpu) 209 210 def wake_up(self, ts_list, pid, cpu, fork): 211 old_rq = self.prev.rqs[cpu] 212 if fork: 213 new_rq = old_rq.wake_up_new(pid) 214 else: 215 new_rq = old_rq.wake_up(pid) 216 217 if new_rq is old_rq: 218 return 219 self.rqs[cpu] = new_rq 220 self.__update_total_load(old_rq, new_rq) 221 ts_list.append(self) 222 self.event_cpus = [cpu] 223 224 def next(self, t): 225 self.end = t 226 return TimeSlice(t, self) 227 228class TimeSliceList(UserList): 229 def __init__(self, arg = []): 230 self.data = arg 231 232 def get_time_slice(self, ts): 233 if len(self.data) == 0: 234 slice = TimeSlice(ts, TimeSlice(-1, None)) 235 else: 236 slice = self.data[-1].next(ts) 237 return slice 238 239 def find_time_slice(self, ts): 240 start = 0 241 end = len(self.data) 242 found = -1 243 searching = True 244 while searching: 245 if start == end or start == end - 1: 246 searching = False 247 248 i = (end + start) / 2 249 if self.data[i].start <= ts and self.data[i].end >= ts: 250 found = i 251 end = i 252 continue 253 254 if self.data[i].end < ts: 255 start = i 256 257 elif self.data[i].start > ts: 258 end = i 259 260 return found 261 262 def set_root_win(self, win): 263 self.root_win = win 264 265 def mouse_down(self, cpu, t): 266 idx = self.find_time_slice(t) 267 if idx == -1: 268 return 269 270 ts = self[idx] 271 rq = ts.rqs[cpu] 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() 277 for t in rq.tasks: 278 raw += "%s \n" % thread_name(t) 279 280 self.root_win.update_summary(raw) 281 282 def update_rectangle_cpu(self, slice, cpu): 283 rq = slice.rqs[cpu] 284 285 if slice.total_load != 0: 286 load_rate = rq.load() / float(slice.total_load) 287 else: 288 load_rate = 0 289 290 red_power = int(0xff - (0xff * load_rate)) 291 color = (0xff, red_power, red_power) 292 293 top_color = None 294 295 if cpu in slice.event_cpus: 296 top_color = rq.event.color() 297 298 self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end) 299 300 def fill_zone(self, start, end): 301 i = self.find_time_slice(start) 302 if i == -1: 303 return 304 305 for i in range(i, len(self.data)): 306 timeslice = self.data[i] 307 if timeslice.start > end: 308 return 309 310 for cpu in timeslice.rqs: 311 self.update_rectangle_cpu(timeslice, cpu) 312 313 def interval(self): 314 if len(self.data) == 0: 315 return (0, 0) 316 317 return (self.data[0].start, self.data[-1].end) 318 319 def nr_rectangles(self): 320 last_ts = self.data[-1] 321 max_cpu = 0 322 for cpu in last_ts.rqs: 323 if cpu > max_cpu: 324 max_cpu = cpu 325 return max_cpu 326 327 328class SchedEventProxy: 329 def __init__(self): 330 self.current_tsk = defaultdict(lambda : -1) 331 self.timeslices = TimeSliceList() 332 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 """ 337 338 on_cpu_task = self.current_tsk[headers.cpu] 339 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) 343 344 threads[prev_pid] = prev_comm 345 threads[next_pid] = next_comm 346 self.current_tsk[headers.cpu] = next_pid 347 348 ts = self.timeslices.get_time_slice(headers.ts()) 349 ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu) 350 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) 354 355 def wake_up(self, headers, comm, pid, success, target_cpu, fork): 356 if success == 0: 357 return 358 ts = self.timeslices.get_time_slice(headers.ts()) 359 ts.wake_up(self.timeslices, pid, target_cpu, fork) 360 361 362def trace_begin(): 363 global parser 364 parser = SchedEventProxy() 365 366def trace_end(): 367 app = wx.App(False) 368 timeslices = parser.timeslices 369 frame = RootFrame(timeslices, "Migration") 370 app.MainLoop() 371 372def 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): 375 pass 376 377def sched__sched_stat_iowait(event_name, context, common_cpu, 378 common_secs, common_nsecs, common_pid, common_comm, 379 common_callchain, comm, pid, delay): 380 pass 381 382def sched__sched_stat_sleep(event_name, context, common_cpu, 383 common_secs, common_nsecs, common_pid, common_comm, 384 common_callchain, comm, pid, delay): 385 pass 386 387def sched__sched_stat_wait(event_name, context, common_cpu, 388 common_secs, common_nsecs, common_pid, common_comm, 389 common_callchain, comm, pid, delay): 390 pass 391 392def 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): 395 pass 396 397def sched__sched_process_wait(event_name, context, common_cpu, 398 common_secs, common_nsecs, common_pid, common_comm, 399 common_callchain, comm, pid, prio): 400 pass 401 402def sched__sched_process_exit(event_name, context, common_cpu, 403 common_secs, common_nsecs, common_pid, common_comm, 404 common_callchain, comm, pid, prio): 405 pass 406 407def sched__sched_process_free(event_name, context, common_cpu, 408 common_secs, common_nsecs, common_pid, common_comm, 409 common_callchain, comm, pid, prio): 410 pass 411 412def 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, 415 dest_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) 419 420def 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): 424 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) 429 430def 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, 433 target_cpu): 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) 437 438def sched__sched_wakeup(event_name, context, common_cpu, 439 common_secs, common_nsecs, common_pid, common_comm, 440 common_callchain, comm, pid, prio, success, 441 target_cpu): 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) 445 446def sched__sched_wait_task(event_name, context, common_cpu, 447 common_secs, common_nsecs, common_pid, common_comm, 448 common_callchain, comm, pid, prio): 449 pass 450 451def sched__sched_kthread_stop_ret(event_name, context, common_cpu, 452 common_secs, common_nsecs, common_pid, common_comm, 453 common_callchain, ret): 454 pass 455 456def sched__sched_kthread_stop(event_name, context, common_cpu, 457 common_secs, common_nsecs, common_pid, common_comm, 458 common_callchain, comm, pid): 459 pass 460 461def trace_unhandled(event_name, context, event_fields_dict): 462 pass 463