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 ns = ticksps / 1000000000 54 ticks /= ns 55 if (ticks < 1000): 56 return (str(ticks) + "ns") 57 ticks /= 1000 58 if (ticks < 1000): 59 return (str(ticks) + "us") 60 ticks /= 1000 61 if (ticks < 1000): 62 return (str(ticks) + "ms") 63 ticks /= 1000 64 return (str(ticks) + "s") 65 66class Scaler(Frame): 67 def __init__(self, master, target): 68 Frame.__init__(self, master) 69 self.scale = Scale(self, command=self.scaleset, 70 from_=1000, to_=1000000, orient=HORIZONTAL, resolution=1000) 71 self.label = Label(self, text="Ticks per pixel") 72 self.label.pack(side=LEFT) 73 self.scale.pack(fill="both", expand=1) 74 self.target = target 75 self.scale.set(target.scaleget()) 76 self.initialized = 1 77 78 def scaleset(self, value): 79 self.target.scaleset(int(value)) 80 81 def set(self, value): 82 self.scale.set(value) 83 84class Status(Frame): 85 def __init__(self, master): 86 Frame.__init__(self, master) 87 self.label = Label(self, bd=1, relief=SUNKEN, anchor=W) 88 self.label.pack(fill="both", expand=1) 89 self.clear() 90 91 def set(self, str): 92 self.label.config(text=str) 93 94 def clear(self): 95 self.label.config(text="") 96 97 def startup(self, str): 98 self.set(str) 99 root.update() 100 101class EventConf(Frame): 102 def __init__(self, master, name, color, enabled): 103 Frame.__init__(self, master) 104 self.name = name 105 self.color = StringVar() 106 self.color_default = color 107 self.color_current = color 108 self.color.set(color) 109 self.enabled = IntVar() 110 self.enabled_default = enabled 111 self.enabled_current = enabled 112 self.enabled.set(enabled) 113 self.draw() 114 115 def draw(self): 116 self.label = Label(self, text=self.name, anchor=W) 117 self.sample = Canvas(self, width=24, height=24, 118 bg='grey') 119 self.rect = self.sample.create_rectangle(0, 0, 24, 24, 120 fill=self.color.get()) 121 self.list = OptionMenu(self, self.color, 122 "dark red", "red", "pink", 123 "dark orange", "orange", 124 "yellow", "light yellow", 125 "dark green", "green", "light green", 126 "dark blue", "blue", "light blue", 127 "dark violet", "violet", "purple", 128 "dark grey", "light grey", 129 "white", "black", 130 command=self.setcolor) 131 self.checkbox = Checkbutton(self, text="enabled", 132 variable=self.enabled) 133 self.label.grid(row=0, column=0, sticky=E+W) 134 self.sample.grid(row=0, column=1) 135 self.list.grid(row=0, column=2, sticky=E+W) 136 self.checkbox.grid(row=0, column=3) 137 self.columnconfigure(0, weight=1) 138 self.columnconfigure(2, minsize=110) 139 140 def setcolor(self, color): 141 self.color.set(color) 142 self.sample.itemconfigure(self.rect, fill=color) 143 144 def apply(self): 145 cchange = 0 146 echange = 0 147 if (self.color_current != self.color.get()): 148 cchange = 1 149 if (self.enabled_current != self.enabled.get()): 150 echange = 1 151 self.color_current = self.color.get() 152 self.enabled_current = self.enabled.get() 153 if (echange != 0): 154 if (self.enabled_current): 155 graph.setcolor(self.name, self.color_current) 156 else: 157 graph.hide(self.name) 158 return 159 if (cchange != 0): 160 graph.setcolor(self.name, self.color_current) 161 162 def revert(self): 163 self.setcolor(self.color_current) 164 self.enabled.set(self.enabled_current) 165 166 def default(self): 167 self.setcolor(self.color_default) 168 self.enabled.set(self.enabled_default) 169 170class EventConfigure(Toplevel): 171 def __init__(self): 172 Toplevel.__init__(self) 173 self.resizable(0, 0) 174 self.title("Event Configuration") 175 self.items = LabelFrame(self, text="Event Type") 176 self.buttons = Frame(self) 177 self.drawbuttons() 178 self.items.grid(row=0, column=0, sticky=E+W) 179 self.columnconfigure(0, weight=1) 180 self.buttons.grid(row=1, column=0, sticky=E+W) 181 self.types = [] 182 self.irow = 0 183 for type in configtypes: 184 self.additem(type.name, type.color, type.enabled) 185 186 def additem(self, name, color, enabled=1): 187 item = EventConf(self.items, name, color, enabled) 188 self.types.append(item) 189 item.grid(row=self.irow, column=0, sticky=E+W) 190 self.irow += 1 191 192 def drawbuttons(self): 193 self.apply = Button(self.buttons, text="Apply", 194 command=self.apress) 195 self.revert = Button(self.buttons, text="Revert", 196 command=self.rpress) 197 self.default = Button(self.buttons, text="Default", 198 command=self.dpress) 199 self.apply.grid(row=0, column=0, sticky=E+W) 200 self.revert.grid(row=0, column=1, sticky=E+W) 201 self.default.grid(row=0, column=2, sticky=E+W) 202 self.buttons.columnconfigure(0, weight=1) 203 self.buttons.columnconfigure(1, weight=1) 204 self.buttons.columnconfigure(2, weight=1) 205 206 def apress(self): 207 for item in self.types: 208 item.apply() 209 210 def rpress(self): 211 for item in self.types: 212 item.revert() 213 214 def dpress(self): 215 for item in self.types: 216 item.default() 217 218class EventView(Toplevel): 219 def __init__(self, event, canvas): 220 Toplevel.__init__(self) 221 self.resizable(0, 0) 222 self.title("Event") 223 self.event = event 224 self.frame = Frame(self) 225 self.frame.grid(row=0, column=0, sticky=N+S+E+W) 226 self.buttons = Frame(self) 227 self.buttons.grid(row=1, column=0, sticky=E+W) 228 self.canvas = canvas 229 self.drawlabels() 230 self.drawbuttons() 231 event.displayref(canvas) 232 self.bind("<Destroy>", self.destroycb) 233 234 def destroycb(self, event): 235 self.unbind("<Destroy>") 236 if (self.event != None): 237 self.event.displayunref(self.canvas) 238 self.event = None 239 self.destroy() 240 241 def clearlabels(self): 242 for label in self.frame.grid_slaves(): 243 label.grid_remove() 244 245 def drawlabels(self): 246 ypos = 0 247 labels = self.event.labels() 248 while (len(labels) < 7): 249 labels.append(("", "", 0)) 250 for label in labels: 251 name, value, linked = label 252 l = Label(self.frame, text=name, bd=1, width=15, 253 relief=SUNKEN, anchor=W) 254 if (linked): 255 fgcolor = "blue" 256 else: 257 fgcolor = "black" 258 r = Label(self.frame, text=value, bd=1, 259 relief=SUNKEN, anchor=W, fg=fgcolor) 260 l.grid(row=ypos, column=0, sticky=E+W) 261 r.grid(row=ypos, column=1, sticky=E+W) 262 if (linked): 263 r.bind("<Button-1>", self.linkpress) 264 ypos += 1 265 self.frame.columnconfigure(1, minsize=80) 266 267 def drawbuttons(self): 268 self.back = Button(self.buttons, text="<", command=self.bpress) 269 self.forw = Button(self.buttons, text=">", command=self.fpress) 270 self.new = Button(self.buttons, text="new", command=self.npress) 271 self.back.grid(row=0, column=0, sticky=E+W) 272 self.forw.grid(row=0, column=1, sticky=E+W) 273 self.new.grid(row=0, column=2, sticky=E+W) 274 self.buttons.columnconfigure(2, weight=1) 275 276 def newevent(self, event): 277 self.event.displayunref(self.canvas) 278 self.clearlabels() 279 self.event = event 280 self.event.displayref(self.canvas) 281 self.drawlabels() 282 283 def npress(self): 284 EventView(self.event, self.canvas) 285 286 def bpress(self): 287 prev = self.event.prev() 288 if (prev == None): 289 return 290 while (prev.real == 0): 291 prev = prev.prev() 292 if (prev == None): 293 return 294 self.newevent(prev) 295 296 def fpress(self): 297 next = self.event.next() 298 if (next == None): 299 return 300 while (next.real == 0): 301 next = next.next() 302 if (next == None): 303 return 304 self.newevent(next) 305 306 def linkpress(self, wevent): 307 event = self.event.getlinked() 308 if (event != None): 309 self.newevent(event) 310 311class Event: 312 name = "none" 313 color = "grey" 314 def __init__(self, source, cpu, timestamp, last=0): 315 self.source = source 316 self.cpu = cpu 317 self.timestamp = int(timestamp) 318 self.entries = [] 319 self.real = 1 320 self.idx = None 321 self.state = 0 322 self.item = None 323 self.dispcnt = 0 324 self.linked = None 325 if (last): 326 source.lastevent(self) 327 else: 328 source.event(self) 329 330 def status(self): 331 statstr = self.name + " " + self.source.name 332 statstr += " on: cpu" + str(self.cpu) 333 statstr += " at: " + str(self.timestamp) 334 statstr += self.stattxt() 335 status.set(statstr) 336 337 def stattxt(self): 338 return "" 339 340 def textadd(self, tuple): 341 pass 342 self.entries.append(tuple) 343 344 def labels(self): 345 return [("Source:", self.source.name, 0), 346 ("Event:", self.name, 0), 347 ("CPU:", self.cpu, 0), 348 ("Timestamp:", self.timestamp, 0)] + self.entries 349 def mouseenter(self, canvas, item): 350 self.displayref(canvas) 351 self.status() 352 353 def mouseexit(self, canvas, item): 354 self.displayunref(canvas) 355 status.clear() 356 357 def mousepress(self, canvas, item): 358 EventView(self, canvas) 359 360 def next(self): 361 return self.source.eventat(self.idx + 1) 362 363 def prev(self): 364 return self.source.eventat(self.idx - 1) 365 366 def displayref(self, canvas): 367 if (self.dispcnt == 0): 368 canvas.itemconfigure(self.item, width=2) 369 self.dispcnt += 1 370 371 def displayunref(self, canvas): 372 self.dispcnt -= 1 373 if (self.dispcnt == 0): 374 canvas.itemconfigure(self.item, width=0) 375 canvas.tag_raise("point", "state") 376 377 def getlinked(self): 378 return self.linked.findevent(self.timestamp) 379 380class PointEvent(Event): 381 def __init__(self, thread, cpu, timestamp, last=0): 382 Event.__init__(self, thread, cpu, timestamp, last) 383 384 def draw(self, canvas, xpos, ypos): 385 l = canvas.create_oval(xpos - 6, ypos + 1, xpos + 6, ypos - 11, 386 fill=self.color, tags=("all", "point", "event") 387 + (self.name,), width=0) 388 canvas.events[l] = self 389 self.item = l 390 if (self.enabled == 0): 391 canvas.itemconfigure(l, state="hidden") 392 393 return (xpos) 394 395class StateEvent(Event): 396 def __init__(self, thread, cpu, timestamp, last=0): 397 Event.__init__(self, thread, cpu, timestamp, last) 398 self.duration = 0 399 self.skipnext = 0 400 self.skipself = 0 401 self.state = 1 402 403 def draw(self, canvas, xpos, ypos): 404 next = self.nextstate() 405 if (self.skipself == 1 or next == None): 406 return (xpos) 407 while (self.skipnext): 408 skipped = next 409 next.skipself = 1 410 next.real = 0 411 next = next.nextstate() 412 if (next == None): 413 next = skipped 414 self.skipnext -= 1 415 self.duration = next.timestamp - self.timestamp 416 delta = self.duration / canvas.ratio 417 l = canvas.create_rectangle(xpos, ypos, 418 xpos + delta, ypos - 10, fill=self.color, width=0, 419 tags=("all", "state", "event") + (self.name,)) 420 canvas.events[l] = self 421 self.item = l 422 if (self.enabled == 0): 423 canvas.itemconfigure(l, state="hidden") 424 425 return (xpos + delta) 426 427 def stattxt(self): 428 return " duration: " + ticks2sec(self.duration) 429 430 def nextstate(self): 431 next = self.next() 432 while (next != None and next.state == 0): 433 next = next.next() 434 return (next) 435 436 def labels(self): 437 return [("Source:", self.source.name, 0), 438 ("Event:", self.name, 0), 439 ("Timestamp:", self.timestamp, 0), 440 ("CPU:", self.cpu, 0), 441 ("Duration:", ticks2sec(self.duration), 0)] \ 442 + self.entries 443 444class Count(Event): 445 name = "Count" 446 color = "red" 447 enabled = 1 448 def __init__(self, source, cpu, timestamp, count): 449 self.count = int(count) 450 Event.__init__(self, source, cpu, timestamp) 451 self.duration = 0 452 self.textadd(("count:", self.count, 0)) 453 454 def draw(self, canvas, xpos, ypos): 455 next = self.next() 456 self.duration = next.timestamp - self.timestamp 457 delta = self.duration / canvas.ratio 458 yhight = self.source.yscale() * self.count 459 l = canvas.create_rectangle(xpos, ypos - yhight, 460 xpos + delta, ypos, fill=self.color, width=0, 461 tags=("all", "count", "event") + (self.name,)) 462 canvas.events[l] = self 463 self.item = l 464 if (self.enabled == 0): 465 canvas.itemconfigure(l, state="hidden") 466 return (xpos + delta) 467 468 def stattxt(self): 469 return " count: " + str(self.count) 470 471configtypes.append(Count) 472 473class Running(StateEvent): 474 name = "running" 475 color = "green" 476 enabled = 1 477 def __init__(self, thread, cpu, timestamp, prio): 478 StateEvent.__init__(self, thread, cpu, timestamp) 479 self.prio = prio 480 self.textadd(("prio:", self.prio, 0)) 481 482configtypes.append(Running) 483 484class Idle(StateEvent): 485 name = "idle" 486 color = "grey" 487 enabled = 0 488 def __init__(self, thread, cpu, timestamp, prio): 489 StateEvent.__init__(self, thread, cpu, timestamp) 490 self.prio = prio 491 self.textadd(("prio:", self.prio, 0)) 492 493configtypes.append(Idle) 494 495class Yielding(StateEvent): 496 name = "yielding" 497 color = "yellow" 498 enabled = 1 499 def __init__(self, thread, cpu, timestamp, prio): 500 StateEvent.__init__(self, thread, cpu, timestamp) 501 self.skipnext = 2 502 self.prio = prio 503 self.textadd(("prio:", self.prio, 0)) 504 505configtypes.append(Yielding) 506 507class Swapped(StateEvent): 508 name = "swapped" 509 color = "violet" 510 enabled = 1 511 def __init__(self, thread, cpu, timestamp, prio): 512 StateEvent.__init__(self, thread, cpu, timestamp) 513 self.prio = prio 514 self.textadd(("prio:", self.prio, 0)) 515 516configtypes.append(Swapped) 517 518class Suspended(StateEvent): 519 name = "suspended" 520 color = "purple" 521 enabled = 1 522 def __init__(self, thread, cpu, timestamp, prio): 523 StateEvent.__init__(self, thread, cpu, timestamp) 524 self.prio = prio 525 self.textadd(("prio:", self.prio, 0)) 526 527configtypes.append(Suspended) 528 529class Iwait(StateEvent): 530 name = "iwait" 531 color = "grey" 532 enabled = 0 533 def __init__(self, thread, cpu, timestamp, prio): 534 StateEvent.__init__(self, thread, cpu, timestamp) 535 self.prio = prio 536 self.textadd(("prio:", self.prio, 0)) 537 538configtypes.append(Iwait) 539 540class Preempted(StateEvent): 541 name = "preempted" 542 color = "red" 543 enabled = 1 544 def __init__(self, thread, cpu, timestamp, prio, bythread): 545 StateEvent.__init__(self, thread, cpu, timestamp) 546 self.skipnext = 2 547 self.prio = prio 548 self.linked = bythread 549 self.textadd(("prio:", self.prio, 0)) 550 self.textadd(("by thread:", self.linked.name, 1)) 551 552configtypes.append(Preempted) 553 554class Sleep(StateEvent): 555 name = "sleep" 556 color = "blue" 557 enabled = 1 558 def __init__(self, thread, cpu, timestamp, prio, wmesg): 559 StateEvent.__init__(self, thread, cpu, timestamp) 560 self.prio = prio 561 self.wmesg = wmesg 562 self.textadd(("prio:", self.prio, 0)) 563 self.textadd(("wmesg:", self.wmesg, 0)) 564 565 def stattxt(self): 566 statstr = StateEvent.stattxt(self) 567 statstr += " sleeping on: " + self.wmesg 568 return (statstr) 569 570configtypes.append(Sleep) 571 572class Blocked(StateEvent): 573 name = "blocked" 574 color = "dark red" 575 enabled = 1 576 def __init__(self, thread, cpu, timestamp, prio, lock): 577 StateEvent.__init__(self, thread, cpu, timestamp) 578 self.prio = prio 579 self.lock = lock 580 self.textadd(("prio:", self.prio, 0)) 581 self.textadd(("lock:", self.lock, 0)) 582 583 def stattxt(self): 584 statstr = StateEvent.stattxt(self) 585 statstr += " blocked on: " + self.lock 586 return (statstr) 587 588configtypes.append(Blocked) 589 590class KsegrpRunq(StateEvent): 591 name = "KsegrpRunq" 592 color = "orange" 593 enabled = 1 594 def __init__(self, thread, cpu, timestamp, prio, bythread): 595 StateEvent.__init__(self, thread, cpu, timestamp) 596 self.prio = prio 597 self.linked = bythread 598 self.textadd(("prio:", self.prio, 0)) 599 self.textadd(("by thread:", self.linked.name, 1)) 600 601configtypes.append(KsegrpRunq) 602 603class Runq(StateEvent): 604 name = "Runq" 605 color = "yellow" 606 enabled = 1 607 def __init__(self, thread, cpu, timestamp, prio, bythread): 608 StateEvent.__init__(self, thread, cpu, timestamp) 609 self.prio = prio 610 self.linked = bythread 611 self.textadd(("prio:", self.prio, 0)) 612 self.textadd(("by thread:", self.linked.name, 1)) 613 614configtypes.append(Runq) 615 616class Sched_exit(StateEvent): 617 name = "exit" 618 color = "grey" 619 enabled = 0 620 def __init__(self, thread, cpu, timestamp, prio): 621 StateEvent.__init__(self, thread, cpu, timestamp) 622 self.name = "sched_exit" 623 self.prio = prio 624 self.textadd(("prio:", self.prio, 0)) 625 626configtypes.append(Sched_exit) 627 628class Padevent(StateEvent): 629 def __init__(self, thread, cpu, timestamp, last=0): 630 StateEvent.__init__(self, thread, cpu, timestamp, last) 631 self.name = "pad" 632 self.real = 0 633 634 def draw(self, canvas, xpos, ypos): 635 next = self.next() 636 if (next == None): 637 return (xpos) 638 self.duration = next.timestamp - self.timestamp 639 delta = self.duration / canvas.ratio 640 return (xpos + delta) 641 642class Tick(PointEvent): 643 name = "tick" 644 color = "black" 645 enabled = 0 646 def __init__(self, thread, cpu, timestamp, prio, stathz): 647 PointEvent.__init__(self, thread, cpu, timestamp) 648 self.prio = prio 649 self.textadd(("prio:", self.prio, 0)) 650 651configtypes.append(Tick) 652 653class Prio(PointEvent): 654 name = "prio" 655 color = "black" 656 enabled = 0 657 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread): 658 PointEvent.__init__(self, thread, cpu, timestamp) 659 self.prio = prio 660 self.newprio = newprio 661 self.linked = bythread 662 self.textadd(("new prio:", self.newprio, 0)) 663 self.textadd(("prio:", self.prio, 0)) 664 if (self.linked != self.source): 665 self.textadd(("by thread:", self.linked.name, 1)) 666 else: 667 self.textadd(("by thread:", self.linked.name, 0)) 668 669configtypes.append(Prio) 670 671class Lend(PointEvent): 672 name = "lend" 673 color = "black" 674 enabled = 0 675 def __init__(self, thread, cpu, timestamp, prio, tothread): 676 PointEvent.__init__(self, thread, cpu, timestamp) 677 self.prio = prio 678 self.linked = tothread 679 self.textadd(("prio:", self.prio, 0)) 680 self.textadd(("to thread:", self.linked.name, 1)) 681 682configtypes.append(Lend) 683 684class Wokeup(PointEvent): 685 name = "wokeup" 686 color = "black" 687 enabled = 0 688 def __init__(self, thread, cpu, timestamp, ranthread): 689 PointEvent.__init__(self, thread, cpu, timestamp) 690 self.linked = ranthread 691 self.textadd(("ran thread:", self.linked.name, 1)) 692 693configtypes.append(Wokeup) 694 695class EventSource: 696 def __init__(self, name): 697 self.name = name 698 self.events = [] 699 self.cpu = 0 700 self.cpux = 0 701 702 def fixup(self): 703 pass 704 705 def event(self, event): 706 self.events.insert(0, event) 707 708 def remove(self, event): 709 self.events.remove(event) 710 711 def lastevent(self, event): 712 self.events.append(event) 713 714 def draw(self, canvas, ypos): 715 xpos = 10 716 self.cpux = 10 717 self.cpu = self.events[1].cpu 718 for i in range(0, len(self.events)): 719 self.events[i].idx = i 720 for event in self.events: 721 if (event.cpu != self.cpu and event.cpu != -1): 722 self.drawcpu(canvas, xpos, ypos) 723 self.cpux = xpos 724 self.cpu = event.cpu 725 xpos = event.draw(canvas, xpos, ypos) 726 self.drawcpu(canvas, xpos, ypos) 727 728 def drawname(self, canvas, ypos): 729 ypos = ypos - (self.ysize() / 2) 730 canvas.create_text(10, ypos, anchor="w", text=self.name) 731 732 def drawcpu(self, canvas, xpos, ypos): 733 cpu = int(self.cpu) 734 if (cpu == 0): 735 color = 'light grey' 736 elif (cpu == 1): 737 color = 'dark grey' 738 elif (cpu == 2): 739 color = 'light blue' 740 elif (cpu == 3): 741 color = 'light green' 742 elif (cpu == 4): 743 color = 'blanched almond' 744 elif (cpu == 5): 745 color = 'slate grey' 746 elif (cpu == 6): 747 color = 'light slate blue' 748 elif (cpu == 7): 749 color = 'thistle' 750 else: 751 color = "white" 752 l = canvas.create_rectangle(self.cpux, 753 ypos - self.ysize() - canvas.bdheight, 754 xpos, ypos + canvas.bdheight, fill=color, width=0, 755 tags=("all", "cpuinfo")) 756 757 def ysize(self): 758 return (None) 759 760 def eventat(self, i): 761 if (i >= len(self.events)): 762 return (None) 763 event = self.events[i] 764 return (event) 765 766 def findevent(self, timestamp): 767 for event in self.events: 768 if (event.timestamp >= timestamp and event.real): 769 return (event) 770 return (None) 771 772class Thread(EventSource): 773 names = {} 774 def __init__(self, td, pcomm): 775 EventSource.__init__(self, pcomm) 776 self.str = td 777 try: 778 cnt = Thread.names[pcomm] 779 except: 780 Thread.names[pcomm] = 0 781 return 782 Thread.names[pcomm] = cnt + 1 783 784 def fixup(self): 785 cnt = Thread.names[self.name] 786 if (cnt == 0): 787 return 788 cnt -= 1 789 Thread.names[self.name] = cnt 790 self.name += " td" + str(cnt) 791 792 def ysize(self): 793 return (10) 794 795class Counter(EventSource): 796 max = 0 797 def __init__(self, name): 798 EventSource.__init__(self, name) 799 800 def event(self, event): 801 EventSource.event(self, event) 802 try: 803 count = event.count 804 except: 805 return 806 count = int(count) 807 if (count > Counter.max): 808 Counter.max = count 809 810 def ysize(self): 811 return (80) 812 813 def yscale(self): 814 return (self.ysize() / Counter.max) 815 816 817class KTRFile: 818 def __init__(self, file): 819 self.timestamp_first = None 820 self.timestamp_last = None 821 self.lineno = -1 822 self.threads = [] 823 self.sources = [] 824 self.ticks = {} 825 self.load = {} 826 self.crit = {} 827 828 self.parse(file) 829 self.fixup() 830 global ticksps 831 ticksps = self.ticksps() 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