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 else: 743 color = "white" 744 l = canvas.create_rectangle(self.cpux, 745 ypos - self.ysize() - canvas.bdheight, 746 xpos, ypos + canvas.bdheight, fill=color, width=0, 747 tags=("all", "cpuinfo")) 748 749 def ysize(self): 750 return (None) 751 752 def eventat(self, i): 753 if (i >= len(self.events)): 754 return (None) 755 event = self.events[i] 756 return (event) 757 758 def findevent(self, timestamp): 759 for event in self.events: 760 if (event.timestamp >= timestamp and event.real): 761 return (event) 762 return (None) 763 764class Thread(EventSource): 765 names = {} 766 def __init__(self, td, pcomm): 767 EventSource.__init__(self, pcomm) 768 self.str = td 769 try: 770 cnt = Thread.names[pcomm] 771 except: 772 Thread.names[pcomm] = 0 773 return 774 Thread.names[pcomm] = cnt + 1 775 776 def fixup(self): 777 cnt = Thread.names[self.name] 778 if (cnt == 0): 779 return 780 cnt -= 1 781 Thread.names[self.name] = cnt 782 self.name += " td" + str(cnt) 783 784 def ysize(self): 785 return (10) 786 787class Counter(EventSource): 788 max = 0 789 def __init__(self, name): 790 EventSource.__init__(self, name) 791 792 def event(self, event): 793 EventSource.event(self, event) 794 try: 795 count = event.count 796 except: 797 return 798 count = int(count) 799 if (count > Counter.max): 800 Counter.max = count 801 802 def ysize(self): 803 return (80) 804 805 def yscale(self): 806 return (self.ysize() / Counter.max) 807 808 809class KTRFile: 810 def __init__(self, file): 811 self.timestamp_first = None 812 self.timestamp_last = None 813 self.lineno = -1 814 self.threads = [] 815 self.sources = [] 816 self.ticks = {} 817 self.load = {} 818 self.crit = {} 819 820 self.parse(file) 821 self.fixup() 822 global ticksps 823 ticksps = self.ticksps() 824 825 def parse(self, file): 826 try: 827 ifp = open(file) 828 except: 829 print "Can't open", file 830 sys.exit(1) 831 832 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+" 833 tdname = "(\S+)\(([^)]*)\)" 834 crittdname = "(\S+)\s+\(\d+,\s+([^)]*)\)" 835 836 ktrstr = "mi_switch: " + tdname 837 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)" 838 switchout_re = re.compile(ktrhdr + ktrstr) 839 840 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle" 841 idled_re = re.compile(ktrhdr + ktrstr) 842 843 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by " 844 ktrstr += tdname 845 preempted_re = re.compile(ktrhdr + ktrstr) 846 847 ktrstr = "mi_switch: running " + tdname + " prio (\d+)" 848 switchin_re = re.compile(ktrhdr + ktrstr) 849 850 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname 851 sched_add_re = re.compile(ktrhdr + ktrstr) 852 853 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname 854 setrunqueue_re = re.compile(ktrhdr + ktrstr) 855 856 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname 857 sched_rem_re = re.compile(ktrhdr + ktrstr) 858 859 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)" 860 sched_exit_re = re.compile(ktrhdr + ktrstr) 861 862 ktrstr = "statclock: " + tdname + " prio (\d+)" 863 ktrstr += " stathz (\d+)" 864 sched_clock_re = re.compile(ktrhdr + ktrstr) 865 866 ktrstr = "sched_prio: " + tdname + " prio (\d+)" 867 ktrstr += " newprio (\d+) by " + tdname 868 sched_prio_re = re.compile(ktrhdr + ktrstr) 869 870 cpuload_re = re.compile(ktrhdr + "load: (\d+)") 871 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)") 872 873 ktrstr = "critical_\S+ by thread " + crittdname + " to (\d+)" 874 critsec_re = re.compile(ktrhdr + ktrstr) 875 876 parsers = [[cpuload_re, self.cpuload], 877 [loadglobal_re, self.loadglobal], 878 [switchin_re, self.switchin], 879 [switchout_re, self.switchout], 880 [sched_add_re, self.sched_add], 881 [setrunqueue_re, self.sched_rem], 882 [sched_prio_re, self.sched_prio], 883 [preempted_re, self.preempted], 884 [sched_rem_re, self.sched_rem], 885 [sched_exit_re, self.sched_exit], 886 [sched_clock_re, self.sched_clock], 887 [critsec_re, self.critsec], 888 [idled_re, self.idled]] 889 890 for line in ifp.readlines(): 891 self.lineno += 1 892 if ((self.lineno % 1024) == 0): 893 status.startup("Parsing line " + 894 str(self.lineno)) 895 for p in parsers: 896 m = p[0].match(line) 897 if (m != None): 898 p[1](*m.groups()) 899 break 900 # if (m == None): 901 # print line, 902 903 def checkstamp(self, timestamp): 904 timestamp = int(timestamp) 905 if (self.timestamp_first == None): 906 self.timestamp_first = timestamp 907 if (timestamp > self.timestamp_first): 908 print "Bad timestamp on line ", self.lineno 909 return (0) 910 self.timestamp_last = timestamp 911 return (1) 912 913 def timespan(self): 914 return (self.timestamp_first - self.timestamp_last); 915 916 def ticksps(self): 917 return (self.timespan() / self.ticks[0]) * int(self.stathz) 918 919 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock): 920 TDI_SUSPENDED = 0x0001 921 TDI_SLEEPING = 0x0002 922 TDI_SWAPPED = 0x0004 923 TDI_LOCK = 0x0008 924 TDI_IWAIT = 0x0010 925 926 if (self.checkstamp(timestamp) == 0): 927 return 928 inhibit = int(inhibit) 929 thread = self.findtd(td, pcomm) 930 if (inhibit & TDI_SWAPPED): 931 Swapped(thread, cpu, timestamp, prio) 932 elif (inhibit & TDI_SLEEPING): 933 Sleep(thread, cpu, timestamp, prio, wmesg) 934 elif (inhibit & TDI_LOCK): 935 Blocked(thread, cpu, timestamp, prio, lock) 936 elif (inhibit & TDI_IWAIT): 937 Iwait(thread, cpu, timestamp, prio) 938 elif (inhibit & TDI_SUSPENDED): 939 Suspended(thread, cpu, timestamp, prio) 940 elif (inhibit == 0): 941 Yielding(thread, cpu, timestamp, prio) 942 else: 943 print "Unknown event", inhibit 944 sys.exit(1) 945 946 def idled(self, cpu, timestamp, td, pcomm, prio): 947 if (self.checkstamp(timestamp) == 0): 948 return 949 thread = self.findtd(td, pcomm) 950 Idle(thread, cpu, timestamp, prio) 951 952 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 953 if (self.checkstamp(timestamp) == 0): 954 return 955 thread = self.findtd(td, pcomm) 956 Preempted(thread, cpu, timestamp, prio, 957 self.findtd(bytd, bypcomm)) 958 959 def switchin(self, cpu, timestamp, td, pcomm, prio): 960 if (self.checkstamp(timestamp) == 0): 961 return 962 thread = self.findtd(td, pcomm) 963 Running(thread, cpu, timestamp, prio) 964 965 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 966 if (self.checkstamp(timestamp) == 0): 967 return 968 thread = self.findtd(td, pcomm) 969 bythread = self.findtd(bytd, bypcomm) 970 Runq(thread, cpu, timestamp, prio, bythread) 971 Wokeup(bythread, cpu, timestamp, thread) 972 973 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 974 if (self.checkstamp(timestamp) == 0): 975 return 976 thread = self.findtd(td, pcomm) 977 KsegrpRunq(thread, cpu, timestamp, prio, 978 self.findtd(bytd, bypcomm)) 979 980 def sched_exit(self, cpu, timestamp, td, pcomm, prio): 981 if (self.checkstamp(timestamp) == 0): 982 return 983 thread = self.findtd(td, pcomm) 984 Sched_exit(thread, cpu, timestamp, prio) 985 986 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz): 987 if (self.checkstamp(timestamp) == 0): 988 return 989 self.stathz = stathz 990 cpu = int(cpu) 991 try: 992 ticks = self.ticks[cpu] 993 except: 994 self.ticks[cpu] = 0 995 self.ticks[cpu] += 1 996 thread = self.findtd(td, pcomm) 997 Tick(thread, cpu, timestamp, prio, stathz) 998 999 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm): 1000 if (prio == newprio): 1001 return 1002 if (self.checkstamp(timestamp) == 0): 1003 return 1004 thread = self.findtd(td, pcomm) 1005 bythread = self.findtd(bytd, bypcomm) 1006 Prio(thread, cpu, timestamp, prio, newprio, bythread) 1007 Lend(bythread, cpu, timestamp, newprio, thread) 1008 1009 def cpuload(self, cpu, timestamp, count): 1010 if (self.checkstamp(timestamp) == 0): 1011 return 1012 cpu = int(cpu) 1013 try: 1014 load = self.load[cpu] 1015 except: 1016 load = Counter("cpu" + str(cpu) + " load") 1017 self.load[cpu] = load 1018 self.sources.insert(0, load) 1019 Count(load, cpu, timestamp, count) 1020 1021 def loadglobal(self, cpu, timestamp, count): 1022 if (self.checkstamp(timestamp) == 0): 1023 return 1024 cpu = 0 1025 try: 1026 load = self.load[cpu] 1027 except: 1028 load = Counter("CPU load") 1029 self.load[cpu] = load 1030 self.sources.insert(0, load) 1031 Count(load, cpu, timestamp, count) 1032 1033 def critsec(self, cpu, timestamp, td, pcomm, to): 1034 if (self.checkstamp(timestamp) == 0): 1035 return 1036 cpu = int(cpu) 1037 try: 1038 crit = self.crit[cpu] 1039 except: 1040 crit = Counter("Critical Section") 1041 self.crit[cpu] = crit 1042 self.sources.insert(0, crit) 1043 Count(crit, cpu, timestamp, to) 1044 1045 def findtd(self, td, pcomm): 1046 for thread in self.threads: 1047 if (thread.str == td and thread.name == pcomm): 1048 return thread 1049 thread = Thread(td, pcomm) 1050 self.threads.append(thread) 1051 self.sources.append(thread) 1052 return (thread) 1053 1054 def fixup(self): 1055 for source in self.sources: 1056 Padevent(source, -1, self.timestamp_last) 1057 Padevent(source, -1, self.timestamp_first, last=1) 1058 source.fixup() 1059 1060class SchedDisplay(Canvas): 1061 def __init__(self, master): 1062 self.ratio = 1 1063 self.ktrfile = None 1064 self.sources = None 1065 self.bdheight = 10 1066 self.events = {} 1067 1068 Canvas.__init__(self, master, width=800, height=500, bg='grey', 1069 scrollregion=(0, 0, 800, 500)) 1070 1071 def setfile(self, ktrfile): 1072 self.ktrfile = ktrfile 1073 self.sources = ktrfile.sources 1074 1075 def draw(self): 1076 ypos = 0 1077 xsize = self.xsize() 1078 for source in self.sources: 1079 status.startup("Drawing " + source.name) 1080 self.create_line(0, ypos, xsize, ypos, 1081 width=1, fill="black", tags=("all",)) 1082 ypos += self.bdheight 1083 ypos += source.ysize() 1084 source.draw(self, ypos) 1085 ypos += self.bdheight 1086 try: 1087 self.tag_raise("point", "state") 1088 self.tag_lower("cpuinfo", "all") 1089 except: 1090 pass 1091 self.create_line(0, ypos, xsize, ypos, 1092 width=1, fill="black", tags=("all",)) 1093 self.tag_bind("event", "<Enter>", self.mouseenter) 1094 self.tag_bind("event", "<Leave>", self.mouseexit) 1095 self.tag_bind("event", "<Button-1>", self.mousepress) 1096 1097 def mouseenter(self, event): 1098 item, = self.find_withtag(CURRENT) 1099 event = self.events[item] 1100 event.mouseenter(self, item) 1101 1102 def mouseexit(self, event): 1103 item, = self.find_withtag(CURRENT) 1104 event = self.events[item] 1105 event.mouseexit(self, item) 1106 1107 def mousepress(self, event): 1108 item, = self.find_withtag(CURRENT) 1109 event = self.events[item] 1110 event.mousepress(self, item) 1111 1112 def drawnames(self, canvas): 1113 status.startup("Drawing names") 1114 ypos = 0 1115 canvas.configure(scrollregion=(0, 0, 1116 canvas["width"], self.ysize())) 1117 for source in self.sources: 1118 canvas.create_line(0, ypos, canvas["width"], ypos, 1119 width=1, fill="black", tags=("all",)) 1120 ypos += self.bdheight 1121 ypos += source.ysize() 1122 source.drawname(canvas, ypos) 1123 ypos += self.bdheight 1124 canvas.create_line(0, ypos, canvas["width"], ypos, 1125 width=1, fill="black", tags=("all",)) 1126 1127 def xsize(self): 1128 return ((self.ktrfile.timespan() / self.ratio) + 20) 1129 1130 def ysize(self): 1131 ysize = 0 1132 for source in self.sources: 1133 ysize += source.ysize() + (self.bdheight * 2) 1134 return (ysize) 1135 1136 def scaleset(self, ratio): 1137 if (self.ktrfile == None): 1138 return 1139 oldratio = self.ratio 1140 xstart, ystart = self.xview() 1141 length = (float(self["width"]) / self.xsize()) 1142 middle = xstart + (length / 2) 1143 1144 self.ratio = ratio 1145 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize())) 1146 self.scale("all", 0, 0, float(oldratio) / ratio, 1) 1147 1148 length = (float(self["width"]) / self.xsize()) 1149 xstart = middle - (length / 2) 1150 self.xview_moveto(xstart) 1151 1152 def scaleget(self): 1153 return self.ratio 1154 1155 def setcolor(self, tag, color): 1156 self.itemconfigure(tag, state="normal", fill=color) 1157 1158 def hide(self, tag): 1159 self.itemconfigure(tag, state="hidden") 1160 1161class GraphMenu(Frame): 1162 def __init__(self, master): 1163 Frame.__init__(self, master, bd=2, relief=RAISED) 1164 self.view = Menubutton(self, text="Configure") 1165 self.viewmenu = Menu(self.view, tearoff=0) 1166 self.viewmenu.add_command(label="Events", 1167 command=self.econf) 1168 self.view["menu"] = self.viewmenu 1169 self.view.pack(side=LEFT) 1170 1171 def econf(self): 1172 EventConfigure() 1173 1174 1175class SchedGraph(Frame): 1176 def __init__(self, master): 1177 Frame.__init__(self, master) 1178 self.menu = None 1179 self.names = None 1180 self.display = None 1181 self.scale = None 1182 self.status = None 1183 self.pack(expand=1, fill="both") 1184 self.buildwidgets() 1185 self.layout() 1186 self.draw(sys.argv[1]) 1187 1188 def buildwidgets(self): 1189 global status 1190 self.menu = GraphMenu(self) 1191 self.display = SchedDisplay(self) 1192 self.names = Canvas(self, 1193 width=100, height=self.display["height"], 1194 bg='grey', scrollregion=(0, 0, 50, 100)) 1195 self.scale = Scaler(self, self.display) 1196 status = self.status = Status(self) 1197 self.scrollY = Scrollbar(self, orient="vertical", 1198 command=self.display_yview) 1199 self.display.scrollX = Scrollbar(self, orient="horizontal", 1200 command=self.display.xview) 1201 self.display["xscrollcommand"] = self.display.scrollX.set 1202 self.display["yscrollcommand"] = self.scrollY.set 1203 self.names["yscrollcommand"] = self.scrollY.set 1204 1205 def layout(self): 1206 self.columnconfigure(1, weight=1) 1207 self.rowconfigure(1, weight=1) 1208 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W) 1209 self.names.grid(row=1, column=0, sticky=N+S) 1210 self.display.grid(row=1, column=1, sticky=W+E+N+S) 1211 self.scrollY.grid(row=1, column=2, sticky=N+S) 1212 self.display.scrollX.grid(row=2, column=0, columnspan=2, 1213 sticky=E+W) 1214 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W) 1215 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W) 1216 1217 def draw(self, file): 1218 self.master.update() 1219 ktrfile = KTRFile(file) 1220 self.display.setfile(ktrfile) 1221 self.display.drawnames(self.names) 1222 self.display.draw() 1223 self.scale.set(250000) 1224 self.display.xview_moveto(0) 1225 1226 def display_yview(self, *args): 1227 self.names.yview(*args) 1228 self.display.yview(*args) 1229 1230 def setcolor(self, tag, color): 1231 self.display.setcolor(tag, color) 1232 1233 def hide(self, tag): 1234 self.display.hide(tag) 1235 1236if (len(sys.argv) != 2): 1237 print "usage:", sys.argv[0], "<ktr file>" 1238 sys.exit(1) 1239 1240root = Tk() 1241root.title("Scheduler Graph") 1242graph = SchedGraph(root) 1243root.mainloop() 1244