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