1 /* 2 * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.ui; 26 27 import java.math.*; 28 import java.awt.*; 29 import java.awt.font.*; 30 import java.awt.geom.*; 31 import java.awt.event.*; 32 import java.io.*; 33 import javax.swing.*; 34 import javax.swing.event.*; 35 import java.util.*; 36 37 import sun.jvm.hotspot.debugger.*; 38 import sun.jvm.hotspot.debugger.dummy.*; 39 import sun.jvm.hotspot.utilities.*; 40 41 /** A subclass of JPanel which displays a hex dump of memory along 42 with annotations describing the significance of various 43 pieces. This can be used to implement either a stack or heap 44 inspector. */ 45 46 public class AnnotatedMemoryPanel extends JPanel { 47 private boolean is64Bit; 48 private Debugger debugger; 49 private long addressSize; 50 private HighPrecisionJScrollBar scrollBar; 51 private Font font; 52 private int bytesPerLine; 53 private int paintCount; 54 private String unmappedAddrString; 55 // Type of this is an IntervalTree indexed by Interval<Address> and 56 // with user data of type Annotation 57 private IntervalTree annotations = 58 new IntervalTree(new Comparator() { 59 public int compare(Object o1, Object o2) { 60 Address a1 = (Address) o1; 61 Address a2 = (Address) o2; 62 63 if ((a1 == null) && (a2 == null)) { 64 return 0; 65 } else if (a1 == null) { 66 return -1; 67 } else if (a2 == null) { 68 return 1; 69 } 70 71 if (a1.equals(a2)) { 72 return 0; 73 } else if (a1.lessThan(a2)) { 74 return -1; 75 } 76 return 1; 77 } 78 }); 79 // Keep track of the last start address at which we painted, so we 80 // can scroll annotations 81 private Address lastStartAddr; 82 // This contains the list of currently-visible IntervalNodes, in 83 // sorted order by their low endpoint, in the form of a 84 // List<Annotation>. These annotations have already been laid out. 85 private java.util.List visibleAnnotations; 86 // Darker colors than defaults for better readability 87 private static Color[] colors = { 88 new Color(0.0f, 0.0f, 0.6f), // blue 89 new Color(0.6f, 0.0f, 0.6f), // magenta 90 new Color(0.0f, 0.8f, 0.0f), // green 91 new Color(0.8f, 0.3f, 0.0f), // orange 92 new Color(0.0f, 0.6f, 0.8f), // cyan 93 new Color(0.2f, 0.2f, 0.2f), // dark gray 94 }; 95 96 /** Default is 32-bit mode */ 97 public AnnotatedMemoryPanel(Debugger debugger) { 98 this(debugger, false); 99 } 100 101 public AnnotatedMemoryPanel(Debugger debugger, boolean is64Bit, Address addrValue, Address addrLow, Address addrHigh) { 102 super(); 103 init(debugger, is64Bit, addressToBigInt(addrValue), addressToBigInt(addrLow), addressToBigInt(addrHigh)); 104 } 105 106 public AnnotatedMemoryPanel(Debugger debugger, boolean is64Bit ) { 107 super(); 108 init(debugger, is64Bit, defaultMemoryLocation(is64Bit), defaultMemoryLow(is64Bit), defaultMemoryHigh(is64Bit)); 109 } 110 111 static class AnnoX { 112 int lineX; 113 Address highBound; 114 115 public AnnoX(int lineX, Address highBound) { 116 this.lineX = lineX; 117 this.highBound = highBound; 118 } 119 } 120 121 public synchronized void paintComponent(Graphics g) { 122 // System.err.println("AnnotatedMemoryPanel.paintComponent() " + ++paintCount); 123 super.paintComponent(g); 124 125 // Clone the Graphics so we don't screw up its state for Swing 126 // drawing (as this code otherwise does) 127 g = g.create(); 128 129 g.setFont(font); 130 g.setColor(Color.black); 131 Rectangle rect = new Rectangle(); 132 getBounds(rect); 133 String firstAddressString = null; 134 int lineHeight; 135 int addrWidth; 136 { 137 Rectangle2D bounds = GraphicsUtilities.getStringBounds(unmappedAddrString, g); 138 lineHeight = (int) bounds.getHeight(); 139 addrWidth = (int) bounds.getWidth(); 140 } 141 int addrX = (int) (0.25 * addrWidth); 142 int dataX = (int) (addrX + (1.5 * addrWidth)); 143 int lineStartX = dataX + addrWidth + 5; 144 int annoStartX = (int) (lineStartX + (0.75 * addrWidth)); 145 146 int numLines = rect.height / lineHeight; 147 148 BigInteger startVal = scrollBar.getValueHP(); 149 BigInteger perLine = new BigInteger(Integer.toString((int) addressSize)); 150 // lineCount and maxLines are both 1 less than expected 151 BigInteger lineCount = new BigInteger(Integer.toString((int) (numLines - 1))); 152 BigInteger maxLines = scrollBar.getMaximumHP().subtract(scrollBar.getMinimumHP()).divide(perLine); 153 if (lineCount.compareTo(maxLines) > 0) { 154 lineCount = maxLines; 155 } 156 BigInteger offsetVal = lineCount.multiply(perLine); 157 BigInteger endVal = startVal.add(offsetVal); 158 if (endVal.compareTo(scrollBar.getMaximumHP()) > 0) { 159 startVal = scrollBar.getMaximumHP().subtract(offsetVal); 160 endVal = scrollBar.getMaximumHP(); 161 // Sure seems like this call will cause us to immediately redraw... 162 scrollBar.setValueHP(startVal); 163 } 164 scrollBar.setVisibleAmountHP(offsetVal.add(perLine)); 165 scrollBar.setBlockIncrementHP(offsetVal); 166 167 Address startAddr = bigIntToAddress(startVal); 168 Address endAddr = bigIntToAddress(endVal); 169 170 // Scroll last-known annotations 171 int scrollOffset = 0; 172 if (lastStartAddr != null) { 173 scrollOffset = (int) lastStartAddr.minus(startAddr); 174 } else { 175 if (startAddr != null) { 176 scrollOffset = (int) (-1 * startAddr.minus(lastStartAddr)); 177 } 178 } 179 scrollOffset = scrollOffset * lineHeight / (int) addressSize; 180 scrollAnnotations(scrollOffset); 181 lastStartAddr = startAddr; 182 183 int curY = lineHeight; 184 Address curAddr = startAddr; 185 for (int i = 0; i < numLines; i++) { 186 String s = bigIntToHexString(startVal); 187 g.drawString(s, addrX, curY); 188 try { 189 s = addressToString(startAddr.getAddressAt(i * addressSize)); 190 } 191 catch (UnmappedAddressException e) { 192 s = unmappedAddrString; 193 } 194 g.drawString(s, dataX, curY); 195 curY += lineHeight; 196 startVal = startVal.add(perLine); 197 } 198 199 // Query for visible annotations (little slop to ensure we get the 200 // top and bottom) 201 // FIXME: it would be nice to have a more static layout; that is, 202 // if something scrolls off the bottom of the screen, other 203 // annotations still visible shouldn't change position 204 java.util.List va = 205 annotations.findAllNodesIntersecting(new Interval(startAddr.addOffsetTo(-addressSize), 206 endAddr.addOffsetTo(2 * addressSize))); 207 208 // Render them 209 int curLineX = lineStartX; 210 int curTextX = annoStartX; 211 int curColorIdx = 0; 212 if (g instanceof Graphics2D) { 213 Stroke stroke = new BasicStroke(3.0f); 214 ((Graphics2D) g).setStroke(stroke); 215 } 216 217 Stack drawStack = new Stack(); 218 219 layoutAnnotations(va, g, curTextX, startAddr, lineHeight); 220 221 for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { 222 Annotation anno = (Annotation) iter.next(); 223 Interval interval = anno.getInterval(); 224 225 if (!drawStack.empty()) { 226 // See whether we can pop any items off the stack 227 boolean shouldContinue = true; 228 do { 229 AnnoX annoX = (AnnoX) drawStack.peek(); 230 if (annoX.highBound.lessThanOrEqual((Address) interval.getLowEndpoint())) { 231 curLineX = annoX.lineX; 232 drawStack.pop(); 233 shouldContinue = !drawStack.empty(); 234 } else { 235 shouldContinue = false; 236 } 237 } while (shouldContinue); 238 } 239 240 // Draw a line covering the interval 241 Address lineStartAddr = (Address) interval.getLowEndpoint(); 242 // Give it a little slop 243 int lineStartY = (int) (lineStartAddr.minus(startAddr) * lineHeight / addressSize) + 244 (lineHeight / 3); 245 Address lineEndAddr = (Address) interval.getHighEndpoint(); 246 drawStack.push(new AnnoX(curLineX, lineEndAddr)); 247 int lineEndY = (int) (lineEndAddr.minus(startAddr) * lineHeight / addressSize); 248 g.setColor(anno.getColor()); 249 g.drawLine(curLineX, lineStartY, curLineX, lineEndY); 250 // Draw line to text 251 g.drawLine(curLineX, lineStartY, curTextX - 10, anno.getY() - (lineHeight / 2)); 252 curLineX += 8; 253 anno.draw(g); 254 ++curColorIdx; 255 } 256 } 257 258 /** Add an annotation covering the address range [annotation.lowAddress, 259 annotation.highAddress); that is, it includes the low address and does not 260 include the high address. */ 261 public synchronized void addAnnotation(Annotation annotation) { 262 annotations.insert(annotation.getInterval(), annotation); 263 } 264 265 /** Makes the given address visible somewhere in the window */ 266 public synchronized void makeVisible(Address addr) { 267 BigInteger bi = addressToBigInt(addr); 268 scrollBar.setValueHP(bi); 269 } 270 271 public void print() { 272 printOn(System.out); 273 } 274 275 public void printOn(PrintStream tty) { 276 annotations.printOn(tty); 277 } 278 279 //---------------------------------------------------------------------- 280 // Internals only below this point 281 // 282 283 private void init(Debugger debugger, boolean is64Bit, BigInteger addrValue, BigInteger addrLow, BigInteger addrHigh) { 284 this.is64Bit = is64Bit; 285 this.debugger = debugger; 286 if (is64Bit) { 287 addressSize = 8; 288 unmappedAddrString = "??????????????????"; 289 } else { 290 addressSize = 4; 291 unmappedAddrString = "??????????"; 292 } 293 setLayout(new BorderLayout()); 294 setupScrollBar(addrValue, addrLow, addrHigh); 295 add(scrollBar, BorderLayout.EAST); 296 visibleAnnotations = new ArrayList(); 297 setBackground(Color.white); 298 addHierarchyBoundsListener(new HierarchyBoundsListener() { 299 public void ancestorMoved(HierarchyEvent e) { 300 } 301 302 public void ancestorResized(HierarchyEvent e) { 303 // FIXME: should perform incremental layout 304 // System.err.println("Ancestor resized"); 305 } 306 }); 307 308 if (font == null) { 309 font = GraphicsUtilities.lookupFont("Courier"); 310 } 311 if (font == null) { 312 throw new RuntimeException("Error looking up monospace font Courier"); 313 } 314 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), "PageDown"); 315 getActionMap().put("PageDown", new AbstractAction() { 316 public void actionPerformed(ActionEvent e) { 317 scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getBlockIncrementHP())); 318 } 319 }); 320 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "PageUp"); 321 getActionMap().put("PageUp", new AbstractAction() { 322 public void actionPerformed(ActionEvent e) { 323 scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getBlockIncrementHP())); 324 } 325 }); 326 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Down"); 327 getActionMap().put("Down", new AbstractAction() { 328 public void actionPerformed(ActionEvent e) { 329 scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getUnitIncrementHP())); 330 } 331 }); 332 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Up"); 333 getActionMap().put("Up", new AbstractAction() { 334 public void actionPerformed(ActionEvent e) { 335 scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getUnitIncrementHP())); 336 } 337 }); 338 setEnabled(true); 339 } 340 341 private void setupScrollBar(BigInteger value, BigInteger min, BigInteger max) { 342 scrollBar = new HighPrecisionJScrollBar( Scrollbar.VERTICAL, value, min, max); 343 if (is64Bit) { 344 bytesPerLine = 8; 345 // 64-bit mode 346 scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { 347 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 348 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08})); 349 scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { 350 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 351 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40})); 352 } else { 353 // 32-bit mode 354 bytesPerLine = 4; 355 scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { 356 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04})); 357 scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { 358 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20})); 359 } 360 scrollBar.addChangeListener(new ChangeListener() { 361 public void stateChanged(ChangeEvent e) { 362 HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource(); 363 repaint(); 364 } 365 }); 366 } 367 368 private static BigInteger defaultMemoryLocation(boolean is64Bit) { 369 if (is64Bit) { 370 return new BigInteger(1, new byte[] { 371 (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, 372 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 373 } else { 374 return new BigInteger(1, new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 375 } 376 } 377 378 private static BigInteger defaultMemoryLow(boolean is64Bit) { 379 if (is64Bit) { 380 return new BigInteger(1, new byte[] { 381 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 382 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 383 } else { 384 return new BigInteger(1, new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 385 } 386 } 387 388 private static BigInteger defaultMemoryHigh(boolean is64Bit) { 389 if (is64Bit) { 390 return new BigInteger(1, new byte[] { 391 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 392 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); 393 } else { 394 return new BigInteger(1, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); 395 } 396 } 397 398 private void setupScrollBar() { 399 setupScrollBar(defaultMemoryLocation(is64Bit), 400 defaultMemoryLow(is64Bit), 401 defaultMemoryHigh(is64Bit)); 402 } 403 404 private String bigIntToHexString(BigInteger bi) { 405 StringBuffer buf = new StringBuffer(); 406 buf.append("0x"); 407 String val = bi.toString(16); 408 for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { 409 buf.append('0'); 410 } 411 buf.append(val); 412 return buf.toString(); 413 } 414 415 private Address bigIntToAddress(BigInteger i) { 416 String s = bigIntToHexString(i); 417 return debugger.parseAddress(s); 418 } 419 420 private BigInteger addressToBigInt(Address a) { 421 String s = addressToString(a); 422 if (!s.startsWith("0x")) { 423 throw new NumberFormatException(s); 424 } 425 return new BigInteger(s.substring(2), 16); 426 } 427 428 private String addressToString(Address a) { 429 if (a == null) { 430 if (is64Bit) { 431 return "0x0000000000000000"; 432 } else { 433 return "0x00000000"; 434 } 435 } 436 return a.toString(); 437 } 438 439 /** Scrolls the visible annotations by the given Y amount */ 440 private void scrollAnnotations(int y) { 441 for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { 442 Annotation anno = (Annotation) iter.next(); 443 anno.setY(anno.getY() + y); 444 } 445 } 446 447 /** Takes the list of currently-visible annotations (in the form of 448 a List<IntervalNode>) and lays them out given the current 449 visible position and the already-visible annotations. Does not 450 perturb the layouts of the currently-visible annotations. */ 451 private void layoutAnnotations(java.util.List va, 452 Graphics g, 453 int x, 454 Address startAddr, 455 int lineHeight) { 456 // Handle degenerate case early: no visible annotations. 457 if (va.size() == 0) { 458 visibleAnnotations.clear(); 459 return; 460 } 461 462 // We have two ranges of visible annotations: the one from the 463 // last repaint and the currently visible set. We want to preserve 464 // the layouts of the previously-visible annotations that are 465 // currently visible (to avoid color flashing, jumping, etc.) 466 // while making the coloring of the new annotations fit as well as 467 // possible. Note that annotations may appear and disappear from 468 // any point in the previously-visible list, but the ordering of 469 // the visible annotations is always the same. 470 471 // This is really a constrained graph-coloring problem. This 472 // simple algorithm only takes into account half of the 473 // constraints (for example, the layout of the previous 474 // annotation, where it should be taking into account the layout 475 // of the previous and next annotations that were in the 476 // previously-visible list). There are situations where it can 477 // generate overlapping annotations and adjacent annotations with 478 // the same color; generally visible when scrolling line-by-line 479 // rather than page-by-page. In some of these situations, will 480 // have to move previously laid-out annotations. FIXME: revisit 481 // this. 482 483 // Index of last annotation which we didn't know how to lay out 484 int deferredIndex = -1; 485 // We "lay out after" this one 486 Annotation constraintAnnotation = null; 487 // The first constraint annotation 488 Annotation firstConstraintAnnotation = null; 489 // The index from which we search forward in the 490 // visibleAnnotations list. This reduces the amount of work we do. 491 int searchIndex = 0; 492 // The new set of annotations 493 java.util.List newAnnos = new ArrayList(); 494 495 for (Iterator iter = va.iterator(); iter.hasNext(); ) { 496 Annotation anno = (Annotation) ((IntervalNode) iter.next()).getData(); 497 498 // Search forward for this one 499 boolean found = false; 500 for (int i = searchIndex; i < visibleAnnotations.size(); i++) { 501 Annotation el = (Annotation) visibleAnnotations.get(i); 502 // See whether we can abort the search unsuccessfully because 503 // we went forward too far 504 if (el.getLowAddress().greaterThan(anno.getLowAddress())) { 505 break; 506 } 507 if (el == anno) { 508 // Found successfully. 509 found = true; 510 searchIndex = i; 511 constraintAnnotation = anno; 512 if (firstConstraintAnnotation == null) { 513 firstConstraintAnnotation = constraintAnnotation; 514 } 515 break; 516 } 517 } 518 519 if (!found) { 520 if (constraintAnnotation != null) { 521 layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); 522 constraintAnnotation = anno; 523 } else { 524 // Defer layout of this annotation until later 525 ++deferredIndex; 526 } 527 } 528 529 newAnnos.add(anno); 530 } 531 532 if (firstConstraintAnnotation != null) { 533 // Go back and lay out deferred annotations 534 for (int i = deferredIndex; i >= 0; i--) { 535 Annotation anno = (Annotation) newAnnos.get(i); 536 layoutBefore(anno, firstConstraintAnnotation, g, x, startAddr, lineHeight); 537 firstConstraintAnnotation = anno; 538 } 539 } else { 540 // Didn't find any overlap between the old and new annotations. 541 // Lay out in a feed-forward fashion. 542 if (Assert.ASSERTS_ENABLED) { 543 Assert.that(constraintAnnotation == null, "logic error in layout code"); 544 } 545 for (Iterator iter = newAnnos.iterator(); iter.hasNext(); ) { 546 Annotation anno = (Annotation) iter.next(); 547 layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); 548 constraintAnnotation = anno; 549 } 550 } 551 552 visibleAnnotations = newAnnos; 553 } 554 555 /** Lays out the given annotation before the optional constraint 556 annotation, obeying constraints imposed by that annotation if it 557 is specified. */ 558 private void layoutBefore(Annotation anno, Annotation constraintAnno, 559 Graphics g, int x, 560 Address startAddr, int lineHeight) { 561 anno.computeWidthAndHeight(g); 562 // Color 563 if (constraintAnno != null) { 564 anno.setColor(prevColor(constraintAnno.getColor())); 565 } else { 566 anno.setColor(colors[0]); 567 } 568 // X 569 anno.setX(x); 570 // Tentative Y 571 anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + 572 (5 * lineHeight / 6)); 573 // See whether Y overlaps with last anno's Y; if so, move this one up 574 if ((constraintAnno != null) && (anno.getY() + anno.getHeight() > constraintAnno.getY())) { 575 anno.setY(constraintAnno.getY() - anno.getHeight()); 576 } 577 } 578 579 /** Lays out the given annotation after the optional constraint 580 annotation, obeying constraints imposed by that annotation if it 581 is specified. */ 582 private void layoutAfter(Annotation anno, Annotation constraintAnno, 583 Graphics g, int x, 584 Address startAddr, int lineHeight) { 585 anno.computeWidthAndHeight(g); 586 // Color 587 if (constraintAnno != null) { 588 anno.setColor(nextColor(constraintAnno.getColor())); 589 } else { 590 anno.setColor(colors[0]); 591 } 592 // X 593 anno.setX(x); 594 // Tentative Y 595 anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + 596 (5 * lineHeight / 6)); 597 // See whether Y overlaps with last anno's Y; if so, move this one down 598 if ((constraintAnno != null) && (anno.getY() < (constraintAnno.getY() + constraintAnno.getHeight()))) { 599 anno.setY(constraintAnno.getY() + constraintAnno.getHeight()); 600 } 601 } 602 603 /** Returns previous color in our color palette */ 604 private Color prevColor(Color c) { 605 int i = findColorIndex(c); 606 if (i == 0) { 607 return colors[colors.length - 1]; 608 } else { 609 return colors[i - 1]; 610 } 611 } 612 613 /** Returns next color in our color palette */ 614 private Color nextColor(Color c) { 615 return colors[(findColorIndex(c) + 1) % colors.length]; 616 } 617 618 private int findColorIndex(Color c) { 619 for (int i = 0; i < colors.length; i++) { 620 if (colors[i] == c) { 621 return i; 622 } 623 } 624 throw new IllegalArgumentException(); 625 } 626 627 public static void main(String[] args) { 628 JFrame frame = new JFrame(); 629 DummyDebugger debugger = new DummyDebugger(new MachineDescriptionIntelX86()); 630 AnnotatedMemoryPanel anno = new AnnotatedMemoryPanel(debugger); 631 frame.getContentPane().add(anno); 632 anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000000"), 633 debugger.parseAddress("0x80000040"), 634 "Stack Frame for \"foo\"")); 635 anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000010"), 636 debugger.parseAddress("0x80000020"), 637 "Locals for \"foo\"")); 638 anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000020"), 639 debugger.parseAddress("0x80000030"), 640 "Expression stack for \"foo\"")); 641 642 frame.setSize(400, 300); 643 frame.addWindowListener(new WindowAdapter() { 644 public void windowClosed(WindowEvent e) { 645 System.exit(0); 646 } 647 public void windowClosing(WindowEvent e) { 648 System.exit(0); 649 } 650 }); 651 frame.setVisible(true); 652 } 653 }