schedgraph.py revision 152131
1#!/usr/local/bin/python 2 3# Copyright (c) 2002-2003, Jeffrey Roberson <jeff@freebsd.org> 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice unmodified, this list of conditions, and the following 11# disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26# 27# $FreeBSD: head/tools/sched/schedgraph.py 152131 2005-11-06 17:43:25Z rwatson $ 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 819 self.parse(file) 820 self.fixup() 821 global ticksps 822 ticksps = self.ticksps() 823 824 def parse(self, file): 825 try: 826 ifp = open(file) 827 except: 828 print "Can't open", file 829 sys.exit(1) 830 831 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+" 832 tdname = "(\S+)\(([^)]*)\)" 833 834 ktrstr = "mi_switch: " + tdname 835 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)" 836 switchout_re = re.compile(ktrhdr + ktrstr) 837 838 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle" 839 idled_re = re.compile(ktrhdr + ktrstr) 840 841 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by " 842 ktrstr += tdname 843 preempted_re = re.compile(ktrhdr + ktrstr) 844 845 ktrstr = "mi_switch: running " + tdname + " prio (\d+)" 846 switchin_re = re.compile(ktrhdr + ktrstr) 847 848 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname 849 sched_add_re = re.compile(ktrhdr + ktrstr) 850 851 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname 852 setrunqueue_re = re.compile(ktrhdr + ktrstr) 853 854 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname 855 sched_rem_re = re.compile(ktrhdr + ktrstr) 856 857 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)" 858 sched_exit_re = re.compile(ktrhdr + ktrstr) 859 860 ktrstr = "statclock: " + tdname + " prio (\d+)" 861 ktrstr += " stathz (\d+)" 862 sched_clock_re = re.compile(ktrhdr + ktrstr) 863 864 ktrstr = "sched_prio: " + tdname + " prio (\d+)" 865 ktrstr += " newprio (\d+) by " + tdname 866 sched_prio_re = re.compile(ktrhdr + ktrstr) 867 868 cpuload_re = re.compile(ktrhdr + "load: (\d+)") 869 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)") 870 871 parsers = [[cpuload_re, self.cpuload], 872 [loadglobal_re, self.loadglobal], 873 [switchin_re, self.switchin], 874 [switchout_re, self.switchout], 875 [sched_add_re, self.sched_add], 876 [setrunqueue_re, self.sched_rem], 877 [sched_prio_re, self.sched_prio], 878 [preempted_re, self.preempted], 879 [sched_rem_re, self.sched_rem], 880 [sched_exit_re, self.sched_exit], 881 [sched_clock_re, self.sched_clock], 882 [idled_re, self.idled]] 883 884 for line in ifp.readlines(): 885 self.lineno += 1 886 if ((self.lineno % 1024) == 0): 887 status.startup("Parsing line " + 888 str(self.lineno)) 889 for p in parsers: 890 m = p[0].match(line) 891 if (m != None): 892 p[1](*m.groups()) 893 break 894 # if (m == None): 895 # print line, 896 897 def checkstamp(self, timestamp): 898 timestamp = int(timestamp) 899 if (self.timestamp_first == None): 900 self.timestamp_first = timestamp 901 if (timestamp > self.timestamp_first): 902 print "Bad timestamp on line ", self.lineno 903 return (0) 904 self.timestamp_last = timestamp 905 return (1) 906 907 def timespan(self): 908 return (self.timestamp_first - self.timestamp_last); 909 910 def ticksps(self): 911 return (self.timespan() / self.ticks[0]) * int(self.stathz) 912 913 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock): 914 TDI_SUSPENDED = 0x0001 915 TDI_SLEEPING = 0x0002 916 TDI_SWAPPED = 0x0004 917 TDI_LOCK = 0x0008 918 TDI_IWAIT = 0x0010 919 920 if (self.checkstamp(timestamp) == 0): 921 return 922 inhibit = int(inhibit) 923 thread = self.findtd(td, pcomm) 924 if (inhibit & TDI_SWAPPED): 925 Swapped(thread, cpu, timestamp, prio) 926 elif (inhibit & TDI_SLEEPING): 927 Sleep(thread, cpu, timestamp, prio, wmesg) 928 elif (inhibit & TDI_LOCK): 929 Blocked(thread, cpu, timestamp, prio, lock) 930 elif (inhibit & TDI_IWAIT): 931 Iwait(thread, cpu, timestamp, prio) 932 elif (inhibit & TDI_SUSPENDED): 933 Suspended(thread, cpu, timestamp, prio) 934 elif (inhibit == 0): 935 Yielding(thread, cpu, timestamp, prio) 936 else: 937 print "Unknown event", inhibit 938 sys.exit(1) 939 940 def idled(self, cpu, timestamp, td, pcomm, prio): 941 if (self.checkstamp(timestamp) == 0): 942 return 943 thread = self.findtd(td, pcomm) 944 Idle(thread, cpu, timestamp, prio) 945 946 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 947 if (self.checkstamp(timestamp) == 0): 948 return 949 thread = self.findtd(td, pcomm) 950 Preempted(thread, cpu, timestamp, prio, 951 self.findtd(bytd, bypcomm)) 952 953 def switchin(self, cpu, timestamp, td, pcomm, prio): 954 if (self.checkstamp(timestamp) == 0): 955 return 956 thread = self.findtd(td, pcomm) 957 Running(thread, cpu, timestamp, prio) 958 959 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 960 if (self.checkstamp(timestamp) == 0): 961 return 962 thread = self.findtd(td, pcomm) 963 bythread = self.findtd(bytd, bypcomm) 964 Runq(thread, cpu, timestamp, prio, bythread) 965 Wokeup(bythread, cpu, timestamp, thread) 966 967 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 968 if (self.checkstamp(timestamp) == 0): 969 return 970 thread = self.findtd(td, pcomm) 971 KsegrpRunq(thread, cpu, timestamp, prio, 972 self.findtd(bytd, bypcomm)) 973 974 def sched_exit(self, cpu, timestamp, td, pcomm, prio): 975 if (self.checkstamp(timestamp) == 0): 976 return 977 thread = self.findtd(td, pcomm) 978 Sched_exit(thread, cpu, timestamp, prio) 979 980 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz): 981 if (self.checkstamp(timestamp) == 0): 982 return 983 self.stathz = stathz 984 cpu = int(cpu) 985 try: 986 ticks = self.ticks[cpu] 987 except: 988 self.ticks[cpu] = 0 989 self.ticks[cpu] += 1 990 thread = self.findtd(td, pcomm) 991 Tick(thread, cpu, timestamp, prio, stathz) 992 993 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm): 994 if (prio == newprio): 995 return 996 if (self.checkstamp(timestamp) == 0): 997 return 998 thread = self.findtd(td, pcomm) 999 bythread = self.findtd(bytd, bypcomm) 1000 Prio(thread, cpu, timestamp, prio, newprio, bythread) 1001 Lend(bythread, cpu, timestamp, newprio, thread) 1002 1003 def cpuload(self, cpu, timestamp, count): 1004 if (self.checkstamp(timestamp) == 0): 1005 return 1006 cpu = int(cpu) 1007 try: 1008 load = self.load[cpu] 1009 except: 1010 load = Counter("cpu" + str(cpu) + " load") 1011 self.load[cpu] = load 1012 self.sources.insert(0, load) 1013 Count(load, cpu, timestamp, count) 1014 1015 def loadglobal(self, cpu, timestamp, count): 1016 if (self.checkstamp(timestamp) == 0): 1017 return 1018 cpu = 0 1019 try: 1020 load = self.load[cpu] 1021 except: 1022 load = Counter("CPU load") 1023 self.load[cpu] = load 1024 self.sources.insert(0, load) 1025 Count(load, cpu, timestamp, count) 1026 1027 def findtd(self, td, pcomm): 1028 for thread in self.threads: 1029 if (thread.str == td and thread.name == pcomm): 1030 return thread 1031 thread = Thread(td, pcomm) 1032 self.threads.append(thread) 1033 self.sources.append(thread) 1034 return (thread) 1035 1036 def fixup(self): 1037 for source in self.sources: 1038 Padevent(source, -1, self.timestamp_last) 1039 Padevent(source, -1, self.timestamp_first, last=1) 1040 source.fixup() 1041 1042class SchedDisplay(Canvas): 1043 def __init__(self, master): 1044 self.ratio = 1 1045 self.ktrfile = None 1046 self.sources = None 1047 self.bdheight = 10 1048 self.events = {} 1049 1050 Canvas.__init__(self, master, width=800, height=500, bg='grey', 1051 scrollregion=(0, 0, 800, 500)) 1052 1053 def setfile(self, ktrfile): 1054 self.ktrfile = ktrfile 1055 self.sources = ktrfile.sources 1056 1057 def draw(self): 1058 ypos = 0 1059 xsize = self.xsize() 1060 for source in self.sources: 1061 status.startup("Drawing " + source.name) 1062 self.create_line(0, ypos, xsize, ypos, 1063 width=1, fill="black", tags=("all",)) 1064 ypos += self.bdheight 1065 ypos += source.ysize() 1066 source.draw(self, ypos) 1067 ypos += self.bdheight 1068 try: 1069 self.tag_raise("point", "state") 1070 self.tag_lower("cpuinfo", "all") 1071 except: 1072 pass 1073 self.create_line(0, ypos, xsize, ypos, 1074 width=1, fill="black", tags=("all",)) 1075 self.tag_bind("event", "<Enter>", self.mouseenter) 1076 self.tag_bind("event", "<Leave>", self.mouseexit) 1077 self.tag_bind("event", "<Button-1>", self.mousepress) 1078 1079 def mouseenter(self, event): 1080 item, = self.find_withtag(CURRENT) 1081 event = self.events[item] 1082 event.mouseenter(self, item) 1083 1084 def mouseexit(self, event): 1085 item, = self.find_withtag(CURRENT) 1086 event = self.events[item] 1087 event.mouseexit(self, item) 1088 1089 def mousepress(self, event): 1090 item, = self.find_withtag(CURRENT) 1091 event = self.events[item] 1092 event.mousepress(self, item) 1093 1094 def drawnames(self, canvas): 1095 status.startup("Drawing names") 1096 ypos = 0 1097 canvas.configure(scrollregion=(0, 0, 1098 canvas["width"], self.ysize())) 1099 for source in self.sources: 1100 canvas.create_line(0, ypos, canvas["width"], ypos, 1101 width=1, fill="black", tags=("all",)) 1102 ypos += self.bdheight 1103 ypos += source.ysize() 1104 source.drawname(canvas, ypos) 1105 ypos += self.bdheight 1106 canvas.create_line(0, ypos, canvas["width"], ypos, 1107 width=1, fill="black", tags=("all",)) 1108 1109 def xsize(self): 1110 return ((self.ktrfile.timespan() / self.ratio) + 20) 1111 1112 def ysize(self): 1113 ysize = 0 1114 for source in self.sources: 1115 ysize += source.ysize() + (self.bdheight * 2) 1116 return (ysize) 1117 1118 def scaleset(self, ratio): 1119 if (self.ktrfile == None): 1120 return 1121 oldratio = self.ratio 1122 xstart, ystart = self.xview() 1123 length = (float(self["width"]) / self.xsize()) 1124 middle = xstart + (length / 2) 1125 1126 self.ratio = ratio 1127 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize())) 1128 self.scale("all", 0, 0, float(oldratio) / ratio, 1) 1129 1130 length = (float(self["width"]) / self.xsize()) 1131 xstart = middle - (length / 2) 1132 self.xview_moveto(xstart) 1133 1134 def scaleget(self): 1135 return self.ratio 1136 1137 def setcolor(self, tag, color): 1138 self.itemconfigure(tag, state="normal", fill=color) 1139 1140 def hide(self, tag): 1141 self.itemconfigure(tag, state="hidden") 1142 1143class GraphMenu(Frame): 1144 def __init__(self, master): 1145 Frame.__init__(self, master, bd=2, relief=RAISED) 1146 self.view = Menubutton(self, text="Configure") 1147 self.viewmenu = Menu(self.view, tearoff=0) 1148 self.viewmenu.add_command(label="Events", 1149 command=self.econf) 1150 self.view["menu"] = self.viewmenu 1151 self.view.pack(side=LEFT) 1152 1153 def econf(self): 1154 EventConfigure() 1155 1156 1157class SchedGraph(Frame): 1158 def __init__(self, master): 1159 Frame.__init__(self, master) 1160 self.menu = None 1161 self.names = None 1162 self.display = None 1163 self.scale = None 1164 self.status = None 1165 self.pack(expand=1, fill="both") 1166 self.buildwidgets() 1167 self.layout() 1168 self.draw(sys.argv[1]) 1169 1170 def buildwidgets(self): 1171 global status 1172 self.menu = GraphMenu(self) 1173 self.display = SchedDisplay(self) 1174 self.names = Canvas(self, 1175 width=100, height=self.display["height"], 1176 bg='grey', scrollregion=(0, 0, 50, 100)) 1177 self.scale = Scaler(self, self.display) 1178 status = self.status = Status(self) 1179 self.scrollY = Scrollbar(self, orient="vertical", 1180 command=self.display_yview) 1181 self.display.scrollX = Scrollbar(self, orient="horizontal", 1182 command=self.display.xview) 1183 self.display["xscrollcommand"] = self.display.scrollX.set 1184 self.display["yscrollcommand"] = self.scrollY.set 1185 self.names["yscrollcommand"] = self.scrollY.set 1186 1187 def layout(self): 1188 self.columnconfigure(1, weight=1) 1189 self.rowconfigure(1, weight=1) 1190 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W) 1191 self.names.grid(row=1, column=0, sticky=N+S) 1192 self.display.grid(row=1, column=1, sticky=W+E+N+S) 1193 self.scrollY.grid(row=1, column=2, sticky=N+S) 1194 self.display.scrollX.grid(row=2, column=0, columnspan=2, 1195 sticky=E+W) 1196 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W) 1197 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W) 1198 1199 def draw(self, file): 1200 self.master.update() 1201 ktrfile = KTRFile(file) 1202 self.display.setfile(ktrfile) 1203 self.display.drawnames(self.names) 1204 self.display.draw() 1205 self.scale.set(250000) 1206 self.display.xview_moveto(0) 1207 1208 def display_yview(self, *args): 1209 self.names.yview(*args) 1210 self.display.yview(*args) 1211 1212 def setcolor(self, tag, color): 1213 self.display.setcolor(tag, color) 1214 1215 def hide(self, tag): 1216 self.display.hide(tag) 1217 1218if (len(sys.argv) != 2): 1219 print "usage:", sys.argv[0], "<ktr file>" 1220 sys.exit(1) 1221 1222root = Tk() 1223root.title("Scheduler Graph") 1224graph = SchedGraph(root) 1225root.mainloop() 1226