/*******************************************************************************
* Copyright (c) 2008, 2009 ARM Limited 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:
* ARM Limited - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.internal.core.model;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.IAddressFactory;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.event.ICDIBreakpointMovedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIBreakpointProblemEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIChangedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDICreatedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIDestroyedEvent;
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.model.ICDIInstruction;
import org.eclipse.cdt.debug.core.cdi.model.ICDILocationBreakpoint;
import org.eclipse.cdt.debug.core.cdi.model.ICDIMixedInstruction;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
import org.eclipse.cdt.debug.core.model.ICStackFrame;
import org.eclipse.cdt.debug.core.model.IDisassemblyInstruction;
import org.eclipse.cdt.debug.core.model.IDisassemblyLine;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
public class DisassemblyRetrieval extends CDebugElement implements ICDIEventListener {
public static final int FLAGS_SHOW_INSTRUCTIONS = 0x1;
public static final int FLAGS_SHOW_SOURCE = 0x2;
private Object fInput = null;
private BigInteger fBaseElement = null;
private int fCurrentOffset = 0;
private int fFlags = 0;
private IDisassemblyLine[] fLines;
public DisassemblyRetrieval( CDebugTarget target ) {
super( target );
fLines = new IDisassemblyLine[0];
CDebugCorePlugin.getDefault().getDisassemblyContextService().register( this );
getCDISession().getEventManager().addEventListener( this );
}
public void dispose() {
getCDISession().getEventManager().removeEventListener( this );
CDebugCorePlugin.getDefault().getDisassemblyContextService().unregister( this );
}
/* (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 ( ICDIEvent event : events ) {
Object source = event.getSource();
if ( (event instanceof ICDICreatedEvent
|| event instanceof ICDIChangedEvent
|| event instanceof ICDIDestroyedEvent
|| event instanceof ICDIBreakpointMovedEvent
|| event instanceof ICDIBreakpointProblemEvent )
&& source instanceof ICDILocationBreakpoint ) {
BigInteger address = ((ICDILocationBreakpoint)source).getLocator().getAddress();
if ( address != null ) {
int index = getIndexForAddress( address, fLines );
if ( index >= 0 ) {
fireEvent( new DebugEvent( fLines[index], DebugEvent.CHANGE, DebugEvent.STATE ) );
}
if ( event instanceof ICDIBreakpointMovedEvent ) {
address = ((ICDIBreakpointMovedEvent)event).getNewLocation().getAddress();
if ( address != null ) {
index = getIndexForAddress( address, fLines );
if ( index >= 0 ) {
fireEvent( new DebugEvent( fLines[index], DebugEvent.CHANGE, DebugEvent.STATE ) );
}
}
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.model.IDisassemblyRetrieval#getInput()
*/
public Object getInput() {
return fInput;
}
public BigInteger getBaseElement() {
return fBaseElement;
}
public int getCurrentOffset() {
return fCurrentOffset;
}
public IDisassemblyLine[] getLines() {
return fLines;
}
public void changeBase( Object input, int offset, int flags ) throws DebugException {
if ( input instanceof ICStackFrame ) {
fInput = input;
ICStackFrame frame = (ICStackFrame)input;
IAddress frameAddress = frame.getAddress();
BigInteger address = (frameAddress != null) ? frameAddress.getValue() : BigInteger.valueOf(0);
if ( !containsAddress( address, fLines ) ) {
fCurrentOffset = 0;
reset();
}
else if ( flags != fFlags ) {
reset();
}
else {
fCurrentOffset += getDistance( fBaseElement, address );
}
fFlags = flags;
fBaseElement = address;
}
}
public void retrieveDisassembly( Object input, Object base, int offset, int lineCount, boolean reveal, int flags ) throws DebugException {
fFlags = flags;
boolean showSource = ( (flags & FLAGS_SHOW_SOURCE) != 0 );
List<IDisassemblyLine> lines = new ArrayList<IDisassemblyLine>( lineCount );
BigInteger startAddress = getCurrentStartAddress();
BigInteger address = null;
if ( startAddress != null ) {
if ( getCurrentOffset() > offset ) {
// scrolling up
address = startAddress.subtract( BigInteger.valueOf( getMinInstructionSize() * (getCurrentOffset() - offset) ) );
}
else if ( getCurrentOffset() < offset ) {
// scrolling down
IDisassemblyInstruction next = getNextInstruction( startAddress, fLines );
if ( next != null )
address = next.getAdress().getValue();
}
else {
address = startAddress;
}
}
if ( address == null )
address = fBaseElement;
lines.addAll( Arrays.asList( disassembleDown( address, lineCount, showSource ) ) );
fLines = lines.toArray( new IDisassemblyLine[lines.size()] );
fCurrentOffset = offset;
}
private boolean containsAddress( BigInteger address, IDisassemblyLine[] lines ) {
return ( getIndexForAddress( address, lines ) >= 0 );
}
public void reset() {
fLines = new IDisassemblyLine[0];
}
private int getDistance( BigInteger address1, BigInteger address2 ) {
int index1 = getIndexForAddress( address1, fLines );
Assert.isTrue( index1 >=0 );
int index2 = getIndexForAddress( address2, fLines );
Assert.isTrue( index2 >=0 );
return index2 - index1;
}
private int getIndexForAddress( BigInteger address, IDisassemblyLine[] lines ) {
for ( int i = 0; i < lines.length; ++i ) {
if ( lines[i] instanceof IDisassemblyInstruction &&
address.compareTo( ((IDisassemblyInstruction)lines[i]).getAdress().getValue() ) == 0 )
return i;
}
return -1;
}
private BigInteger getCurrentStartAddress() {
for ( IDisassemblyLine l : fLines ) {
if ( l instanceof IDisassemblyInstruction )
return ((IDisassemblyInstruction)l).getAdress().getValue();
}
return null;
}
private IDisassemblyLine[] disassembleDown( BigInteger address, int lineCount, boolean mixed ) throws DebugException {
BigInteger startAddress = address;
IDisassemblyLine[] lines = new IDisassemblyLine[0];
BigInteger endAddress = startAddress;
if ( lines.length < lineCount && endAddress.compareTo( getGlobalEndAddress() ) < 0 ) {
endAddress = address.add( BigInteger.valueOf( lineCount * getMaxInstructionSize() ) );
if ( endAddress.compareTo( getGlobalEndAddress() ) > 0 )
endAddress = getGlobalEndAddress();
lines = disassemble( address, endAddress, mixed );
IDisassemblyInstruction firstInstruction = getFirstInstruction( lines );
IDisassemblyInstruction lastInstruction = getLastInstruction( lines );
if ( firstInstruction != null && lastInstruction != null ) {
if ( startAddress.compareTo( firstInstruction.getAdress().getValue() ) < 0 ) {
lines = appendLines( disassemble( startAddress, firstInstruction.getAdress().getValue(), mixed ), lines );
}
startAddress = lastInstruction.getAdress().getValue();
if ( startAddress.compareTo( endAddress ) < 0 ) {
IDisassemblyLine[] extraLines = new IDisassemblyLine[0];
while( extraLines.length == 0 && endAddress.compareTo( getGlobalEndAddress() ) < 0 ) {
endAddress = endAddress.add( BigInteger.valueOf( getMaxInstructionSize() ) );
extraLines = disassemble( startAddress, endAddress, mixed );
}
lines = appendLines( lines, extraLines );
}
}
}
int size = Math.min( lineCount, lines.length );
IDisassemblyLine[] result = new IDisassemblyLine[size];
int start = getIndexForAddress( address, lines );
if ( start != -1 ) {
System.arraycopy( lines, start, result, 0, size );
}
return result;
}
private IDisassemblyLine[] disassemble( BigInteger startAddress, BigInteger endAddress, boolean mixed ) throws DebugException {
List<IDisassemblyLine> list = new ArrayList<IDisassemblyLine>();
ICDITarget cdiTarget = (ICDITarget)getDebugTarget().getAdapter( ICDITarget.class );
try {
ICDIMixedInstruction[] mixedInstructions = null;
ICDIInstruction[] asmInstructions = null;
if ( mixed ) {
mixedInstructions = cdiTarget.getMixedInstructions( startAddress, endAddress );
if ( mixedInstructions.length == 0
|| mixedInstructions.length == 1
&& mixedInstructions[0].getInstructions().length == 0 ) {
mixedInstructions = null;
}
}
if ( mixedInstructions == null ) {
asmInstructions = cdiTarget.getInstructions( startAddress, endAddress );
}
if ( mixedInstructions != null ) {
for ( ICDIMixedInstruction mi : mixedInstructions ) {
ICDIInstruction[] instructions = mi.getInstructions();
if ( instructions.length > 0 ) {
list.add( new DisassemblySourceLine( (CDebugTarget)getDebugTarget(), fBaseElement, mi ) );
for ( ICDIInstruction i : instructions ) {
list.add( new DisassemblyInstruction( (CDebugTarget)getDebugTarget(), fBaseElement, i ) );
}
}
}
}
else if ( asmInstructions != null ) {
for ( ICDIInstruction i : asmInstructions ) {
list.add( new DisassemblyInstruction( (CDebugTarget)getDebugTarget(), fBaseElement, i ) );
}
}
}
catch( CDIException exc ) {
throw new DebugException( new Status( IStatus.ERROR, "dummy", exc.getDetailMessage(), exc ) ); //$NON-NLS-1$
}
return list.toArray( new IDisassemblyLine[list.size()] );
}
private int getMaxInstructionSize() {
return 4;
}
private int getMinInstructionSize() {
return 1;
}
private IDisassemblyInstruction getNextInstruction( BigInteger address, IDisassemblyLine[] lines ) {
int index = getIndexForAddress( address, lines );
if ( index == -1 || index == lines.length - 1 )
return null;
for ( int i = index + 1; i < lines.length; ++i ) {
if ( lines[i] instanceof IDisassemblyInstruction )
return (IDisassemblyInstruction)lines[i];
}
return null;
}
private IDisassemblyInstruction getFirstInstruction( IDisassemblyLine[] lines ) {
for ( IDisassemblyLine l : lines ) {
if ( l instanceof IDisassemblyInstruction )
return (IDisassemblyInstruction)l;
}
return null;
}
private IDisassemblyInstruction getLastInstruction( IDisassemblyLine[] lines ) {
for ( int i = lines.length - 1; i >= 0; --i ) {
if ( lines[i] instanceof IDisassemblyInstruction )
return (IDisassemblyInstruction)lines[i];
}
return null;
}
private BigInteger getGlobalEndAddress() {
return getAddressFactory().getMax().getValue();
}
private IAddressFactory getAddressFactory() {
return ((CDebugTarget)getDebugTarget()).getAddressFactory();
}
private IDisassemblyLine[] appendLines( IDisassemblyLine[] lines1, IDisassemblyLine[] lines2 ) {
List<IDisassemblyLine> list = new ArrayList<IDisassemblyLine>( lines1.length + lines2.length );
list.addAll( Arrays.asList( lines1 ) );
for ( IDisassemblyLine l : lines2 ) {
if ( !list.contains( l ) )
list.add( l );
}
return list.toArray( new IDisassemblyLine[list.size()] );
}
}