/******************************************************************************* * Copyright (c) 2004, 2010 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.debug.internal.core; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.core.IAddressFactory; import org.eclipse.cdt.core.IAddressFactory2; import org.eclipse.cdt.debug.core.CDebugCorePlugin; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.cdt.debug.core.cdi.CDIException; import org.eclipse.cdt.debug.core.cdi.model.ICDIExpression; import org.eclipse.cdt.debug.core.cdi.model.ICDIMemorySpaceEncoder; import org.eclipse.cdt.debug.core.cdi.model.ICDIMemorySpaceManagement; import org.eclipse.cdt.debug.core.cdi.model.ICDITarget; import org.eclipse.cdt.debug.core.model.ICType; import org.eclipse.cdt.debug.core.model.ICValue; import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlock; import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval; import org.eclipse.cdt.debug.internal.core.model.CDebugTarget; import org.eclipse.cdt.debug.internal.core.model.CExpression; import org.eclipse.cdt.debug.internal.core.model.CMemoryBlockExtension; import org.eclipse.cdt.debug.internal.core.model.CStackFrame; import org.eclipse.cdt.debug.internal.core.model.CThread; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; 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.IStackFrame; import org.eclipse.debug.core.model.IValue; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.ibm.icu.text.MessageFormat; /** * Implements the memory retrieval features based on the CDI model. */ public class CMemoryBlockRetrievalExtension extends PlatformObject implements IMemorySpaceAwareMemoryBlockRetrieval { private static final String MEMORY_BLOCK_EXPRESSION_LIST = "memoryBlockExpressionList"; //$NON-NLS-1$ private static final String MEMORY_BLOCK_EXPRESSION_ITEM = "memoryBlockExpressionItem"; //$NON-NLS-1$ private static final String MEMORY_BLOCK_EXPRESSION = "expression"; //$NON-NLS-1$ private static final String MEMORY_BLOCK_MEMSPACEID = "memorySpaceID"; //$NON-NLS-1$ private static final String ATTR_MEMORY_BLOCK_MEMSPACEID_TEXT = "text"; //$NON-NLS-1$ private static final String ATTR_MEMORY_BLOCK_EXPRESSION_TEXT = "text"; //$NON-NLS-1$ CDebugTarget fDebugTarget; /** * Constructor for CMemoryBlockRetrievalExtension. */ public CMemoryBlockRetrievalExtension( CDebugTarget debugTarget ) { fDebugTarget = debugTarget; } protected CDebugTarget getDebugTarget() { return fDebugTarget; } public void initialize() { ILaunchConfiguration config = getDebugTarget().getLaunch().getLaunchConfiguration(); try { String memento = config.getAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_MEMORY_BLOCKS, "" ); //$NON-NLS-1$ if ( memento != null && memento.trim().length() != 0 ) initializeFromMemento( memento ); } catch( CoreException e ) { CDebugCorePlugin.log( e ); } } private void parseMementoExprItem(Element element, List<String> expressions, List<String> memorySpaceIDs) { NodeList list = element.getChildNodes(); int length = list.getLength(); String exp = null; String memorySpaceID = null; for( int i = 0; i < length; ++i ) { Node node = list.item( i ); if ( node.getNodeType() == Node.ELEMENT_NODE ) { Element entry = (Element)node; if ( entry.getNodeName().equalsIgnoreCase( MEMORY_BLOCK_EXPRESSION ) ) { exp = entry.getAttribute( ATTR_MEMORY_BLOCK_EXPRESSION_TEXT ); } else if ( entry.getNodeName().equalsIgnoreCase( MEMORY_BLOCK_MEMSPACEID ) ) { memorySpaceID = entry.getAttribute( ATTR_MEMORY_BLOCK_MEMSPACEID_TEXT ); } } } if (exp != null) { expressions.add( exp ); memorySpaceIDs.add( memorySpaceID ); } } private void initializeFromMemento( String memento ) throws CoreException { Element root = DebugPlugin.parseDocument( memento ); if ( root.getNodeName().equalsIgnoreCase( MEMORY_BLOCK_EXPRESSION_LIST ) ) { List<String> expressions = new ArrayList<String>(); List<String> memorySpaceIDs = new ArrayList<String>(); NodeList list = root.getChildNodes(); int length = list.getLength(); for( int i = 0; i < length; ++i ) { Node node = list.item( i ); if ( node.getNodeType() == Node.ELEMENT_NODE ) { Element entry = (Element)node; if ( entry.getNodeName().equalsIgnoreCase( MEMORY_BLOCK_EXPRESSION_ITEM ) ) { parseMementoExprItem(entry, expressions, memorySpaceIDs); } } } createMemoryBlocks( expressions.toArray( new String[expressions.size()]) , memorySpaceIDs.toArray( new String[memorySpaceIDs.size()])); return; } abort( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.3" ), null ); //$NON-NLS-1$ } /** * Convert a simple literal address (e.g., "0x1000") to a BigInteger value * using the debug target's address factory. * * We throw a NumberFormatException if the string is not a valid literal * address. If the backend implements the new&improved factory interface, * we'll throw a NumberFormatException if the string is a literal address * but is outside of the valid range. Old address factories will simply * truncate the value. * * @param expression * @return * @throws DebugException if target not available */ private BigInteger evaluateLiteralAddress(String addr) throws DebugException { CDebugTarget target = getDebugTarget(); if (target == null) { throw new DebugException(new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, InternalDebugCoreMessages.getString("CMemoryBlockRetrievalExtension.CDebugTarget_not_available"), null)); //$NON-NLS-1$ } IAddressFactory addrFactory = target.getAddressFactory(); if (addrFactory instanceof IAddressFactory2) { return ((IAddressFactory2)addrFactory).createAddress(addr, false).getValue(); } else { return addrFactory.createAddress(addr).getValue(); } } private void createMemoryBlocks( String[] expressions, String[] memorySpaceIDs ) { List<CMemoryBlockExtension> list = new ArrayList<CMemoryBlockExtension>( expressions.length ); for ( int i = 0; i < expressions.length; ++i ) { try { IAddress address = getDebugTarget().getAddressFactory().createAddress( expressions[i] ); if ( address != null ) { if (memorySpaceIDs[i] == null) { list.add( new CMemoryBlockExtension( getDebugTarget(), address.toHexAddressString(), address.getValue() ) ); } else { list.add( new CMemoryBlockExtension( getDebugTarget(), expressions[i], address.getValue(), memorySpaceIDs[i] ) ); } } } catch (NumberFormatException exc) { CDebugCorePlugin.log(exc); } } DebugPlugin.getDefault().getMemoryBlockManager().addMemoryBlocks( list.toArray( new IMemoryBlock[list.size()] ) ); } public String getMemento() throws CoreException { IMemoryBlock[] blocks = DebugPlugin.getDefault().getMemoryBlockManager().getMemoryBlocks( getDebugTarget() ); Document document = DebugPlugin.newDocument(); Element exprList = document.createElement( MEMORY_BLOCK_EXPRESSION_LIST ); for ( int i = 0; i < blocks.length; ++i ) { if ( blocks[i] instanceof IMemoryBlockExtension ) { IMemoryBlockExtension memBlockExt = (IMemoryBlockExtension)blocks[i]; Element exprItem = document.createElement( MEMORY_BLOCK_EXPRESSION_ITEM ); exprList.appendChild(exprItem); String memorySpaceID = null; if (memBlockExt instanceof IMemorySpaceAwareMemoryBlock) { memorySpaceID = ((IMemorySpaceAwareMemoryBlock)memBlockExt).getMemorySpaceID(); } BigInteger addrBigInt = memBlockExt.getBigBaseAddress(); Element child = document.createElement( MEMORY_BLOCK_EXPRESSION ); child.setAttribute( ATTR_MEMORY_BLOCK_EXPRESSION_TEXT, "0x" + addrBigInt.toString(16) ); //$NON-NLS-1$ exprItem.appendChild( child ); if (memorySpaceID != null) { child = document.createElement( MEMORY_BLOCK_MEMSPACEID ); child.setAttribute( ATTR_MEMORY_BLOCK_MEMSPACEID_TEXT, memorySpaceID); exprItem.appendChild( child ); } } } document.appendChild( exprList ); return DebugPlugin.serializeDocument( document ); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IMemoryBlockExtensionRetrieval#getExtendedMemoryBlock(java.lang.String, org.eclipse.debug.core.model.IDebugElement) */ public IMemoryBlockExtension getExtendedMemoryBlock( String expression, Object selected ) throws DebugException { return getMemoryBlock(expression, selected, null); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.internal.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval#getExtendedMemoryBlock(java.lang.String, java.lang.Object, java.lang.String) */ public IMemorySpaceAwareMemoryBlock getMemoryBlock( String expression, Object selected, String memorySpaceID ) throws DebugException { String address = null; CExpression exp = null; String msg = null; try { if (selected instanceof IDebugElement) { IDebugElement debugElement = (IDebugElement)selected; IDebugTarget target = debugElement.getDebugTarget(); if (!(target instanceof CDebugTarget)) { throw new DebugException( new Status( IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, msg, null ) ); } // See if the expression is a simple numeric value; if it is, we can avoid some costly // processing (calling the backend to resolve the expression) try { return new CMemoryBlockExtension((CDebugTarget)target, expression, evaluateLiteralAddress(expression), memorySpaceID); } catch (NumberFormatException nfexc) {} // OK, expression is not a simple literal address; keep trucking and try to resolve as expression CStackFrame frame = getStackFrame( debugElement ); if ( frame != null ) { // Get the address of the expression ICDIExpression cdiExpression = frame.getCDITarget().createExpression( expression ); exp = new CExpression( frame, cdiExpression, null ); IValue value = exp.getValue(); if ( value instanceof ICValue ) { ICType type = ((ICValue)value).getType(); if ( type != null ) { // get the address for the expression, allow all types String rawExpr = exp.getExpressionString(); String voidExpr = "(void *)(" + rawExpr + ')'; //$NON-NLS-1$ String attempts[] = { rawExpr, voidExpr }; for (int i = 0; i < attempts.length; i++) { String expr = attempts[i]; address = frame.evaluateExpressionToString(expr); if (address != null) { try { BigInteger a = (address.startsWith("0x")) ? new BigInteger(address.substring(2), 16) : new BigInteger(address); //$NON-NLS-1$ return new CMemoryBlockExtension((CDebugTarget) target, expression, a, memorySpaceID); } catch (NumberFormatException e) { // not pointer? lets cast it to void* if (i == 0) continue; throw e; } } } } else { msg = MessageFormat.format( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.1" ), new String[] { expression } ); //$NON-NLS-1$ } } else { msg = MessageFormat.format( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.2" ), new String[] { expression } ); //$NON-NLS-1$ } } } } catch( CDIException e ) { msg = e.getMessage(); } catch( NumberFormatException e ) { msg = MessageFormat.format( InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.0" ), new String[] { expression } ); //$NON-NLS-1$ } finally { if (exp != null) { exp.dispose(); } } throw new DebugException( new Status( IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, msg, null ) ); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval() */ public boolean supportsStorageRetrieval() { return true; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long) */ public IMemoryBlock getMemoryBlock( long startAddress, long length ) throws DebugException { String expression = Long.toHexString(startAddress); BigInteger address = new BigInteger(expression, 16); expression = "0x" + expression; //$NON-NLS-1$ return new CMemoryBlockExtension( getDebugTarget(), expression, address ); } private CStackFrame getStackFrame( IDebugElement selected ) throws DebugException { if ( selected instanceof CStackFrame ) { return (CStackFrame)selected; } if ( selected instanceof CThread ) { IStackFrame frame = ((CThread)selected).getTopStackFrame(); if ( frame instanceof CStackFrame ) return (CStackFrame)frame; } return null; } public void save() { ILaunchConfiguration config = getDebugTarget().getLaunch().getLaunchConfiguration(); try { ILaunchConfigurationWorkingCopy wc = config.getWorkingCopy(); wc.setAttribute( ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_MEMORY_BLOCKS, getMemento() ); wc.doSave(); } catch( CoreException e ) { CDebugCorePlugin.log( e.getStatus() ); } } /** * Throws an internal error exception */ private void abort( String message, Throwable e ) throws CoreException { IStatus s = new Status( IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, message, e ); throw new CoreException( s ); } public void dispose() { // Fire a terminate event so our hosts can clean up. See 255120 and 283586 DebugPlugin.getDefault().fireDebugEventSet( new DebugEvent[]{new DebugEvent( this, DebugEvent.TERMINATE )}); // Minimize leaks in case we are ourselves are leaked fDebugTarget = null; } /** * Checks the CDI backend to see is memory spaces are supported and actually * available for the target process. * * @return true if the backend supports memory spaces */ public boolean hasMemorySpaces() { return getMemorySpaces().length > 0; } /** * @see org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval#getMemorySpaces(java.lang.Object, org.eclipse.cdt.debug.internal.core.model.provisional.IRequestListener) */ public void getMemorySpaces(final Object context, GetMemorySpacesRequest request) { // We're not very asynchronous in CDI. DSF is another story. Also, note // that we ignore the context. That's because we know that there's only // one instance of this object per process object, and all elements of // the project object (process, threads, frames) will have the same // memory spaces request.setMemorySpaces(getMemorySpaces()); request.done(); } /** * This variant is called by code that is CDI-specific. This method and its * uses predate the introduction of the DSF/CDI-agnostic * IMemorySpaceAwareMemoryBlockRetrieval * * @return the memory spaces available in this debug session */ public String [] getMemorySpaces(){ if (fDebugTarget != null) { ICDITarget cdiTarget = fDebugTarget.getCDITarget(); if (cdiTarget instanceof ICDIMemorySpaceManagement) return ((ICDIMemorySpaceManagement)cdiTarget).getMemorySpaces(); } return new String[0]; } /** * The default encoding of an {expression, memory space ID} pair into a * string. A CDI client can provide custom decoding by implementing * ICDIMemorySpaceEncoder */ public static String encodeAddressDefault(String expression, String memorySpaceID) { return memorySpaceID + ':' + expression; } /* (non-Javadoc) * @see org.eclipse.cdt.debug.internal.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval#encodeAddress(java.math.BigInteger, java.lang.String) */ public String encodeAddress(final String expression, final String memorySpaceID) { // See if the CDI client provides customized encoding/decoding if (fDebugTarget != null) { ICDITarget cdiTarget = fDebugTarget.getCDITarget(); if (cdiTarget instanceof ICDIMemorySpaceEncoder) { return ((ICDIMemorySpaceEncoder)cdiTarget).encodeAddress(expression, memorySpaceID); } } // Nope; use default encoding return encodeAddressDefault(expression, memorySpaceID); } /* * The default decoding of a string into an {expression, memory space ID} * pair. A CDI client can provide custom decoding by implementing ICDIMemorySpaceEncoder */ public static DecodeResult decodeAddressDefault(String str) throws CoreException { int index = str.lastIndexOf(':'); // minimum is "<space>:<expression>" if ((index == -1) || (index == str.length()-1)) { IStatus s = new Status( IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.5" ), null ); //$NON-NLS-1$ throw new CoreException( s ); } final String memorySpaceID = str.substring(0, index); final String expression = str.substring(index+1); return new DecodeResult() { public String getMemorySpaceId() { return memorySpaceID; } public String getExpression() { return expression; } }; } /* (non-Javadoc) * @see org.eclipse.cdt.debug.internal.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval#decodeAddress(java.lang.String, java.lang.StringBuffer) */ public DecodeResult decodeAddress(final String str) throws CoreException { // See if the CDI client provides customized encoding/decoding if (fDebugTarget != null) { ICDITarget cdiTarget = fDebugTarget.getCDITarget(); if (cdiTarget instanceof ICDIMemorySpaceEncoder) { try { final ICDIMemorySpaceEncoder.DecodeResult result = ((ICDIMemorySpaceEncoder)cdiTarget).decodeAddress(str); return new DecodeResult() { public String getMemorySpaceId() { return result.getMemorySpaceId(); } public String getExpression() { return result.getExpression(); } }; } catch (CDIException exc) { IStatus s = new Status(IStatus.ERROR, CDebugCorePlugin.getUniqueIdentifier(), CDebugCorePlugin.INTERNAL_ERROR, InternalDebugCoreMessages.getString( "CMemoryBlockRetrievalExtension.invalid_encoded_addresses" ), exc); //$NON-NLS-1$ throw new CoreException(s); } } } // Nope; use default decoding return decodeAddressDefault(str); } /* (non-Javadoc) * @see org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlockRetrieval#creatingBlockRequiresMemorySpaceID() */ public boolean creatingBlockRequiresMemorySpaceID() { // A behavioral control we're not extending to CDI clients, but is being // extended to DSF ones. return false; } }