/******************************************************************************* * Copyright (c) 2004, 2007 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 for MemoryViewTab * * @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)); } } // 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)); } // 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(); // 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 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