| /******************************************************************************* |
| * Copyright (c) 2004, 2008 IBM Corporation 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: |
| * 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.memory.IMemoryRenderingUpdater; |
| import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; |
| import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil; |
| import org.eclipse.debug.ui.DebugUITools; |
| import org.eclipse.debug.ui.IDebugUIConstants; |
| import org.eclipse.debug.ui.memory.AbstractTableRendering; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| |
| /** |
| * Content Provider used by AbstractTableRendering |
| * |
| * @since 3.0 |
| */ |
| public class TableRenderingContentProvider extends BasicDebugViewContentProvider { |
| |
| // lines currently being displayed by the table rendering |
| protected Vector lineCache; |
| |
| // Cache to allow the content provider to comppute change information |
| // Cache is taken by copying the lineCache after a suspend event |
| // or change event from the the memory block. |
| protected Hashtable contentCache; |
| |
| // cache in the form of MemoryByte |
| // needed for reorganizing cache when the row size changes |
| private MemoryByte[] fContentCacheInBytes; |
| private String fContentCacheStartAddress; |
| |
| private BigInteger fBufferTopAddress; |
| |
| private TableRenderingContentInput fInput; |
| private BigInteger fBufferEndAddress; |
| |
| private boolean fDynamicLoad; |
| |
| /** |
| * @param memoryBlock |
| * @param newTab |
| */ |
| public TableRenderingContentProvider() |
| { |
| lineCache = new Vector(); |
| contentCache = new Hashtable(); |
| initializeDynamicLoad(); |
| |
| 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 |
| getTableRendering(fInput).displayTable(); |
| } |
| } catch (DebugException e) { |
| getTableRendering(fInput).displayError(e); |
| } |
| } |
| |
| public void dispose() { |
| 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 { |
| getMemoryFromMemoryBlock(); |
| } catch (DebugException e) { |
| DebugUIPlugin.log(e.getStatus()); |
| getTableRendering(fInput).displayError(e); |
| return lineCache.toArray(); |
| } |
| } |
| |
| if (lineCache.isEmpty()) |
| return lineCache.toArray(); |
| |
| // check to see if the row size has changed |
| TableRenderingLine line = (TableRenderingLine)lineCache.get(0); |
| int currentRowSize = line.getByteArray().length; |
| int renderingRowSize = getTableRendering(fInput).getBytesPerLine(); |
| |
| if (renderingRowSize != currentRowSize) |
| { |
| try { |
| reorganizeContentCache(renderingRowSize); |
| reorganizeLines(lineCache, renderingRowSize); |
| } catch (DebugException e) { |
| DebugUIPlugin.log(e.getStatus()); |
| getTableRendering(fInput).displayError(e); |
| return lineCache.toArray(); |
| } |
| } |
| return lineCache.toArray(); |
| } |
| |
| private void getMemoryFromMemoryBlock() throws DebugException { |
| IMemoryBlock memoryBlock = fInput.getMemoryBlock(); |
| if (memoryBlock instanceof IMemoryBlockExtension) |
| { |
| loadContentForExtendedMemoryBlock(); |
| getTableRendering(fInput).displayTable(); |
| } |
| else |
| { |
| loadContentForSimpleMemoryBlock(); |
| getTableRendering(fInput).displayTable(); |
| } |
| } |
| |
| /** |
| * @throws DebugException |
| */ |
| public void loadContentForSimpleMemoryBlock() throws DebugException { |
| // get as much memory as the memory block can handle |
| fInput.setPreBuffer(0); |
| fInput.setPostBuffer(0); |
| long startAddress = fInput.getMemoryBlock().getStartAddress(); |
| BigInteger address = BigInteger.valueOf(startAddress); |
| long length = fInput.getMemoryBlock().getLength(); |
| long numLines = length / getTableRendering(fInput).getBytesPerLine(); |
| getMemoryToFitTable(address, numLines, fInput.isUpdateDelta()); |
| } |
| |
| /** |
| * @throws DebugException |
| */ |
| public void loadContentForExtendedMemoryBlock() throws DebugException { |
| |
| // do not load if number of lines needed is < 0 |
| if (fInput.getNumLines() <= 0) |
| return; |
| |
| // calculate top buffered address |
| BigInteger loadAddress = fInput.getLoadAddress(); |
| if (loadAddress == null) |
| { |
| loadAddress = new BigInteger("0"); //$NON-NLS-1$ |
| } |
| |
| BigInteger mbStart = fInput.getStartAddress(); |
| BigInteger mbEnd = fInput.getEndAddress(); |
| |
| // check that the load address is within range |
| if (loadAddress.compareTo(mbStart) < 0 || loadAddress.compareTo(mbEnd) > 0) |
| { |
| // default load address to memory block base address |
| loadAddress = ((IMemoryBlockExtension)getMemoryBlock()).getBigBaseAddress(); |
| fInput.setLoadAddress(loadAddress); |
| } |
| |
| // if address is still out of range, throw an exception |
| if (loadAddress.compareTo(mbStart) < 0 || loadAddress.compareTo(mbEnd) > 0) |
| { |
| throw new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.TableRenderingContentProvider_0 + loadAddress.toString(16), null)); |
| } |
| |
| int addressableUnitsPerLine = getTableRendering(fInput).getAddressableUnitPerLine(); |
| BigInteger bufferStart = loadAddress.subtract(BigInteger.valueOf(fInput.getPreBuffer()*addressableUnitsPerLine)); |
| BigInteger bufferEnd = loadAddress.add(BigInteger.valueOf(fInput.getPostBuffer()*addressableUnitsPerLine)); |
| bufferEnd = bufferEnd.add(BigInteger.valueOf(fInput.getNumLines()*addressableUnitsPerLine)); |
| |
| if (isDynamicLoad()) |
| { |
| if (bufferStart.compareTo(mbStart) < 0) |
| bufferStart = mbStart; |
| |
| if (bufferEnd.compareTo(mbEnd) > 0) |
| { |
| bufferEnd = mbEnd; |
| |
| int numLines = bufferEnd.subtract(bufferStart).divide(BigInteger.valueOf(addressableUnitsPerLine)).intValue(); |
| if (numLines < fInput.getNumLines()) |
| { |
| // re-calculate buffer start since we may not have enough lines to popoulate the view |
| bufferStart = bufferEnd.subtract(BigInteger.valueOf(fInput.getNumLines()*addressableUnitsPerLine)); |
| bufferStart = bufferStart.subtract(BigInteger.valueOf(fInput.getPreBuffer()*addressableUnitsPerLine)); |
| } |
| |
| // if after adjusting buffer start, it goes before the memory block start |
| // address, adjust it back |
| if (bufferStart.compareTo(mbStart) < 0) |
| bufferStart = mbStart; |
| } |
| |
| // buffer end must be greater than buffer start |
| if (bufferEnd.compareTo(bufferStart) <= 0) |
| throw new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.TableRenderingContentProvider_1, null)); |
| |
| int numLines = bufferEnd.subtract(bufferStart).divide(BigInteger.valueOf(addressableUnitsPerLine)).intValue()+1; |
| // get stoarage to fit the memory view tab size |
| getMemoryToFitTable(bufferStart, numLines, fInput.isUpdateDelta()); |
| } |
| else |
| { |
| if (bufferStart.compareTo(mbStart) < 0) |
| bufferStart = mbStart; |
| |
| if (bufferEnd.compareTo(mbEnd) > 0) |
| { |
| bufferStart = mbEnd.subtract(BigInteger.valueOf((fInput.getNumLines()-1)*addressableUnitsPerLine)); |
| bufferEnd = mbEnd; |
| |
| // after adjusting buffer start, check if it's smaller than memory block's start address |
| if (bufferStart.compareTo(mbStart) < 0) |
| bufferStart = mbStart; |
| } |
| |
| // buffer end must be greater than buffer start |
| if (bufferEnd.compareTo(bufferStart) <= 0) |
| throw new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.TableRenderingContentProvider_2, null)); |
| |
| int numLines = fInput.getNumLines(); |
| int bufferNumLines = bufferEnd.subtract(bufferStart).divide(BigInteger.valueOf(addressableUnitsPerLine)).intValue()+1; |
| |
| if (bufferNumLines < numLines) |
| numLines = bufferNumLines; |
| |
| // get stoarage to fit the memory view tab size |
| getMemoryToFitTable(bufferStart, 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; |
| try { |
| addressSize = getAddressSize(startingAddress); |
| } catch (DebugException e1) { |
| dbgEvt = e1; |
| error = true; |
| addressSize = 4; |
| } |
| |
| int addressLength = addressSize * IInternalDebugUIConstants.CHAR_PER_BYTE; |
| |
| // align to the closest boundary based on addressable size per line |
| if ( getMemoryBlock() instanceof IMemoryBlockExtension) |
| { |
| startingAddress = MemoryViewUtil.alignToBoundary(startingAddress, getTableRendering(fInput).getAddressableUnitPerLine()); |
| } |
| |
| IMemoryBlockExtension extMemoryBlock = null; |
| MemoryByte[] memoryBuffer = null; |
| |
| String paddedString = DebugUIPlugin.getDefault().getPreferenceStore().getString(IDebugUIConstants.PREF_PADDED_STR); |
| |
| long reqNumBytes = 0; |
| try |
| { |
| if (fInput.getMemoryBlock() instanceof IMemoryBlockExtension) |
| { |
| reqNumBytes = getTableRendering(fInput).getBytesPerLine() * numberOfLines; |
| // get memory from memory block |
| extMemoryBlock = (IMemoryBlockExtension) fInput.getMemoryBlock(); |
| |
| long reqNumberOfUnits = getTableRendering(fInput).getAddressableUnitPerLine() * numberOfLines; |
| |
| memoryBuffer = extMemoryBlock.getBytesFromAddress(startingAddress, reqNumberOfUnits); |
| |
| if(memoryBuffer == null) |
| { |
| DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.MemoryViewContentProvider_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.MemoryViewContentProvider_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 = fInput.getMemoryBlock().getLength() + prefillNumBytes; |
| |
| // figure out number of dummy bytes to append |
| while (reqNumBytes % getTableRendering(fInput).getBytesPerLine() != 0) |
| { |
| reqNumBytes ++; |
| } |
| |
| numberOfLines = reqNumBytes / getTableRendering(fInput).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.setWritable(false); |
| tmp.setReadable(false); |
| tmp.setEndianessKnown(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.setReadable(true); |
| tmp.setWritable(true); |
| tmp.setEndianessKnown(false); |
| 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.setWritable(false); |
| tmp.setReadable(false); |
| tmp.setEndianessKnown(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)); |
| } |
| |
| // 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++) |
| { |
| MemoryByte mb = new MemoryByte(); |
| mb.setReadable(false); |
| mb.setWritable(false); |
| mb.setEndianessKnown(false); |
| newBuffer.add(mb); |
| } |
| |
| memoryBuffer = (MemoryByte[])newBuffer.toArray(new MemoryByte[newBuffer.size()]); |
| |
| } |
| |
| // clear line cache |
| 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 |
| organizeLines(numberOfLines, updateDelta, addressLength, memoryBuffer, paddedString, address, manageDelta); |
| |
| if (error){ |
| throw dbgEvt; |
| } |
| } |
| |
| private void organizeLines(long numberOfLines, boolean updateDelta, int addressLength, MemoryByte[] memoryBuffer, String paddedString, String address, boolean manageDelta) |
| { |
| 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$ |
| } |
| } |
| int bytesPerLine = getTableRendering(fInput).getBytesPerLine(); |
| MemoryByte[] memory = new MemoryByte[bytesPerLine]; |
| 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 * bytesPerLine; |
| j < i * bytesPerLine + bytesPerLine; |
| 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.HISTORY_KNOWN; |
| changeFlag ^= MemoryByte.HISTORY_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].isHistoryKnown()) |
| { |
| 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 && !getTableRendering(fInput).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 && getTableRendering(fInput).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); |
| fBufferEndAddress = bigInt; |
| int addressableUnit = getTableRendering(fInput).getBytesPerLine()/getTableRendering(fInput).getAddressableSize(); |
| address = bigInt.add(BigInteger.valueOf(addressableUnit)).toString(16); |
| } |
| } |
| |
| /** |
| * @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)(getTableRendering(fInput).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].setWritable(false); |
| memoryBuffer[i].setReadable(false); |
| memoryBuffer[i].setEndianessKnown(false); |
| } |
| return memoryBuffer; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.ui.internal.views.BasicDebugViewContentProvider#doHandleDebugEvent(org.eclipse.debug.core.DebugEvent) |
| */ |
| protected void doHandleDebugEvent(DebugEvent event) { |
| |
| if (getTableRendering(fInput).isVisible()) |
| { |
| // only do this if it's visible |
| // still need to clear content cache if the rendering |
| // is not visible |
| if (isUpdateManagedByMB()) |
| return; |
| } |
| |
| // do nothing if the debug event did not come from a debug element comes from non-debug element |
| if (!(event.getSource() instanceof IDebugElement)) |
| return; |
| |
| // do not try to recover if the content input has not been created |
| if (fInput == null) |
| 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){ |
| getTableRendering(fInput).updateLabels(); |
| } |
| else |
| { |
| updateContent(); |
| } |
| } |
| |
| // if the suspend evnet happens from the debug target that the |
| // 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 rendering is not visible |
| */ |
| 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; |
| } |
| |
| takeContentSnapshot(); |
| |
| //do not handle event if the rendering is not visible |
| if (!getTableRendering(fInput).isVisible()) |
| return; |
| |
| getTableRendering(fInput).refresh(); |
| |
| } |
| |
| /** |
| * Take a snapshot on the content, marking the lines as monitored |
| */ |
| public void takeContentSnapshot() |
| { |
| // cache content before getting new ones |
| TableRenderingLine[] lines =(TableRenderingLine[]) lineCache.toArray(new TableRenderingLine[lineCache.size()]); |
| fContentCacheInBytes = convertLinesToBytes(lines); |
| fContentCacheStartAddress = lines[0].getAddress(); |
| |
| if (contentCache != null) |
| { |
| contentCache.clear(); |
| } |
| |
| //do not handle event if the rendering is not visible |
| if (!getTableRendering(fInput).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 (!getTableRendering(fInput).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(); |
| } |
| |
| /** |
| * @return buffer's top address |
| */ |
| public BigInteger getBufferTopAddress() |
| { |
| return fBufferTopAddress; |
| } |
| |
| public BigInteger getBufferEndAddress() |
| { |
| return fBufferEndAddress; |
| } |
| |
| /** |
| * Calculate address size of the given address |
| * @param address |
| * @return size of address from the debuggee |
| */ |
| public int getAddressSize(BigInteger address) throws DebugException |
| { |
| // 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 && !lineCache.isEmpty()) |
| { |
| 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 addressableUnit = getTableRendering(fInput).getAddressableUnitPerLine(); |
| lastAddress = lastAddress.add(BigInteger.valueOf(addressableUnit)).subtract(BigInteger.valueOf(1)); |
| |
| if (startAddress.compareTo(address) <= 0 && |
| lastAddress.compareTo(address) >= 0) |
| { |
| return false; |
| } |
| return true; |
| } |
| return true; |
| } |
| |
| public void clearContentCache() |
| { |
| fContentCacheInBytes = new MemoryByte[0]; |
| fContentCacheStartAddress = null; |
| contentCache.clear(); |
| } |
| |
| /** |
| * @return if the memory block would manage its own update. |
| */ |
| private boolean isUpdateManagedByMB() |
| { |
| IMemoryBlock memoryBlock = getMemoryBlock(); |
| |
| IMemoryRenderingUpdater managedMB = null; |
| if (memoryBlock instanceof IMemoryRenderingUpdater) |
| { |
| managedMB = (IMemoryRenderingUpdater)memoryBlock; |
| } |
| |
| if (managedMB == null) |
| managedMB = (IMemoryRenderingUpdater)memoryBlock.getAdapter(IMemoryRenderingUpdater.class); |
| |
| // do not handle event if if the memory block wants to do its |
| // own update |
| if (managedMB != null && managedMB.supportsManagedUpdate(getTableRendering(fInput))) |
| return true; |
| |
| return false; |
| } |
| |
| public boolean isDynamicLoad() |
| { |
| return fDynamicLoad; |
| } |
| |
| private void initializeDynamicLoad() |
| { |
| fDynamicLoad = DebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IDebugPreferenceConstants.PREF_DYNAMIC_LOAD_MEM); |
| } |
| |
| public void setDynamicLoad(boolean dynamicLoad) |
| { |
| fDynamicLoad = dynamicLoad; |
| } |
| |
| private void reorganizeLines(Vector lines, int numBytesPerLine) throws DebugException |
| { |
| if (lines == null || lines.isEmpty()) |
| return; |
| |
| Object[] objs = lines.toArray(); |
| |
| if (objs.length > 0) |
| { |
| TableRenderingLine[] renderingLines = (TableRenderingLine[])lines.toArray(new TableRenderingLine[lines.size()]); |
| MemoryByte[] buffer = convertLinesToBytes(renderingLines); |
| BigInteger lineAddress = new BigInteger(renderingLines[0].getAddress(), 16); |
| int numberOfLines = buffer.length / numBytesPerLine; |
| boolean updateDelta = false; |
| int addressLength = getAddressSize(lineAddress) * IInternalDebugUIConstants.CHAR_PER_BYTE; |
| MemoryByte[] memoryBuffer = buffer; |
| String address =renderingLines[0].getAddress(); |
| String paddedString = DebugUITools.getPreferenceStore().getString(IDebugUIConstants.PREF_PADDED_STR); |
| |
| // set to false to preserve information delta information |
| 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(); |
| } |
| lineCache.clear(); |
| |
| organizeLines(numberOfLines, updateDelta, addressLength, memoryBuffer, paddedString, address, manageDelta); |
| } |
| } |
| |
| private void reorganizeContentCache(int bytesPerLine) |
| { |
| // if content cache is empty, do nothing |
| if (contentCache == null || contentCache.isEmpty() |
| || fContentCacheInBytes.length == 0 || fContentCacheStartAddress == null) |
| return; |
| |
| MemoryByte[] bytes = fContentCacheInBytes; |
| TableRenderingLine[] convertedLines = convertBytesToLines(bytes, bytesPerLine, new BigInteger(fContentCacheStartAddress, 16)); |
| |
| contentCache.clear(); |
| for (int i=0; i<convertedLines.length; i++) |
| { |
| contentCache.put(convertedLines[i].getAddress(), convertedLines[i]); |
| } |
| } |
| |
| private MemoryByte[] convertLinesToBytes(TableRenderingLine[] lines) |
| { |
| // convert the lines back to a buffer of MemoryByte |
| TableRenderingLine temp = lines[0]; |
| int lineLength = temp.getLength(); |
| |
| MemoryByte[] buffer = new MemoryByte[lines.length * lineLength]; |
| for (int i=0; i<lines.length; i++) |
| { |
| TableRenderingLine line = lines[i]; |
| MemoryByte[] bytes = line.getBytes(); |
| System.arraycopy(bytes, 0, buffer, i*lineLength, lineLength); |
| } |
| return buffer; |
| } |
| |
| private TableRenderingLine[] convertBytesToLines(MemoryByte[] bytes, int bytesPerLine, BigInteger startAddress) |
| { |
| int numOfLines = bytes.length / bytesPerLine; |
| String address = startAddress.toString(16); |
| int addressLength; |
| try { |
| addressLength = getAddressSize(startAddress) * IInternalDebugUIConstants.CHAR_PER_BYTE; |
| } catch (DebugException e) { |
| DebugUIPlugin.log(e); |
| addressLength = 4 * IInternalDebugUIConstants.CHAR_PER_BYTE; |
| } |
| ArrayList lines = new ArrayList(); |
| String paddedString = DebugUITools.getPreferenceStore().getString(IDebugUIConstants.PREF_PADDED_STR); |
| |
| for (int i=0; i<numOfLines; i++) |
| { |
| MemoryByte[] temp = new MemoryByte[bytesPerLine]; |
| System.arraycopy(bytes, i*bytesPerLine, temp, 0, bytesPerLine); |
| |
| String tmpAddress = address.toUpperCase(); |
| if (tmpAddress.length() < addressLength) |
| { |
| for (int j = 0; tmpAddress.length() < addressLength; j++) |
| { |
| tmpAddress = "0" + tmpAddress; //$NON-NLS-1$ |
| } |
| } |
| |
| TableRenderingLine newLine = new TableRenderingLine(tmpAddress, temp, lines.size(), paddedString); |
| lines.add(newLine); |
| |
| // increment row address |
| BigInteger bigInt = new BigInteger(address, 16); |
| fBufferEndAddress = bigInt; |
| int addressableUnit = getTableRendering(fInput).getBytesPerLine()/getTableRendering(fInput).getAddressableSize(); |
| address = bigInt.add(BigInteger.valueOf(addressableUnit)).toString(16); |
| } |
| |
| return (TableRenderingLine[])lines.toArray(new TableRenderingLine[lines.size()]); |
| } |
| |
| private AbstractTableRendering getTableRendering(TableRenderingContentInput input) |
| { |
| return (AbstractTableRendering)input.getAdapter(AbstractTableRendering.class); |
| } |
| } |