1#!/usr/local/bin/python 2 3# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org> 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice unmodified, this list of conditions, and the following 11# disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26# 27# $FreeBSD$ 28 29import sys 30import re 31from Tkinter import * 32 33# To use: 34# - Install the ports/x11-toolkits/py-tkinter package. 35# - Add KTR_SCHED to KTR_COMPILE and KTR_MASK in your KERNCONF 36# - It is encouraged to increase KTR_ENTRIES size to 32768 to gather 37# enough information for analysis. 38# - Rebuild kernel with proper changes to KERNCONF. 39# - Dump the trace to a file: 'ktrdump -ct > ktr.out' 40# - Run the python script: 'python schedgraph.py ktr.out' 41# 42# To do: 43# 1) Add a per-thread summary display 44# 2) Add bounding box style zoom. 45# 3) Click to center. 46# 4) Implement some sorting mechanism. 47 48ticksps = None 49status = None 50configtypes = [] 51 52def ticks2sec(ticks): 53 us = ticksps / 1000000 54 ticks /= us 55 if (ticks < 1000): 56 return (str(ticks) + "us") 57 ticks /= 1000 58 if (ticks < 1000): 59 return (str(ticks) + "ms") 60 ticks /= 1000 61 return (str(ticks) + "s") 62 63class Scaler(Frame): 64 def __init__(self, master, target): 65 Frame.__init__(self, master) 66 self.scale = Scale(self, command=self.scaleset, 67 from_=1000, to_=10000000, orient=HORIZONTAL, 68 resolution=1000) 69 self.label = Label(self, text="Ticks per pixel") 70 self.label.pack(side=LEFT) 71 self.scale.pack(fill="both", expand=1) 72 self.target = target 73 self.scale.set(target.scaleget()) 74 self.initialized = 1 75 76 def scaleset(self, value): 77 self.target.scaleset(int(value)) 78 79 def set(self, value): 80 self.scale.set(value) 81 82class Status(Frame): 83 def __init__(self, master): 84 Frame.__init__(self, master) 85 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W) 86 self.label.pack(fill="both", expand=1) 87 self.clear() 88 89 def set(self, str): 90 self.label.config(text=str) 91 92 def clear(self): 93 self.label.config(text="") 94 95 def startup(self, str): 96 self.set(str) 97 root.update() 98 99class EventConf(Frame): 100 def __init__(self, master, name, color, enabled): 101 Frame.__init__(self, master) 102 self.name = name 103 self.color = StringVar() 104 self.color_default = color 105 self.color_current = color 106 self.color.set(color) 107 self.enabled = IntVar() 108 self.enabled_default = enabled 109 self.enabled_current = enabled 110 self.enabled.set(enabled) 111 self.draw() 112 113 def draw(self): 114 self.label = Label(self, text=self.name, anchor=W) 115 self.sample = Canvas(self, width=24, height=24, 116 bg='grey') 117 self.rect = self.sample.create_rectangle(0, 0, 24, 24, 118 fill=self.color.get()) 119 self.list = OptionMenu(self, self.color, 120 "dark red", "red", "pink", 121 "dark orange", "orange", 122 "yellow", "light yellow", 123 "dark green", "green", "light green", 124 "dark blue", "blue", "light blue", 125 "dark violet", "violet", "purple", 126 "dark grey", "light grey", 127 "white", "black", 128 command=self.setcolor) 129 self.checkbox = Checkbutton(self, text="enabled", 130 variable=self.enabled) 131 self.label.grid(row=0, column=0, sticky=E+W) 132 self.sample.grid(row=0, column=1) 133 self.list.grid(row=0, column=2, sticky=E+W) 134 self.checkbox.grid(row=0, column=3) 135 self.columnconfigure(0, weight=1) 136 self.columnconfigure(2, minsize=110) 137 138 def setcolor(self, color): 139 self.color.set(color) 140 self.sample.itemconfigure(self.rect, fill=color) 141 142 def apply(self): 143 cchange = 0 144 echange = 0 145 if (self.color_current != self.color.get()): 146 cchange = 1 147 if (self.enabled_current != self.enabled.get()): 148 echange = 1 149 self.color_current = self.color.get() 150 self.enabled_current = self.enabled.get() 151 if (echange != 0): 152 if (self.enabled_current): 153 graph.setcolor(self.name, self.color_current) 154 else: 155 graph.hide(self.name) 156 return 157 if (cchange != 0): 158 graph.setcolor(self.name, self.color_current) 159 160 def revert(self): 161 self.setcolor(self.color_current) 162 self.enabled.set(self.enabled_current) 163 164 def default(self): 165 self.setcolor(self.color_default) 166 self.enabled.set(self.enabled_default) 167 168class EventConfigure(Toplevel): 169 def __init__(self): 170 Toplevel.__init__(self) 171 self.resizable(0, 0) 172 self.title("Event Configuration") 173 self.items = LabelFrame(self, text="Event Type") 174 self.buttons = Frame(self) 175 self.drawbuttons() 176 self.items.grid(row=0, column=0, sticky=E+W) 177 self.columnconfigure(0, weight=1) 178 self.buttons.grid(row=1, column=0, sticky=E+W) 179 self.types = [] 180 self.irow = 0 181 for type in configtypes: 182 self.additem(type.name, type.color, type.enabled) 183 184 def additem(self, name, color, enabled=1): 185 item = EventConf(self.items, name, color, enabled) 186 self.types.append(item) 187 item.grid(row=self.irow, column=0, sticky=E+W) 188 self.irow += 1 189 190 def drawbuttons(self): 191 self.apply = Button(self.buttons, text="Apply", 192 command=self.apress) 193 self.revert = Button(self.buttons, text="Revert", 194 command=self.rpress) 195 self.default = Button(self.buttons, text="Default", 196 command=self.dpress) 197 self.apply.grid(row=0, column=0, sticky=E+W) 198 self.revert.grid(row=0, column=1, sticky=E+W) 199 self.default.grid(row=0, column=2, sticky=E+W) 200 self.buttons.columnconfigure(0, weight=1) 201 self.buttons.columnconfigure(1, weight=1) 202 self.buttons.columnconfigure(2, weight=1) 203 204 def apress(self): 205 for item in self.types: 206 item.apply() 207 208 def rpress(self): 209 for item in self.types: 210 item.revert() 211 212 def dpress(self): 213 for item in self.types: 214 item.default() 215 216class EventView(Toplevel): 217 def __init__(self, event, canvas): 218 Toplevel.__init__(self) 219 self.resizable(0, 0) 220 self.title("Event") 221 self.event = event 222 self.frame = Frame(self) 223 self.frame.grid(row=0, column=0, sticky=N+S+E+W) 224 self.buttons = Frame(self) 225 self.buttons.grid(row=1, column=0, sticky=E+W) 226 self.canvas = canvas 227 self.drawlabels() 228 self.drawbuttons() 229 event.displayref(canvas) 230 self.bind("<Destroy>", self.destroycb) 231 232 def destroycb(self, event): 233 self.unbind("<Destroy>") 234 if (self.event != None): 235 self.event.displayunref(self.canvas) 236 self.event = None 237 self.destroy() 238 239 def clearlabels(self): 240 for label in self.frame.grid_slaves(): 241 label.grid_remove() 242 243 def drawlabels(self): 244 ypos = 0 245 labels = self.event.labels() 246 while (len(labels) < 7): 247 labels.append(("", "", 0)) 248 for label in labels: 249 name, value, linked = label 250 l = Label(self.frame, text=name, bd=1, width=15, 251 relief=SUNKEN, anchor=W) 252 if (linked): 253 fgcolor = "blue" 254 else: 255 fgcolor = "black" 256 r = Label(self.frame, text=value, bd=1, 257 relief=SUNKEN, anchor=W, fg=fgcolor) 258 l.grid(row=ypos, column=0, sticky=E+W) 259 r.grid(row=ypos, column=1, sticky=E+W) 260 if (linked): 261 r.bind("<Button-1>", self.linkpress) 262 ypos += 1 263 self.frame.columnconfigure(1, minsize=80) 264 265 def drawbuttons(self): 266 self.back = Button(self.buttons, text="<", command=self.bpress) 267 self.forw = Button(self.buttons, text=">", command=self.fpress) 268 self.new = Button(self.buttons, text="new", command=self.npress) 269 self.back.grid(row=0, column=0, sticky=E+W) 270 self.forw.grid(row=0, column=1, sticky=E+W) 271 self.new.grid(row=0, column=2, sticky=E+W) 272 self.buttons.columnconfigure(2, weight=1) 273 274 def newevent(self, event): 275 self.event.displayunref(self.canvas) 276 self.clearlabels() 277 self.event = event 278 self.event.displayref(self.canvas) 279 self.drawlabels() 280 281 def npress(self): 282 EventView(self.event, self.canvas) 283 284 def bpress(self): 285 prev = self.event.prev() 286 if (prev == None): 287 return 288 while (prev.real == 0): 289 prev = prev.prev() 290 if (prev == None): 291 return 292 self.newevent(prev) 293 294 def fpress(self): 295 next = self.event.next() 296 if (next == None): 297 return 298 while (next.real == 0): 299 next = next.next() 300 if (next == None): 301 return 302 self.newevent(next) 303 304 def linkpress(self, wevent): 305 event = self.event.getlinked() 306 if (event != None): 307 self.newevent(event) 308 309class Event: 310 name = "none" 311 color = "grey" 312 def __init__(self, source, cpu, timestamp, last=0): 313 self.source = source 314 self.cpu = cpu 315 self.timestamp = int(timestamp) 316 self.entries = [] 317 self.real = 1 318 self.idx = None 319 self.state = 0 320 self.item = None 321 self.dispcnt = 0 322 self.linked = None 323 if (last): 324 source.lastevent(self) 325 else: 326 source.event(self) 327 328 def status(self): 329 statstr = self.name + " " + self.source.name 330 statstr += " on: cpu" + str(self.cpu) 331 statstr += " at: " + str(self.timestamp) 332 statstr += self.stattxt() 333 status.set(statstr) 334 335 def stattxt(self): 336 return "" 337 338 def textadd(self, tuple): 339 pass 340 self.entries.append(tuple) 341 342 def labels(self): 343 return [("Source:", self.source.name, 0), 344 ("Event:", self.name, 0), 345 ("CPU:", self.cpu, 0), 346 ("Timestamp:", self.timestamp, 0)] + self.entries 347 def mouseenter(self, canvas, item): 348 self.displayref(canvas) 349 self.status() 350 351 def mouseexit(self, canvas, item): 352 self.displayunref(canvas) 353 status.clear() 354 355 def mousepress(self, canvas, item): 356 EventView(self, canvas) 357 358 def next(self): 359 return self.source.eventat(self.idx + 1) 360 361 def prev(self): 362 return self.source.eventat(self.idx - 1) 363 364 def displayref(self, canvas): 365 if (self.dispcnt == 0): 366 canvas.itemconfigure(self.item, width=2) 367 self.dispcnt += 1 368 369 def displayunref(self, canvas): 370 self.dispcnt -= 1 371 if (self.dispcnt == 0): 372 canvas.itemconfigure(self.item, width=0) 373 canvas.tag_raise("point", "state") 374 375 def getlinked(self): 376 return self.linked.findevent(self.timestamp) 377 378class PointEvent(Event): 379 def __init__(self, thread, cpu, timestamp, last=0): 380 Event.__init__(self, thread, cpu, timestamp, last) 381 382 def draw(self, canvas, xpos, ypos): 383 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11, 384 fill=self.color, tags=("all", "point", "event") 385 + (self.name,), width=0) 386 canvas.events[l] = self 387 self.item = l 388 if (self.enabled == 0): 389 canvas.itemconfigure(l, state="hidden") 390 391 return (xpos) 392 393class StateEvent(Event): 394 def __init__(self, thread, cpu, timestamp, last=0): 395 Event.__init__(self, thread, cpu, timestamp, last) 396 self.duration = 0 397 self.skipnext = 0 398 self.skipself = 0 399 self.state = 1 400 401 def draw(self, canvas, xpos, ypos): 402 next = self.nextstate() 403 if (self.skipself == 1 or next == None): 404 return (xpos) 405 while (self.skipnext): 406 skipped = next 407 next.skipself = 1 408 next.real = 0 409 next = next.nextstate() 410 if (next == None): 411 next = skipped 412 self.skipnext -= 1 413 self.duration = next.timestamp - self.timestamp 414 if (self.duration < 0): 415 self.duration = 0 416 print "Unsynchronized timestamp" 417 print self.cpu, self.timestamp 418 print next.cpu, next.timestamp 419 delta = self.duration / canvas.ratio 420 l = canvas.create_rectangle(xpos, ypos, 421 xpos + delta, ypos - 10, fill=self.color, width=0, 422 tags=("all", "state", "event") + (self.name,)) 423 canvas.events[l] = self 424 self.item = l 425 if (self.enabled == 0): 426 canvas.itemconfigure(l, state="hidden") 427 428 return (xpos + delta) 429 430 def stattxt(self): 431 return " duration: " + ticks2sec(self.duration) 432 433 def nextstate(self): 434 next = self.next() 435 while (next != None and next.state == 0): 436 next = next.next() 437 return (next) 438 439 def labels(self): 440 return [("Source:", self.source.name, 0), 441 ("Event:", self.name, 0), 442 ("Timestamp:", self.timestamp, 0), 443 ("CPU:", self.cpu, 0), 444 ("Duration:", ticks2sec(self.duration), 0)] \ 445 + self.entries 446 447class Count(Event): 448 name = "Count" 449 color = "red" 450 enabled = 1 451 def __init__(self, source, cpu, timestamp, count): 452 self.count = int(count) 453 Event.__init__(self, source, cpu, timestamp) 454 self.duration = 0 455 self.textadd(("count:", self.count, 0)) 456 457 def draw(self, canvas, xpos, ypos): 458 next = self.next() 459 self.duration = next.timestamp - self.timestamp 460 delta = self.duration / canvas.ratio 461 yhight = self.source.yscale() * self.count 462 l = canvas.create_rectangle(xpos, ypos - yhight, 463 xpos + delta, ypos, fill=self.color, width=0, 464 tags=("all", "count", "event") + (self.name,)) 465 canvas.events[l] = self 466 self.item = l 467 if (self.enabled == 0): 468 canvas.itemconfigure(l, state="hidden") 469 return (xpos + delta) 470 471 def stattxt(self): 472 return " count: " + str(self.count) 473 474configtypes.append(Count) 475 476class Running(StateEvent): 477 name = "running" 478 color = "green" 479 enabled = 1 480 def __init__(self, thread, cpu, timestamp, prio): 481 StateEvent.__init__(self, thread, cpu, timestamp) 482 self.prio = prio 483 self.textadd(("prio:", self.prio, 0)) 484 485configtypes.append(Running) 486 487class Idle(StateEvent): 488 name = "idle" 489 color = "grey" 490 enabled = 0 491 def __init__(self, thread, cpu, timestamp, prio): 492 StateEvent.__init__(self, thread, cpu, timestamp) 493 self.prio = prio 494 self.textadd(("prio:", self.prio, 0)) 495 496configtypes.append(Idle) 497 498class Yielding(StateEvent): 499 name = "yielding" 500 color = "yellow" 501 enabled = 1 502 def __init__(self, thread, cpu, timestamp, prio): 503 StateEvent.__init__(self, thread, cpu, timestamp) 504 self.skipnext = 1 505 self.prio = prio 506 self.textadd(("prio:", self.prio, 0)) 507 508configtypes.append(Yielding) 509 510class Swapped(StateEvent): 511 name = "swapped" 512 color = "violet" 513 enabled = 1 514 def __init__(self, thread, cpu, timestamp, prio): 515 StateEvent.__init__(self, thread, cpu, timestamp) 516 self.prio = prio 517 self.textadd(("prio:", self.prio, 0)) 518 519configtypes.append(Swapped) 520 521class Suspended(StateEvent): 522 name = "suspended" 523 color = "purple" 524 enabled = 1 525 def __init__(self, thread, cpu, timestamp, prio): 526 StateEvent.__init__(self, thread, cpu, timestamp) 527 self.prio = prio 528 self.textadd(("prio:", self.prio, 0)) 529 530configtypes.append(Suspended) 531 532class Iwait(StateEvent): 533 name = "iwait" 534 color = "grey" 535 enabled = 0 536 def __init__(self, thread, cpu, timestamp, prio): 537 StateEvent.__init__(self, thread, cpu, timestamp) 538 self.prio = prio 539 self.textadd(("prio:", self.prio, 0)) 540 541configtypes.append(Iwait) 542 543class Preempted(StateEvent): 544 name = "preempted" 545 color = "red" 546 enabled = 1 547 def __init__(self, thread, cpu, timestamp, prio, bythread): 548 StateEvent.__init__(self, thread, cpu, timestamp) 549 self.skipnext = 1 550 self.prio = prio 551 self.linked = bythread 552 self.textadd(("prio:", self.prio, 0)) 553 self.textadd(("by thread:", self.linked.name, 1)) 554 555configtypes.append(Preempted) 556 557class Sleep(StateEvent): 558 name = "sleep" 559 color = "blue" 560 enabled = 1 561 def __init__(self, thread, cpu, timestamp, prio, wmesg): 562 StateEvent.__init__(self, thread, cpu, timestamp) 563 self.prio = prio 564 self.wmesg = wmesg 565 self.textadd(("prio:", self.prio, 0)) 566 self.textadd(("wmesg:", self.wmesg, 0)) 567 568 def stattxt(self): 569 statstr = StateEvent.stattxt(self) 570 statstr += " sleeping on: " + self.wmesg 571 return (statstr) 572 573configtypes.append(Sleep) 574 575class Blocked(StateEvent): 576 name = "blocked" 577 color = "dark red" 578 enabled = 1 579 def __init__(self, thread, cpu, timestamp, prio, lock): 580 StateEvent.__init__(self, thread, cpu, timestamp) 581 self.prio = prio 582 self.lock = lock 583 self.textadd(("prio:", self.prio, 0)) 584 self.textadd(("lock:", self.lock, 0)) 585 586 def stattxt(self): 587 statstr = StateEvent.stattxt(self) 588 statstr += " blocked on: " + self.lock 589 return (statstr) 590 591configtypes.append(Blocked) 592 593class KsegrpRunq(StateEvent): 594 name = "KsegrpRunq" 595 color = "orange" 596 enabled = 1 597 def __init__(self, thread, cpu, timestamp, prio, bythread): 598 StateEvent.__init__(self, thread, cpu, timestamp) 599 self.prio = prio 600 self.linked = bythread 601 self.textadd(("prio:", self.prio, 0)) 602 self.textadd(("by thread:", self.linked.name, 1)) 603 604configtypes.append(KsegrpRunq) 605 606class Runq(StateEvent): 607 name = "Runq" 608 color = "yellow" 609 enabled = 1 610 def __init__(self, thread, cpu, timestamp, prio, bythread): 611 StateEvent.__init__(self, thread, cpu, timestamp) 612 self.prio = prio 613 self.linked = bythread 614 self.textadd(("prio:", self.prio, 0)) 615 self.textadd(("by thread:", self.linked.name, 1)) 616 617configtypes.append(Runq) 618 619class Sched_exit(StateEvent): 620 name = "exit" 621 color = "grey" 622 enabled = 0 623 def __init__(self, thread, cpu, timestamp, prio): 624 StateEvent.__init__(self, thread, cpu, timestamp) 625 self.name = "sched_exit" 626 self.prio = prio 627 self.textadd(("prio:", self.prio, 0)) 628 629configtypes.append(Sched_exit) 630 631class Padevent(StateEvent): 632 def __init__(self, thread, cpu, timestamp, last=0): 633 StateEvent.__init__(self, thread, cpu, timestamp, last) 634 self.name = "pad" 635 self.real = 0 636 637 def draw(self, canvas, xpos, ypos): 638 next = self.next() 639 if (next == None): 640 return (xpos) 641 self.duration = next.timestamp - self.timestamp 642 delta = self.duration / canvas.ratio 643 return (xpos + delta) 644 645class Tick(PointEvent): 646 name = "tick" 647 color = "black" 648 enabled = 0 649 def __init__(self, thread, cpu, timestamp, prio, stathz): 650 PointEvent.__init__(self, thread, cpu, timestamp) 651 self.prio = prio 652 self.textadd(("prio:", self.prio, 0)) 653 654configtypes.append(Tick) 655 656class Prio(PointEvent): 657 name = "prio" 658 color = "black" 659 enabled = 0 660 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread): 661 PointEvent.__init__(self, thread, cpu, timestamp) 662 self.prio = prio 663 self.newprio = newprio 664 self.linked = bythread 665 self.textadd(("new prio:", self.newprio, 0)) 666 self.textadd(("prio:", self.prio, 0)) 667 if (self.linked != self.source): 668 self.textadd(("by thread:", self.linked.name, 1)) 669 else: 670 self.textadd(("by thread:", self.linked.name, 0)) 671 672configtypes.append(Prio) 673 674class Lend(PointEvent): 675 name = "lend" 676 color = "black" 677 enabled = 0 678 def __init__(self, thread, cpu, timestamp, prio, tothread): 679 PointEvent.__init__(self, thread, cpu, timestamp) 680 self.prio = prio 681 self.linked = tothread 682 self.textadd(("prio:", self.prio, 0)) 683 self.textadd(("to thread:", self.linked.name, 1)) 684 685configtypes.append(Lend) 686 687class Wokeup(PointEvent): 688 name = "wokeup" 689 color = "black" 690 enabled = 0 691 def __init__(self, thread, cpu, timestamp, ranthread): 692 PointEvent.__init__(self, thread, cpu, timestamp) 693 self.linked = ranthread 694 self.textadd(("ran thread:", self.linked.name, 1)) 695 696configtypes.append(Wokeup) 697 698class EventSource: 699 def __init__(self, name): 700 self.name = name 701 self.events = [] 702 self.cpu = 0 703 self.cpux = 0 704 705 def fixup(self): 706 pass 707 708 def event(self, event): 709 self.events.insert(0, event) 710 711 def remove(self, event): 712 self.events.remove(event) 713 714 def lastevent(self, event): 715 self.events.append(event) 716 717 def draw(self, canvas, ypos): 718 xpos = 10 719 self.cpux = 10 720 self.cpu = self.events[1].cpu 721 for i in range(0, len(self.events)): 722 self.events[i].idx = i 723 for event in self.events: 724 if (event.cpu != self.cpu and event.cpu != -1): 725 self.drawcpu(canvas, xpos, ypos) 726 self.cpux = xpos 727 self.cpu = event.cpu 728 xpos = event.draw(canvas, xpos, ypos) 729 self.drawcpu(canvas, xpos, ypos) 730 731 def drawname(self, canvas, ypos): 732 ypos = ypos - (self.ysize() / 2) 733 canvas.create_text(10, ypos, anchor="w", text=self.name) 734 735 def drawcpu(self, canvas, xpos, ypos): 736 cpu = int(self.cpu) 737 if (cpu == 0): 738 color = 'light grey' 739 elif (cpu == 1): 740 color = 'dark grey' 741 elif (cpu == 2): 742 color = 'light blue' 743 elif (cpu == 3): 744 color = 'light green' 745 elif (cpu == 4): 746 color = 'blanched almond' 747 elif (cpu == 5): 748 color = 'slate grey' 749 elif (cpu == 6): 750 color = 'light slate blue' 751 elif (cpu == 7): 752 color = 'thistle' 753 else: 754 color = "white" 755 l = canvas.create_rectangle(self.cpux, 756 ypos - self.ysize() - canvas.bdheight, 757 xpos, ypos + canvas.bdheight, fill=color, width=0, 758 tags=("all", "cpuinfo")) 759 760 def ysize(self): 761 return (None) 762 763 def eventat(self, i): 764 if (i >= len(self.events)): 765 return (None) 766 event = self.events[i] 767 return (event) 768 769 def findevent(self, timestamp): 770 for event in self.events: 771 if (event.timestamp >= timestamp and event.real): 772 return (event) 773 return (None) 774 775class Thread(EventSource): 776 names = {} 777 def __init__(self, td, pcomm): 778 EventSource.__init__(self, pcomm) 779 self.str = td 780 try: 781 cnt = Thread.names[pcomm] 782 except: 783 Thread.names[pcomm] = 0 784 return 785 Thread.names[pcomm] = cnt + 1 786 787 def fixup(self): 788 cnt = Thread.names[self.name] 789 if (cnt == 0): 790 return 791 cnt -= 1 792 Thread.names[self.name] = cnt 793 self.name += " td" + str(cnt) 794 795 def ysize(self): 796 return (10) 797 798class Counter(EventSource): 799 max = 0 800 def __init__(self, name): 801 EventSource.__init__(self, name) 802 803 def event(self, event): 804 EventSource.event(self, event) 805 try: 806 count = event.count 807 except: 808 return 809 count = int(count) 810 if (count > Counter.max): 811 Counter.max = count 812 813 def ysize(self): 814 return (80) 815 816 def yscale(self): 817 return (self.ysize() / Counter.max) 818 819 820class KTRFile: 821 def __init__(self, file): 822 self.timestamp_first = {} 823 self.timestamp_last = {} 824 self.timestamp_adjust = {} 825 self.timestamp_f = None 826 self.timestamp_l = None 827 self.lineno = -1 828 self.threads = [] 829 self.sources = [] 830 self.ticks = {} 831 self.load = {} 832 self.crit = {} 833 self.stathz = 0 834 835 self.parse(file) 836 self.fixup() 837 global ticksps 838 print "first", self.timestamp_f, "last", self.timestamp_l 839 print "time span", self.timespan() 840 print "stathz", self.stathz 841 ticksps = self.ticksps() 842 print "Ticks per second", ticksps 843 844 def parse(self, file): 845 try: 846 ifp = open(file) 847 except: 848 print "Can't open", file 849 sys.exit(1) 850 851 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+" 852 tdname = "(\S+)\(([^)]*)\)" 853 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)" 854 855 ktrstr = "mi_switch: " + tdname 856 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)" 857 switchout_re = re.compile(ktrhdr + ktrstr) 858 859 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle" 860 idled_re = re.compile(ktrhdr + ktrstr) 861 862 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by " 863 ktrstr += tdname 864 preempted_re = re.compile(ktrhdr + ktrstr) 865 866 ktrstr = "mi_switch: running " + tdname + " prio (\d+)" 867 switchin_re = re.compile(ktrhdr + ktrstr) 868 869 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname 870 sched_add_re = re.compile(ktrhdr + ktrstr) 871 872 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname 873 setrunqueue_re = re.compile(ktrhdr + ktrstr) 874 875 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname 876 sched_rem_re = re.compile(ktrhdr + ktrstr) 877 878 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)" 879 sched_exit_re = re.compile(ktrhdr + ktrstr) 880 881 ktrstr = "statclock: " + tdname + " prio (\d+)" 882 ktrstr += " stathz (\d+)" 883 sched_clock_re = re.compile(ktrhdr + ktrstr) 884 885 ktrstr = "sched_prio: " + tdname + " prio (\d+)" 886 ktrstr += " newprio (\d+) by " + tdname 887 sched_prio_re = re.compile(ktrhdr + ktrstr) 888 889 cpuload_re = re.compile(ktrhdr + "load: (\d+)") 890 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)") 891 892 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)" 893 critsec_re = re.compile(ktrhdr + ktrstr) 894 895 parsers = [[cpuload_re, self.cpuload], 896 [loadglobal_re, self.loadglobal], 897 [switchin_re, self.switchin], 898 [switchout_re, self.switchout], 899 [sched_add_re, self.sched_add], 900 [setrunqueue_re, self.sched_rem], 901 [sched_prio_re, self.sched_prio], 902 [preempted_re, self.preempted], 903 [sched_rem_re, self.sched_rem], 904 [sched_exit_re, self.sched_exit], 905 [sched_clock_re, self.sched_clock], 906 [critsec_re, self.critsec], 907 [idled_re, self.idled]] 908 909 lines = ifp.readlines() 910 self.synchstamp(lines) 911 for line in lines: 912 self.lineno += 1 913 if ((self.lineno % 1024) == 0): 914 status.startup("Parsing line " + 915 str(self.lineno)) 916 for p in parsers: 917 m = p[0].match(line) 918 if (m != None): 919 p[1](*m.groups()) 920 break 921 # if (m == None): 922 # print line, 923 924 def synchstamp(self, lines): 925 status.startup("Rationalizing Timestamps") 926 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*") 927 for line in lines: 928 m = tstamp_re.match(line) 929 if (m != None): 930 self.addstamp(*m.groups()) 931 self.pickstamp() 932 self.monostamp(lines) 933 934 935 def monostamp(self, lines): 936 laststamp = None 937 tstamp_re = re.compile("\s+\d+\s+(\d+)\s+(\d+)\s+.*") 938 for line in lines: 939 m = tstamp_re.match(line) 940 if (m == None): 941 continue 942 (cpu, timestamp) = m.groups() 943 timestamp = int(timestamp) 944 cpu = int(cpu) 945 timestamp -= self.timestamp_adjust[cpu] 946 if (laststamp != None and timestamp > laststamp): 947 self.timestamp_adjust[cpu] += timestamp - laststamp 948 laststamp = timestamp 949 950 def addstamp(self, cpu, timestamp): 951 timestamp = int(timestamp) 952 cpu = int(cpu) 953 try: 954 if (timestamp > self.timestamp_first[cpu]): 955 return 956 except: 957 self.timestamp_first[cpu] = timestamp 958 self.timestamp_last[cpu] = timestamp 959 960 def pickstamp(self): 961 base = self.timestamp_last[0] 962 for i in range(0, len(self.timestamp_last)): 963 if (self.timestamp_last[i] < base): 964 base = self.timestamp_last[i] 965 966 print "Adjusting to base stamp", base 967 for i in range(0, len(self.timestamp_last)): 968 self.timestamp_adjust[i] = self.timestamp_last[i] - base; 969 print "CPU ", i, "adjust by ", self.timestamp_adjust[i] 970 971 self.timestamp_f = 0 972 for i in range(0, len(self.timestamp_first)): 973 first = self.timestamp_first[i] - self.timestamp_adjust[i] 974 if (first > self.timestamp_f): 975 self.timestamp_f = first 976 977 self.timestamp_l = 0 978 for i in range(0, len(self.timestamp_last)): 979 last = self.timestamp_last[i] - self.timestamp_adjust[i] 980 if (last > self.timestamp_l): 981 self.timestamp_l = last 982 983 984 def checkstamp(self, cpu, timestamp): 985 cpu = int(cpu) 986 timestamp = int(timestamp) 987 if (timestamp > self.timestamp_first[cpu]): 988 print "Bad timestamp on line ", self.lineno 989 return (0) 990 timestamp -= self.timestamp_adjust[cpu] 991 return (timestamp) 992 993 def timespan(self): 994 return (self.timestamp_f - self.timestamp_l); 995 996 def ticksps(self): 997 return (self.timespan() / self.ticks[0]) * int(self.stathz) 998 999 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock): 1000 TDI_SUSPENDED = 0x0001 1001 TDI_SLEEPING = 0x0002 1002 TDI_SWAPPED = 0x0004 1003 TDI_LOCK = 0x0008 1004 TDI_IWAIT = 0x0010 1005 1006 timestamp = self.checkstamp(cpu, timestamp) 1007 if (timestamp == 0): 1008 return 1009 inhibit = int(inhibit) 1010 thread = self.findtd(td, pcomm) 1011 if (inhibit & TDI_SWAPPED): 1012 Swapped(thread, cpu, timestamp, prio) 1013 elif (inhibit & TDI_SLEEPING): 1014 Sleep(thread, cpu, timestamp, prio, wmesg) 1015 elif (inhibit & TDI_LOCK): 1016 Blocked(thread, cpu, timestamp, prio, lock) 1017 elif (inhibit & TDI_IWAIT): 1018 Iwait(thread, cpu, timestamp, prio) 1019 elif (inhibit & TDI_SUSPENDED): 1020 Suspended(thread, cpu, timestamp, prio) 1021 elif (inhibit == 0): 1022 Yielding(thread, cpu, timestamp, prio) 1023 else: 1024 print "Unknown event", inhibit 1025 sys.exit(1) 1026 1027 def idled(self, cpu, timestamp, td, pcomm, prio): 1028 timestamp = self.checkstamp(cpu, timestamp) 1029 if (timestamp == 0): 1030 return 1031 thread = self.findtd(td, pcomm) 1032 Idle(thread, cpu, timestamp, prio) 1033 1034 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 1035 timestamp = self.checkstamp(cpu, timestamp) 1036 if (timestamp == 0): 1037 return 1038 thread = self.findtd(td, pcomm) 1039 Preempted(thread, cpu, timestamp, prio, 1040 self.findtd(bytd, bypcomm)) 1041 1042 def switchin(self, cpu, timestamp, td, pcomm, prio): 1043 timestamp = self.checkstamp(cpu, timestamp) 1044 if (timestamp == 0): 1045 return 1046 thread = self.findtd(td, pcomm) 1047 Running(thread, cpu, timestamp, prio) 1048 1049 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 1050 timestamp = self.checkstamp(cpu, timestamp) 1051 if (timestamp == 0): 1052 return 1053 thread = self.findtd(td, pcomm) 1054 bythread = self.findtd(bytd, bypcomm) 1055 Runq(thread, cpu, timestamp, prio, bythread) 1056 Wokeup(bythread, cpu, timestamp, thread) 1057 1058 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 1059 timestamp = self.checkstamp(cpu, timestamp) 1060 if (timestamp == 0): 1061 return 1062 thread = self.findtd(td, pcomm) 1063 KsegrpRunq(thread, cpu, timestamp, prio, 1064 self.findtd(bytd, bypcomm)) 1065 1066 def sched_exit(self, cpu, timestamp, td, pcomm, prio): 1067 timestamp = self.checkstamp(cpu, timestamp) 1068 if (timestamp == 0): 1069 return 1070 thread = self.findtd(td, pcomm) 1071 Sched_exit(thread, cpu, timestamp, prio) 1072 1073 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz): 1074 timestamp = self.checkstamp(cpu, timestamp) 1075 if (timestamp == 0): 1076 return 1077 self.stathz = stathz 1078 cpu = int(cpu) 1079 try: 1080 ticks = self.ticks[cpu] 1081 except: 1082 self.ticks[cpu] = 0 1083 self.ticks[cpu] += 1 1084 thread = self.findtd(td, pcomm) 1085 Tick(thread, cpu, timestamp, prio, stathz) 1086 1087 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm): 1088 if (prio == newprio): 1089 return 1090 timestamp = self.checkstamp(cpu, timestamp) 1091 if (timestamp == 0): 1092 return 1093 thread = self.findtd(td, pcomm) 1094 bythread = self.findtd(bytd, bypcomm) 1095 Prio(thread, cpu, timestamp, prio, newprio, bythread) 1096 Lend(bythread, cpu, timestamp, newprio, thread) 1097 1098 def cpuload(self, cpu, timestamp, count): 1099 timestamp = self.checkstamp(cpu, timestamp) 1100 if (timestamp == 0): 1101 return 1102 cpu = int(cpu) 1103 try: 1104 load = self.load[cpu] 1105 except: 1106 load = Counter("cpu" + str(cpu) + " load") 1107 self.load[cpu] = load 1108 self.sources.insert(0, load) 1109 Count(load, cpu, timestamp, count) 1110 1111 def loadglobal(self, cpu, timestamp, count): 1112 timestamp = self.checkstamp(cpu, timestamp) 1113 if (timestamp == 0): 1114 return 1115 cpu = 0 1116 try: 1117 load = self.load[cpu] 1118 except: 1119 load = Counter("CPU load") 1120 self.load[cpu] = load 1121 self.sources.insert(0, load) 1122 Count(load, cpu, timestamp, count) 1123 1124 def critsec(self, cpu, timestamp, td, pcomm, to): 1125 timestamp = self.checkstamp(cpu, timestamp) 1126 if (timestamp == 0): 1127 return 1128 cpu = int(cpu) 1129 try: 1130 crit = self.crit[cpu] 1131 except: 1132 crit = Counter("Critical Section") 1133 self.crit[cpu] = crit 1134 self.sources.insert(0, crit) 1135 Count(crit, cpu, timestamp, to) 1136 1137 def findtd(self, td, pcomm): 1138 for thread in self.threads: 1139 if (thread.str == td and thread.name == pcomm): 1140 return thread 1141 thread = Thread(td, pcomm) 1142 self.threads.append(thread) 1143 self.sources.append(thread) 1144 return (thread) 1145 1146 def fixup(self): 1147 for source in self.sources: 1148 Padevent(source, -1, self.timestamp_l) 1149 Padevent(source, -1, self.timestamp_f, last=1) 1150 source.fixup() 1151 1152class SchedDisplay(Canvas): 1153 def __init__(self, master): 1154 self.ratio = 1 1155 self.ktrfile = None 1156 self.sources = None 1157 self.bdheight = 10 1158 self.events = {} 1159 1160 Canvas.__init__(self, master, width=800, height=500, bg='grey', 1161 scrollregion=(0, 0, 800, 500)) 1162 1163 def setfile(self, ktrfile): 1164 self.ktrfile = ktrfile 1165 self.sources = ktrfile.sources 1166 1167 def draw(self): 1168 ypos = 0 1169 xsize = self.xsize() 1170 for source in self.sources: 1171 status.startup("Drawing " + source.name) 1172 self.create_line(0, ypos, xsize, ypos, 1173 width=1, fill="black", tags=("all",)) 1174 ypos += self.bdheight 1175 ypos += source.ysize() 1176 source.draw(self, ypos) 1177 ypos += self.bdheight 1178 try: 1179 self.tag_raise("point", "state") 1180 self.tag_lower("cpuinfo", "all") 1181 except: 1182 pass 1183 self.create_line(0, ypos, xsize, ypos, 1184 width=1, fill="black", tags=("all",)) 1185 self.tag_bind("event", "<Enter>", self.mouseenter) 1186 self.tag_bind("event", "<Leave>", self.mouseexit) 1187 self.tag_bind("event", "<Button-1>", self.mousepress) 1188 1189 def mouseenter(self, event): 1190 item, = self.find_withtag(CURRENT) 1191 event = self.events[item] 1192 event.mouseenter(self, item) 1193 1194 def mouseexit(self, event): 1195 item, = self.find_withtag(CURRENT) 1196 event = self.events[item] 1197 event.mouseexit(self, item) 1198 1199 def mousepress(self, event): 1200 item, = self.find_withtag(CURRENT) 1201 event = self.events[item] 1202 event.mousepress(self, item) 1203 1204 def drawnames(self, canvas): 1205 status.startup("Drawing names") 1206 ypos = 0 1207 canvas.configure(scrollregion=(0, 0, 1208 canvas["width"], self.ysize())) 1209 for source in self.sources: 1210 canvas.create_line(0, ypos, canvas["width"], ypos, 1211 width=1, fill="black", tags=("all",)) 1212 ypos += self.bdheight 1213 ypos += source.ysize() 1214 source.drawname(canvas, ypos) 1215 ypos += self.bdheight 1216 canvas.create_line(0, ypos, canvas["width"], ypos, 1217 width=1, fill="black", tags=("all",)) 1218 1219 def xsize(self): 1220 return ((self.ktrfile.timespan() / self.ratio) + 20) 1221 1222 def ysize(self): 1223 ysize = 0 1224 for source in self.sources: 1225 ysize += source.ysize() + (self.bdheight * 2) 1226 return (ysize) 1227 1228 def scaleset(self, ratio): 1229 if (self.ktrfile == None): 1230 return 1231 oldratio = self.ratio 1232 xstart, ystart = self.xview() 1233 length = (float(self["width"]) / self.xsize()) 1234 middle = xstart + (length / 2) 1235 1236 self.ratio = ratio 1237 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize())) 1238 self.scale("all", 0, 0, float(oldratio) / ratio, 1) 1239 1240 length = (float(self["width"]) / self.xsize()) 1241 xstart = middle - (length / 2) 1242 self.xview_moveto(xstart) 1243 1244 def scaleget(self): 1245 return self.ratio 1246 1247 def setcolor(self, tag, color): 1248 self.itemconfigure(tag, state="normal", fill=color) 1249 1250 def hide(self, tag): 1251 self.itemconfigure(tag, state="hidden") 1252 1253class GraphMenu(Frame): 1254 def __init__(self, master): 1255 Frame.__init__(self, master, bd=2, relief=RAISED) 1256 self.view = Menubutton(self, text="Configure") 1257 self.viewmenu = Menu(self.view, tearoff=0) 1258 self.viewmenu.add_command(label="Events", 1259 command=self.econf) 1260 self.view["menu"] = self.viewmenu 1261 self.view.pack(side=LEFT) 1262 1263 def econf(self): 1264 EventConfigure() 1265 1266 1267class SchedGraph(Frame): 1268 def __init__(self, master): 1269 Frame.__init__(self, master) 1270 self.menu = None 1271 self.names = None 1272 self.display = None 1273 self.scale = None 1274 self.status = None 1275 self.pack(expand=1, fill="both") 1276 self.buildwidgets() 1277 self.layout() 1278 self.draw(sys.argv[1]) 1279 1280 def buildwidgets(self): 1281 global status 1282 self.menu = GraphMenu(self) 1283 self.display = SchedDisplay(self) 1284 self.names = Canvas(self, 1285 width=100, height=self.display["height"], 1286 bg='grey', scrollregion=(0, 0, 50, 100)) 1287 self.scale = Scaler(self, self.display) 1288 status = self.status = Status(self) 1289 self.scrollY = Scrollbar(self, orient="vertical", 1290 command=self.display_yview) 1291 self.display.scrollX = Scrollbar(self, orient="horizontal", 1292 command=self.display.xview) 1293 self.display["xscrollcommand"] = self.display.scrollX.set 1294 self.display["yscrollcommand"] = self.scrollY.set 1295 self.names["yscrollcommand"] = self.scrollY.set 1296 1297 def layout(self): 1298 self.columnconfigure(1, weight=1) 1299 self.rowconfigure(1, weight=1) 1300 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W) 1301 self.names.grid(row=1, column=0, sticky=N+S) 1302 self.display.grid(row=1, column=1, sticky=W+E+N+S) 1303 self.scrollY.grid(row=1, column=2, sticky=N+S) 1304 self.display.scrollX.grid(row=2, column=0, columnspan=2, 1305 sticky=E+W) 1306 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W) 1307 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W) 1308 1309 def draw(self, file): 1310 self.master.update() 1311 ktrfile = KTRFile(file) 1312 self.display.setfile(ktrfile) 1313 self.display.drawnames(self.names) 1314 self.display.draw() 1315 self.scale.set(250000) 1316 self.display.xview_moveto(0) 1317 1318 def display_yview(self, *args): 1319 self.names.yview(*args) 1320 self.display.yview(*args) 1321 1322 def setcolor(self, tag, color): 1323 self.display.setcolor(tag, color) 1324 1325 def hide(self, tag): 1326 self.display.hide(tag) 1327 1328if (len(sys.argv) != 2): 1329 print "usage:", sys.argv[0], "<ktr file>" 1330 sys.exit(1) 1331 1332root = Tk() 1333root.title("Scheduler Graph") 1334graph = SchedGraph(root) 1335root.mainloop() 1336