| /******************************************************************************* |
| * Copyright (c) 2006, 2009 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 |
| * WindRiver - Bug 192028 [Memory View] Memory view does not |
| * display memory blocks that do not reference IDebugTarget |
| *******************************************************************************/ |
| |
| 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)); |
| |
| // 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 |
| 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)); |
| 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 = descriptor.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 |
| 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(); |
| |
| // check for null target to not calculate and retrieve memory for standard debug model |
| if (target != null && (target.isDisconnected() || target.isTerminated())) |
| return new Object[0]; |
| |
| 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 |
| 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 |
| 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 (dbgEvt != null){ |
| 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; |
| } |
| } |