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