/******************************************************************************* * Copyright (c) 2006, 2015 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Ted R Williams (Wind River Systems, Inc.) - initial implementation * Randy Rohrbach (Wind River Systems, Inc.) - Copied and modified to create the floating point plugin *******************************************************************************/ package org.eclipse.cdt.debug.ui.memory.floatingpoint; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.Vector; import org.eclipse.cdt.debug.ui.memory.floatingpoint.FPutilities.FPDataType; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IMemoryBlock; import org.eclipse.debug.core.model.IMemoryBlockExtension; import org.eclipse.debug.core.model.MemoryByte; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil; import org.eclipse.debug.internal.ui.views.memory.renderings.GoToAddressComposite; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.UIJob; @SuppressWarnings("restriction") public class Rendering extends Composite implements IDebugEventSetListener { // The IMemoryRendering parent private FPRendering fParent; // Controls protected FPAddressPane fAddressPane; protected FPDataPane fDataPane; private GoToAddressComposite fAddressBar; protected Control fAddressBarControl; private Selection fSelection = new Selection(); // Internal management BigInteger fViewportAddress = null; // Default visibility for performance BigInteger fMemoryBlockStartAddress = null; // Starting address BigInteger fMemoryBlockEndAddress = null; // Ending address protected BigInteger fBaseAddress = null; // Base address protected int fColumnCount = 0; // Auto calculate can be disabled by user, making this user settable protected int fBytesPerRow = 0; // Number of bytes per row are displayed int fScrollSelection = 0; // Scroll selection private BigInteger fCaretAddress = null; // Caret/cursor position private boolean fCellEditState = false; // Cell editing mode: 'true' = currently editing a cell; 'false' = not editing a cell private BigInteger cellEditAddress = null; // The address of the cell currently being edited private BigInteger memoryAddress = null; // The memory address associated with the cell that currently being edited private StringBuffer fEditBuffer = null; // Character buffer used during editing static boolean initialDisplayModeSet = false; // Initial display mode been set to the same endianness as the target // Constants used to identify the panes public final static int PANE_ADDRESS = 1; public final static int PANE_DATA = 2; // Decimal precision used when converting between scroll units and number of memory // rows. Calculations do not need to be exact; two decimal places is good enough. static private final MathContext SCROLL_CONVERSION_PRECISION = new MathContext(2); // Constants used to identify text, maybe java should be queried for all available sets public final static int TEXT_ISO_8859_1 = 1; public final static int TEXT_USASCII = 2; public final static int TEXT_UTF8 = 3; protected final static int TEXT_UTF16 = 4; // Internal constants public final static int COLUMNS_AUTO_SIZE_TO_FIT = 0; // View internal settings private int fCellPadding = 2; private int fPaneSpacing = 16; private String fPaddingString = " "; //$NON-NLS-1$ // Flag whether the memory cache is dirty private boolean fCacheDirty = false; // Update modes public final static int UPDATE_ALWAYS = 1; public final static int UPDATE_ON_BREAKPOINT = 2; public final static int UPDATE_MANUAL = 3; public int fUpdateMode = UPDATE_ALWAYS; // Constants for cell-width calculations private static final int DECIMAL_POINT_SIZE = 1; private static final int SIGN_SIZE = 1; private static final int EXPONENT_CHARACTER_SIZE = 1; private static final int EXPONENT_VALUE_SIZE = 3; // User settings private FPDataType fFPDataType = FPDataType.FLOAT; // Default to float data type private int fDisplayedPrecision = 8; // The default number of digits of displayed precision private int fCharsPerColumn = charsPerColumn(); // Figure out the initial cell-width size private int fColumnsSetting = COLUMNS_AUTO_SIZE_TO_FIT; // Default column setting private boolean fIsTargetLittleEndian = true; // Default target endian setting private boolean fIsDisplayLittleEndian = true; // Default display endian setting private boolean fEditInserMode = false; // Insert mode: true = replace existing number, false = overstrike // Constructors public Rendering(Composite parent, FPRendering renderingParent) { super(parent, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.H_SCROLL | SWT.V_SCROLL); this.setFont(JFaceResources.getFont(IInternalDebugUIConstants.FONT_NAME)); // TODO: internal? this.fParent = renderingParent; // Initialize the viewport start if (fParent.getMemoryBlock() != null) { // This is base address from user. // Honor it if the block has no limits or the base is within the block limits. // Fix Bug 414519. BigInteger base = fParent.getBigBaseAddress(); fViewportAddress = fParent.getMemoryBlockStartAddress(); // The viewport address will be null if memory may be retrieved at any // address less than this memory block's base. If so use the base address. if (fViewportAddress == null) fViewportAddress = base; else { BigInteger blockEndAddr = fParent.getMemoryBlockEndAddress(); if (base.compareTo(fViewportAddress) > 0) { if (blockEndAddr == null || base.compareTo(blockEndAddr) < 0) fViewportAddress = base; } } fBaseAddress = fViewportAddress; } // Instantiate the panes, TODO default visibility from state or plugin.xml? this.fAddressPane = createAddressPane(); this.fDataPane = createDataPane(); fAddressBar = new GoToAddressComposite(); fAddressBarControl = fAddressBar.createControl(parent); Button button = fAddressBar.getButton(IDialogConstants.OK_ID); if (button != null) { button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { doGoToAddress(); } }); button = fAddressBar.getButton(IDialogConstants.CANCEL_ID); if (button != null) { button.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setVisibleAddressBar(false); } }); } } fAddressBar.getExpressionWidget().addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { doGoToAddress(); } }); fAddressBar.getExpressionWidget().addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ESC) setVisibleAddressBar(false); super.keyPressed(e); } }); this.fAddressBarControl.setVisible(false); getHorizontalBar().addSelectionListener(createHorizontalBarSelectionListener()); getVerticalBar().addSelectionListener(createVerticalBarSelectinListener()); this.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent pe) { pe.gc.setBackground(Rendering.this.getFPRendering().getColorBackground()); pe.gc.fillRectangle(0, 0, Rendering.this.getBounds().width, Rendering.this.getBounds().height); } }); setLayout(); this.addControlListener(new ControlListener() { @Override public void controlMoved(ControlEvent ce) { } @Override public void controlResized(ControlEvent ce) { packColumns(); } }); DebugPlugin.getDefault().addDebugEventListener(this); } // Determine how many characters are allowed in the column private int charsPerColumn() { return fDisplayedPrecision + DECIMAL_POINT_SIZE + SIGN_SIZE + EXPONENT_CHARACTER_SIZE + EXPONENT_VALUE_SIZE + fCellPadding; } // Establish the visible layout of the view protected void setLayout() { this.setLayout(new Layout() { @Override public void layout(Composite composite, boolean changed) { int xOffset = 0; if (Rendering.this.getHorizontalBar().isVisible()) xOffset = Rendering.this.getHorizontalBar().getSelection(); int x = xOffset * -1; int y = 0; if (fAddressBarControl.isVisible()) { fAddressBarControl.setBounds(0, 0, Rendering.this.getBounds().width, fAddressBarControl.computeSize(100, 30).y); // FIXME // y = fAddressBarControl.getBounds().height; } if (fAddressPane.isPaneVisible()) { fAddressPane.setBounds(x, y, fAddressPane.computeSize(0, 0).x, Rendering.this.getBounds().height - y); x = fAddressPane.getBounds().x + fAddressPane.getBounds().width; } if (fDataPane.isPaneVisible()) { fDataPane.setBounds(x, y, fDataPane.computeSize(0, 0).x, Rendering.this.getBounds().height - y); x = fDataPane.getBounds().x + fDataPane.getBounds().width; } ScrollBar horizontal = Rendering.this.getHorizontalBar(); horizontal.setVisible(true); horizontal.setMinimum(0); horizontal.setMaximum(fDataPane.getBounds().x + fDataPane.getBounds().width + xOffset); @SuppressWarnings("unused") int temp = horizontal.getMaximum(); horizontal.setThumb(getClientArea().width); horizontal.setPageIncrement(40); // TODO ? horizontal.setIncrement(20); // TODO ? } @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { return new Point(100, 100); // dummy data } }); } // Handles for the caret/cursor movement keys protected void handleDownArrow() { fViewportAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow())); ensureViewportAddressDisplayable(); redrawPanes(); } protected void handleUpArrow() { fViewportAddress = fViewportAddress.subtract(BigInteger.valueOf(getAddressableCellsPerRow())); ensureViewportAddressDisplayable(); redrawPanes(); } protected void handlePageDown() { fViewportAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow() * (Rendering.this.getRowCount() - 1))); ensureViewportAddressDisplayable(); redrawPanes(); } protected void handlePageUp() { fViewportAddress = fViewportAddress.subtract(BigInteger.valueOf(getAddressableCellsPerRow() * (Rendering.this.getRowCount() - 1))); ensureViewportAddressDisplayable(); redrawPanes(); } protected SelectionListener createHorizontalBarSelectionListener() { return new SelectionListener() { @Override public void widgetSelected(SelectionEvent se) { Rendering.this.layout(); } @Override public void widgetDefaultSelected(SelectionEvent se) { // Do nothing } }; } protected SelectionListener createVerticalBarSelectinListener() { return new SelectionListener() { @Override public void widgetSelected(SelectionEvent se) { switch (se.detail) { case SWT.ARROW_DOWN: handleDownArrow(); break; case SWT.PAGE_DOWN: handlePageDown(); break; case SWT.ARROW_UP: handleUpArrow(); break; case SWT.PAGE_UP: handlePageUp(); break; case SWT.SCROLL_LINE: // See: BUG 203068 selection event details broken on GTK < 2.6 default: { if (getVerticalBar().getSelection() == getVerticalBar().getMinimum()) { // Set view port start address to the start address of the Memory Block fViewportAddress = Rendering.this.getMemoryBlockStartAddress(); } else if (getVerticalBar().getSelection() == getVerticalBar().getMaximum()) { // The view port end address should be less or equal to the the end address of the Memory Block // Set view port address to be bigger than the end address of the Memory Block for now // and let ensureViewportAddressDisplayable() to figure out the correct view port start address fViewportAddress = Rendering.this.getMemoryBlockEndAddress(); } else { // Figure out the delta, ignore events with no delta int deltaScroll = getVerticalBar().getSelection() - fScrollSelection; if (deltaScroll == 0) break; BigInteger deltaRows = scrollbar2rows(deltaScroll); BigInteger newAddress = fViewportAddress.add(BigInteger.valueOf(getAddressableCellsPerRow()).multiply(deltaRows)); fViewportAddress = newAddress; } ensureViewportAddressDisplayable(); // Update tooltip; FIXME conversion from slider to scrollbar // getVerticalBar().setToolTipText(Rendering.this.getAddressString(fViewportAddress)); // Update the addresses in the Address pane. if (fAddressPane.isPaneVisible()) fAddressPane.redraw(); redrawPanes(); break; } } } @Override public void widgetDefaultSelected(SelectionEvent se) { // do nothing } }; } protected FPAddressPane createAddressPane() { return new FPAddressPane(this); } protected FPDataPane createDataPane() { return new FPDataPane(this); } public FPRendering getFPRendering() // TODO rename { return fParent; } protected void setCaretAddress(BigInteger address) { fCaretAddress = address; } protected BigInteger getCaretAddress() { // Return the caret address if it has been set. Otherwise return the viewport address. // When the rendering is first created, the caret is unset until the user clicks somewhere // in the rendering. It also reset (unset) when the user gives us a new viewport address return (fCaretAddress != null) ? fCaretAddress : fViewportAddress; } void doGoToAddress() { try { BigInteger address = fAddressBar.getGoToAddress(this.getMemoryBlockStartAddress(), this.getCaretAddress()); getFPRendering().gotoAddress(address); setVisibleAddressBar(false); } catch (NumberFormatException e1) { // FIXME log? } } // Ensure that all addresses displayed are within the addressable range protected void ensureViewportAddressDisplayable() { if (fViewportAddress.compareTo(Rendering.this.getMemoryBlockStartAddress()) < 0) { fViewportAddress = Rendering.this.getMemoryBlockStartAddress(); } else if (getViewportEndAddress().compareTo(getMemoryBlockEndAddress().add(BigInteger.ONE)) > 0) { fViewportAddress = getMemoryBlockEndAddress().subtract(BigInteger.valueOf(getAddressableCellsPerRow() * getRowCount() - 1)); } setScrollSelection(); } public FPIMemorySelection getSelection() { return fSelection; } protected int getHistoryDepth() { return fViewportCache.getHistoryDepth(); } protected void setHistoryDepth(int depth) { fViewportCache.setHistoryDepth(depth); } public void logError(String message, Exception e) { Status status = new Status(IStatus.ERROR, fParent.getRenderingId(), DebugException.INTERNAL_ERROR, message, e); FPRenderingPlugin.getDefault().getLog().log(status); } public void handleFontPreferenceChange(Font font) { setFont(font); Control controls[] = this.getRenderingPanes(); for (int index = 0; index < controls.length; index++) controls[index].setFont(font); packColumns(); layout(true); } public void setPaddingString(String padding) { fPaddingString = padding; refresh(); } public char getPaddingCharacter() { return fPaddingString.charAt(0); // use only the first character } static int suspendCount = 0; @Override public void handleDebugEvents(DebugEvent[] events) { if (this.isDisposed()) return; boolean isChangeOnly = false; boolean isSuspend = false; boolean isBreakpointHit = false; for (int index = 0; index < events.length; index++) { if (events[0].getSource() instanceof IDebugElement) { final int kind = events[index].getKind(); final int detail = events[index].getDetail(); final IDebugElement source = (IDebugElement) events[index].getSource(); /* * We have to make sure we are comparing memory blocks here. It pretty much is now the * case that the IDebugTarget is always null. Almost no one in the Embedded Space is * using anything but CDT/DSF or CDT/TCF at this point. The older CDI stuff will still * be using the old Debug Model API. But this will generate the same memory block and * a legitimate IDebugTarget which will match properly. */ if( source.equals( getMemoryBlock() ) && source.getDebugTarget() == getMemoryBlock().getDebugTarget() ) { if ((detail & DebugEvent.BREAKPOINT) != 0) isBreakpointHit = true; if (kind == DebugEvent.SUSPEND) { handleSuspendEvent(detail); isSuspend = true; } else if (kind == DebugEvent.CHANGE) { handleChangeEvent(); isChangeOnly = true; } } } } if (isSuspend) handleSuspend(isBreakpointHit); else if (isChangeOnly) handleChange(); } protected void handleSuspend(boolean isBreakpointHit) { if (getUpdateMode() == UPDATE_ALWAYS || (getUpdateMode() == UPDATE_ON_BREAKPOINT && isBreakpointHit)) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { archiveDeltas(); refresh(); } }); } } protected void handleChange() { if (getUpdateMode() == UPDATE_ALWAYS) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { refresh(); } }); } } protected void handleSuspendEvent(int detail) { } protected void handleChangeEvent() { } // Return true to enable development debug print statements public boolean isDebug() { return false; } protected IMemoryBlockExtension getMemoryBlock() { IMemoryBlock block = fParent.getMemoryBlock(); if (block != null) return block.getAdapter(IMemoryBlockExtension.class); return null; } public BigInteger getBigBaseAddress() { return fParent.getBigBaseAddress(); } public int getAddressableSize() { return fParent.getAddressableSize(); } protected FPIViewportCache getViewportCache() { return fViewportCache; } public FPMemoryByte[] getBytes(BigInteger address, int bytes) throws DebugException { return getViewportCache().getBytes(address, bytes); } // Default visibility for performance ViewportCache fViewportCache = new ViewportCache(); private interface Request { } class ViewportCache extends Thread implements FPIViewportCache { class ArchiveDeltas implements Request { } class AddressPair implements Request { BigInteger startAddress; BigInteger endAddress; public AddressPair(BigInteger start, BigInteger end) { startAddress = start; endAddress = end; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj instanceof AddressPair) { return ((AddressPair) obj).startAddress.equals(startAddress) && ((AddressPair) obj).endAddress.equals(endAddress); } return false; } @Override public int hashCode() { return super.hashCode() + startAddress.hashCode() + endAddress.hashCode(); } } class MemoryUnit implements Cloneable { BigInteger start; BigInteger end; FPMemoryByte[] bytes; @Override public MemoryUnit clone() { MemoryUnit b = new MemoryUnit(); b.start = this.start; b.end = this.end; b.bytes = new FPMemoryByte[this.bytes.length]; for (int index = 0; index < this.bytes.length; index++) b.bytes[index] = new FPMemoryByte(this.bytes[index].getValue()); return b; } public boolean isValid() { return this.start != null && this.end != null && this.bytes != null; } } @SuppressWarnings("hiding") private HashMap<BigInteger, FPMemoryByte[]> fEditBuffer = new HashMap<>(); private boolean fDisposed = false; private Object fLastQueued = null; private Vector<Object> fQueue = new Vector<>(); protected MemoryUnit fCache = null; protected MemoryUnit fHistoryCache[] = new MemoryUnit[0]; protected int fHistoryDepth = 0; public ViewportCache() { start(); } @Override public void dispose() { fDisposed = true; synchronized (fQueue) { fQueue.notify(); } } public int getHistoryDepth() { return fHistoryDepth; } public void setHistoryDepth(int depth) { fHistoryDepth = depth; fHistoryCache = new MemoryUnit[fHistoryDepth]; } @Override public void refresh() { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ if (fCache != null) { queueRequest(fViewportAddress, getViewportEndAddress()); } } @Override public void archiveDeltas() { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ if (fCache != null) { queueRequestArchiveDeltas(); } } private void queueRequest(BigInteger startAddress, BigInteger endAddress) { AddressPair pair = new AddressPair(startAddress, endAddress); queue(pair); } private void queueRequestArchiveDeltas() { ArchiveDeltas archive = new ArchiveDeltas(); queue(archive); } private void queue(Object element) { synchronized (fQueue) { if (!(fQueue.size() > 0 && element.equals(fLastQueued))) { fQueue.addElement(element); fLastQueued = element; } fQueue.notify(); } } @Override public void run() { while (!fDisposed) { AddressPair pair = null; boolean archiveDeltas = false; synchronized (fQueue) { if (fQueue.size() > 0) { Request request = (Request) fQueue.elementAt(0); Class<?> type = request.getClass(); while (fQueue.size() > 0 && type.isInstance(fQueue.elementAt(0))) { request = (Request) fQueue.elementAt(0); fQueue.removeElementAt(0); } if (request instanceof ArchiveDeltas) archiveDeltas = true; else if (request instanceof AddressPair) pair = (AddressPair) request; } } if (archiveDeltas) { for (int i = fViewportCache.getHistoryDepth() - 1; i > 0; i--) fHistoryCache[i] = fHistoryCache[i - 1]; fHistoryCache[0] = fCache.clone(); } else if (pair != null) { populateCache(pair.startAddress, pair.endAddress); } else { synchronized (fQueue) { try { if (fQueue.isEmpty()) { fQueue.wait(); } } catch (Exception e) { // do nothing } } } } } // Cache memory necessary to paint viewport // TODO: user setting to buffer +/- x lines // TODO: reuse existing cache? probably only a minor performance gain private void populateCache(final BigInteger startAddress, final BigInteger endAddress) { try { IMemoryBlockExtension memoryBlock = getMemoryBlock(); BigInteger lengthInBytes = endAddress.subtract(startAddress); BigInteger addressableSize = BigInteger.valueOf(getAddressableSize()); long units = lengthInBytes.divide(addressableSize) .add(lengthInBytes.mod(addressableSize).compareTo(BigInteger.ZERO) > 0 ? BigInteger.ONE : BigInteger.ZERO).longValue(); // CDT (and maybe other backends) will call setValue() on these MemoryBlock objects. We // don't want this to happen, because it interferes with this rendering's own change history. // Ideally, we should strictly use the back end change notification and history, but it is // only guaranteed to work for bytes within the address range of the MemoryBlock. MemoryByte readBytes[] = memoryBlock.getBytesFromAddress(startAddress, units); FPMemoryByte cachedBytes[] = new FPMemoryByte[readBytes.length]; for (int index = 0; index < readBytes.length; index++) cachedBytes[index] = new FPMemoryByte(readBytes[index].getValue(), readBytes[index].getFlags()); // Derive the target endian from the read MemoryBytes. if (cachedBytes.length > 0) if (cachedBytes[0].isEndianessKnown()) setTargetLittleEndian(!cachedBytes[0].isBigEndian()); // The first time we execute this method, set the display endianness to the target endianness. if (!initialDisplayModeSet) { setDisplayLittleEndian(isTargetLittleEndian()); initialDisplayModeSet = true; } // Re-order bytes within unit to be a sequential byte stream if the endian is already little if (isTargetLittleEndian()) { // There isn't an order when the unit size is one, so skip for performance if (addressableSize.compareTo(BigInteger.ONE) != 0) { int unitSize = addressableSize.intValue(); FPMemoryByte cachedBytesAsByteSequence[] = new FPMemoryByte[cachedBytes.length]; for (int unit = 0; unit < units; unit++) { for (int unitbyte = 0; unitbyte < unitSize; unitbyte++) { cachedBytesAsByteSequence[unit * unitSize + unitbyte] = cachedBytes[unit * unitSize + unitSize - unitbyte]; } } cachedBytes = cachedBytesAsByteSequence; } } final FPMemoryByte[] cachedBytesFinal = cachedBytes; fCache = new MemoryUnit(); fCache.start = startAddress; fCache.end = endAddress; fCache.bytes = cachedBytesFinal; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { // Generate deltas for (int historyIndex = 0; historyIndex < getHistoryDepth(); historyIndex++) { if (fHistoryCache[historyIndex] != null && fHistoryCache[historyIndex].isValid()) { BigInteger maxStart = startAddress.max(fHistoryCache[historyIndex].start); BigInteger minEnd = endAddress.min(fHistoryCache[historyIndex].end).subtract(BigInteger.ONE); BigInteger overlapLength = minEnd.subtract(maxStart); if (overlapLength.compareTo(BigInteger.valueOf(0)) > 0) { // there is overlap int offsetIntoOld = maxStart.subtract(fHistoryCache[historyIndex].start).intValue(); int offsetIntoNew = maxStart.subtract(startAddress).intValue(); for (int i = overlapLength.intValue(); i >= 0; i--) { cachedBytesFinal[offsetIntoNew + i].setChanged(historyIndex, cachedBytesFinal[offsetIntoNew + i].getValue() != fHistoryCache[historyIndex].bytes[offsetIntoOld + i] .getValue()); } // There are several scenarios where the history cache must be updated from the data cache, so that when a // cell is edited the font color changes appropriately. The following code deals with the different cases. if (historyIndex != 0) continue; int dataStart = fCache.start.intValue(); int dataEnd = fCache.end.intValue(); int dataLength = fCache.bytes.length; int historyStart = fHistoryCache[0].start.intValue(); int historyEnd = fHistoryCache[0].end.intValue(); int historyLength = fHistoryCache[0].bytes.length; // Case 1: The data cache is smaller than the history cache; the data cache's // address range is fully covered by the history cache. Do nothing. if ((dataStart >= historyStart) && (dataEnd <= historyEnd)) continue; // Case 2: The data and history cache's do not overlap at all if (((dataStart < historyStart) && (dataEnd < historyStart)) || (dataStart > historyEnd)) { // Create a new history cache: Copy the data cache bytes to the history cache MemoryUnit newHistoryCache = new MemoryUnit(); newHistoryCache.start = fCache.start; newHistoryCache.end = fCache.end; int newHistoryCacheSize = fCache.bytes.length; newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; for (int index = 0; index < newHistoryCacheSize; index++) newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); fHistoryCache[0] = newHistoryCache; continue; } // Case 3: The data cache starts at a lower address than the history cache, but overlaps the history cache if ((dataStart < historyStart) && ((dataEnd >= historyStart) && (dataEnd <= historyEnd))) { // Create a new history cache with the missing data from the main cache and append the old history to it. int missingDataByteCount = historyStart - dataStart; int historyCacheSize = historyLength; int newHistoryCacheSize = missingDataByteCount + historyLength; if (missingDataByteCount <= 0 && historyCacheSize <= 0) break; MemoryUnit newHistoryCache = new MemoryUnit(); newHistoryCache.start = fCache.start; newHistoryCache.end = fHistoryCache[0].end; newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; // Copy the missing bytes from the beginning of the main cache to the history cache. for (int index = 0; index < missingDataByteCount; index++) newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); // Copy the remaining bytes from the old history cache to the new history cache for (int index = 0; index < historyCacheSize; index++) newHistoryCache.bytes[index + missingDataByteCount] = new FPMemoryByte(fHistoryCache[0].bytes[index].getValue()); fHistoryCache[0] = newHistoryCache; continue; } // Case 4: The data cache starts at a higher address than the history cache if (((dataStart >= historyStart) && (dataStart <= historyEnd)) && (dataEnd > historyEnd)) { // Append the missing main cache bytes to the history cache. int missingDataByteCount = dataEnd - historyEnd; int historyCacheSize = historyEnd - historyStart; int newHistoryCacheSize = missingDataByteCount + historyLength; if (missingDataByteCount > 0 && historyCacheSize > 0) { MemoryUnit newHistoryCache = new MemoryUnit(); newHistoryCache.start = fHistoryCache[0].start; newHistoryCache.end = fCache.end; newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; // Copy the old history bytes to the new history cache System.arraycopy(fHistoryCache[0].bytes, 0, newHistoryCache.bytes, 0, historyLength); // Copy the bytes from the main cache that are not in the history cache to the end of the new history cache. for (int index = 0; index < missingDataByteCount; index++) { int srcIndex = dataLength - missingDataByteCount + index; int dstIndex = historyLength + index; newHistoryCache.bytes[dstIndex] = new FPMemoryByte(fCache.bytes[srcIndex].getValue()); } fHistoryCache[0] = newHistoryCache; continue; } } // Case 5 - The data cache is greater than the history cache and fully covers it if (dataStart < historyStart && dataEnd > historyEnd) { int start = 0; int end = 0; // Create a new history cache to reflect the entire data cache MemoryUnit newHistoryCache = new MemoryUnit(); newHistoryCache.start = fCache.start; newHistoryCache.end = fCache.end; int newHistoryCacheSize = fCache.bytes.length; newHistoryCache.bytes = new FPMemoryByte[newHistoryCacheSize]; int topByteCount = historyStart - dataStart; int bottomByteCount = dataEnd - historyEnd; // Copy the bytes from the beginning of the data cache to the new history cache for (int index = 0; index < topByteCount; index++) newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); // Copy the old history cache bytes to the new history cache start = topByteCount; end = topByteCount + historyLength; for (int index = start; index < end; index++) newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); // Copy the bytes from the end of the data cache to the new history cache start = topByteCount + historyLength; end = topByteCount + historyLength + bottomByteCount; for (int index = start; index < end; index++) newHistoryCache.bytes[index] = new FPMemoryByte(fCache.bytes[index].getValue()); fHistoryCache[0] = newHistoryCache; continue; } } } } // If the history does not exist, populate the history with the just populated // cache. This solves the use case of (1) connect to target; (2) edit memory // before the first suspend debug event; (3) paint differences in changed color. if (fHistoryCache[0] == null) fHistoryCache[0] = fCache.clone(); Rendering.this.redrawPanes(); } }); } catch (Exception e) { // User can scroll to any memory, whether it's valid on the target or not. It doesn't make // much sense to fill up the Eclipse error log with such "failures." So, comment out for now. // logError(FPRenderingMessages.getString("FAILURE_READ_MEMORY"), e); //$NON-NLS-1$ } } // Bytes will be fetched from cache @Override public FPMemoryByte[] getBytes(BigInteger address, int bytesRequested) throws DebugException { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ if (containsEditedCell(address)) // Cell size cannot be switched during an edit return getEditedMemory(address); boolean contains = false; if (fCache != null && fCache.start != null) { // See if all of the data requested is in the cache BigInteger dataEnd = address.add(BigInteger.valueOf(bytesRequested)); if (fCache.start.compareTo(address) <= 0 && fCache.end.compareTo(dataEnd) >= 0 && fCache.bytes.length > 0) contains = true; } if (contains) { int offset = address.subtract(fCache.start).intValue(); FPMemoryByte bytes[] = new FPMemoryByte[bytesRequested]; for (int index = 0; index < bytes.length; index++) bytes[index] = fCache.bytes[offset + index]; return bytes; } FPMemoryByte bytes[] = new FPMemoryByte[bytesRequested]; for (int index = 0; index < bytes.length; index++) { bytes[index] = new FPMemoryByte(); bytes[index].setReadable(false); } fViewportCache.queueRequest(fViewportAddress, getViewportEndAddress()); return bytes; } @Override public boolean containsEditedCell(BigInteger address) { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ return fEditBuffer.containsKey(address); } public FPMemoryByte[] getEditedMemory(BigInteger address) { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ return fEditBuffer.get(address); } @Override public void clearEditBuffer() { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ fEditBuffer.clear(); Rendering.this.redrawPanes(); } @Override public void writeEditBuffer() { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ Set<BigInteger> keySet = fEditBuffer.keySet(); Iterator<BigInteger> iterator = keySet.iterator(); while (iterator.hasNext()) { BigInteger address = iterator.next(); FPMemoryByte[] bytes = fEditBuffer.get(address); byte byteValue[] = new byte[bytes.length]; for (int index = 0; index < bytes.length; index++) byteValue[index] = bytes[index].getValue(); try { IMemoryBlockExtension block = getMemoryBlock(); BigInteger offset = address.subtract(block.getBigBaseAddress()); block.setValue(offset, byteValue); } catch (Exception e) { MemoryViewUtil.openError(FPRenderingMessages.getString("FAILURE_WRITE_MEMORY"), "", e); //$NON-NLS-1$ //$NON-NLS-2$ logError(FPRenderingMessages.getString("FAILURE_WRITE_MEMORY"), e); //$NON-NLS-1$ } } clearEditBuffer(); } @Override public void setEditedValue(BigInteger address, FPMemoryByte[] bytes) { assert Thread.currentThread().equals(Display.getDefault().getThread()) : FPRenderingMessages.getString("CALLED_ON_NON_DISPATCH_THREAD"); //$NON-NLS-1$ fEditBuffer.put(address, bytes); Rendering.this.redrawPanes(); } } public void setVisibleAddressBar(boolean visible) { fAddressBarControl.setVisible(visible); if (visible) { String selectedStr = "0x" + getCaretAddress().toString(16); //$NON-NLS-1$ Text text = fAddressBar.getExpressionWidget(); text.setText(selectedStr); text.setSelection(0, text.getCharCount()); fAddressBar.getExpressionWidget().setFocus(); } layout(true); layoutPanes(); } public void setDirty(boolean needRefresh) { fCacheDirty = needRefresh; } public boolean isDirty() { return fCacheDirty; } @Override public void dispose() { DebugPlugin.getDefault().removeDebugEventListener(this); if (fViewportCache != null) { fViewportCache.dispose(); fViewportCache = null; } super.dispose(); } class Selection implements FPIMemorySelection { private BigInteger fStartHigh = null; private BigInteger fStartLow = null; private BigInteger fEndHigh = null; private BigInteger fEndLow = null; @Override public void clear() { fEndHigh = fEndLow = fStartHigh = fStartLow = null; redrawPanes(); } @Override public boolean hasSelection() { return fStartHigh != null && fStartLow != null && fEndHigh != null && fEndLow != null; } @Override public boolean isSelected(BigInteger address) { // Do we have valid start and end addresses? if (getEnd() == null || getStart() == null) return false; // If end is greater than start if (getEnd().compareTo(getStart()) >= 0) { // If address is greater-than-or-equal-to start and less then end, return true if (address.compareTo(getStart()) >= 0 && address.compareTo(getEnd()) < 0) return true; } // If start is greater than end else if (getStart().compareTo(getEnd()) >= 0) { // If address is greater-than-or-equal-to zero and less than start, return true if (address.compareTo(getEnd()) >= 0 && address.compareTo(getStart()) < 0) return true; } return false; } // Set selection start @Override public void setStart(BigInteger high, BigInteger low) { if (high == null && low == null) { if (fStartHigh != null && fStartLow != null) { fStartHigh = null; fStartLow = null; redrawPanes(); } return; } boolean changed = false; if (fStartHigh == null || !high.equals(fStartHigh)) { fStartHigh = high; changed = true; } if (fStartLow == null || !low.equals(fStartLow)) { fStartLow = low; changed = true; } if (changed) redrawPanes(); } // Set selection end @Override public void setEnd(BigInteger high, BigInteger low) { if (high == null && low == null) { if (fEndHigh != null && fEndLow != null) { fEndHigh = null; fEndLow = null; redrawPanes(); } return; } boolean changed = false; if (fEndHigh == null || !high.equals(fEndHigh)) { fEndHigh = high; changed = true; } if (fEndLow == null || !low.equals(fEndLow)) { fEndLow = low; changed = true; } if (changed) redrawPanes(); } @Override public BigInteger getHigh() { if (!hasSelection()) return null; return getStart().max(getEnd()); } @Override public BigInteger getLow() { if (!hasSelection()) return null; return getStart().min(getEnd()); } @Override public BigInteger getStart() { // If there is no start, return null if (fStartHigh == null) return null; // If there is no end, return the high address of the start if (fEndHigh == null) return fStartHigh; // If Start High/Low equal End High/Low, return a low start and high end if (fStartHigh.equals(fEndHigh) && fStartLow.equals(fEndLow)) return fStartLow; BigInteger differenceEndToStartHigh = fEndHigh.subtract(fStartHigh).abs(); BigInteger differenceEndToStartLow = fEndHigh.subtract(fStartLow).abs(); // Return the start high or start low based on which creates a larger selection if (differenceEndToStartHigh.compareTo(differenceEndToStartLow) > 0) return fStartHigh; return fStartLow; } @Override public BigInteger getStartLow() { return fStartLow; } @Override public BigInteger getEnd() { // If there is no end, return null if (fEndHigh == null) return null; // *** Temporary for debugging *** if (fStartHigh == null || fStartLow == null) { return null; } // If Start High/Low equal End High/Low, return a low start and high end if (fStartHigh.equals(fEndHigh) && fStartLow.equals(fEndLow)) return fStartHigh; BigInteger differenceStartToEndHigh = fStartHigh.subtract(fEndHigh).abs(); BigInteger differenceStartToEndLow = fStartHigh.subtract(fEndLow).abs(); // Return the start high or start low based on which creates a larger selection if (differenceStartToEndHigh.compareTo(differenceStartToEndLow) >= 0) return fEndHigh; return fEndLow; } } public void setPaneVisible(int pane, boolean visible) { switch (pane) { case PANE_ADDRESS: fAddressPane.setPaneVisible(visible); break; case PANE_DATA: fDataPane.setPaneVisible(visible); break; } fireSettingsChanged(); layoutPanes(); } public boolean getPaneVisible(int pane) { switch (pane) { case PANE_ADDRESS: return fAddressPane.isPaneVisible(); case PANE_DATA: return fDataPane.isPaneVisible(); default: return false; } } protected void packColumns() { int availableWidth = Rendering.this.getSize().x; if (fAddressPane.isPaneVisible()) { availableWidth -= fAddressPane.computeSize(0, 0).x; availableWidth -= Rendering.this.getRenderSpacing() * 2; } int combinedWidth = 0; if (fDataPane.isPaneVisible()) combinedWidth += fDataPane.getCellWidth(); if (getColumnsSetting() == Rendering.COLUMNS_AUTO_SIZE_TO_FIT) { if (combinedWidth == 0) fColumnCount = 0; else { fColumnCount = availableWidth / combinedWidth; if (fColumnCount == 0) fColumnCount = 1; // Paint one column even if only part can show in view } } else fColumnCount = getColumnsSetting(); try { // Update the number of bytes per row; the max/min scroll range and the current thumbnail position. fBytesPerRow = getCharsPerColumn() * getColumnCount(); getVerticalBar().setMinimum(1); // scrollbar maximum range is Integer.MAX_VALUE. getVerticalBar().setMaximum(getMaxScrollRange().min(BigInteger.valueOf(Integer.MAX_VALUE)).intValue()); getVerticalBar().setIncrement(1); getVerticalBar().setPageIncrement(this.getRowCount() - 1); // FIXME: conversion of slider to scrollbar // fScrollBar.setToolTipText(Rendering.this.getAddressString(fViewportAddress)); setScrollSelection(); } catch (Exception e) { // FIXME precautionary } Rendering.this.redraw(); Rendering.this.redrawPanes(); } public FPAbstractPane[] getRenderingPanes() { return new FPAbstractPane[] { fAddressPane, fDataPane }; } public int getCellPadding() { return fCellPadding; } protected int getRenderSpacing() { return fPaneSpacing; } public void refresh() { if (!this.isDisposed()) { if (this.isVisible() && getViewportCache() != null) { getViewportCache().refresh(); } else { setDirty(true); fParent.updateRenderingLabels(); } } } protected void archiveDeltas() { this.getViewportCache().archiveDeltas(); } public void gotoAddress(BigInteger address) { // Ensure that the GoTo address is within the addressable range if ((address.compareTo(this.getMemoryBlockStartAddress()) < 0) || (address.compareTo(this.getMemoryBlockEndAddress()) > 0)) return; fViewportAddress = address; // Reset the caret and selection state (no caret and no selection) fCaretAddress = null; fSelection = new Selection(); redrawPanes(); } public void setViewportStartAddress(BigInteger newAddress) { fViewportAddress = newAddress; } public BigInteger getViewportStartAddress() { return fViewportAddress; } public BigInteger getViewportEndAddress() { return fViewportAddress.add(BigInteger.valueOf(this.getBytesPerRow() * getRowCount() / getAddressableSize())); } public String getAddressString(BigInteger address) { StringBuffer addressString = new StringBuffer(address.toString(16).toUpperCase()); for (int chars = getAddressBytes() * 2 - addressString.length(); chars > 0; chars--) addressString.insert(0, '0'); addressString.insert(0, "0x"); //$NON-NLS-1$ return addressString.toString(); } protected int getAddressBytes() { return fParent.getAddressSize(); } public Control getAddressBarControl() { return fAddressBarControl; } public int getColumnCount() { return fColumnCount; } public int getColumnsSetting() { return fColumnsSetting; } protected void setBytesPerRow(int count) { fBytesPerRow = count; } protected void setColumnCount(int count) { fColumnCount = count; } public void setColumnsSetting(int columns) { if (fColumnsSetting != columns) { fColumnsSetting = columns; fireSettingsChanged(); layoutPanes(); } } protected void ensureVisible(BigInteger address) { BigInteger viewportStart = this.getViewportStartAddress(); BigInteger viewportEnd = this.getViewportEndAddress(); boolean isAddressBeforeViewportStart = address.compareTo(viewportStart) < 0; boolean isAddressAfterViewportEnd = address.compareTo(viewportEnd) > 0; if (isAddressBeforeViewportStart || isAddressAfterViewportEnd) gotoAddress(address); } protected int getRowCount() { int rowCount = 0; Control panes[] = getRenderingPanes(); for (int i = 0; i < panes.length; i++) if (panes[i] instanceof FPAbstractPane) rowCount = Math.max(rowCount, ((FPAbstractPane) panes[i]).getRowCount()); return rowCount; } protected int getBytesPerRow() { return fBytesPerRow; } protected int getAddressableCellsPerRow() { return getBytesPerRow() / getAddressableSize(); } public int getAddressesPerColumn() { return this.getCharsPerColumn() / getAddressableSize(); } /** * @return Set current scroll selection */ protected void setScrollSelection() { BigInteger selection = getViewportStartAddress().divide(BigInteger.valueOf(getAddressableCellsPerRow())); fScrollSelection = rows2scrollbar(selection); getVerticalBar().setSelection(fScrollSelection); } /** * compute the maximum scrolling range. * * @return number of lines that rendering can display */ private BigInteger getMaxScrollRange() { BigInteger difference = getMemoryBlockEndAddress().subtract(getMemoryBlockStartAddress()).add(BigInteger.ONE); BigInteger maxScrollRange = difference.divide(BigInteger.valueOf(getAddressableCellsPerRow())); if (maxScrollRange.multiply(BigInteger.valueOf(getAddressableCellsPerRow())).compareTo(difference) != 0) maxScrollRange = maxScrollRange.add(BigInteger.ONE); // Support targets with an addressable size greater than 1 maxScrollRange = maxScrollRange.divide(BigInteger.valueOf(getAddressableSize())); return maxScrollRange; } /** * The scroll range is limited by SWT. Because it can be less than the number * of rows (of memory) that we need to display, we need an arithmetic mapping. * * @return ratio this function returns how many rows a scroll bar unit * represents. The number will be some fractional value, up to but * not exceeding the value 1. I.e., when the scroll range exceeds * the row range, we use a 1:1 mapping. */ private final BigDecimal getScrollRatio() { BigInteger maxRange = getMaxScrollRange(); if (maxRange.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { return new BigDecimal(maxRange).divide(BigDecimal.valueOf(Integer.MAX_VALUE), SCROLL_CONVERSION_PRECISION); } return BigDecimal.ONE; } /** * Convert memory row units to scroll bar units. The scroll range is limited * by SWT. Because it can be less than the number of rows (of memory) that * we need to display, we need an arithmetic mapping. * * @param rows * units of memory * @return scrollbar units */ private int rows2scrollbar(BigInteger rows) { return new BigDecimal(rows).divide(getScrollRatio(), SCROLL_CONVERSION_PRECISION).intValue(); } /** * Convert scroll bar units to memory row units. The scroll range is limited * by SWT. Because it can be less than the number of rows (of memory) that * we need to display, we need an arithmetic mapping. * * @param scrollbarUnits * scrollbar units * @return number of rows of memory */ BigInteger scrollbar2rows(int scrollbarUnits) { return getScrollRatio().multiply(BigDecimal.valueOf(scrollbarUnits), SCROLL_CONVERSION_PRECISION).toBigInteger(); } /** * @return start address of the memory block */ protected BigInteger getMemoryBlockStartAddress() { if (fMemoryBlockStartAddress == null) fMemoryBlockStartAddress = fParent.getMemoryBlockStartAddress(); if (fMemoryBlockStartAddress == null) fMemoryBlockStartAddress = BigInteger.ZERO; return fMemoryBlockStartAddress; } /** * @return end address of the memory block */ protected BigInteger getMemoryBlockEndAddress() { if (fMemoryBlockEndAddress == null) fMemoryBlockEndAddress = fParent.getMemoryBlockEndAddress(); return fMemoryBlockEndAddress; } public FPDataType getFPDataType() { return fFPDataType; } public void setFPDataType(FPDataType numberType) { if (fFPDataType == numberType) return; fFPDataType = numberType; fireSettingsChanged(); layoutPanes(); } public int getUpdateMode() { return fUpdateMode; } public void setUpdateMode(int fUpdateMode) { this.fUpdateMode = fUpdateMode; } protected String getCharacterSet(int mode) { switch (mode) { case Rendering.TEXT_UTF8: return "UTF8"; //$NON-NLS-1$ case Rendering.TEXT_UTF16: return "UTF16"; //$NON-NLS-1$ case Rendering.TEXT_USASCII: return "US-ASCII"; //$NON-NLS-1$ case Rendering.TEXT_ISO_8859_1: default: return "ISO-8859-1"; //$NON-NLS-1$ } } public int getBytesPerCharacter() { return 1; } public boolean isTargetLittleEndian() { return fIsTargetLittleEndian; } public void setTargetLittleEndian(boolean littleEndian) { if (fIsTargetLittleEndian == littleEndian) return; fParent.setTargetMemoryLittleEndian(littleEndian); fIsTargetLittleEndian = littleEndian; Display.getDefault().asyncExec(new Runnable() { @Override public void run() { fireSettingsChanged(); layoutPanes(); } }); } public boolean isDisplayLittleEndian() { return fIsDisplayLittleEndian; } public void setDisplayLittleEndian(boolean isLittleEndian) { if (fIsDisplayLittleEndian == isLittleEndian) return; fIsDisplayLittleEndian = isLittleEndian; fireSettingsChanged(); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { layoutPanes(); } }); } public int getCharsPerColumn() { return fCharsPerColumn; } public int getDisplayedPrecision() { return fDisplayedPrecision; } // Set the number of precision digits that are displayed in the view public void setDisplayedPrecision(int displayedPrecision) { if (fDisplayedPrecision != displayedPrecision) { fDisplayedPrecision = displayedPrecision; fCharsPerColumn = charsPerColumn(); fireSettingsChanged(); layoutPanes(); } } protected void redrawPane(int paneId) { if (!isDisposed() && this.isVisible()) { FPAbstractPane pane = null; if (paneId == Rendering.PANE_ADDRESS) { pane = fAddressPane; } else if (paneId == Rendering.PANE_DATA) { pane = fDataPane; } if (pane != null && pane.isPaneVisible()) { pane.redraw(); pane.setRowCount(); if (pane.isFocusControl()) pane.updateTheCaret(); } } fParent.updateRenderingLabels(); } protected void redrawPanes() { if (!isDisposed() && this.isVisible()) { if (fAddressPane.isPaneVisible()) { fAddressPane.redraw(); fAddressPane.setRowCount(); if (fAddressPane.isFocusControl()) fAddressPane.updateTheCaret(); } if (fDataPane.isPaneVisible()) { fDataPane.redraw(); fDataPane.setRowCount(); if (fDataPane.isFocusControl()) fDataPane.updateTheCaret(); } } fParent.updateRenderingLabels(); } void layoutPanes() { packColumns(); layout(true); redraw(); redrawPanes(); } void fireSettingsChanged() { fAddressPane.settingsChanged(); fDataPane.settingsChanged(); } protected void copyAddressToClipboard() { Clipboard clip = null; try { clip = new Clipboard(getDisplay()); String addressString = "0x" + getCaretAddress().toString(16); //$NON-NLS-1$ TextTransfer plainTextTransfer = TextTransfer.getInstance(); clip.setContents(new Object[] { addressString }, new Transfer[] { plainTextTransfer }); } finally { if (clip != null) { clip.dispose(); } } } // Given an array of bytes, the data type and endianness, return a scientific notation string representation public String sciNotationString(FPMemoryByte byteArray[], FPDataType fpDataType, boolean isLittleEndian) { StringBuffer textString = null; // Check the byte array for readability for (int index = 0; index < byteArray.length; index++) if (!byteArray[index].isReadable()) return FPutilities.fillString(fCharsPerColumn, '?'); // Convert the byte array to a floating point value and check to see if it's within the range // of the data type. If the valid range is exceeded, set string to "-Infinity" or "Infinity". ByteOrder byteOrder = isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; if (fpDataType == FPDataType.FLOAT) { float floatValue = ByteBuffer.wrap(FPutilities.memoryBytesToByteArray(byteArray)).order(byteOrder).getFloat(); floatValue = FPutilities.floatLimitCheck(floatValue); if (floatValue == Float.NEGATIVE_INFINITY) textString = new StringBuffer("-Infinity"); //$NON-NLS-1$ if (floatValue == Float.POSITIVE_INFINITY) textString = new StringBuffer(" Infinity"); //$NON-NLS-1$ } if (fpDataType == FPDataType.DOUBLE) { double doubleValue = ByteBuffer.wrap(FPutilities.memoryBytesToByteArray(byteArray)).order(byteOrder).getDouble(); doubleValue = FPutilities.doubleLimitCheck(doubleValue); if (doubleValue == Double.NEGATIVE_INFINITY) textString = new StringBuffer("-Infinity"); //$NON-NLS-1$ if (doubleValue == Double.POSITIVE_INFINITY) textString = new StringBuffer(" Infinity"); //$NON-NLS-1$ } // If we do not already have a StringBuffer value, Convert the value to // a string. In any case, pad the string with spaces to the cell width. if (textString == null) textString = new StringBuffer(FPutilities.byteArrayToSciNotation(fpDataType, isLittleEndian, byteArray, fDisplayedPrecision)); return (textString.append(FPutilities.fillString(fCharsPerColumn - textString.length(), getPaddingCharacter()))).toString(); } // Convert the floating point edit buffer string to a byte array and update memory public void convertAndUpdateCell(BigInteger memoryAddress, String editBuffer) { if (editBuffer != null && editBuffer.length() > 0) { // Convert the edit buffer string to byte arrays byte[] targetByteArray = new byte[getFPDataType().getByteLength()]; targetByteArray = FPutilities.floatingStringToByteArray(getFPDataType(), editBuffer, getFPDataType().getBitsize()); // If we're in little endian mode, reverse the bytes, which is required // due to lower-level internal conversion when written to memory. if (fIsDisplayLittleEndian) targetByteArray = FPutilities.reverseByteOrder(targetByteArray); FPMemoryByte[] newMemoryBytes = new FPMemoryByte[targetByteArray.length]; for (int index = 0; index < targetByteArray.length; index++) { newMemoryBytes[index] = new FPMemoryByte(targetByteArray[index]); newMemoryBytes[index].setBigEndian(!isTargetLittleEndian()); newMemoryBytes[index].setEdited(true); newMemoryBytes[index].setChanged(true); } // Apply the change and make it visible in the view getViewportCache().setEditedValue(memoryAddress, newMemoryBytes); getViewportCache().writeEditBuffer(); redraw(); } else { // The edit buffer string is null or has a zero length. final String errorText = NLS.bind(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TEXT"), "<blanks>"); //$NON-NLS-1$ //$NON-NLS-2$ try { // Restore the previous value setEditBuffer(new StringBuffer(fDataPane.bytesToSciNotation(getBytes(fCaretAddress, getFPDataType().getByteLength())))); } catch (DebugException e) { e.printStackTrace(); } // Put together the pop-up window components and show the user the error String statusString = FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_STATUS"); //$NON-NLS-1$ Status status = new Status(IStatus.ERROR, FPRenderingPlugin.getUniqueIdentifier(), statusString); FPutilities.popupMessage(FPRenderingMessages.getString("FPRendering.ERROR_FPENTRY_POPUP_TITLE"), errorText, status); //$NON-NLS-1$ } } // Getter/setter for Insert Mode state public void setInsertMode(boolean insertMode) { this.fEditInserMode = insertMode; } public boolean insertMode() { return fEditInserMode; } // Getter/setter for cell edit state: true = we're in cell edit mode; false = we're not in cell edit mode public boolean isEditingCell() { return fCellEditState; } public void setEditingCell(boolean state) { this.fCellEditState = state; } // Getter/setter for the address of the cell currently being edited public BigInteger getCellEditAddress() { return cellEditAddress; } public void setCellEditAddress(BigInteger cellEditAddress) { this.cellEditAddress = cellEditAddress; } // Getter/Setter for the memory address of the cell currently being edited public BigInteger getMemoryAddress() { return memoryAddress; } public void setMemoryAddress(BigInteger memoryAddress) { this.memoryAddress = memoryAddress; } // Getter/setter for storing the raw/uninterpreted/not-converted-to-scientific-notation text entry string public StringBuffer getEditBuffer() { return fEditBuffer; } public void setEditBuffer(StringBuffer cellChars) { this.fEditBuffer = cellChars; } // Enter cell-edit mode public void startCellEditing(BigInteger cellAddress, BigInteger memoryAddress, String editString) { setEditBuffer(new StringBuffer(editString)); setCellEditAddress(cellAddress); setMemoryAddress(memoryAddress); setEditingCell(true); } // Exit cell-edit mode public void endCellEditing() { setEditingCell(false); setCellEditAddress(null); setMemoryAddress(null); setEditBuffer(null); } // Floating point number edit mode status-line display: 'true' = display edit mode, 'false' clear edit mode public void displayEditModeIndicator(final boolean indicatorON) { UIJob job = new UIJob("FP Renderer Edit Indicator") //$NON-NLS-1$ { @Override public IStatus runInUIThread(IProgressMonitor monitor) { String statusLineMessage; IViewPart viewInstance = null; if (indicatorON) { // Construct the edit mode message statusLineMessage = NLS.bind(FPRenderingMessages.getString("FPRendering.EDIT_MODE"), //$NON-NLS-1$ (insertMode() ? FPRenderingMessages.getString("FPRendering.EDIT_MODE_INSERT") : //$NON-NLS-1$ FPRenderingMessages.getString("FPRendering.EDIT_MODE_OVERWRITE"))); //$NON-NLS-1$ } else { // 'null' = clear the message statusLineMessage = null; } // Get the window and page references IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (window == null) return Status.OK_STATUS; IWorkbenchPage page = window.getActivePage(); if (page == null) return Status.OK_STATUS; // Update (or clear) the Workbench status line when the Memory and Memory Browser views are in focus viewInstance = page.findView("org.eclipse.debug.ui.MemoryView"); // TODO: programmatically retrieve ID //$NON-NLS-1$ if (viewInstance != null) viewInstance.getViewSite().getActionBars().getStatusLineManager().setMessage(statusLineMessage); viewInstance = page.findView("org.eclipse.cdt.debug.ui.memory.memorybrowser.MemoryBrowser"); // TODO: programmatically retrieve ID //$NON-NLS-1$ if (viewInstance != null) viewInstance.getViewSite().getActionBars().getStatusLineManager().setMessage(statusLineMessage); return Status.OK_STATUS; } }; job.setSystem(true); job.schedule(); } // Calculate memory address from the cell address public BigInteger cellToMemoryAddress(BigInteger cellAddress) { BigInteger vpStart = getViewportStartAddress(); BigInteger colChars = BigInteger.valueOf(getCharsPerColumn()); BigInteger dtBytes = BigInteger.valueOf(getFPDataType().getByteLength()); return vpStart.add(((cellAddress.subtract(vpStart)).divide(colChars)).multiply(dtBytes)); } }