1 /* 2 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * 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 font = GraphicsUtilities.lookupFont("DejaVu Sans Mono"); 313 } 314 if (font == null) { 315 throw new RuntimeException("Error looking up monospace font Courier"); 316 } 317 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), "PageDown"); 318 getActionMap().put("PageDown", new AbstractAction() { 319 public void actionPerformed(ActionEvent e) { 320 scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getBlockIncrementHP())); 321 } 322 }); 323 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "PageUp"); 324 getActionMap().put("PageUp", new AbstractAction() { 325 public void actionPerformed(ActionEvent e) { 326 scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getBlockIncrementHP())); 327 } 328 }); 329 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Down"); 330 getActionMap().put("Down", new AbstractAction() { 331 public void actionPerformed(ActionEvent e) { 332 scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getUnitIncrementHP())); 333 } 334 }); 335 getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Up"); 336 getActionMap().put("Up", new AbstractAction() { 337 public void actionPerformed(ActionEvent e) { 338 scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getUnitIncrementHP())); 339 } 340 }); 341 setEnabled(true); 342 } 343 344 private void setupScrollBar(BigInteger value, BigInteger min, BigInteger max) { 345 scrollBar = new HighPrecisionJScrollBar( Scrollbar.VERTICAL, value, min, max); 346 if (is64Bit) { 347 bytesPerLine = 8; 348 // 64-bit mode 349 scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { 350 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 351 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08})); 352 scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { 353 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 354 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40})); 355 } else { 356 // 32-bit mode 357 bytesPerLine = 4; 358 scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { 359 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04})); 360 scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { 361 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20})); 362 } 363 scrollBar.addChangeListener(new ChangeListener() { 364 public void stateChanged(ChangeEvent e) { 365 HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource(); 366 repaint(); 367 } 368 }); 369 } 370 371 private static BigInteger defaultMemoryLocation(boolean is64Bit) { 372 if (is64Bit) { 373 return new BigInteger(1, new byte[] { 374 (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, 375 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 376 } else { 377 return new BigInteger(1, new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 378 } 379 } 380 381 private static BigInteger defaultMemoryLow(boolean is64Bit) { 382 if (is64Bit) { 383 return new BigInteger(1, new byte[] { 384 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 385 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 386 } else { 387 return new BigInteger(1, new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); 388 } 389 } 390 391 private static BigInteger defaultMemoryHigh(boolean is64Bit) { 392 if (is64Bit) { 393 return new BigInteger(1, new byte[] { 394 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 395 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); 396 } else { 397 return new BigInteger(1, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); 398 } 399 } 400 401 private void setupScrollBar() { 402 setupScrollBar(defaultMemoryLocation(is64Bit), 403 defaultMemoryLow(is64Bit), 404 defaultMemoryHigh(is64Bit)); 405 } 406 407 private String bigIntToHexString(BigInteger bi) { 408 StringBuffer buf = new StringBuffer(); 409 buf.append("0x"); 410 String val = bi.toString(16); 411 for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { 412 buf.append('0'); 413 } 414 buf.append(val); 415 return buf.toString(); 416 } 417 418 private Address bigIntToAddress(BigInteger i) { 419 String s = bigIntToHexString(i); 420 return debugger.parseAddress(s); 421 } 422 423 private BigInteger addressToBigInt(Address a) { 424 String s = addressToString(a); 425 if (!s.startsWith("0x")) { 426 throw new NumberFormatException(s); 427 } 428 return new BigInteger(s.substring(2), 16); 429 } 430 431 private String addressToString(Address a) { 432 if (a == null) { 433 if (is64Bit) { 434 return "0x0000000000000000"; 435 } else { 436 return "0x00000000"; 437 } 438 } 439 return a.toString(); 440 } 441 442 /** Scrolls the visible annotations by the given Y amount */ 443 private void scrollAnnotations(int y) { 444 for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { 445 Annotation anno = (Annotation) iter.next(); 446 anno.setY(anno.getY() + y); 447 } 448 } 449 450 /** Takes the list of currently-visible annotations (in the form of 451 a List<IntervalNode>) and lays them out given the current 452 visible position and the already-visible annotations. Does not 453 perturb the layouts of the currently-visible annotations. */ 454 private void layoutAnnotations(java.util.List va, 455 Graphics g, 456 int x, 457 Address startAddr, 458 int lineHeight) { 459 // Handle degenerate case early: no visible annotations. 460 if (va.size() == 0) { 461 visibleAnnotations.clear(); 462 return; 463 } 464 465 // We have two ranges of visible annotations: the one from the 466 // last repaint and the currently visible set. We want to preserve 467 // the layouts of the previously-visible annotations that are 468 // currently visible (to avoid color flashing, jumping, etc.) 469 // while making the coloring of the new annotations fit as well as 470 // possible. Note that annotations may appear and disappear from 471 // any point in the previously-visible list, but the ordering of 472 // the visible annotations is always the same. 473 474 // This is really a constrained graph-coloring problem. This 475 // simple algorithm only takes into account half of the 476 // constraints (for example, the layout of the previous 477 // annotation, where it should be taking into account the layout 478 // of the previous and next annotations that were in the 479 // previously-visible list). There are situations where it can 480 // generate overlapping annotations and adjacent annotations with 481 // the same color; generally visible when scrolling line-by-line 482 // rather than page-by-page. In some of these situations, will 483 // have to move previously laid-out annotations. FIXME: revisit 484 // this. 485 486 // Index of last annotation which we didn't know how to lay out 487 int deferredIndex = -1; 488 // We "lay out after" this one 489 Annotation constraintAnnotation = null; 490 // The first constraint annotation 491 Annotation firstConstraintAnnotation = null; 492 // The index from which we search forward in the 493 // visibleAnnotations list. This reduces the amount of work we do. 494 int searchIndex = 0; 495 // The new set of annotations 496 java.util.List newAnnos = new ArrayList(); 497 498 for (Iterator iter = va.iterator(); iter.hasNext(); ) { 499 Annotation anno = (Annotation) ((IntervalNode) iter.next()).getData(); 500 501 // Search forward for this one 502 boolean found = false; 503 for (int i = searchIndex; i < visibleAnnotations.size(); i++) { 504 Annotation el = (Annotation) visibleAnnotations.get(i); 505 // See whether we can abort the search unsuccessfully because 506 // we went forward too far 507 if (el.getLowAddress().greaterThan(anno.getLowAddress())) { 508 break; 509 } 510 if (el == anno) { 511 // Found successfully. 512 found = true; 513 searchIndex = i; 514 constraintAnnotation = anno; 515 if (firstConstraintAnnotation == null) { 516 firstConstraintAnnotation = constraintAnnotation; 517 } 518 break; 519 } 520 } 521 522 if (!found) { 523 if (constraintAnnotation != null) { 524 layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); 525 constraintAnnotation = anno; 526 } else { 527 // Defer layout of this annotation until later 528 ++deferredIndex; 529 } 530 } 531 532 newAnnos.add(anno); 533 } 534 535 if (firstConstraintAnnotation != null) { 536 // Go back and lay out deferred annotations 537 for (int i = deferredIndex; i >= 0; i--) { 538 Annotation anno = (Annotation) newAnnos.get(i); 539 layoutBefore(anno, firstConstraintAnnotation, g, x, startAddr, lineHeight); 540 firstConstraintAnnotation = anno; 541 } 542 } else { 543 // Didn't find any overlap between the old and new annotations. 544 // Lay out in a feed-forward fashion. 545 if (Assert.ASSERTS_ENABLED) { 546 Assert.that(constraintAnnotation == null, "logic error in layout code"); 547 } 548 for (Iterator iter = newAnnos.iterator(); iter.hasNext(); ) { 549 Annotation anno = (Annotation) iter.next(); 550 layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); 551 constraintAnnotation = anno; 552 } 553 } 554 555 visibleAnnotations = newAnnos; 556 } 557 558 /** Lays out the given annotation before the optional constraint 559 annotation, obeying constraints imposed by that annotation if it 560 is specified. */ 561 private void layoutBefore(Annotation anno, Annotation constraintAnno, 562 Graphics g, int x, 563 Address startAddr, int lineHeight) { 564 anno.computeWidthAndHeight(g); 565 // Color 566 if (constraintAnno != null) { 567 anno.setColor(prevColor(constraintAnno.getColor())); 568 } else { 569 anno.setColor(colors[0]); 570 } 571 // X 572 anno.setX(x); 573 // Tentative Y 574 anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + 575 (5 * lineHeight / 6)); 576 // See whether Y overlaps with last anno's Y; if so, move this one up 577 if ((constraintAnno != null) && (anno.getY() + anno.getHeight() > constraintAnno.getY())) { 578 anno.setY(constraintAnno.getY() - anno.getHeight()); 579 } 580 } 581 582 /** Lays out the given annotation after the optional constraint 583 annotation, obeying constraints imposed by that annotation if it 584 is specified. */ 585 private void layoutAfter(Annotation anno, Annotation constraintAnno, 586 Graphics g, int x, 587 Address startAddr, int lineHeight) { 588 anno.computeWidthAndHeight(g); 589 // Color 590 if (constraintAnno != null) { 591 anno.setColor(nextColor(constraintAnno.getColor())); 592 } else { 593 anno.setColor(colors[0]); 594 } 595 // X 596 anno.setX(x); 597 // Tentative Y 598 anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + 599 (5 * lineHeight / 6)); 600 // See whether Y overlaps with last anno's Y; if so, move this one down 601 if ((constraintAnno != null) && (anno.getY() < (constraintAnno.getY() + constraintAnno.getHeight()))) { 602 anno.setY(constraintAnno.getY() + constraintAnno.getHeight()); 603 } 604 } 605 606 /** Returns previous color in our color palette */ 607 private Color prevColor(Color c) { 608 int i = findColorIndex(c); 609 if (i == 0) { 610 return colors[colors.length - 1]; 611 } else { 612 return colors[i - 1]; 613 } 614 } 615 616 /** Returns next color in our color palette */ 617 private Color nextColor(Color c) { 618 return colors[(findColorIndex(c) + 1) % colors.length]; 619 } 620 621 private int findColorIndex(Color c) { 622 for (int i = 0; i < colors.length; i++) { 623 if (colors[i] == c) { 624 return i; 625 } 626 } 627 throw new IllegalArgumentException(); 628 } 629 630 public static void main(String[] args) { 631 JFrame frame = new JFrame(); 632 DummyDebugger debugger = new DummyDebugger(new MachineDescriptionIntelX86()); 633 AnnotatedMemoryPanel anno = new AnnotatedMemoryPanel(debugger); 634 frame.getContentPane().add(anno); 635 anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000000"), 636 debugger.parseAddress("0x80000040"), 637 "Stack Frame for \"foo\"")); 638 anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000010"), 639 debugger.parseAddress("0x80000020"), 640 "Locals for \"foo\"")); 641 anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000020"), 642 debugger.parseAddress("0x80000030"), 643 "Expression stack for \"foo\"")); 644 645 frame.setSize(400, 300); 646 frame.addWindowListener(new WindowAdapter() { 647 public void windowClosed(WindowEvent e) { 648 System.exit(0); 649 } 650 public void windowClosing(WindowEvent e) { 651 System.exit(0); 652 } 653 }); 654 frame.setVisible(true); 655 } 656 }