diff options
author | Darin Wright | 2005-02-09 17:32:56 +0000 |
---|---|---|
committer | Darin Wright | 2005-02-09 17:32:56 +0000 |
commit | 83e4b6dd9c22b45d48ceb0e6e5cb6bc89ef3a622 (patch) | |
tree | 0ec585ed18f0af5ef01ab38b5650fa144c73e5c5 /org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/renderings/TableRenderingContentProvider.java | |
parent | d7a08b6c24aa2141579dc137e2fc0ecdd276236a (diff) | |
download | eclipse.platform.debug-83e4b6dd9c22b45d48ceb0e6e5cb6bc89ef3a622.tar.gz eclipse.platform.debug-83e4b6dd9c22b45d48ceb0e6e5cb6bc89ef3a622.tar.xz eclipse.platform.debug-83e4b6dd9c22b45d48ceb0e6e5cb6bc89ef3a622.zip |
Bug 84799 - Implement Memory View and renderings with new rendering APIs
Diffstat (limited to 'org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/renderings/TableRenderingContentProvider.java')
-rw-r--r-- | org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/renderings/TableRenderingContentProvider.java | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/renderings/TableRenderingContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/renderings/TableRenderingContentProvider.java new file mode 100644 index 000000000..63e2755fd --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/renderings/TableRenderingContentProvider.java @@ -0,0 +1,683 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.debug.internal.ui.views.memory.renderings; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.model.IDebugElement; +import org.eclipse.debug.core.model.IDebugTarget; +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.DebugUIMessages; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; + +/** + * Content provider for MemoryViewTab + * + * @since 3.0 + */ +public class TableRenderingContentProvider extends BasicDebugViewContentProvider { + + private static final String PREFIX = "MemoryViewContentProvider."; //$NON-NLS-1$ + private static final String UNABLE_TO_RETRIEVE_CONTENT = PREFIX + "Unable_to_retrieve_content"; //$NON-NLS-1$ + + // cached information + protected Vector lineCache; + + // keeps track of all memory line ever retrieved + // allow us to compare and compute deltas + protected Hashtable contentCache; + + private BigInteger fBufferTopAddress; + + private TableRenderingContentInput fInput; + + /** + * @param memoryBlock + * @param newTab + */ + public TableRenderingContentProvider() + { + lineCache = new Vector(); + contentCache = new Hashtable(); + + DebugPlugin.getDefault().addDebugEventListener(this); + } + + /** + * @param viewer + */ + public void setViewer(StructuredViewer viewer) + { + fViewer = viewer; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) + */ + public void inputChanged(Viewer v, Object oldInput, Object newInput) { + try { + if (newInput instanceof TableRenderingContentInput) + { + fInput = (TableRenderingContentInput)newInput; + if (fInput.getMemoryBlock() instanceof IMemoryBlockExtension) + loadContentForExtendedMemoryBlock(); + else + loadContentForSimpleMemoryBlock(); + + // tell rendering to display table if the loading is successful + fInput.getMemoryRendering().displayTable(); + } + } catch (DebugException e) { + DebugUIPlugin.log(e.getStatus()); + fInput.getMemoryRendering().displayError(e); + } + } + + public void dispose() { + + // fTabItem disposed by view tab + + DebugPlugin.getDefault().removeDebugEventListener(this); + + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(Object parent) { + + // if cache is empty, get memory + if (lineCache.isEmpty()) { + + try { + IMemoryBlock memoryBlock = fInput.getMemoryBlock(); + if (memoryBlock instanceof IMemoryBlockExtension) + { + loadContentForExtendedMemoryBlock(); + fInput.getMemoryRendering().displayTable(); + } + else + { + loadContentForSimpleMemoryBlock(); + fInput.getMemoryRendering().displayTable(); + } + } catch (DebugException e) { + DebugUIPlugin.log(e.getStatus()); + fInput.getMemoryRendering().displayError(e); + return lineCache.toArray(); + } + } + return lineCache.toArray(); + } + + /** + * @throws DebugException + */ + private void loadContentForSimpleMemoryBlock() throws DebugException { + // get as much memory as the memory block can handle + fInput.setPreBuffer(0); + fInput.setPostBuffer(0); + fInput.setDefaultBufferSize(0); + long startAddress = fInput.getMemoryBlock().getStartAddress(); + BigInteger address = BigInteger.valueOf(startAddress); + long length = fInput.getMemoryBlock().getLength(); + long numLines = length / fInput.getMemoryRendering().getBytesPerLine(); + getMemoryToFitTable(address, numLines, fInput.isUpdateDelta()); + } + + /** + * @throws DebugException + */ + private void loadContentForExtendedMemoryBlock() throws DebugException { + // calculate top buffered address + BigInteger address = fInput.getStartingAddress(); + if (address == null) + { + address = new BigInteger("0"); //$NON-NLS-1$ + } + BigInteger bigInt = address; + if (bigInt.compareTo(BigInteger.valueOf(32)) <= 0) { + fInput.setPreBuffer(0); + } else { + fInput.setPreBuffer(bigInt.divide(BigInteger.valueOf(32)).min(BigInteger.valueOf(fInput.getDefaultBufferSize())).intValue()); + } + int addressibleUnit = fInput.getMemoryRendering().getAddressibleUnitPerLine(); + address = bigInt.subtract(BigInteger.valueOf(addressibleUnit*fInput.getPostBuffer())); + + if (address.compareTo(BigInteger.valueOf(0)) < 0) + address = BigInteger.valueOf(0); + + int numLines = fInput.getNumVisibleLines()+fInput.getPostBuffer()+fInput.getPostBuffer(); + + // get stoarage to fit the memory view tab size + getMemoryToFitTable(address, numLines, fInput.isUpdateDelta()); + } + + /** + * @return the memroy block + */ + public IMemoryBlock getMemoryBlock() { + return fInput.getMemoryBlock(); + } + + /** + * Get memory to fit table + * @param startingAddress + * @param numberOfLines + * @param updateDelta + * @throws DebugException + */ + public void getMemoryToFitTable(BigInteger startingAddress, long numberOfLines, boolean updateDelta) throws DebugException + { + // do not ask for memory from memory block if the debug target + // is already terminated + IDebugTarget target = fInput.getMemoryBlock().getDebugTarget(); + + if (target.isDisconnected() || target.isTerminated()) + return; + + boolean error = false; + DebugException dbgEvt = null; + + // calculate address size + String adjustedAddress = startingAddress.toString(16); + + int addressSize = getAddressSize(startingAddress); + + int addressLength = addressSize * IInternalDebugUIConstants.CHAR_PER_BYTE; + + // align starting address with double word boundary + if (fInput.getMemoryBlock() instanceof IMemoryBlockExtension) + { + if (!adjustedAddress.endsWith("0")) //$NON-NLS-1$ + { + adjustedAddress = adjustedAddress.substring(0, adjustedAddress.length() - 1); + adjustedAddress += "0"; //$NON-NLS-1$ + startingAddress = new BigInteger(adjustedAddress, 16); + } + } + + IMemoryBlockExtension extMemoryBlock = null; + MemoryByte[] memoryBuffer = null; + + String paddedString = DebugUIPlugin.getDefault().getPreferenceStore().getString(IDebugPreferenceConstants.PREF_PADDED_STR); + + long reqNumBytes = 0; + try + { + if (fInput.getMemoryBlock() instanceof IMemoryBlockExtension) + { + reqNumBytes = fInput.getMemoryRendering().getBytesPerLine() * numberOfLines; + // get memory from memory block + extMemoryBlock = (IMemoryBlockExtension) fInput.getMemoryBlock(); + + long reqNumberOfUnits = fInput.getMemoryRendering().getAddressibleUnitPerLine() * numberOfLines; + + memoryBuffer = extMemoryBlock.getBytesFromAddress(startingAddress, reqNumberOfUnits); + + if(memoryBuffer == null) + { + DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.getString(UNABLE_TO_RETRIEVE_CONTENT), null)); + throw e; + } + } + else + { + // get memory from memory block + byte[] memory = fInput.getMemoryBlock().getBytes(); + + if (memory == null) + { + DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.getString(UNABLE_TO_RETRIEVE_CONTENT), null)); + throw e; + } + + int prefillNumBytes = 0; + + // number of bytes need to prefill + if (!startingAddress.toString(16).endsWith("0")) //$NON-NLS-1$ + { + adjustedAddress = startingAddress.toString(16).substring(0, adjustedAddress.length() - 1); + adjustedAddress += "0"; //$NON-NLS-1$ + BigInteger adjustedStart = new BigInteger(adjustedAddress, 16); + prefillNumBytes = startingAddress.subtract(adjustedStart).intValue(); + startingAddress = adjustedStart; + } + reqNumBytes = memory.length + prefillNumBytes; + + // figure out number of dummy bytes to append + while (reqNumBytes % fInput.getMemoryRendering().getBytesPerLine() != 0) + { + reqNumBytes ++; + } + + numberOfLines = reqNumBytes / fInput.getMemoryRendering().getBytesPerLine(); + + // create memory byte for IMemoryBlock + memoryBuffer = new MemoryByte[(int)reqNumBytes]; + + // prefill buffer to ensure double-word alignment + for (int i=0; i<prefillNumBytes; i++) + { + MemoryByte tmp = new MemoryByte(); + tmp.setValue((byte)0); + tmp.setReadonly(true); + tmp.setValid(false); + memoryBuffer[i] = tmp; + } + + // fill buffer with memory returned by debug adapter + int j = prefillNumBytes; // counter for memoryBuffer + for (int i=0; i<memory.length; i++) + { + MemoryByte tmp = new MemoryByte(); + tmp.setValue(memory[i]); + tmp.setValid(true); + memoryBuffer[j] = tmp; + j++; + } + + // append to buffer to fill up the entire line + for (int i=j; i<memoryBuffer.length; i++) + { + MemoryByte tmp = new MemoryByte(); + tmp.setValue((byte)0); + tmp.setReadonly(true); + tmp.setValid(false); + memoryBuffer[i] = tmp; + } + } + } + catch (DebugException e) + { + memoryBuffer = makeDummyContent(numberOfLines); + + // finish creating the content provider before throwing an event + error = true; + dbgEvt = e; + } + catch (Throwable e) + { + // catch all errors from this process just to be safe + memoryBuffer = makeDummyContent(numberOfLines); + + // finish creating the content provider before throwing an event + error = true; + dbgEvt = new DebugException(DebugUIPlugin.newErrorStatus(e.getMessage(), e)); + DebugUIPlugin.log(e); + } + + // if debug adapter did not return enough memory, create dummy memory + if (memoryBuffer.length < reqNumBytes) + { + ArrayList newBuffer = new ArrayList(); + + for (int i=0; i<memoryBuffer.length; i++) + { + newBuffer.add(memoryBuffer[i]); + } + + for (int i=memoryBuffer.length; i<reqNumBytes; i++) + { + byte value = 0; + byte flags = 0; + flags |= MemoryByte.READONLY; + newBuffer.add(new MemoryByte(value, flags)); + } + + memoryBuffer = (MemoryByte[])newBuffer.toArray(new MemoryByte[newBuffer.size()]); + + } + + // clear line cacheit' + if (!lineCache.isEmpty()) + { + lineCache.clear(); + } + String address = startingAddress.toString(16); + // save address of the top of buffer + fBufferTopAddress = startingAddress; + + boolean manageDelta = true; + + // If change information is not managed by the memory block + // The view tab will manage it and calculate delta information + // for its content cache. + if (fInput.getMemoryBlock() instanceof IMemoryBlockExtension) + { + manageDelta = !((IMemoryBlockExtension)fInput.getMemoryBlock()).supportsChangeManagement(); + } + + // put memory information into MemoryViewLine + for (int i = 0; i < numberOfLines; i++) + { //chop the raw memory up + String tmpAddress = address.toUpperCase(); + if (tmpAddress.length() < addressLength) + { + for (int j = 0; tmpAddress.length() < addressLength; j++) + { + tmpAddress = "0" + tmpAddress; //$NON-NLS-1$ + } + } + MemoryByte[] memory = new MemoryByte[fInput.getMemoryRendering().getBytesPerLine()]; + boolean isMonitored = true; + + // counter for memory, starts from 0 to number of bytes per line + int k = 0; + // j is the counter for memArray, memory returned by debug adapter + for (int j = i * fInput.getMemoryRendering().getBytesPerLine(); + j < i * fInput.getMemoryRendering().getBytesPerLine() + fInput.getMemoryRendering().getBytesPerLine(); + j++) + { + + byte changeFlag = memoryBuffer[j].getFlags(); + if (manageDelta) + { + // turn off both change and known bits to make sure that + // the change bits returned by debug adapters do not take + // any effect + + changeFlag |= MemoryByte.KNOWN; + changeFlag ^= MemoryByte.KNOWN; + + changeFlag |= MemoryByte.CHANGED; + changeFlag ^= MemoryByte.CHANGED; + } + + MemoryByte newByteObj = new MemoryByte(memoryBuffer[j].getValue(), changeFlag); + memory[k] = newByteObj; + k++; + + + if (!manageDelta) + { + // If the byte is marked as unknown, the line is not monitored + if (!memoryBuffer[j].isKnown()) + { + isMonitored = false; + } + } + } + + TableRenderingLine newLine = new TableRenderingLine(tmpAddress, memory, lineCache.size(), paddedString); + + TableRenderingLine oldLine = (TableRenderingLine)contentCache.get(newLine.getAddress()); + + if (manageDelta) + { + if (oldLine != null) + newLine.isMonitored = true; + else + newLine.isMonitored = false; + } + else + { + // check the byte for information + newLine.isMonitored = isMonitored; + } + + // calculate delta info for the memory view line + if (manageDelta && !fInput.getMemoryRendering().isDisplayingError()) + { + if (updateDelta) + { + if (oldLine != null) + { + newLine.markDeltas(oldLine); + } + } + else + { + if (oldLine != null) + { + // deltas can only be reused if the line has not been changed + // otherwise, force a refresh + if (newLine.isLineChanged(oldLine)) + { + newLine.markDeltas(oldLine); + } + else + { + newLine.copyDeltas(oldLine); + } + } + } + } + else if (manageDelta && fInput.getMemoryRendering().isDisplayingError()) + { + // show as unmonitored if the view tab is previoulsy displaying error + newLine.isMonitored = false; + } + lineCache.add(newLine); + + // increment row address + BigInteger bigInt = new BigInteger(address, 16); + int addressibleUnit = fInput.getMemoryRendering().getBytesPerLine()/fInput.getMemoryRendering().getAddressibleSize(); + address = bigInt.add(BigInteger.valueOf(addressibleUnit)).toString(16); + } + + if (error){ + throw dbgEvt; + } + } + + /** + * @param numberOfLines + * @return an array of dummy MemoryByte + */ + private MemoryByte[] makeDummyContent(long numberOfLines) { + MemoryByte[] memoryBuffer; + // make up dummy memory, needed for recovery in case the debug adapter + // is capable of retrieving memory again + + int numBytes = (int)(fInput.getMemoryRendering().getBytesPerLine() * numberOfLines); + memoryBuffer = new MemoryByte[numBytes]; + + for (int i=0; i<memoryBuffer.length; i++){ + memoryBuffer[i] = new MemoryByte(); + memoryBuffer[i].setValue((byte)0); + memoryBuffer[i].setReadonly(true); + } + return memoryBuffer; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.internal.views.BasicDebugViewContentProvider#doHandleDebugEvent(org.eclipse.debug.core.DebugEvent) + */ + protected void doHandleDebugEvent(DebugEvent event) { + + // do nothing if the debug event did not come from a debug element comes from non-debug element + if (!(event.getSource() instanceof IDebugElement)) + return; + + IDebugElement src = (IDebugElement)event.getSource(); + + // if a debug event happens from the memory block + // invoke contentChanged to get content of the memory block updated + if (event.getKind() == DebugEvent.CHANGE && event.getSource() == fInput.getMemoryBlock()) + { + if (event.getDetail() == DebugEvent.STATE){ + fInput.getMemoryRendering().updateLabels(); + } + else + { + updateContent(); + } + } + + // if the suspend evnet happens from the debug target that the blocked + // memory block belongs to + if (event.getKind() == DebugEvent.SUSPEND && src.getDebugTarget() == fInput.getMemoryBlock().getDebugTarget()) + { + updateContent(); + } + + } + + /** + * Update content of the view tab if the content of the memory block has changed + * or if its base address has changed + * Update will not be performed if the memory block has not been changed or + * if the view tab is disabled. + */ + public void updateContent() + { + IDebugTarget dt = fInput.getMemoryBlock().getDebugTarget(); + + // no need to update if debug target is disconnected or terminated + if (dt.isDisconnected() || dt.isTerminated()) + { + return; + } + + // cache content before getting new ones + TableRenderingLine[] lines =(TableRenderingLine[]) lineCache.toArray(new TableRenderingLine[lineCache.size()]); + if (contentCache != null) + { + contentCache.clear(); + } + + //do not handle event if the rendering is not visible + if (!fInput.getMemoryRendering().isVisible()) + return; + + // use existing lines as cache is the rendering is not currently displaying + // error. Otherwise, leave contentCache empty as we do not have updated + // content. + if (!fInput.getMemoryRendering().isDisplayingError()) + { + for (int i=0; i<lines.length; i++) + { + contentCache.put(lines[i].getAddress(), lines[i]); + lines[i].isMonitored = true; + } + } + + // reset all the deltas currently stored in contentCache + // This will ensure that changes will be recomputed when user scrolls + // up or down the memory view. + resetDeltas(); + fInput.getMemoryRendering().refresh(); + + } + + /** + * @return buffer's top address + */ + public BigInteger getBufferTopAddress() + { + return fBufferTopAddress; + } + + /** + * Calculate address size of the given address + * @param address + * @return size of address from the debuggee + */ + public int getAddressSize(BigInteger address) + { + // calculate address size + String adjustedAddress = address.toString(16); + + int addressSize = 0; + if (fInput.getMemoryBlock() instanceof IMemoryBlockExtension) + { + addressSize = ((IMemoryBlockExtension)fInput.getMemoryBlock()).getAddressSize(); + } + + // handle IMemoryBlock and invalid address size returned by IMemoryBlockExtension + if (addressSize <= 0) + { + if (adjustedAddress.length() > 8) + { + addressSize = 8; + } + else + { + addressSize = 4; + } + } + + return addressSize; + } + + /** + * @return base address of memory block + */ + public BigInteger getContentBaseAddress() + { + return fInput.getContentBaseAddress(); + } + + /** + * Clear all delta information in the lines + */ + public void resetDeltas() + { + Enumeration enumeration = contentCache.elements(); + + while (enumeration.hasMoreElements()) + { + TableRenderingLine line = (TableRenderingLine)enumeration.nextElement(); + line.unmarkDeltas(); + } + } + + /** + * Check if address is out of buffered range + * @param address + * @return true if address is out of bufferred range, false otherwise + */ + public boolean isAddressOutOfRange(BigInteger address) + { + if (lineCache != null) + { + TableRenderingLine first = (TableRenderingLine)lineCache.firstElement(); + TableRenderingLine last = (TableRenderingLine) lineCache.lastElement(); + + if (first == null ||last == null) + return true; + + BigInteger startAddress = new BigInteger(first.getAddress(), 16); + BigInteger lastAddress = new BigInteger(last.getAddress(), 16); + int addressibleUnit = fInput.getMemoryRendering().getAddressibleUnitPerLine(); + lastAddress = lastAddress.add(BigInteger.valueOf(addressibleUnit)); + + if (startAddress.compareTo(address) <= 0 && + lastAddress.compareTo(address) >= 0) + { + return false; + } + return true; + } + return true; + } + + public void clearContentCache() + { + contentCache.clear(); + } +} |