blob: a85116f33185adc15fe9ed9095ae27d26c4487fa [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 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.elements.adapters;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Vector;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugException;
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.memory.provisional.AbstractAsyncTableRendering;
import org.eclipse.debug.internal.ui.memory.provisional.MemoryViewPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.provisional.AsynchronousContentAdapter;
import org.eclipse.debug.internal.ui.views.memory.MemoryViewUtil;
import org.eclipse.debug.internal.ui.views.memory.renderings.MemorySegment;
import org.eclipse.debug.internal.ui.views.memory.renderings.TableRenderingContentDescriptor;
import org.eclipse.debug.ui.memory.IMemoryRendering;
public class MemoryBlockContentAdapter extends AsynchronousContentAdapter {
// 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;
public MemoryBlockContentAdapter()
{
contentCache = new Hashtable();
}
protected Object[] getChildren(Object parent, IPresentationContext context)
throws CoreException {
if (!(parent instanceof IMemoryBlock))
return new Object[0];
if (!(context instanceof MemoryViewPresentationContext))
return new Object[0];
MemoryViewPresentationContext memoryViewContext = (MemoryViewPresentationContext)context;
IMemoryRendering rendering = memoryViewContext.getRendering();
if (!(rendering instanceof AbstractAsyncTableRendering))
return new Object[0];
try
{
return getMemoryFromMemoryBlock(memoryViewContext);
} catch (DebugException e) {
throw e;
}
}
protected boolean hasChildren(Object element, IPresentationContext context)
throws CoreException {
if (context instanceof MemoryViewPresentationContext)
{
if (((MemoryViewPresentationContext)context).getRendering() != null)
return true;
}
return false;
}
protected boolean supportsPartId(String id) {
return true;
}
private Object[] getMemoryFromMemoryBlock(MemoryViewPresentationContext context) throws DebugException {
IMemoryBlock memoryBlock = context.getRendering().getMemoryBlock();
if (memoryBlock instanceof IMemoryBlockExtension)
{
return loadContentForExtendedMemoryBlock(context);
}
return loadContentForSimpleMemoryBlock(context);
}
/**
* @throws DebugException
*/
public Object[] loadContentForSimpleMemoryBlock(MemoryViewPresentationContext context) throws DebugException {
AbstractAsyncTableRendering rendering = getTableRendering(context);
if (rendering != null)
{
IMemoryBlock memoryBlock = rendering.getMemoryBlock();
long startAddress = memoryBlock.getStartAddress();
BigInteger address = BigInteger.valueOf(startAddress);
long length = memoryBlock.getLength();
long numLines = length / rendering.getBytesPerLine();
return getMemoryToFitTable(address, numLines, context);
}
return EMPTY;
}
/**
* @throws DebugException
*/
public Object[] loadContentForExtendedMemoryBlock(MemoryViewPresentationContext context) throws DebugException {
AbstractAsyncTableRendering rendering = getTableRendering(context);
if (rendering != null)
{
TableRenderingContentDescriptor descriptor = (TableRenderingContentDescriptor)rendering.getAdapter(TableRenderingContentDescriptor.class);
if (descriptor == null)
return new Object[0];
if (descriptor.getNumLines() <= 0)
return new Object[0];
// calculate top buffered address
BigInteger loadAddress = descriptor.getLoadAddress();
if (loadAddress == null)
{
loadAddress = new BigInteger("0"); //$NON-NLS-1$
}
BigInteger mbStart = descriptor.getStartAddress();
BigInteger mbEnd = descriptor.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)descriptor.getMemoryBlock()).getBigBaseAddress();
descriptor.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 = rendering.getAddressableUnitPerLine();
BigInteger bufferStart = loadAddress.subtract(BigInteger.valueOf(descriptor.getPreBuffer()*addressableUnitsPerLine));
BigInteger bufferEnd = loadAddress.add(BigInteger.valueOf(descriptor.getPostBuffer()*addressableUnitsPerLine));
bufferEnd = bufferEnd.add(BigInteger.valueOf(descriptor.getNumLines()*addressableUnitsPerLine));
// TODO: should rely on input to tell us what to load
// instead of having the content adapter override the setting
if (descriptor.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 < descriptor.getNumLines())
{
// re-calculate buffer start since we may not have enough lines to popoulate the view
bufferStart = bufferEnd.subtract(BigInteger.valueOf(descriptor.getNumLines()*addressableUnitsPerLine));
bufferStart = bufferStart.subtract(BigInteger.valueOf(descriptor.getPreBuffer()*addressableUnitsPerLine));
}
}
// 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
return getMemoryToFitTable(bufferStart, numLines,context);
}
else
{
if (bufferStart.compareTo(mbStart) < 0)
bufferStart = mbStart;
if (bufferEnd.compareTo(mbEnd) > 0)
{
bufferStart = mbEnd.subtract(BigInteger.valueOf((descriptor.getNumLines()-1)*addressableUnitsPerLine));
}
// buffer end must be greater than buffer start
if (bufferEnd.compareTo(bufferStart) <= 0)
throw new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.TableRenderingContentProvider_2, null));
int numLines = descriptor.getNumLines();
// get stoarage to fit the memory view tab size
return getMemoryToFitTable(bufferStart, numLines, context);
}
}
return EMPTY;
}
/**
* Get memory to fit table
* @param startingAddress
* @param numberOfLines
* @param updateDelta
* @throws DebugException
*/
public Object[] getMemoryToFitTable(BigInteger startAddress, long numberOfLines, MemoryViewPresentationContext context) throws DebugException
{
AbstractAsyncTableRendering tableRendering = getTableRendering(context);
if (tableRendering == null)
{
DebugException e = new DebugException(DebugUIPlugin.newErrorStatus(DebugUIMessages.MemoryViewContentProvider_Unable_to_retrieve_content, null));
throw e;
}
TableRenderingContentDescriptor descriptor = (TableRenderingContentDescriptor)tableRendering.getAdapter(TableRenderingContentDescriptor.class);
if(descriptor == null)
return new Object[0];
// do not ask for memory from memory block if the debug target
// is already terminated
IDebugTarget target = descriptor.getMemoryBlock().getDebugTarget();
if (target.isDisconnected() || target.isTerminated())
return new Object[0];
boolean error = false;
DebugException dbgEvt = null;
String adjustedAddress = startAddress.toString(16);
// align to the closest boundary based on addressable size per line
if (descriptor.isAlignAddressToBoundary() && descriptor.getMemoryBlock() instanceof IMemoryBlockExtension)
{
startAddress = MemoryViewUtil.alignToBoundary(startAddress, tableRendering.getAddressableUnitPerLine());
}
IMemoryBlockExtension extMemoryBlock = null;
MemoryByte[] memoryBuffer = null;
long reqNumBytes = 0;
try
{
if (descriptor.getMemoryBlock() instanceof IMemoryBlockExtension)
{
reqNumBytes = tableRendering.getBytesPerLine() * numberOfLines;
// get memory from memory block
extMemoryBlock = (IMemoryBlockExtension) descriptor.getMemoryBlock();
long reqNumberOfUnits = tableRendering.getAddressableUnitPerLine() * numberOfLines;
memoryBuffer = extMemoryBlock.getBytesFromAddress(startAddress, 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 = descriptor.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 (!startAddress.toString(16).endsWith("0")) //$NON-NLS-1$
{
adjustedAddress = startAddress.toString(16).substring(0, adjustedAddress.length() - 1);
adjustedAddress += "0"; //$NON-NLS-1$
BigInteger adjustedStart = new BigInteger(adjustedAddress, 16);
prefillNumBytes = startAddress.subtract(adjustedStart).intValue();
startAddress = adjustedStart;
}
reqNumBytes = descriptor.getMemoryBlock().getLength() + prefillNumBytes;
// figure out number of dummy bytes to append
while (reqNumBytes % tableRendering.getBytesPerLine() != 0)
{
reqNumBytes ++;
}
numberOfLines = reqNumBytes / tableRendering.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, tableRendering.getBytesPerLine());
// 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, tableRendering.getBytesPerLine());
// 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()]);
}
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 (descriptor.getMemoryBlock() instanceof IMemoryBlockExtension)
{
manageDelta = !((IMemoryBlockExtension)descriptor.getMemoryBlock()).supportsChangeManagement();
}
if (error){
throw dbgEvt;
}
// put memory information into MemoryViewLine
return organizeLines(numberOfLines, memoryBuffer, startAddress, manageDelta, context);
}
private Object[] organizeLines(long numberOfLines, MemoryByte[] memoryBuffer, BigInteger address, boolean manageDelta, MemoryViewPresentationContext context)
{
Vector lineCache = new Vector();
IMemoryRendering rendering = context.getRendering();
if (!(rendering instanceof AbstractAsyncTableRendering))
return lineCache.toArray();
AbstractAsyncTableRendering tableRendering = (AbstractAsyncTableRendering)rendering;
int addressableUnit = tableRendering.getBytesPerLine()/tableRendering.getAddressableSize();
for (int i = 0; i < numberOfLines; i++)
{
int bytesPerLine = tableRendering.getBytesPerLine();
MemoryByte[] memory = new MemoryByte[bytesPerLine];
// 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++;
}
MemorySegment newLine = new MemorySegment(address, memory, addressableUnit);
lineCache.add(newLine);
address = address.add(BigInteger.valueOf(addressableUnit));
}
return lineCache.toArray();
}
/**
* @param numberOfLines
* @return an array of dummy MemoryByte
*/
private MemoryByte[] makeDummyContent(long numberOfLines, int bytesPerLine) {
MemoryByte[] memoryBuffer;
// make up dummy memory, needed for recovery in case the debug adapter
// is capable of retrieving memory again
int numBytes = (int)(bytesPerLine * 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;
}
protected AbstractAsyncTableRendering getTableRendering(MemoryViewPresentationContext context)
{
IMemoryRendering memRendering = context.getRendering();
if (memRendering != null && memRendering instanceof AbstractAsyncTableRendering)
{
return (AbstractAsyncTableRendering)memRendering;
}
return null;
}
}