| /* |
| * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| package sun.jvm.hotspot.ui; |
| |
| import java.math.*; |
| import java.awt.*; |
| import java.awt.font.*; |
| import java.awt.geom.*; |
| import java.awt.event.*; |
| import java.io.*; |
| import javax.swing.*; |
| import javax.swing.event.*; |
| import java.util.*; |
| |
| import sun.jvm.hotspot.debugger.*; |
| import sun.jvm.hotspot.debugger.dummy.*; |
| import sun.jvm.hotspot.utilities.*; |
| |
| /** A subclass of JPanel which displays a hex dump of memory along |
| with annotations describing the significance of various |
| pieces. This can be used to implement either a stack or heap |
| inspector. */ |
| |
| public class AnnotatedMemoryPanel extends JPanel { |
| private boolean is64Bit; |
| private Debugger debugger; |
| private long addressSize; |
| private HighPrecisionJScrollBar scrollBar; |
| private Font font; |
| private int bytesPerLine; |
| private int paintCount; |
| private String unmappedAddrString; |
| // Type of this is an IntervalTree indexed by Interval<Address> and |
| // with user data of type Annotation |
| private IntervalTree annotations = |
| new IntervalTree(new Comparator() { |
| public int compare(Object o1, Object o2) { |
| Address a1 = (Address) o1; |
| Address a2 = (Address) o2; |
| |
| if ((a1 == null) && (a2 == null)) { |
| return 0; |
| } else if (a1 == null) { |
| return -1; |
| } else if (a2 == null) { |
| return 1; |
| } |
| |
| if (a1.equals(a2)) { |
| return 0; |
| } else if (a1.lessThan(a2)) { |
| return -1; |
| } |
| return 1; |
| } |
| }); |
| // Keep track of the last start address at which we painted, so we |
| // can scroll annotations |
| private Address lastStartAddr; |
| // This contains the list of currently-visible IntervalNodes, in |
| // sorted order by their low endpoint, in the form of a |
| // List<Annotation>. These annotations have already been laid out. |
| private java.util.List visibleAnnotations; |
| // Darker colors than defaults for better readability |
| private static Color[] colors = { |
| new Color(0.0f, 0.0f, 0.6f), // blue |
| new Color(0.6f, 0.0f, 0.6f), // magenta |
| new Color(0.0f, 0.8f, 0.0f), // green |
| new Color(0.8f, 0.3f, 0.0f), // orange |
| new Color(0.0f, 0.6f, 0.8f), // cyan |
| new Color(0.2f, 0.2f, 0.2f), // dark gray |
| }; |
| |
| /** Default is 32-bit mode */ |
| public AnnotatedMemoryPanel(Debugger debugger) { |
| this(debugger, false); |
| } |
| |
| public AnnotatedMemoryPanel(Debugger debugger, boolean is64Bit, Address addrValue, Address addrLow, Address addrHigh) { |
| super(); |
| init(debugger, is64Bit, addressToBigInt(addrValue), addressToBigInt(addrLow), addressToBigInt(addrHigh)); |
| } |
| |
| public AnnotatedMemoryPanel(Debugger debugger, boolean is64Bit ) { |
| super(); |
| init(debugger, is64Bit, defaultMemoryLocation(is64Bit), defaultMemoryLow(is64Bit), defaultMemoryHigh(is64Bit)); |
| } |
| |
| static class AnnoX { |
| int lineX; |
| Address highBound; |
| |
| public AnnoX(int lineX, Address highBound) { |
| this.lineX = lineX; |
| this.highBound = highBound; |
| } |
| } |
| |
| public synchronized void paintComponent(Graphics g) { |
| // System.err.println("AnnotatedMemoryPanel.paintComponent() " + ++paintCount); |
| super.paintComponent(g); |
| |
| // Clone the Graphics so we don't screw up its state for Swing |
| // drawing (as this code otherwise does) |
| g = g.create(); |
| |
| g.setFont(font); |
| g.setColor(Color.black); |
| Rectangle rect = new Rectangle(); |
| getBounds(rect); |
| String firstAddressString = null; |
| int lineHeight; |
| int addrWidth; |
| { |
| Rectangle2D bounds = GraphicsUtilities.getStringBounds(unmappedAddrString, g); |
| lineHeight = (int) bounds.getHeight(); |
| addrWidth = (int) bounds.getWidth(); |
| } |
| int addrX = (int) (0.25 * addrWidth); |
| int dataX = (int) (addrX + (1.5 * addrWidth)); |
| int lineStartX = dataX + addrWidth + 5; |
| int annoStartX = (int) (lineStartX + (0.75 * addrWidth)); |
| |
| int numLines = rect.height / lineHeight; |
| |
| BigInteger startVal = scrollBar.getValueHP(); |
| BigInteger perLine = new BigInteger(Integer.toString((int) addressSize)); |
| // lineCount and maxLines are both 1 less than expected |
| BigInteger lineCount = new BigInteger(Integer.toString((int) (numLines - 1))); |
| BigInteger maxLines = scrollBar.getMaximumHP().subtract(scrollBar.getMinimumHP()).divide(perLine); |
| if (lineCount.compareTo(maxLines) > 0) { |
| lineCount = maxLines; |
| } |
| BigInteger offsetVal = lineCount.multiply(perLine); |
| BigInteger endVal = startVal.add(offsetVal); |
| if (endVal.compareTo(scrollBar.getMaximumHP()) > 0) { |
| startVal = scrollBar.getMaximumHP().subtract(offsetVal); |
| endVal = scrollBar.getMaximumHP(); |
| // Sure seems like this call will cause us to immediately redraw... |
| scrollBar.setValueHP(startVal); |
| } |
| scrollBar.setVisibleAmountHP(offsetVal.add(perLine)); |
| scrollBar.setBlockIncrementHP(offsetVal); |
| |
| Address startAddr = bigIntToAddress(startVal); |
| Address endAddr = bigIntToAddress(endVal); |
| |
| // Scroll last-known annotations |
| int scrollOffset = 0; |
| if (lastStartAddr != null) { |
| scrollOffset = (int) lastStartAddr.minus(startAddr); |
| } else { |
| if (startAddr != null) { |
| scrollOffset = (int) (-1 * startAddr.minus(lastStartAddr)); |
| } |
| } |
| scrollOffset = scrollOffset * lineHeight / (int) addressSize; |
| scrollAnnotations(scrollOffset); |
| lastStartAddr = startAddr; |
| |
| int curY = lineHeight; |
| Address curAddr = startAddr; |
| for (int i = 0; i < numLines; i++) { |
| String s = bigIntToHexString(startVal); |
| g.drawString(s, addrX, curY); |
| try { |
| s = addressToString(startAddr.getAddressAt(i * addressSize)); |
| } |
| catch (UnmappedAddressException e) { |
| s = unmappedAddrString; |
| } |
| g.drawString(s, dataX, curY); |
| curY += lineHeight; |
| startVal = startVal.add(perLine); |
| } |
| |
| // Query for visible annotations (little slop to ensure we get the |
| // top and bottom) |
| // FIXME: it would be nice to have a more static layout; that is, |
| // if something scrolls off the bottom of the screen, other |
| // annotations still visible shouldn't change position |
| java.util.List va = |
| annotations.findAllNodesIntersecting(new Interval(startAddr.addOffsetTo(-addressSize), |
| endAddr.addOffsetTo(2 * addressSize))); |
| |
| // Render them |
| int curLineX = lineStartX; |
| int curTextX = annoStartX; |
| int curColorIdx = 0; |
| if (g instanceof Graphics2D) { |
| Stroke stroke = new BasicStroke(3.0f); |
| ((Graphics2D) g).setStroke(stroke); |
| } |
| |
| Stack drawStack = new Stack(); |
| |
| layoutAnnotations(va, g, curTextX, startAddr, lineHeight); |
| |
| for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { |
| Annotation anno = (Annotation) iter.next(); |
| Interval interval = anno.getInterval(); |
| |
| if (!drawStack.empty()) { |
| // See whether we can pop any items off the stack |
| boolean shouldContinue = true; |
| do { |
| AnnoX annoX = (AnnoX) drawStack.peek(); |
| if (annoX.highBound.lessThanOrEqual((Address) interval.getLowEndpoint())) { |
| curLineX = annoX.lineX; |
| drawStack.pop(); |
| shouldContinue = !drawStack.empty(); |
| } else { |
| shouldContinue = false; |
| } |
| } while (shouldContinue); |
| } |
| |
| // Draw a line covering the interval |
| Address lineStartAddr = (Address) interval.getLowEndpoint(); |
| // Give it a little slop |
| int lineStartY = (int) (lineStartAddr.minus(startAddr) * lineHeight / addressSize) + |
| (lineHeight / 3); |
| Address lineEndAddr = (Address) interval.getHighEndpoint(); |
| drawStack.push(new AnnoX(curLineX, lineEndAddr)); |
| int lineEndY = (int) (lineEndAddr.minus(startAddr) * lineHeight / addressSize); |
| g.setColor(anno.getColor()); |
| g.drawLine(curLineX, lineStartY, curLineX, lineEndY); |
| // Draw line to text |
| g.drawLine(curLineX, lineStartY, curTextX - 10, anno.getY() - (lineHeight / 2)); |
| curLineX += 8; |
| anno.draw(g); |
| ++curColorIdx; |
| } |
| } |
| |
| /** Add an annotation covering the address range [annotation.lowAddress, |
| annotation.highAddress); that is, it includes the low address and does not |
| include the high address. */ |
| public synchronized void addAnnotation(Annotation annotation) { |
| annotations.insert(annotation.getInterval(), annotation); |
| } |
| |
| /** Makes the given address visible somewhere in the window */ |
| public synchronized void makeVisible(Address addr) { |
| BigInteger bi = addressToBigInt(addr); |
| scrollBar.setValueHP(bi); |
| } |
| |
| public void print() { |
| printOn(System.out); |
| } |
| |
| public void printOn(PrintStream tty) { |
| annotations.printOn(tty); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Internals only below this point |
| // |
| |
| private void init(Debugger debugger, boolean is64Bit, BigInteger addrValue, BigInteger addrLow, BigInteger addrHigh) { |
| this.is64Bit = is64Bit; |
| this.debugger = debugger; |
| if (is64Bit) { |
| addressSize = 8; |
| unmappedAddrString = "??????????????????"; |
| } else { |
| addressSize = 4; |
| unmappedAddrString = "??????????"; |
| } |
| setLayout(new BorderLayout()); |
| setupScrollBar(addrValue, addrLow, addrHigh); |
| add(scrollBar, BorderLayout.EAST); |
| visibleAnnotations = new ArrayList(); |
| setBackground(Color.white); |
| addHierarchyBoundsListener(new HierarchyBoundsListener() { |
| public void ancestorMoved(HierarchyEvent e) { |
| } |
| |
| public void ancestorResized(HierarchyEvent e) { |
| // FIXME: should perform incremental layout |
| // System.err.println("Ancestor resized"); |
| } |
| }); |
| |
| if (font == null) { |
| font = GraphicsUtilities.lookupFont("Courier"); |
| } |
| if (font == null) { |
| throw new RuntimeException("Error looking up monospace font Courier"); |
| } |
| getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), "PageDown"); |
| getActionMap().put("PageDown", new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getBlockIncrementHP())); |
| } |
| }); |
| getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "PageUp"); |
| getActionMap().put("PageUp", new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getBlockIncrementHP())); |
| } |
| }); |
| getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Down"); |
| getActionMap().put("Down", new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| scrollBar.setValueHP(scrollBar.getValueHP().add(scrollBar.getUnitIncrementHP())); |
| } |
| }); |
| getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Up"); |
| getActionMap().put("Up", new AbstractAction() { |
| public void actionPerformed(ActionEvent e) { |
| scrollBar.setValueHP(scrollBar.getValueHP().subtract(scrollBar.getUnitIncrementHP())); |
| } |
| }); |
| setEnabled(true); |
| } |
| |
| private void setupScrollBar(BigInteger value, BigInteger min, BigInteger max) { |
| scrollBar = new HighPrecisionJScrollBar( Scrollbar.VERTICAL, value, min, max); |
| if (is64Bit) { |
| bytesPerLine = 8; |
| // 64-bit mode |
| scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x08})); |
| scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x40})); |
| } else { |
| // 32-bit mode |
| bytesPerLine = 4; |
| scrollBar.setUnitIncrementHP(new BigInteger(1, new byte[] { |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04})); |
| scrollBar.setBlockIncrementHP(new BigInteger(1, new byte[] { |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x20})); |
| } |
| scrollBar.addChangeListener(new ChangeListener() { |
| public void stateChanged(ChangeEvent e) { |
| HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource(); |
| repaint(); |
| } |
| }); |
| } |
| |
| private static BigInteger defaultMemoryLocation(boolean is64Bit) { |
| if (is64Bit) { |
| return new BigInteger(1, new byte[] { |
| (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); |
| } else { |
| return new BigInteger(1, new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}); |
| } |
| } |
| |
| private static BigInteger defaultMemoryLow(boolean is64Bit) { |
| if (is64Bit) { |
| return new BigInteger(1, new byte[] { |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, |
| (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); |
| } else { |
| return new BigInteger(1, new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); |
| } |
| } |
| |
| private static BigInteger defaultMemoryHigh(boolean is64Bit) { |
| if (is64Bit) { |
| return new BigInteger(1, new byte[] { |
| (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, |
| (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); |
| } else { |
| return new BigInteger(1, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFC}); |
| } |
| } |
| |
| private void setupScrollBar() { |
| setupScrollBar(defaultMemoryLocation(is64Bit), |
| defaultMemoryLow(is64Bit), |
| defaultMemoryHigh(is64Bit)); |
| } |
| |
| private String bigIntToHexString(BigInteger bi) { |
| StringBuffer buf = new StringBuffer(); |
| buf.append("0x"); |
| String val = bi.toString(16); |
| for (int i = 0; i < ((2 * addressSize) - val.length()); i++) { |
| buf.append('0'); |
| } |
| buf.append(val); |
| return buf.toString(); |
| } |
| |
| private Address bigIntToAddress(BigInteger i) { |
| String s = bigIntToHexString(i); |
| return debugger.parseAddress(s); |
| } |
| |
| private BigInteger addressToBigInt(Address a) { |
| String s = addressToString(a); |
| if (!s.startsWith("0x")) { |
| throw new NumberFormatException(s); |
| } |
| return new BigInteger(s.substring(2), 16); |
| } |
| |
| private String addressToString(Address a) { |
| if (a == null) { |
| if (is64Bit) { |
| return "0x0000000000000000"; |
| } else { |
| return "0x00000000"; |
| } |
| } |
| return a.toString(); |
| } |
| |
| /** Scrolls the visible annotations by the given Y amount */ |
| private void scrollAnnotations(int y) { |
| for (Iterator iter = visibleAnnotations.iterator(); iter.hasNext(); ) { |
| Annotation anno = (Annotation) iter.next(); |
| anno.setY(anno.getY() + y); |
| } |
| } |
| |
| /** Takes the list of currently-visible annotations (in the form of |
| a List<IntervalNode>) and lays them out given the current |
| visible position and the already-visible annotations. Does not |
| perturb the layouts of the currently-visible annotations. */ |
| private void layoutAnnotations(java.util.List va, |
| Graphics g, |
| int x, |
| Address startAddr, |
| int lineHeight) { |
| // Handle degenerate case early: no visible annotations. |
| if (va.size() == 0) { |
| visibleAnnotations.clear(); |
| return; |
| } |
| |
| // We have two ranges of visible annotations: the one from the |
| // last repaint and the currently visible set. We want to preserve |
| // the layouts of the previously-visible annotations that are |
| // currently visible (to avoid color flashing, jumping, etc.) |
| // while making the coloring of the new annotations fit as well as |
| // possible. Note that annotations may appear and disappear from |
| // any point in the previously-visible list, but the ordering of |
| // the visible annotations is always the same. |
| |
| // This is really a constrained graph-coloring problem. This |
| // simple algorithm only takes into account half of the |
| // constraints (for example, the layout of the previous |
| // annotation, where it should be taking into account the layout |
| // of the previous and next annotations that were in the |
| // previously-visible list). There are situations where it can |
| // generate overlapping annotations and adjacent annotations with |
| // the same color; generally visible when scrolling line-by-line |
| // rather than page-by-page. In some of these situations, will |
| // have to move previously laid-out annotations. FIXME: revisit |
| // this. |
| |
| // Index of last annotation which we didn't know how to lay out |
| int deferredIndex = -1; |
| // We "lay out after" this one |
| Annotation constraintAnnotation = null; |
| // The first constraint annotation |
| Annotation firstConstraintAnnotation = null; |
| // The index from which we search forward in the |
| // visibleAnnotations list. This reduces the amount of work we do. |
| int searchIndex = 0; |
| // The new set of annotations |
| java.util.List newAnnos = new ArrayList(); |
| |
| for (Iterator iter = va.iterator(); iter.hasNext(); ) { |
| Annotation anno = (Annotation) ((IntervalNode) iter.next()).getData(); |
| |
| // Search forward for this one |
| boolean found = false; |
| for (int i = searchIndex; i < visibleAnnotations.size(); i++) { |
| Annotation el = (Annotation) visibleAnnotations.get(i); |
| // See whether we can abort the search unsuccessfully because |
| // we went forward too far |
| if (el.getLowAddress().greaterThan(anno.getLowAddress())) { |
| break; |
| } |
| if (el == anno) { |
| // Found successfully. |
| found = true; |
| searchIndex = i; |
| constraintAnnotation = anno; |
| if (firstConstraintAnnotation == null) { |
| firstConstraintAnnotation = constraintAnnotation; |
| } |
| break; |
| } |
| } |
| |
| if (!found) { |
| if (constraintAnnotation != null) { |
| layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); |
| constraintAnnotation = anno; |
| } else { |
| // Defer layout of this annotation until later |
| ++deferredIndex; |
| } |
| } |
| |
| newAnnos.add(anno); |
| } |
| |
| if (firstConstraintAnnotation != null) { |
| // Go back and lay out deferred annotations |
| for (int i = deferredIndex; i >= 0; i--) { |
| Annotation anno = (Annotation) newAnnos.get(i); |
| layoutBefore(anno, firstConstraintAnnotation, g, x, startAddr, lineHeight); |
| firstConstraintAnnotation = anno; |
| } |
| } else { |
| // Didn't find any overlap between the old and new annotations. |
| // Lay out in a feed-forward fashion. |
| if (Assert.ASSERTS_ENABLED) { |
| Assert.that(constraintAnnotation == null, "logic error in layout code"); |
| } |
| for (Iterator iter = newAnnos.iterator(); iter.hasNext(); ) { |
| Annotation anno = (Annotation) iter.next(); |
| layoutAfter(anno, constraintAnnotation, g, x, startAddr, lineHeight); |
| constraintAnnotation = anno; |
| } |
| } |
| |
| visibleAnnotations = newAnnos; |
| } |
| |
| /** Lays out the given annotation before the optional constraint |
| annotation, obeying constraints imposed by that annotation if it |
| is specified. */ |
| private void layoutBefore(Annotation anno, Annotation constraintAnno, |
| Graphics g, int x, |
| Address startAddr, int lineHeight) { |
| anno.computeWidthAndHeight(g); |
| // Color |
| if (constraintAnno != null) { |
| anno.setColor(prevColor(constraintAnno.getColor())); |
| } else { |
| anno.setColor(colors[0]); |
| } |
| // X |
| anno.setX(x); |
| // Tentative Y |
| anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + |
| (5 * lineHeight / 6)); |
| // See whether Y overlaps with last anno's Y; if so, move this one up |
| if ((constraintAnno != null) && (anno.getY() + anno.getHeight() > constraintAnno.getY())) { |
| anno.setY(constraintAnno.getY() - anno.getHeight()); |
| } |
| } |
| |
| /** Lays out the given annotation after the optional constraint |
| annotation, obeying constraints imposed by that annotation if it |
| is specified. */ |
| private void layoutAfter(Annotation anno, Annotation constraintAnno, |
| Graphics g, int x, |
| Address startAddr, int lineHeight) { |
| anno.computeWidthAndHeight(g); |
| // Color |
| if (constraintAnno != null) { |
| anno.setColor(nextColor(constraintAnno.getColor())); |
| } else { |
| anno.setColor(colors[0]); |
| } |
| // X |
| anno.setX(x); |
| // Tentative Y |
| anno.setY((int) (((Address) anno.getInterval().getLowEndpoint()).minus(startAddr) * lineHeight / addressSize) + |
| (5 * lineHeight / 6)); |
| // See whether Y overlaps with last anno's Y; if so, move this one down |
| if ((constraintAnno != null) && (anno.getY() < (constraintAnno.getY() + constraintAnno.getHeight()))) { |
| anno.setY(constraintAnno.getY() + constraintAnno.getHeight()); |
| } |
| } |
| |
| /** Returns previous color in our color palette */ |
| private Color prevColor(Color c) { |
| int i = findColorIndex(c); |
| if (i == 0) { |
| return colors[colors.length - 1]; |
| } else { |
| return colors[i - 1]; |
| } |
| } |
| |
| /** Returns next color in our color palette */ |
| private Color nextColor(Color c) { |
| return colors[(findColorIndex(c) + 1) % colors.length]; |
| } |
| |
| private int findColorIndex(Color c) { |
| for (int i = 0; i < colors.length; i++) { |
| if (colors[i] == c) { |
| return i; |
| } |
| } |
| throw new IllegalArgumentException(); |
| } |
| |
| public static void main(String[] args) { |
| JFrame frame = new JFrame(); |
| DummyDebugger debugger = new DummyDebugger(new MachineDescriptionIntelX86()); |
| AnnotatedMemoryPanel anno = new AnnotatedMemoryPanel(debugger); |
| frame.getContentPane().add(anno); |
| anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000000"), |
| debugger.parseAddress("0x80000040"), |
| "Stack Frame for \"foo\"")); |
| anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000010"), |
| debugger.parseAddress("0x80000020"), |
| "Locals for \"foo\"")); |
| anno.addAnnotation(new Annotation(debugger.parseAddress("0x80000020"), |
| debugger.parseAddress("0x80000030"), |
| "Expression stack for \"foo\"")); |
| |
| frame.setSize(400, 300); |
| frame.addWindowListener(new WindowAdapter() { |
| public void windowClosed(WindowEvent e) { |
| System.exit(0); |
| } |
| public void windowClosing(WindowEvent e) { |
| System.exit(0); |
| } |
| }); |
| frame.setVisible(true); |
| } |
| } |