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