schedgraph.py revision 139321
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 139321 2004-12-26 03:25:08Z 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 while (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.skipnext -= 1 405 self.duration = next.timestamp - self.timestamp 406 delta = self.duration / canvas.ratio 407 l = canvas.create_rectangle(xpos, ypos, 408 xpos + delta, ypos - 10, fill=self.color, width=0, 409 tags=("all", "state", "event") + (self.name,)) 410 canvas.events[l] = self 411 self.item = l 412 if (self.enabled == 0): 413 canvas.itemconfigure(l, state="hidden") 414 415 return (xpos + delta) 416 417 def stattxt(self): 418 return " duration: " + ticks2sec(self.duration) 419 420 def nextstate(self): 421 next = self.next() 422 while (next != None and next.state == 0): 423 next = next.next() 424 return (next) 425 426 def labels(self): 427 return [("Source:", self.source.name, 0), 428 ("Event:", self.name, 0), 429 ("Timestamp:", self.timestamp, 0), 430 ("CPU:", self.cpu, 0), 431 ("Duration:", ticks2sec(self.duration), 0)] \ 432 + self.entries 433 434class Count(Event): 435 name = "Count" 436 color = "red" 437 enabled = 1 438 def __init__(self, source, cpu, timestamp, count): 439 self.count = int(count) 440 Event.__init__(self, source, cpu, timestamp) 441 self.duration = 0 442 self.textadd(("count:", self.count, 0)) 443 444 def draw(self, canvas, xpos, ypos): 445 next = self.next() 446 self.duration = next.timestamp - self.timestamp 447 delta = self.duration / canvas.ratio 448 yhight = self.source.yscale() * self.count 449 l = canvas.create_rectangle(xpos, ypos - yhight, 450 xpos + delta, ypos, fill=self.color, width=0, 451 tags=("all", "count", "event") + (self.name,)) 452 canvas.events[l] = self 453 self.item = l 454 if (self.enabled == 0): 455 canvas.itemconfigure(l, state="hidden") 456 return (xpos + delta) 457 458 def stattxt(self): 459 return " count: " + str(self.count) 460 461configtypes.append(Count) 462 463class Running(StateEvent): 464 name = "running" 465 color = "green" 466 enabled = 1 467 def __init__(self, thread, cpu, timestamp, prio): 468 StateEvent.__init__(self, thread, cpu, timestamp) 469 self.prio = prio 470 self.textadd(("prio:", self.prio, 0)) 471 472configtypes.append(Running) 473 474class Idle(StateEvent): 475 name = "idle" 476 color = "grey" 477 enabled = 0 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(Idle) 484 485class Yielding(StateEvent): 486 name = "yielding" 487 color = "yellow" 488 enabled = 1 489 def __init__(self, thread, cpu, timestamp, prio): 490 StateEvent.__init__(self, thread, cpu, timestamp) 491 self.skipnext = 2 492 self.prio = prio 493 self.textadd(("prio:", self.prio, 0)) 494 495configtypes.append(Yielding) 496 497class Swapped(StateEvent): 498 name = "swapped" 499 color = "violet" 500 enabled = 1 501 def __init__(self, thread, cpu, timestamp, prio): 502 StateEvent.__init__(self, thread, cpu, timestamp) 503 self.prio = prio 504 self.textadd(("prio:", self.prio, 0)) 505 506configtypes.append(Swapped) 507 508class Suspended(StateEvent): 509 name = "suspended" 510 color = "purple" 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(Suspended) 518 519class Iwait(StateEvent): 520 name = "iwait" 521 color = "grey" 522 enabled = 0 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(Iwait) 529 530class Preempted(StateEvent): 531 name = "preempted" 532 color = "red" 533 enabled = 1 534 def __init__(self, thread, cpu, timestamp, prio, bythread): 535 StateEvent.__init__(self, thread, cpu, timestamp) 536 self.skipnext = 2 537 self.prio = prio 538 self.linked = bythread 539 self.textadd(("prio:", self.prio, 0)) 540 self.textadd(("by thread:", self.linked.name, 1)) 541 542configtypes.append(Preempted) 543 544class Sleep(StateEvent): 545 name = "sleep" 546 color = "blue" 547 enabled = 1 548 def __init__(self, thread, cpu, timestamp, prio, wmesg): 549 StateEvent.__init__(self, thread, cpu, timestamp) 550 self.prio = prio 551 self.wmesg = wmesg 552 self.textadd(("prio:", self.prio, 0)) 553 self.textadd(("wmesg:", self.wmesg, 0)) 554 555 def stattxt(self): 556 statstr = StateEvent.stattxt(self) 557 statstr += " sleeping on: " + self.wmesg 558 return (statstr) 559 560configtypes.append(Sleep) 561 562class Blocked(StateEvent): 563 name = "blocked" 564 color = "dark red" 565 enabled = 1 566 def __init__(self, thread, cpu, timestamp, prio, lock): 567 StateEvent.__init__(self, thread, cpu, timestamp) 568 self.prio = prio 569 self.lock = lock 570 self.textadd(("prio:", self.prio, 0)) 571 self.textadd(("lock:", self.lock, 0)) 572 573 def stattxt(self): 574 statstr = StateEvent.stattxt(self) 575 statstr += " blocked on: " + self.lock 576 return (statstr) 577 578configtypes.append(Blocked) 579 580class KsegrpRunq(StateEvent): 581 name = "KsegrpRunq" 582 color = "orange" 583 enabled = 1 584 def __init__(self, thread, cpu, timestamp, prio, bythread): 585 StateEvent.__init__(self, thread, cpu, timestamp) 586 self.prio = prio 587 self.linked = bythread 588 self.textadd(("prio:", self.prio, 0)) 589 self.textadd(("by thread:", self.linked.name, 1)) 590 591configtypes.append(KsegrpRunq) 592 593class Runq(StateEvent): 594 name = "Runq" 595 color = "yellow" 596 enabled = 1 597 def __init__(self, thread, cpu, timestamp, prio, bythread): 598 StateEvent.__init__(self, thread, cpu, timestamp) 599 self.prio = prio 600 self.linked = bythread 601 self.textadd(("prio:", self.prio, 0)) 602 self.textadd(("by thread:", self.linked.name, 1)) 603 604configtypes.append(Runq) 605 606class Sched_exit(StateEvent): 607 name = "exit" 608 color = "grey" 609 enabled = 0 610 def __init__(self, thread, cpu, timestamp, prio): 611 StateEvent.__init__(self, thread, cpu, timestamp) 612 self.name = "sched_exit" 613 self.prio = prio 614 self.textadd(("prio:", self.prio, 0)) 615 616configtypes.append(Sched_exit) 617 618class Padevent(StateEvent): 619 def __init__(self, thread, cpu, timestamp, last=0): 620 StateEvent.__init__(self, thread, cpu, timestamp, last) 621 self.name = "pad" 622 self.real = 0 623 624 def draw(self, canvas, xpos, ypos): 625 next = self.next() 626 if (next == None): 627 return (xpos) 628 self.duration = next.timestamp - self.timestamp 629 delta = self.duration / canvas.ratio 630 return (xpos + delta) 631 632class Tick(PointEvent): 633 name = "tick" 634 color = "black" 635 enabled = 0 636 def __init__(self, thread, cpu, timestamp, prio, stathz): 637 PointEvent.__init__(self, thread, cpu, timestamp) 638 self.prio = prio 639 self.textadd(("prio:", self.prio, 0)) 640 641configtypes.append(Tick) 642 643class Prio(PointEvent): 644 name = "prio" 645 color = "black" 646 enabled = 0 647 def __init__(self, thread, cpu, timestamp, prio, newprio, bythread): 648 PointEvent.__init__(self, thread, cpu, timestamp) 649 self.prio = prio 650 self.newprio = newprio 651 self.linked = bythread 652 self.textadd(("new prio:", self.newprio, 0)) 653 self.textadd(("prio:", self.prio, 0)) 654 if (self.linked != self.source): 655 self.textadd(("by thread:", self.linked.name, 1)) 656 else: 657 self.textadd(("by thread:", self.linked.name, 0)) 658 659configtypes.append(Prio) 660 661class Lend(PointEvent): 662 name = "lend" 663 color = "black" 664 enabled = 0 665 def __init__(self, thread, cpu, timestamp, prio, tothread): 666 PointEvent.__init__(self, thread, cpu, timestamp) 667 self.prio = prio 668 self.linked = tothread 669 self.textadd(("prio:", self.prio, 0)) 670 self.textadd(("to thread:", self.linked.name, 1)) 671 672configtypes.append(Lend) 673 674class Wokeup(PointEvent): 675 name = "wokeup" 676 color = "black" 677 enabled = 0 678 def __init__(self, thread, cpu, timestamp, ranthread): 679 PointEvent.__init__(self, thread, cpu, timestamp) 680 self.linked = ranthread 681 self.textadd(("ran thread:", self.linked.name, 1)) 682 683configtypes.append(Wokeup) 684 685class EventSource: 686 def __init__(self, name): 687 self.name = name 688 self.events = [] 689 self.cpu = 0 690 self.cpux = 0 691 692 def fixup(self): 693 pass 694 695 def event(self, event): 696 self.events.insert(0, event) 697 698 def remove(self, event): 699 self.events.remove(event) 700 701 def lastevent(self, event): 702 self.events.append(event) 703 704 def draw(self, canvas, ypos): 705 xpos = 10 706 self.cpux = 10 707 self.cpu = self.events[1].cpu 708 for i in range(0, len(self.events)): 709 self.events[i].idx = i 710 for event in self.events: 711 if (event.cpu != self.cpu and event.cpu != -1): 712 self.drawcpu(canvas, xpos, ypos) 713 self.cpux = xpos 714 self.cpu = event.cpu 715 xpos = event.draw(canvas, xpos, ypos) 716 self.drawcpu(canvas, xpos, ypos) 717 718 def drawname(self, canvas, ypos): 719 ypos = ypos - (self.ysize() / 2) 720 canvas.create_text(10, ypos, anchor="w", text=self.name) 721 722 def drawcpu(self, canvas, xpos, ypos): 723 cpu = int(self.cpu) 724 if (cpu == 0): 725 color = 'light grey' 726 elif (cpu == 1): 727 color = 'dark grey' 728 elif (cpu == 2): 729 color = 'light blue' 730 elif (cpu == 3): 731 color == 'light green' 732 else: 733 color == "white" 734 l = canvas.create_rectangle(self.cpux, 735 ypos - self.ysize() - canvas.bdheight, 736 xpos, ypos + canvas.bdheight, fill=color, width=0, 737 tags=("all", "cpuinfo")) 738 739 def ysize(self): 740 return (None) 741 742 def eventat(self, i): 743 if (i >= len(self.events)): 744 return (None) 745 event = self.events[i] 746 return (event) 747 748 def findevent(self, timestamp): 749 for event in self.events: 750 if (event.timestamp >= timestamp and event.real): 751 return (event) 752 return (None) 753 754class Thread(EventSource): 755 names = {} 756 def __init__(self, td, pcomm): 757 EventSource.__init__(self, pcomm) 758 self.str = td 759 try: 760 cnt = Thread.names[pcomm] 761 except: 762 Thread.names[pcomm] = 0 763 return 764 Thread.names[pcomm] = cnt + 1 765 766 def fixup(self): 767 cnt = Thread.names[self.name] 768 if (cnt == 0): 769 return 770 cnt -= 1 771 Thread.names[self.name] = cnt 772 self.name += " td" + str(cnt) 773 774 def ysize(self): 775 return (10) 776 777class Counter(EventSource): 778 max = 0 779 def __init__(self, name): 780 EventSource.__init__(self, name) 781 782 def event(self, event): 783 EventSource.event(self, event) 784 try: 785 count = event.count 786 except: 787 return 788 count = int(count) 789 if (count > Counter.max): 790 Counter.max = count 791 792 def ysize(self): 793 return (80) 794 795 def yscale(self): 796 return (self.ysize() / Counter.max) 797 798 799class KTRFile: 800 def __init__(self, file): 801 self.timestamp_first = None 802 self.timestamp_last = None 803 self.lineno = -1 804 self.threads = [] 805 self.sources = [] 806 self.ticks = {} 807 self.load = {} 808 809 self.parse(file) 810 self.fixup() 811 global ticksps 812 ticksps = self.ticksps() 813 814 def parse(self, file): 815 try: 816 ifp = open(file) 817 except: 818 print "Can't open", file 819 sys.exit(1) 820 821 ktrhdr = "\s+\d+\s+(\d+)\s+(\d+)\s+" 822 tdname = "(\S+)\(([^)]*)\)" 823 824 ktrstr = "mi_switch: " + tdname 825 ktrstr += " prio (\d+) inhibit (\d+) wmesg (\S+) lock (\S+)" 826 switchout_re = re.compile(ktrhdr + ktrstr) 827 828 ktrstr = "mi_switch: " + tdname + " prio (\d+) idle" 829 idled_re = re.compile(ktrhdr + ktrstr) 830 831 ktrstr = "mi_switch: " + tdname + " prio (\d+) preempted by " 832 ktrstr += tdname 833 preempted_re = re.compile(ktrhdr + ktrstr) 834 835 ktrstr = "mi_switch: running " + tdname + " prio (\d+)" 836 switchin_re = re.compile(ktrhdr + ktrstr) 837 838 ktrstr = "sched_add: " + tdname + " prio (\d+) by " + tdname 839 sched_add_re = re.compile(ktrhdr + ktrstr) 840 841 ktrstr = "setrunqueue: " + tdname + " prio (\d+) by " + tdname 842 setrunqueue_re = re.compile(ktrhdr + ktrstr) 843 844 ktrstr = "sched_rem: " + tdname + " prio (\d+) by " + tdname 845 sched_rem_re = re.compile(ktrhdr + ktrstr) 846 847 ktrstr = "sched_exit_thread: " + tdname + " prio (\d+)" 848 sched_exit_re = re.compile(ktrhdr + ktrstr) 849 850 ktrstr = "statclock: " + tdname + " prio (\d+)" 851 ktrstr += " stathz (\d+)" 852 sched_clock_re = re.compile(ktrhdr + ktrstr) 853 854 ktrstr = "sched_prio: " + tdname + " prio (\d+)" 855 ktrstr += " newprio (\d+) by " + tdname 856 sched_prio_re = re.compile(ktrhdr + ktrstr) 857 858 cpuload_re = re.compile(ktrhdr + "load: (\d+)") 859 loadglobal_re = re.compile(ktrhdr + "global load: (\d+)") 860 861 parsers = [[cpuload_re, self.cpuload], 862 [loadglobal_re, self.loadglobal], 863 [switchin_re, self.switchin], 864 [switchout_re, self.switchout], 865 [sched_add_re, self.sched_add], 866 [setrunqueue_re, self.sched_rem], 867 [sched_prio_re, self.sched_prio], 868 [preempted_re, self.preempted], 869 [sched_rem_re, self.sched_rem], 870 [sched_exit_re, self.sched_exit], 871 [sched_clock_re, self.sched_clock], 872 [idled_re, self.idled]] 873 874 for line in ifp.readlines(): 875 self.lineno += 1 876 if ((self.lineno % 1024) == 0): 877 status.startup("Parsing line " + 878 str(self.lineno)) 879 for p in parsers: 880 m = p[0].match(line) 881 if (m != None): 882 p[1](*m.groups()) 883 break 884 # if (m == None): 885 # print line, 886 887 def checkstamp(self, timestamp): 888 timestamp = int(timestamp) 889 if (self.timestamp_first == None): 890 self.timestamp_first = timestamp 891 if (timestamp > self.timestamp_first): 892 print "Bad timestamp on line ", self.lineno 893 return (0) 894 self.timestamp_last = timestamp 895 return (1) 896 897 def timespan(self): 898 return (self.timestamp_first - self.timestamp_last); 899 900 def ticksps(self): 901 return (self.timespan() / self.ticks[0]) * int(self.stathz) 902 903 def switchout(self, cpu, timestamp, td, pcomm, prio, inhibit, wmesg, lock): 904 TDI_SUSPENDED = 0x0001 905 TDI_SLEEPING = 0x0002 906 TDI_SWAPPED = 0x0004 907 TDI_LOCK = 0x0008 908 TDI_IWAIT = 0x0010 909 910 if (self.checkstamp(timestamp) == 0): 911 return 912 inhibit = int(inhibit) 913 thread = self.findtd(td, pcomm) 914 if (inhibit & TDI_SWAPPED): 915 Swapped(thread, cpu, timestamp, prio) 916 elif (inhibit & TDI_SLEEPING): 917 Sleep(thread, cpu, timestamp, prio, wmesg) 918 elif (inhibit & TDI_LOCK): 919 Blocked(thread, cpu, timestamp, prio, lock) 920 elif (inhibit & TDI_IWAIT): 921 Iwait(thread, cpu, timestamp, prio) 922 elif (inhibit & TDI_SUSPENDED): 923 Suspended(thread, cpu, timestamp, prio) 924 elif (inhibit == 0): 925 Yielding(thread, cpu, timestamp, prio) 926 else: 927 print "Unknown event", inhibit 928 sys.exit(1) 929 930 def idled(self, cpu, timestamp, td, pcomm, prio): 931 if (self.checkstamp(timestamp) == 0): 932 return 933 thread = self.findtd(td, pcomm) 934 Idle(thread, cpu, timestamp, prio) 935 936 def preempted(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 937 if (self.checkstamp(timestamp) == 0): 938 return 939 thread = self.findtd(td, pcomm) 940 Preempted(thread, cpu, timestamp, prio, 941 self.findtd(bytd, bypcomm)) 942 943 def switchin(self, cpu, timestamp, td, pcomm, prio): 944 if (self.checkstamp(timestamp) == 0): 945 return 946 thread = self.findtd(td, pcomm) 947 Running(thread, cpu, timestamp, prio) 948 949 def sched_add(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 950 if (self.checkstamp(timestamp) == 0): 951 return 952 thread = self.findtd(td, pcomm) 953 bythread = self.findtd(bytd, bypcomm) 954 Runq(thread, cpu, timestamp, prio, bythread) 955 Wokeup(bythread, cpu, timestamp, thread) 956 957 def sched_rem(self, cpu, timestamp, td, pcomm, prio, bytd, bypcomm): 958 if (self.checkstamp(timestamp) == 0): 959 return 960 thread = self.findtd(td, pcomm) 961 KsegrpRunq(thread, cpu, timestamp, prio, 962 self.findtd(bytd, bypcomm)) 963 964 def sched_exit(self, cpu, timestamp, td, pcomm, prio): 965 if (self.checkstamp(timestamp) == 0): 966 return 967 thread = self.findtd(td, pcomm) 968 Sched_exit(thread, cpu, timestamp, prio) 969 970 def sched_clock(self, cpu, timestamp, td, pcomm, prio, stathz): 971 if (self.checkstamp(timestamp) == 0): 972 return 973 self.stathz = stathz 974 cpu = int(cpu) 975 try: 976 ticks = self.ticks[cpu] 977 except: 978 self.ticks[cpu] = 0 979 self.ticks[cpu] += 1 980 thread = self.findtd(td, pcomm) 981 Tick(thread, cpu, timestamp, prio, stathz) 982 983 def sched_prio(self, cpu, timestamp, td, pcomm, prio, newprio, bytd, bypcomm): 984 if (prio == newprio): 985 return 986 if (self.checkstamp(timestamp) == 0): 987 return 988 thread = self.findtd(td, pcomm) 989 bythread = self.findtd(bytd, bypcomm) 990 Prio(thread, cpu, timestamp, prio, newprio, bythread) 991 Lend(bythread, cpu, timestamp, newprio, thread) 992 993 def cpuload(self, cpu, timestamp, count): 994 if (self.checkstamp(timestamp) == 0): 995 return 996 cpu = int(cpu) 997 try: 998 load = self.load[cpu] 999 except: 1000 load = Counter("cpu" + str(cpu) + " load") 1001 self.load[cpu] = load 1002 self.sources.insert(0, load) 1003 Count(load, cpu, timestamp, count) 1004 1005 def loadglobal(self, cpu, timestamp, count): 1006 if (self.checkstamp(timestamp) == 0): 1007 return 1008 cpu = 0 1009 try: 1010 load = self.load[cpu] 1011 except: 1012 load = Counter("CPU load") 1013 self.load[cpu] = load 1014 self.sources.insert(0, load) 1015 Count(load, cpu, timestamp, count) 1016 1017 def findtd(self, td, pcomm): 1018 for thread in self.threads: 1019 if (thread.str == td and thread.name == pcomm): 1020 return thread 1021 thread = Thread(td, pcomm) 1022 self.threads.append(thread) 1023 self.sources.append(thread) 1024 return (thread) 1025 1026 def fixup(self): 1027 for source in self.sources: 1028 Padevent(source, -1, self.timestamp_last) 1029 Padevent(source, -1, self.timestamp_first, last=1) 1030 source.fixup() 1031 1032class SchedDisplay(Canvas): 1033 def __init__(self, master): 1034 self.ratio = 1 1035 self.ktrfile = None 1036 self.sources = None 1037 self.bdheight = 10 1038 self.events = {} 1039 1040 Canvas.__init__(self, master, width=800, height=500, bg='grey', 1041 scrollregion=(0, 0, 800, 500)) 1042 1043 def setfile(self, ktrfile): 1044 self.ktrfile = ktrfile 1045 self.sources = ktrfile.sources 1046 1047 def draw(self): 1048 ypos = 0 1049 xsize = self.xsize() 1050 for source in self.sources: 1051 status.startup("Drawing " + source.name) 1052 self.create_line(0, ypos, xsize, ypos, 1053 width=1, fill="black", tags=("all",)) 1054 ypos += self.bdheight 1055 ypos += source.ysize() 1056 source.draw(self, ypos) 1057 ypos += self.bdheight 1058 try: 1059 self.tag_raise("point", "state") 1060 self.tag_lower("cpuinfo", "all") 1061 except: 1062 pass 1063 self.create_line(0, ypos, xsize, ypos, 1064 width=1, fill="black", tags=("all",)) 1065 self.tag_bind("event", "<Enter>", self.mouseenter) 1066 self.tag_bind("event", "<Leave>", self.mouseexit) 1067 self.tag_bind("event", "<Button-1>", self.mousepress) 1068 1069 def mouseenter(self, event): 1070 item, = self.find_withtag(CURRENT) 1071 event = self.events[item] 1072 event.mouseenter(self, item) 1073 1074 def mouseexit(self, event): 1075 item, = self.find_withtag(CURRENT) 1076 event = self.events[item] 1077 event.mouseexit(self, item) 1078 1079 def mousepress(self, event): 1080 item, = self.find_withtag(CURRENT) 1081 event = self.events[item] 1082 event.mousepress(self, item) 1083 1084 def drawnames(self, canvas): 1085 status.startup("Drawing names") 1086 ypos = 0 1087 canvas.configure(scrollregion=(0, 0, 1088 canvas["width"], self.ysize())) 1089 for source in self.sources: 1090 canvas.create_line(0, ypos, canvas["width"], ypos, 1091 width=1, fill="black", tags=("all",)) 1092 ypos += self.bdheight 1093 ypos += source.ysize() 1094 source.drawname(canvas, ypos) 1095 ypos += self.bdheight 1096 canvas.create_line(0, ypos, canvas["width"], ypos, 1097 width=1, fill="black", tags=("all",)) 1098 1099 def xsize(self): 1100 return ((self.ktrfile.timespan() / self.ratio) + 20) 1101 1102 def ysize(self): 1103 ysize = 0 1104 for source in self.sources: 1105 ysize += source.ysize() + (self.bdheight * 2) 1106 return (ysize) 1107 1108 def scaleset(self, ratio): 1109 if (self.ktrfile == None): 1110 return 1111 oldratio = self.ratio 1112 xstart, ystart = self.xview() 1113 length = (float(self["width"]) / self.xsize()) 1114 middle = xstart + (length / 2) 1115 1116 self.ratio = ratio 1117 self.configure(scrollregion=(0, 0, self.xsize(), self.ysize())) 1118 self.scale("all", 0, 0, float(oldratio) / ratio, 1) 1119 1120 length = (float(self["width"]) / self.xsize()) 1121 xstart = middle - (length / 2) 1122 self.xview_moveto(xstart) 1123 1124 def scaleget(self): 1125 return self.ratio 1126 1127 def setcolor(self, tag, color): 1128 self.itemconfigure(tag, state="normal", fill=color) 1129 1130 def hide(self, tag): 1131 self.itemconfigure(tag, state="hidden") 1132 1133class GraphMenu(Frame): 1134 def __init__(self, master): 1135 Frame.__init__(self, master, bd=2, relief=RAISED) 1136 self.view = Menubutton(self, text="Configure") 1137 self.viewmenu = Menu(self.view, tearoff=0) 1138 self.viewmenu.add_command(label="Events", 1139 command=self.econf) 1140 self.view["menu"] = self.viewmenu 1141 self.view.pack(side=LEFT) 1142 1143 def econf(self): 1144 EventConfigure() 1145 1146 1147class SchedGraph(Frame): 1148 def __init__(self, master): 1149 Frame.__init__(self, master) 1150 self.menu = None 1151 self.names = None 1152 self.display = None 1153 self.scale = None 1154 self.status = None 1155 self.pack(expand=1, fill="both") 1156 self.buildwidgets() 1157 self.layout() 1158 self.draw(sys.argv[1]) 1159 1160 def buildwidgets(self): 1161 global status 1162 self.menu = GraphMenu(self) 1163 self.display = SchedDisplay(self) 1164 self.names = Canvas(self, 1165 width=100, height=self.display["height"], 1166 bg='grey', scrollregion=(0, 0, 50, 100)) 1167 self.scale = Scaler(self, self.display) 1168 status = self.status = Status(self) 1169 self.scrollY = Scrollbar(self, orient="vertical", 1170 command=self.display_yview) 1171 self.display.scrollX = Scrollbar(self, orient="horizontal", 1172 command=self.display.xview) 1173 self.display["xscrollcommand"] = self.display.scrollX.set 1174 self.display["yscrollcommand"] = self.scrollY.set 1175 self.names["yscrollcommand"] = self.scrollY.set 1176 1177 def layout(self): 1178 self.columnconfigure(1, weight=1) 1179 self.rowconfigure(1, weight=1) 1180 self.menu.grid(row=0, column=0, columnspan=3, sticky=E+W) 1181 self.names.grid(row=1, column=0, sticky=N+S) 1182 self.display.grid(row=1, column=1, sticky=W+E+N+S) 1183 self.scrollY.grid(row=1, column=2, sticky=N+S) 1184 self.display.scrollX.grid(row=2, column=0, columnspan=2, 1185 sticky=E+W) 1186 self.scale.grid(row=3, column=0, columnspan=3, sticky=E+W) 1187 self.status.grid(row=4, column=0, columnspan=3, sticky=E+W) 1188 1189 def draw(self, file): 1190 self.master.update() 1191 ktrfile = KTRFile(file) 1192 self.display.setfile(ktrfile) 1193 self.display.drawnames(self.names) 1194 self.display.draw() 1195 self.scale.set(250000) 1196 self.display.xview_moveto(0) 1197 1198 def display_yview(self, *args): 1199 self.names.yview(*args) 1200 self.display.yview(*args) 1201 1202 def setcolor(self, tag, color): 1203 self.display.setcolor(tag, color) 1204 1205 def hide(self, tag): 1206 self.display.hide(tag) 1207 1208if (len(sys.argv) != 2): 1209 print "usage:", sys.argv[0], "<ktr file>" 1210 sys.exit(1) 1211 1212root = Tk() 1213root.title("Scheduler Graph") 1214graph = SchedGraph(root) 1215root.mainloop() 1216