/*******************************************************************************
* 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.model;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.ICDISession;
import org.eclipse.cdt.debug.core.cdi.event.ICDIEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener;
import org.eclipse.cdt.debug.core.cdi.event.ICDIMemoryChangedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIRestartedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIResumedEvent;
import org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlock;
import org.eclipse.cdt.debug.core.cdi.model.ICDIMemoryBlockManagement2;
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.ICDIObject;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
import org.eclipse.cdt.debug.core.model.IExecFileInfo;
import org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlock;
import org.eclipse.cdt.debug.internal.core.CMemoryBlockRetrievalExtension;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
import org.eclipse.debug.core.model.MemoryByte;
/**
* Represents a memory block in the CDI model.
*/
public class CMemoryBlockExtension extends CDebugElement implements IMemorySpaceAwareMemoryBlock, ICDIEventListener {
/**
* The address expression this memory block is based on.
*/
private String fExpression;
/**
* The base address of this memory block.
*/
private BigInteger fBaseAddress;
/**
* The memory space identifier; will be null for backends that
* don't require memory space support
*/
private String fMemorySpaceID;
/**
* The underlying CDI memory block.
*/
private ICDIMemoryBlock fCDIBlock;
/**
* The memory bytes values.
*/
private MemoryByte[] fBytes = null;
private Set<BigInteger> fChanges = new HashSet<BigInteger>();
/**
* is fWordSize available?
*/
private boolean fHaveWordSize;
/**
* The number of bytes per address.
*/
private int fWordSize;
/**
* Constructor
*/
public CMemoryBlockExtension( CDebugTarget target, String expression, BigInteger baseAddress ) {
this(target, expression, baseAddress, null);
}
/**
* Constructor that takes a memory space identifier
*/
@SuppressWarnings("deprecation")
public CMemoryBlockExtension( CDebugTarget target, String expression, BigInteger baseAddress, String memorySpaceID ) {
super( target );
fBaseAddress = baseAddress;
fMemorySpaceID = memorySpaceID;
if (memorySpaceID == null) {
fExpression = expression;
}
else {
assert memorySpaceID.length() > 0;
ICDITarget cdiTarget = target.getCDITarget();
if (cdiTarget instanceof ICDIMemorySpaceEncoder) {
// new interface
fExpression = ((ICDIMemorySpaceEncoder)cdiTarget).encodeAddress(expression, memorySpaceID);
}
else if (cdiTarget instanceof ICDIMemorySpaceManagement) {
// old interface
fExpression = ((ICDIMemorySpaceManagement)target.getCDITarget()).addressToString(baseAddress, memorySpaceID);
}
if (fExpression == null) {
// If the backend supports memory spaces, it should implement ICDIMemorySpaceManagement
// Even if it does, it may choose to use our built-in encoding/decoding
fExpression = CMemoryBlockRetrievalExtension.encodeAddressDefault(expression, memorySpaceID);
}
}
}
/**
* Constructor that takes the addressable size
*/
public CMemoryBlockExtension( CDebugTarget target, String expression, BigInteger baseAddress, int wordSize ) {
this( target, expression, baseAddress, wordSize, null );
}
/**
* Constructor that takes the addressable size and a memory space identifier
*/
public CMemoryBlockExtension( CDebugTarget target, String expression, BigInteger baseAddress, int wordSize, String memorySpaceID ) {
super( target );
fExpression = expression;
fBaseAddress = baseAddress;
fWordSize= wordSize;
fHaveWordSize= true;
fMemorySpaceID = memorySpaceID;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getExpression()
*/
public String getExpression() {
return fExpression;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBigBaseAddress()
*/
public BigInteger getBigBaseAddress() {
return fBaseAddress;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getAddressSize()
*/
public int getAddressSize() {
return ((CDebugTarget)getDebugTarget()).getAddressFactory().createAddress( getBigBaseAddress() ).getSize();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getAddressableSize()
*/
public int getAddressableSize() throws DebugException {
if (!fHaveWordSize) {
synchronized (this) {
if (!fHaveWordSize) {
ICDIMemoryBlock block= getCDIBlock();
if (block == null) {
try {
// create a CDI block of an arbitrary size so we can call into
// the backend to determine the memory's addressable size
setCDIBlock( block= createCDIBlock( fBaseAddress, 100 ));
}
catch( CDIException e ) {
targetRequestFailed( e.getMessage(), null );
}
}
if (block != null) {
fWordSize= block.getWordSize();
fHaveWordSize= true;
}
}
}
}
return fWordSize;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#supportBaseAddressModification()
*/
public boolean supportBaseAddressModification() {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#setBaseAddress(java.math.BigInteger)
*/
public void setBaseAddress( BigInteger address ) throws DebugException {
BigInteger current = fBaseAddress;
if (current == address || (current != null && current.equals(address))) {
return; // optimization
}
fBaseAddress = address;
fireChangeEvent(DebugEvent.STATE);
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBytesFromOffset(java.math.BigInteger, long)
*/
public MemoryByte[] getBytesFromOffset( BigInteger unitOffset, long addressableUnits ) throws DebugException {
return getBytesFromAddress(unitOffset.add(getBigBaseAddress()) , addressableUnits);
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBytesFromAddress(java.math.BigInteger, long)
*/
public MemoryByte[] getBytesFromAddress( BigInteger address, long length ) throws DebugException {
ICDIMemoryBlock cdiBlock = getCDIBlock();
if ( fBytes == null || cdiBlock == null ||
cdiBlock.getStartAddress().compareTo( address ) > 0 ||
cdiBlock.getStartAddress().add( BigInteger.valueOf( cdiBlock.getLength()/cdiBlock.getWordSize() ) ).compareTo( address.add( BigInteger.valueOf( length ) ) ) < 0 ) {
synchronized( this ) {
byte[] bytes = null;
try {
cdiBlock = getCDIBlock();
if ( cdiBlock == null ||
cdiBlock.getStartAddress().compareTo( address ) > 0 ||
cdiBlock.getStartAddress().add( BigInteger.valueOf( cdiBlock.getLength()/cdiBlock.getWordSize() ) ).compareTo( address.add( BigInteger.valueOf( length ) ) ) < 0 ) {
if ( cdiBlock != null ) {
disposeCDIBlock();
fBytes = null;
}
setCDIBlock( cdiBlock = createCDIBlock( address, length ) );
}
bytes = getCDIBlock().getBytes();
}
catch( CDIException e ) {
targetRequestFailed( e.getMessage(), null );
}
if (bytes != null) {
fBytes = new MemoryByte[bytes.length];
for ( int i = 0; i < bytes.length; ++i ) {
fBytes[i] = createMemoryByte( bytes[i], getCDIBlock().getFlags( i ), hasChanged( getRealBlockAddress().add( BigInteger.valueOf( i ) ) ) );
}
}
}
}
MemoryByte[] result = new MemoryByte[0];
if ( fBytes != null && cdiBlock != null ) {
int offset = address.subtract( getRealBlockAddress() ).intValue();
int offsetInBytes = offset * cdiBlock.getWordSize();
long lengthInBytes = length * cdiBlock.getWordSize();
if ( offset >= 0 ) {
int size = ( fBytes.length - offsetInBytes >= lengthInBytes ) ? (int)lengthInBytes : fBytes.length - offsetInBytes;
if ( size > 0 ) {
result = new MemoryByte[size];
System.arraycopy( fBytes, offsetInBytes, result, 0, size );
}
}
}
return result;
}
private boolean isBigEndian() {
IExecFileInfo info = (IExecFileInfo)getDebugTarget().getAdapter( IExecFileInfo.class );
if ( info != null ) {
return !info.isLittleEndian();
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockRetrieval()
*/
public IMemoryBlockRetrieval getMemoryBlockRetrieval() {
return (IMemoryBlockRetrieval)getDebugTarget().getAdapter( IMemoryBlockRetrieval.class );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener#handleDebugEvents(org.eclipse.cdt.debug.core.cdi.event.ICDIEvent[])
*/
public void handleDebugEvents( ICDIEvent[] events ) {
for( int i = 0; i < events.length; i++ ) {
ICDIEvent event = events[i];
ICDIObject source = event.getSource();
if ( source == null )
continue;
if ( source.getTarget().equals( getCDITarget() ) ) {
if ( event instanceof ICDIResumedEvent || event instanceof ICDIRestartedEvent ) {
resetChanges();
}
else if ( event instanceof ICDIMemoryChangedEvent ) {
if ( source instanceof ICDIMemoryBlock && source.equals( getCDIBlock() ) ) {
handleChangedEvent( (ICDIMemoryChangedEvent)event );
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlock#getStartAddress()
*/
public long getStartAddress() {
return 0;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlock#getLength()
*/
public long getLength() {
return 0;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlock#getBytes()
*/
public byte[] getBytes() throws DebugException {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlock#supportsValueModification()
*/
public boolean supportsValueModification() {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlock#setValue(long, byte[])
*/
public void setValue( long offset, byte[] bytes ) throws DebugException {
setValue( BigInteger.valueOf( offset ), bytes );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#setValue(java.math.BigInteger, byte[])
*/
public void setValue( BigInteger offset, byte[] bytes ) throws DebugException {
ICDIMemoryBlock block = getCDIBlock();
if ( block != null ) {
BigInteger base = getBigBaseAddress();
BigInteger real = getRealBlockAddress();
long realOffset = base.add( offset ).subtract( real ).longValue();
try {
block.setValue( realOffset, bytes );
}
catch( CDIException e ) {
targetRequestFailed( e.getDetailMessage(), null );
}
}
}
private ICDIMemoryBlock createCDIBlock( BigInteger address, long length) throws CDIException {
ICDIMemoryBlock block = null;
CDebugTarget target = (CDebugTarget)getDebugTarget();
ICDITarget cdiTarget = target.getCDITarget();
if ((fMemorySpaceID != null) && (cdiTarget instanceof ICDIMemoryBlockManagement2)) {
block = ((ICDIMemoryBlockManagement2)cdiTarget).createMemoryBlock(address, fMemorySpaceID, (int)length);
} else {
// Note that CDI clients should ignore the word size
// parameter. It has been deprecated in 4.0. We continue to
// pass in 1 as has always been the case to maintain backwards
// compatibility.
block = cdiTarget.createMemoryBlock( address.toString(), (int)length, 1);
}
block.setFrozen( false );
getCDISession().getEventManager().addEventListener( this );
return block;
}
private void disposeCDIBlock() {
ICDIMemoryBlock block = getCDIBlock();
if ( block != null ) {
try {
((CDebugTarget)getDebugTarget()).getCDITarget().removeBlocks( new ICDIMemoryBlock[]{ block });
}
catch( CDIException e ) {
DebugPlugin.log( e );
}
setCDIBlock( null );
getCDISession().getEventManager().removeEventListener( this );
}
}
private ICDIMemoryBlock getCDIBlock() {
return fCDIBlock;
}
private void setCDIBlock( ICDIMemoryBlock cdiBlock ) {
fCDIBlock = cdiBlock;
}
private BigInteger getRealBlockAddress() {
ICDIMemoryBlock block = getCDIBlock();
return ( block != null ) ? block.getStartAddress() : BigInteger.ZERO;
}
private long getBlockSize() {
ICDIMemoryBlock block = getCDIBlock();
return ( block != null ) ? block.getLength() : 0;
}
private void handleChangedEvent( ICDIMemoryChangedEvent event ) {
ICDIMemoryBlock block = getCDIBlock();
if ( block != null && fBytes != null ) {
MemoryByte[] memBytes = fBytes.clone();
try {
BigInteger start = getRealBlockAddress();
long length = block.getLength();
byte[] newBytes = block.getBytes();
BigInteger[] addresses = event.getAddresses();
saveChanges( addresses );
for ( int i = 0; i < addresses.length; ++i ) {
fChanges.add( addresses[i] );
int addressableSize = fCDIBlock.getWordSize(); // # of bytes per address
if ( addresses[i].compareTo( start ) >= 0 && addresses[i].compareTo( start.add( BigInteger.valueOf( length / addressableSize ) ) ) < 0 ) {
int index = addressableSize * addresses[i].subtract( start ).intValue();
int end = Math.min(Math.min(index + addressableSize, memBytes.length), newBytes.length);
for (index = Math.max(index, 0) ; index < end; index++ ) {
memBytes[index].setChanged( true );
memBytes[index].setValue( newBytes[index] );
}
}
}
fBytes = memBytes;
fireChangeEvent( DebugEvent.CONTENT );
}
catch( CDIException e ) {
DebugPlugin.log( e );
}
}
}
private void saveChanges( BigInteger[] addresses ) {
fChanges.addAll( Arrays.asList( addresses ) );
}
private boolean hasChanged( BigInteger address ) {
return fChanges.contains( address );
}
private void resetChanges() {
if ( fBytes != null ) {
BigInteger[] changes = fChanges.toArray( new BigInteger[fChanges.size()] );
for ( int i = 0; i < changes.length; ++i ) {
BigInteger real = getRealBlockAddress();
if ( real.compareTo( changes[i] ) <= 0 && real.add( BigInteger.valueOf( getBlockSize() ) ).compareTo( changes[i] ) > 0 ) {
int index = changes[i].subtract( real ).intValue();
if ( index >= 0 && index < fBytes.length ) {
fBytes[index].setChanged( false );
}
}
}
}
fChanges.clear();
fireChangeEvent( DebugEvent.CONTENT );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#supportsChangeManagement()
*/
public boolean supportsChangeManagement() {
return true;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#connect(java.lang.Object)
*/
public void connect( Object object ) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#disconnect(java.lang.Object)
*/
public void disconnect( Object object ) {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getConnections()
*/
public Object[] getConnections() {
// TODO Auto-generated method stub
return new Object[0];
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#dispose()
*/
public void dispose() {
fChanges.clear();
ICDIMemoryBlock cdiBlock = getCDIBlock();
if ( cdiBlock != null ) {
try {
((CDebugTarget)getDebugTarget()).getCDITarget().removeBlocks( new ICDIMemoryBlock[] {cdiBlock} );
}
catch( CDIException e ) {
CDebugCorePlugin.log( e );
}
fCDIBlock = null;
}
final ICDISession cdiSession = getCDISession();
if (cdiSession != null) {
cdiSession.getEventManager().removeEventListener( this );
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@SuppressWarnings("rawtypes")
public Object getAdapter( Class adapter ) {
if ( IMemoryBlockRetrieval.class.equals( adapter ) )
return getMemoryBlockRetrieval();
return super.getAdapter( adapter );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockStartAddress()
*/
public BigInteger getMemoryBlockStartAddress() throws DebugException {
return null; // return null to mean not bounded ... according to the spec
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getMemoryBlockEndAddress()
*/
public BigInteger getMemoryBlockEndAddress() throws DebugException {
return null;// return null to mean not bounded ... according to the spec
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IMemoryBlockExtension#getBigLength()
*/
public BigInteger getBigLength() throws DebugException {
ICDIMemoryBlock block = getCDIBlock();
if ( block != null ) {
BigInteger length = new BigInteger( Long.toHexString( block.getLength() ), 16 );
return length;
}
return BigInteger.ZERO;
}
private MemoryByte createMemoryByte( byte value, byte cdiFlags, boolean changed ) {
byte flags = 0;
if ( (cdiFlags & ICDIMemoryBlock.VALID) != 0 ) {
flags |= MemoryByte.HISTORY_KNOWN | MemoryByte.ENDIANESS_KNOWN;
if ( (cdiFlags & ICDIMemoryBlock.READ_ONLY) != 0 ) {
flags |= MemoryByte.READABLE;
}
else {
flags |= MemoryByte.READABLE | MemoryByte.WRITABLE;
}
if ( isBigEndian() ) {
flags |= MemoryByte.BIG_ENDIAN;
}
if ( changed )
flags |= MemoryByte.CHANGED;
}
return new MemoryByte( value, flags );
}
/**
* @see org.eclipse.cdt.debug.core.model.provisional.IMemorySpaceAwareMemoryBlock#getMemorySpaceID()
*/
public String getMemorySpaceID() {
return fMemorySpaceID;
}
}