blob: e5e9d8c496f2845052c728c3891269d2ed439be4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 Nokia 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:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.services;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigInteger;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.EDCTrace;
import org.eclipse.cdt.debug.edc.internal.launch.CSourceLookup;
import org.eclipse.cdt.debug.edc.internal.services.dsf.EDCSymbolReader;
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
import org.eclipse.cdt.debug.edc.internal.symbols.MemoryVariableLocation;
import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglerEABI;
import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglingException;
import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
import org.eclipse.cdt.debug.edc.symbols.IScope;
import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
import org.eclipse.cdt.debug.edc.symbols.IVariable;
import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IStack;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.service.IDsfService;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public abstract class Stack extends AbstractEDCService implements IStack, ICachingService {
public static final String STACK_FRAME = "stack_frame";
public Boolean showAllVariablesEnabled = null;
private final Map<String, List<StackFrameDMC>> stackFrames = Collections
.synchronizedMap(new HashMap<String, List<StackFrameDMC>>());
private final Map<String, Boolean> allFramesCached = Collections
.synchronizedMap(new HashMap<String, Boolean>());
public static class StackFrameData implements IFrameDMData {
public final IAddress address;
public final int level;
public final String function;
public final String module;
private final String file;
private final int lineNumber;
StackFrameData(StackFrameDMC dmc) {
level = dmc.getLevel();
address = dmc.getInstructionPtrAddress();
module = dmc.getModuleName();
file = dmc.getSourceFile(); // "" instead of null if no file.
lineNumber = dmc.getLineNumber();
function = dmc.getFunctionName();
}
public IAddress getAddress() {
return address;
}
public String getFunction() {
return function;
}
public int getLevel() {
return level;
}
// DSF requires non-null return value.
public String getFile() {
return file;
}
public int getLine() {
return lineNumber;
}
public int getColumn() {
return 0;
}
public String getModule() {
return module;
}
private boolean equals(final IAddress right, final IAddress left) {
return right == left || right != null && right.equals(left);
}
private boolean equals(final String right, final String left) {
return right == left || right != null && right.equals(left);
}
@Override
public boolean equals(Object other) {
return
this == other
|| (other != null && other instanceof StackFrameData
&& equals(getAddress(), ((StackFrameData)other).getAddress())
&& equals(getFunction(), ((StackFrameData)other).getFunction())
&& getLevel() == ((StackFrameData)other).getLevel()
&& equals(getFile(), ((StackFrameData)other).getFile())
&& getLine() == ((StackFrameData)other).getLine()
&& getColumn() == ((StackFrameData)other).getColumn()
&& equals(getModule(), ((StackFrameData)other).getModule()));
}
}
/**
* Variable or enumerator context. This interface provides a wrapper
* for treating variables and enumerators the same when needed.
**/
public interface IVariableEnumeratorContext {}
/**
* Enumerator context.
**/
public interface IEnumeratorDMContext {}
public static final class CurrentFrameRegisters implements IFrameRegisters {
private final Registers registers;
private final IEDCExecutionDMC executionDMC;
public CurrentFrameRegisters(IEDCExecutionDMC executionDMC, Registers registers) {
this.executionDMC = executionDMC;
this.registers = registers;
}
public BigInteger getRegister(int regnum, int bytes) throws CoreException {
String value = registers.getRegisterValue(executionDMC, regnum);
if (value == null || value.equals(Registers.REGISTER_VALUE_ERROR))
throw EDCDebugger.newCoreException("failed to read register");
return new BigInteger(value, 16);
}
public void writeRegister(int regnum, int bytes, BigInteger value) throws CoreException {
String id = registers.getRegisterNameFromCommonID(regnum);
if (id != null) {
// if value is negative, using value.toString(16) directly gives values such as '-af'
registers.writeRegister(executionDMC, id, Long.toHexString(value.longValue()));
} else
throw EDCDebugger.newCoreException(MessageFormat.format("could not find register number {0}", regnum));
}
}
/**
* Frame registers read from preserved registers on the stack frame.
*/
public static class PreservedFrameRegisters implements IFrameRegisters {
private final Map<Integer, BigInteger> preservedRegisters;
private final EDCServicesTracker dsfServicesTracker;
private final StackFrameDMC context;
/**
* @param preservedRegisters map of register number to the address
* where the register is saved
* @since 2.0
*/
public PreservedFrameRegisters(EDCServicesTracker dsfServicesTracker,
StackFrameDMC context,
Map<Integer, BigInteger> preservedRegisters) {
this.dsfServicesTracker = dsfServicesTracker;
this.context = context;
this.preservedRegisters = preservedRegisters;
}
public BigInteger getRegister(int regnum, int bytes) throws CoreException {
BigInteger addrVal = preservedRegisters.get(regnum);
if (addrVal != null) {
MemoryVariableLocation location = new MemoryVariableLocation(
dsfServicesTracker, context,
addrVal, true);
return location.readValue(bytes);
}
throw EDCDebugger.newCoreException("cannot read $R" + regnum + " from frame");
}
public void writeRegister(int regnum, int bytes, BigInteger value) throws CoreException {
BigInteger addrVal = preservedRegisters.get(regnum);
if (addrVal != null) {
MemoryVariableLocation location = new MemoryVariableLocation(
dsfServicesTracker, context,
addrVal, true);
location.writeValue(bytes, value);
}
}
}
/**
* Frame registers which always throws an exception.
*/
public static class AlwaysFailingFrameRegisters implements IFrameRegisters {
private final CoreException e;
public AlwaysFailingFrameRegisters(CoreException e) {
this.e = e;
}
public BigInteger getRegister(int regnum, int bytes) throws CoreException {
throw e;
}
public void writeRegister(int regnum, int bytes, BigInteger value)
throws CoreException {
throw e;
}
}
public class StackFrameDMC extends DMContext implements IFrameDMContext, Comparable<StackFrameDMC>,
ISnapshotContributor {
/**
* Stack frame level. Zero is used for the first frame, where the PC is.
*/
public static final String LEVEL_INDEX = "Level";
/**
* If set and True, tells that this frame is the topmost that we can fetch.
*/
public static final String ROOT_FRAME = "root_frame";
public static final String BASE_ADDR = "Base_address";
/**
* @since 2.0 - previously "IP_ADDR"
*/
public static final String INSTRUCTION_PTR_ADDR = "Instruction_address";
public static final String MODULE_NAME = "module_name";
public static final String SOURCE_FILE = "source_file";
public static final String FUNCTION_NAME = "function_name";
public static final String LINE_NUMBER = "line_number";
/**
* For LEVEL_INDEX == 0, if set and True, this tells us that this frame
* is not "authentic" yet, e.g., that the frame still represents the caller's
* state. This means we cannot trust the parameters and locals,
* and must resolve variables from other frames differently.
*/
public static final String IN_PROLOGUE = "in_prologue"; // Boolean
/**
* Provides a Map<Integer, BigInteger> instance which can yield addresses of
* registers pushed into the stack frame if debug info does not provide it.
*/
public static final String PRESERVED_REGISTERS = "preserved_registers";
private static final String FRAME_PROPERTY_CACHE = "_frame_properties";
/**
* @since 2.0 The id of the owning execution dmc
*/
public static final String EXECUTION_DMC_ID = "execution_dmc_id";
private final EDCServicesTracker dsfServicesTracker = Stack.this.getEDCServicesTracker();
private final IEDCExecutionDMC executionDMC;
private final int level;
private IAddress baseAddress;
private IAddress instructionPtrAddress;
private String moduleName = "";
private String sourceFile = "";
private String functionName = "";
private int lineNumber;
private IScope variableScope = null;
private List<VariableDMC> locals;
private List<EnumeratorDMC> enumerators;
private final Map<String, VariableDMC> localsByName = Collections
.synchronizedMap(new HashMap<String, VariableDMC>());
private final Map<String, EnumeratorDMC> enumeratorsByName = Collections
.synchronizedMap(new HashMap<String, EnumeratorDMC>());
private final Map<String, IVariable> thisPtrs = Collections
.synchronizedMap(new LinkedHashMap<String, IVariable>());
private IFunctionScope functionScope;
private IFrameRegisters frameRegisters;
public StackFrameDMC calledFrame;
private TypeEngine typeEngine;
private IEDCModuleDMContext module;
// additional items may be null but are usually set early and used repeatedly
private IAddress instrPtrLinkAddr = null;
private IEDCSymbolReader reader = null;
private IModuleLineEntryProvider provider = null;
private IDebugInfoProvider debugInfoProvider = null;
private IPath symbolFile = null;
/**
* @since 2.0
*/
@SuppressWarnings("unchecked")
public StackFrameDMC(final IEDCExecutionDMC executionDMC, EdcStackFrame edcFrame) {
super(Stack.this, new IDMContext[] { executionDMC }, createFrameID(executionDMC, edcFrame), edcFrame.props);
Map<String, Object> frameProperties = edcFrame.props;
this.executionDMC = executionDMC;
frameProperties.put(EXECUTION_DMC_ID, executionDMC.getID());
this.level = (Integer) frameProperties.get(LEVEL_INDEX);
this.moduleName = (String) frameProperties.get(MODULE_NAME);
this.baseAddress = address(frameProperties.get(BASE_ADDR));
this.instructionPtrAddress = address(frameProperties.get(INSTRUCTION_PTR_ADDR));
// compute the source location
IEDCSymbols symbolsService = getService(Symbols.class);
functionScope = symbolsService.getFunctionAtAddress(executionDMC.getSymbolDMContext(),
instructionPtrAddress);
boolean usingCachedProperties = false;
IEDCModules modules = dsfServicesTracker.getService(IEDCModules.class);
Map<IAddress, Map<String, Object>> cachedFrameProperties
= new HashMap<IAddress, Map<String, Object>>();
if (modules != null) {
module = modules.getModuleByAddress(executionDMC.getSymbolDMContext(), instructionPtrAddress);
if (module != null) {
instrPtrLinkAddr = module.toLinkAddress(instructionPtrAddress);
reader = module.getSymbolReader();
if (reader != null) {
symbolFile = this.reader.getSymbolFile();
if (symbolFile != null) {
// Check the persistent cache
String cacheKey = reader.getSymbolFile().toOSString() + FRAME_PROPERTY_CACHE;
Map<IAddress, Map<String, Object>> cachedData
= EDCDebugger.getDefault().getCache().getCachedData(cacheKey, Map.class,
reader.getModificationDate());
if (cachedData != null) {
cachedFrameProperties = cachedData;
Map<String, Object> cachedProperties
= cachedFrameProperties.get(instrPtrLinkAddr);
if (cachedProperties != null) {
if (cachedProperties.containsKey(SOURCE_FILE))
frameProperties.put(SOURCE_FILE, cachedProperties.get(SOURCE_FILE));
boolean cachedPropertiesHasFunctionName = false;
if (cachedProperties.containsKey(FUNCTION_NAME)) {
Object fnObj = cachedProperties.get(FUNCTION_NAME);
if (fnObj != null
&& fnObj instanceof String
&& ((String)fnObj).length() != 0) {
frameProperties.put(FUNCTION_NAME, fnObj);
cachedPropertiesHasFunctionName = true;
} }
if (!cachedPropertiesHasFunctionName) {
setFunctionName(executionDMC, frameProperties, symbolsService);
cachedProperties.put(FUNCTION_NAME, functionName);
}
if (cachedProperties.containsKey(LINE_NUMBER))
frameProperties.put(LINE_NUMBER, cachedProperties.get(LINE_NUMBER));
usingCachedProperties = true;
} } } } } } // null-checks on cachedProperties <= cachedData <= symbolFile
if (frameProperties.containsKey(SOURCE_FILE)) {
sourceFile = (String) frameProperties.get(SOURCE_FILE);
functionName = (String) frameProperties.get(FUNCTION_NAME);
lineNumber = (Integer) frameProperties.get(LINE_NUMBER);
} else if (frameProperties.containsKey(FUNCTION_NAME)) {
functionName = (String) frameProperties.get(FUNCTION_NAME);
} else if (!usingCachedProperties) {
ILineEntry line
= symbolsService.getLineEntryForAddress(executionDMC.getSymbolDMContext(),
instructionPtrAddress);
if (line != null)
setSourceProperties(frameProperties, line);
setFunctionName(executionDMC, frameProperties, symbolsService);
}
properties.putAll(frameProperties);
if (symbolFile != null) {
String cacheKey = symbolFile.toOSString() + FRAME_PROPERTY_CACHE;
cachedFrameProperties.put(this.instrPtrLinkAddr, frameProperties);
EDCDebugger.getDefault().getCache().putCachedData(cacheKey,
(Serializable)cachedFrameProperties,
this.reader.getModificationDate());
}
if (reader instanceof EDCSymbolReader)
debugInfoProvider = ((EDCSymbolReader)reader).getDebugInfoProvider();
typeEngine = new TypeEngine(getTargetEnvironmentService(), debugInfoProvider);
}
private void setFunctionName(final IEDCExecutionDMC executionDMC,
Map<String, Object> frameProperties, IEDCSymbols symbolsService) {
if (functionScope != null) {
// ignore inlined functions
IFunctionScope containerScope = functionScope;
while (containerScope.getParent() instanceof IFunctionScope) {
containerScope = (IFunctionScope) containerScope.getParent();
}
functionName = unmangle(containerScope.getName());
adjustFunctionSourceInfo(containerScope, frameProperties);
} else {
functionName
= unmangle(symbolsService.getSymbolNameAtAddress(executionDMC.getSymbolDMContext(),
instructionPtrAddress));
}
frameProperties.put(FUNCTION_NAME, functionName);
}
/**
* Modify the name to refer to the inline function within the parent function.
* <p>
* However, ignore the inline function name if the pointer is on the first
* line of the inline function and the "previous" line is
* <br> (a) in the parent function; or
* <br> (b) not in the original inline (meaning it was part of a prior inline); or
* <br> (c) is nested in another inline
* @param container the ultimate function containing the inline(s)
* @param frameProperties so source-file and line-number can also be adjusted
*/
private void adjustFunctionSourceInfo(IFunctionScope container,
Map<String, Object> frameProperties) {
if (functionScope.equals(container)) {
ILineEntry funcFirstEntry = this.getLineEntryInFunction(functionScope);
if (funcFirstEntry != null
&& !instrPtrLinkAddr.equals(funcFirstEntry.getLowAddress())) {
// this case covers the compiler having inline LNT entries
// whose bounds are outside the DWARF function scope boundaries
// for the inlines
setSourceProperties(frameProperties, funcFirstEntry);
}
return; // i.e. never fall through to "inline" re-naming below
}
ILineEntry containerEntry = this.getLineEntryInFunction(container);
if (containerEntry != null && isInlineShouldBeHidden(containerEntry)) {
setSourceProperties(frameProperties, containerEntry);
return;
}
this.functionName
= unmangle(functionScope.getName()) + " inlined in " + this.functionName;
}
/**
* Attempt to determine if the frame's instruction pointer is
* <br>(a) at the first instruction of an inlined function; and
* <br>(b) coincidentally at the first instruction of the line
* entry corresponding to the line that caused the inline to
* be generated.<p>
* @param entry if null, will be calculated based on established
* frame instruction pointer and function scope; can be passed
* in if caller needs line entry for other usage
* @return true if it can be determined that the instruction pointer is
* the first instruction of an inline function and coincidentally the
* first instruction of the line entry for which the inline was generated
* @since 2.0
*/
public boolean isInlineShouldBeHidden(ILineEntry entry) {
if (functionScope == null
|| !(functionScope.getParent() instanceof IFunctionScope)
|| !instrPtrLinkAddr.equals(functionScope.getLowAddress()))
return false;
if (entry == null) {
entry = getLineEntryInFunction(functionScope);
if (entry == null)
return false;
}
if (instrPtrLinkAddr.equals(entry.getLowAddress())) {
ILineEntry prevEntry = getPreviousLineEntry(entry, true);
if (prevEntry != null) {
ILineEntry testEntry = getNextLineEntry(prevEntry, true);
if (entry.equals(testEntry)) {
return true;
}
return false;
}
return true;
}
return false;
}
/**
* Private utility function to call the module's reader's provider's interfaces
* @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#getLineEntryInFunction
* @see IModuleScope#getModuleLineEntryProvider
*/
private ILineEntry getLineEntryInFunction(IFunctionScope func) {
return getModuleLineEntryProvider().getLineEntryInFunction(instrPtrLinkAddr, func);
}
/**
* Private utility function to call the module's reader's provider's interfaces
* @see IModuleScope#getModuleLineEntryProvider
* @return {@link IModuleLineEntryProvider} never <code>null</code>
*/
private IModuleLineEntryProvider getModuleLineEntryProvider() {
if (provider == null && reader != null) {
IModuleScope moduleScope = reader.getModuleScope();
if (moduleScope != null)
provider = moduleScope.getModuleLineEntryProvider();
}
return provider;
}
/**
* Private utility function to call the module's reader's provider's interfaces
* @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#getNextLineEntry
* @see IModuleScope#getModuleLineEntryProvider
*/
private ILineEntry getNextLineEntry(ILineEntry entry, boolean collapseInlineFunctions) {
return getModuleLineEntryProvider().getNextLineEntry(entry, collapseInlineFunctions);
}
/**
* Private utility function to call the module's reader's provider's interfaces
* @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#getPreviousLineEntry
* @see IModuleScope#getModuleLineEntryProvider
*/
private ILineEntry getPreviousLineEntry(ILineEntry entry, boolean collapseInlineFunctions) {
return getModuleLineEntryProvider().getPreviousLineEntry(entry, collapseInlineFunctions);
}
private void setSourceProperties(Map<String, Object> frameProperties,
ILineEntry entry) {
frameProperties.put(SOURCE_FILE, (sourceFile = entry.getFilePath().toOSString()));
frameProperties.put(LINE_NUMBER, (lineNumber = entry.getLineNumber()));
}
private String unmangle(String name) {
if (name == null)
return null;
// unmangle the name
IUnmangler unmangler = null;
if (reader instanceof EDCSymbolReader) {
unmangler = ((EDCSymbolReader) reader).getUnmangler();
}
if (unmangler == null) {
unmangler = new UnmanglerEABI();
}
if (!unmangler.isMangled(name))
return name;
try {
return unmangler.unmangleWithoutArgs(name);
} catch (UnmanglingException e) {
return name;
}
}
private IAddress address(Object obj) {
if (obj instanceof Integer)
return new Addr64(obj.toString());
if (obj instanceof Long)
return new Addr64(obj.toString());
if (obj instanceof String) // the string should be hex string
return new Addr64((String) obj, 16);
return null;
}
private void setInstructionPtrAddress(IAddress ipAddrPtr) {
this.instructionPtrAddress = ipAddrPtr;
if (module != null)
this.instrPtrLinkAddr = module.toLinkAddress(instructionPtrAddress);
}
public IFunctionScope getFunctionScope() {
return functionScope;
}
public String getModuleName() {
return moduleName;
}
/**
* Get source file name if any for the frame.
* @return valid file name or "" otherwise.
*/
public String getSourceFile() {
return sourceFile;
}
public String getFunctionName() {
return functionName;
}
public int getLineNumber() {
return lineNumber;
}
public IEDCExecutionDMC getExecutionDMC() {
return executionDMC;
}
public IAddress getBaseAddress() {
return baseAddress;
}
/**
* @since 2.0
*/
public IAddress getInstructionPtrAddress() {
return instructionPtrAddress;
}
public int getLevel() {
return level;
}
/**
* @since 2.0
*/
public EDCServicesTracker getEDCServicesTracker() {
return Stack.this.getEDCServicesTracker();
}
public int compareTo(StackFrameDMC f) {
if (level < f.level)
return -1;
if (level > f.level)
return +1;
return 0;
}
@Override
public String toString() {
return "StackFrameDMC [baseAddress=" + baseAddress.toHexAddressString() + ", ipAddress="
+ instructionPtrAddress.toHexAddressString() + ", sourceFile=" + sourceFile
+ ", functionName=" + functionName + ", lineNumber="
+ lineNumber + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + getOuterType().hashCode();
result = prime * result
+ ((baseAddress == null) ? 0 : baseAddress.hashCode());
result = prime * result
+ ((executionDMC == null) ? 0 : executionDMC.hashCode());
result = prime * result + level;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
StackFrameDMC other = (StackFrameDMC) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (baseAddress == null) {
if (other.baseAddress != null)
return false;
} else if (!baseAddress.equals(other.baseAddress))
return false;
if (executionDMC == null) {
if (other.executionDMC != null)
return false;
} else if (!executionDMC.equals(other.executionDMC))
return false;
if (level != other.level)
return false;
return true;
}
/**
* Finds a source file using the source lookup director.
*
* @param sourceFile the raw source file location, usually from the symbol data
*
* @return location of the source file
*/
private String findSourceFile(String sourceFile) {
String result = "";
CSourceLookup lookup = getService(CSourceLookup.class);
RunControl runControl = getService(RunControl.class);
CSourceLookupDirector[] directors = lookup.getSourceLookupDirectors(runControl.getRootDMC());
for (CSourceLookupDirector cSourceLookupDirector : directors) {
try {
Object[] elements = cSourceLookupDirector.findSourceElements(sourceFile);
if (elements != null && elements.length > 0)
{
Object element = elements[0];
if (element instanceof File) {
try {
result = (((File) element).getCanonicalPath());
} catch (IOException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
} else if (element instanceof IFile) {
result = (((IFile) element).getLocation().toOSString());
} else if (element instanceof IStorage) {
result = (((IStorage) element).getFullPath().toOSString());
} else if (element instanceof ITranslationUnit) {
result =(((ITranslationUnit) element).getLocation().toOSString());
}
break;
}
} catch (CoreException e1) {
EDCDebugger.getMessageLogger().logError(sourceFile, e1);
}
}
return result;
}
/**
* @since 2.0
*/
public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) {
Element contextElement = document.createElement(STACK_FRAME);
contextElement.setAttribute(PROP_ID, this.getID());
Element propsElement = SnapshotUtils.makeXMLFromProperties(document, getProperties());
contextElement.appendChild(propsElement);
// Locate the actual source file to be included in the album.
if (sourceFile.length() > 0) // No source file for this frame (just module/address)
album.addFile(new Path(findSourceFile(sourceFile)));
return contextElement;
}
@SuppressWarnings("unchecked")
public void loadSnapshot(Element element) {
// fix up registers to use integers again
Map<String, String> preservedRegisters = (Map<String, String>) properties.get(PRESERVED_REGISTERS);
if (preservedRegisters != null) {
Map<Integer, BigInteger> newPreservedRegisters = new HashMap<Integer, BigInteger>();
for (Map.Entry<String, String> entry : preservedRegisters.entrySet()) {
newPreservedRegisters.put(Integer.valueOf(entry.getKey().toString()), new BigInteger(entry.getValue().toString()));
}
properties.put(PRESERVED_REGISTERS, newPreservedRegisters);
}
}
public IVariableDMContext[] getLocals() {
return getLocals(/* boolean useCachedVariables => */ true);
}
private IVariableDMContext[] getLocals(boolean useCachedVariables) {
// may need to refresh the locals list because "Show All Variables"
// toggle has changed
if (showAllVariablesEnabled == null) {
IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
showAllVariablesEnabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, false);
}
boolean enabled = showAllVariablesEnabled.booleanValue();
if (locals != null) {
IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
enabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, showAllVariablesEnabled);
}
if (locals == null || !useCachedVariables || enabled != showAllVariablesEnabled) {
showAllVariablesEnabled = enabled;
locals = new ArrayList<VariableDMC>();
localsByName.clear();
thisPtrs.clear();
IEDCSymbols symbolsService = getService(IEDCSymbols.class);
IFunctionScope scope = symbolsService
.getFunctionAtAddress(executionDMC.getSymbolDMContext(), instructionPtrAddress);
if (scope != null) {
this.variableScope = scope;
}
while (scope != null && instrPtrLinkAddr != null) {
Collection<IVariable> scopedVariables = scope.getScopedVariables(instrPtrLinkAddr);
for (IVariable variable : scopedVariables) {
VariableDMC var = new VariableDMC(Stack.this, this, variable);
String name = variable.getName();
// because of inlined functions, debugger information may indicate that
// more than one "this" pointer is live at one time
if (name != null && name.equals("this")) {
thisPtrs.put(variable.getScope().getName(), variable);
} else {
// now that we've screened out compiler generated "this" variables,
// get rid of other compiler generated variables
// TODO: Allow user to choose whether to show compiler generated variables
if (var.getVariable().isDeclared()) {
VariableDMC haveLocal = localsByName.get(name);
if (haveLocal != null) {
localsByName.remove(name);
locals.remove(haveLocal);
}
locals.add(var);
localsByName.put(name, var);
}
}
}
// if requesting to show all variables, add file-scope globals too
// (this isn't nearly sufficient since globals can show up
// in a header while all code is in the source file)
IScope parentScope = null;
if (showAllVariablesEnabled)
parentScope = scope.getParent();
while (parentScope != null) {
if (parentScope instanceof ICompileUnitScope) {
ICompileUnitScope cuScope = ((ICompileUnitScope) parentScope);
List<ICompileUnitScope> cuScopes = null;
if (this.debugInfoProvider != null) {
cuScopes = debugInfoProvider.getCompileUnitsForFile(cuScope.getFilePath());
} else {
cuScopes = new ArrayList<ICompileUnitScope>(1);
cuScopes.add(cuScope);
}
// add the globals of all compile unit scopes for the source file
String cuFile = ((ICompileUnitScope) parentScope).getFilePath().toOSString();
for (ICompileUnitScope nextCuScope : cuScopes) {
Collection<IVariable> globals = nextCuScope.getVariables();
if (globals != null) {
for (IVariable variable : globals) {
IPath varFile = variable.getDefiningFile();
if (varFile != null && !varFile.toOSString().equalsIgnoreCase(cuFile))
continue;
VariableDMC var = new VariableDMC(Stack.this, this, variable);
String name = var.getName();
VariableDMC haveLocal = localsByName.get(name);
if (haveLocal != null) {
localsByName.remove(name);
locals.remove(haveLocal);
}
locals.add(var);
localsByName.put(name, var);
}
}
}
}
parentScope = parentScope.getParent();
}
if (!(scope.getParent() instanceof IFunctionScope))
break;
scope = (IFunctionScope) scope.getParent();
}
}
// start with "this" pointers, if any
VariableDMC[] localsArray = new VariableDMC[(thisPtrs.isEmpty() ? 0 : 1) + locals.size()];
int i = 0;
if (!thisPtrs.isEmpty())
localsArray[i++] = new VariableDMC(Stack.this, this, getOuterThis());
// TODO For now, turn off ability to see multiple this pointers
// of the form "this$ScopeName"
// for (IVariable variable : thisPtrs.values()) {
// VariableDMC var = new VariableDMC(Stack.this, this, variable);
// var.setName("this$" + variable.getScope().getName());
// localsArray[i++] = var;
// }
for (VariableDMC var : locals)
localsArray[i++] = var;
return localsArray;
}
/**
* From a list of "this" pointers in scope, return the one from the outermost scope
* @return this pointer from the outermost scope
*/
private IVariable getOuterThis() {
if (thisPtrs.isEmpty())
return null;
if (thisPtrs.size() == 1)
return thisPtrs.values().iterator().next();
IVariable outer = null;
for (IVariable variable : thisPtrs.values()) {
if (outer == null)
outer = variable;
else {
IScope outerScope = outer.getScope();
IScope variableScope = variable.getScope();
if ( variableScope.getLowAddress().compareTo(outerScope.getLowAddress()) < 0
|| variableScope.getHighAddress().compareTo(outerScope.getHighAddress()) > 0)
outer = variable;
}
}
return outer;
}
/**
* Find a variable or enumerator by name
*
* @param name required name of the variable or enumerator
* @param qualifiedName optional fully qualified name of the variable or enumerator
* @param localsOnly whether to restrict search to local variables and enumerators only
* @return variable or enumerator, if found; otherwise, null
* @since 2.0
*/
public IVariableEnumeratorContext findVariableOrEnumeratorByName(String name, String qualifiedName, boolean localsOnly) {
if (name == null)
return null;
if (locals == null)
getLocals();
// quickly check for a local variable or enumerator
IVariableEnumeratorContext variableOrEnumerator;
if (qualifiedName != null) {
variableOrEnumerator = localsByName.get(qualifiedName);
if (variableOrEnumerator != null)
return variableOrEnumerator;
}
variableOrEnumerator = localsByName.get(name);
if (variableOrEnumerator != null)
return variableOrEnumerator;
if (enumerators == null)
getEnumerators();
if (qualifiedName != null) {
variableOrEnumerator = enumeratorsByName.get(qualifiedName);
if (variableOrEnumerator != null)
return variableOrEnumerator;
}
variableOrEnumerator = enumeratorsByName.get(name);
if (variableOrEnumerator != null)
return variableOrEnumerator;
if (name.equals("this")) {
if (thisPtrs.isEmpty())
return null;
return new VariableDMC(Stack.this, this, getOuterThis());
}
// TODO For now, turn off ability to see multiple this pointers
// of the form "this$ScopeName"
// if (name.startsWith("this$")) {
// // return the one with the right scope
// if (thisPtrs.isEmpty())
// return null;
// IVariable variable = thisPtrs.get(name.substring("this$".length()));
// if (variable == null)
// return null;
// return new VariableDMC(Stack.this, this, variable);
// }
if (localsOnly || this.getVariableScope() == null)
return null;
// if there is no local variable or enumerator with this name, not very
// efficiently check enclosing scopes for a variable or enumerator
IScope variableScope = this.getVariableScope().getParent();
// to find file scope variables, we may need to check several compile units
// associated with one file
ArrayList<IScope> scopes = new ArrayList<IScope>();
while (variableOrEnumerator == null && variableScope != null) {
// At the module level, match against globals across the entire symbol
// file, even for big symbol files.
if (variableScope instanceof IModuleScope) {
Collection<IVariable> variables = ((IModuleScope)variableScope).getVariablesByName(qualifiedName != null ? qualifiedName : name, true);
if (variables != null && variables.size() > 0) {
// list may contain non-global variables, so return the first global
for (Object varObject : variables) {
if (varObject instanceof IVariable) {
IVariable variable = (IVariable)varObject;
if (variable.getScope() instanceof IModuleScope) {
variableOrEnumerator = new VariableDMC(Stack.this, this, variable);
break;
}
}
}
}
// module scope has no matching global variables
break;
}
scopes.clear();
if (variableScope instanceof ICompileUnitScope) {
// there may be several compile units for a file
// find the module scope parent of the compile unit
IScope parent = variableScope.getParent();
while (parent != null && !(parent instanceof IModuleScope))
parent = parent.getParent();
// find all compile units for the file
if (parent != null) {
IPath currentFile = ((ICompileUnitScope)variableScope).getFilePath();
if (currentFile != null)
for (ICompileUnitScope cu : ((IModuleScope)parent).getCompileUnitsForFile(currentFile))
scopes.add(cu);
}
}
if (scopes.isEmpty())
scopes.add(variableScope);
for (IScope scope : scopes) {
for (IVariable scopeVariable : scope.getVariables()) {
String scopeVariableName = scopeVariable.getName();
if (qualifiedName != null && scopeVariableName.equals(qualifiedName)) {
variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
break;
}
if (scopeVariableName.equals(name)) {
variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
break;
}
}
if (variableOrEnumerator == null && scope instanceof IFunctionScope) {
IFunctionScope functionScope = (IFunctionScope)scope;
for (IVariable scopeVariable : functionScope.getParameters()) {
String scopeVariableName = scopeVariable.getName();
if (qualifiedName != null && scopeVariableName.equals(qualifiedName)) {
variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
break;
}
if (scopeVariableName.equals(name)) {
variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
break;
}
}
}
if (variableOrEnumerator == null) {
for (IEnumerator scopeEnumerator : scope.getEnumerators()) {
String scopeEnumeratorName = scopeEnumerator.getName();
if (qualifiedName != null && scopeEnumeratorName.equals(qualifiedName)) {
variableOrEnumerator = new EnumeratorDMC(this, scopeEnumerator);
break;
}
if (scopeEnumeratorName.equals(name)) {
variableOrEnumerator = new EnumeratorDMC(this, scopeEnumerator);
break;
}
}
}
}
variableScope = variableScope.getParent();
}
return variableOrEnumerator;
}
public IScope getVariableScope() {
return variableScope;
}
public EnumeratorDMC[] getEnumerators() {
if (enumerators == null) {
enumerators = new ArrayList<EnumeratorDMC>();
if (getServicesTracker() != null) {
IEDCSymbols symbolsService = getService(Symbols.class);
if (executionDMC != null && symbolsService != null) {
IFunctionScope scope = symbolsService.getFunctionAtAddress(executionDMC.getSymbolDMContext(),
instructionPtrAddress);
while (scope != null) {
Collection<IEnumerator> localEnumerators = scope.getEnumerators();
for (IEnumerator enumerator : localEnumerators) {
EnumeratorDMC enumeratorDMC = new EnumeratorDMC(this, enumerator);
enumerators.add(enumeratorDMC);
enumeratorsByName.put(enumerator.getName(), enumeratorDMC);
}
if (!(scope.getParent() instanceof IFunctionScope))
break;
scope = (IFunctionScope) scope.getParent();
}
}
}
}
return enumerators.toArray(new EnumeratorDMC[enumerators.size()]);
}
public EnumeratorDMC findEnumeratorbyName(String name) {
if (enumerators == null)
getEnumerators();
return enumeratorsByName.get(name);
}
/**
* Get the view onto registers for this stack frame. For the top stack frame, this
* forwards to the {@link Registers} service. Otherwise, this information
* is synthesized from unwind information in the debug information.
* @return {@link IFrameRegisters}, never <code>null</code>
*/
@SuppressWarnings("unchecked")
public IFrameRegisters getFrameRegisters() {
if (frameRegisters == null) {
if (level == 0) {
// for top of stack, the registers service does the work
final Registers registers = getEDCServicesTracker().getService(Registers.class);
frameRegisters = new CurrentFrameRegisters(executionDMC, registers);
} else {
// see if symbolics can provide unwinding support
if (module != null) {
Symbols symbolsService = getService(Symbols.class);
IFrameRegisterProvider frameRegisterProvider = symbolsService.getFrameRegisterProvider(
executionDMC.getSymbolDMContext(), instructionPtrAddress);
if (frameRegisterProvider != null) {
try {
frameRegisters = frameRegisterProvider.getFrameRegisters(
getSession(), getEDCServicesTracker(), this);
} catch (CoreException e) {
// debug info failure; we should report this
frameRegisters = new AlwaysFailingFrameRegisters(e);
}
}
}
if (frameRegisters == null) {
// no information from symbolics; see if the stack unwinder found anything
final Map<Integer, BigInteger> preservedRegisters = (Map<Integer,BigInteger>) properties.get(
PRESERVED_REGISTERS);
if (preservedRegisters != null) {
frameRegisters = new PreservedFrameRegisters(dsfServicesTracker, StackFrameDMC.this, preservedRegisters);
}
}
if (frameRegisters == null) {
frameRegisters = new AlwaysFailingFrameRegisters(
EDCDebugger.newCoreException("cannot read variables in this frame"));
}
}
}
return frameRegisters;
}
/**
* Get the frame this one has called.
* @return StackFrameDMC or <code>null</code> for top of stack
*/
public StackFrameDMC getCalledFrame() throws CoreException {
return calledFrame;
}
/**
* Get a type engine (which holds cached information about types for use by expressions)
* @return TypeEngine instance
*/
public TypeEngine getTypeEngine() {
return typeEngine;
}
public IEDCModuleDMContext getModule() {
return module;
}
private Stack getOuterType() {
return Stack.this;
}
}
public class VariableData implements IVariableDMData {
private final String name;
public VariableData(VariableDMC variableDMC) {
name = variableDMC.getName();
}
public String getName() {
return name;
}
/**
* was initially implemented as "0".
* currently returns null to guarantee anything calling this
* knows it's getting a useless bit of data.
* @see org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData#getValue()
*/
public String getValue() {
// TODO not implemented
return null;
}
}
public class VariableDMC extends DMContext implements IVariableDMContext, IVariableEnumeratorContext {
public static final String PROP_LOCATION = "Location";
private final IVariable variable;
public VariableDMC(IDsfService service, StackFrameDMC frame, IVariable variable) {
super(Stack.this, new IDMContext[] { frame }, variable.getName(), variable.getName());
this.variable = variable;
}
public IVariable getVariable() {
return variable;
}
}
public class EnumeratorDMC extends DMContext implements IEnumeratorDMContext, IVariableEnumeratorContext {
private final IEnumerator enumerator;
public EnumeratorDMC(StackFrameDMC frame, IEnumerator enumerator) {
super(Stack.this, new IDMContext[] { frame }, enumerator.getName(), enumerator.getName());
this.enumerator = enumerator;
}
public IEnumerator getEnumerator() {
return enumerator;
}
}
/**
* @param classNames
* the type names the service will be registered under. See
* AbstractDsfService#register for details. We tack on base DSF's
* IStack and this class to the list if not provided.
*/
public Stack(DsfSession session, String[] classNames) {
super(session,
massageClassNames(classNames,
new String[] { IStack.class.getName(), Stack.class.getName() }));
}
/**
* @since 2.0
*/
public static String createFrameID(IEDCExecutionDMC executionDMC, EdcStackFrame edcFrame) {
int level = (Integer) edcFrame.props.get(StackFrameDMC.LEVEL_INDEX);
String parentID = executionDMC.getID();
return parentID + ".frame[" + level + "]";
}
@Override
protected void doInitialize(RequestMonitor requestMonitor) {
super.doInitialize(requestMonitor);
getSession().addServiceEventListener(this, null);
}
public void getArguments(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm) {
// never called by DSF. it expects arguments to be lumped in with
// locals.
rm.done();
}
public void getFrameData(IFrameDMContext frameDmc, DataRequestMonitor<IFrameDMData> rm) {
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(frameDmc)); }
rm.setData(new StackFrameData((StackFrameDMC) frameDmc));
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(rm.getData())); }
rm.done();
}
public void getFrames(final IDMContext execContext, final DataRequestMonitor<IFrameDMContext[]> rm) {
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(execContext)); }
final ExecutionDMC execDmc = DMContexts.getAncestorOfType(execContext, ExecutionDMC.class);
if (execDmc != null)
{
if (!execDmc.isSuspended())
{
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "Context is running: " + execDmc, null)); //$NON-NLS-1$
rm.done();
return;
}
asyncExec(new Runnable() {
public void run() {
try {
rm.setData(getFramesForDMC((ExecutionDMC) execContext, 0, ALL_FRAMES));
if (rm.getData().length == 0)
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "No stack frame available for: " + execDmc, null)); //$NON-NLS-1$
} catch (CoreException e) {
Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
EDCDebugger.getMessageLogger().log(s);
rm.setStatus(s);
}
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(rm.getData())); }
rm.done();
}
}, rm);
}
else {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
}
}
public void getLocals(final IFrameDMContext frameCtx, final DataRequestMonitor<IVariableDMContext[]> rm) {
asyncExec(new Runnable() {
public void run() {
final StackFrameDMC frameContext = (StackFrameDMC) frameCtx;
IAddress contextIPAddress = frameContext.getInstructionPtrAddress();
boolean useVariableCache = false;
// the frame context passed in may be "stale". it may prove equal to the current frame,
// but if the instruction ptr address is different, then the locals won't be collected properly
try {
IFrameDMContext[] iFrames = getFramesForDMC(frameContext.getExecutionDMC(), 0, ALL_FRAMES);
for (IFrameDMContext iFrameDMC : iFrames) {
if (frameCtx == iFrameDMC) {
useVariableCache = true;
break;
}
if (frameContext.equals(iFrameDMC)) {
StackFrameDMC frameDMC = (StackFrameDMC)iFrameDMC;
IAddress stackFrameIPAddr = frameDMC.getInstructionPtrAddress();
if (contextIPAddress.equals(stackFrameIPAddr)) {
useVariableCache = true;
} else {
frameContext.setInstructionPtrAddress(stackFrameIPAddr);
}
break;
}
}
rm.setData(frameContext.getLocals(useVariableCache));
} catch (CoreException e) {
EDCDebugger.getMessageLogger().log(e.getStatus());
rm.setStatus(e.getStatus());
}
rm.done();
}
}, rm);
}
public void getStackDepth(IDMContext dmc, final int maxDepth, final DataRequestMonitor<Integer> rm) {
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { dmc, maxDepth })); }
final ExecutionDMC execDmc = DMContexts.getAncestorOfType(dmc, ExecutionDMC.class);
if (execDmc != null)
{
if (!execDmc.isSuspended())
{
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "Context is running: " + execDmc, null)); //$NON-NLS-1$
rm.done();
return;
}
asyncExec(new Runnable() {
public void run() {
int startFrame = 0;
int endFrame = ALL_FRAMES;
if (maxDepth > 0)
endFrame = maxDepth - 1;
try {
rm.setData(getFramesForDMC(execDmc, startFrame, endFrame).length);
if (rm.getData() == 0)
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "No stack frame available for: " + execDmc, null)); //$NON-NLS-1$
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, rm.getData()); }
} catch (CoreException e) {
Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
EDCDebugger.getMessageLogger().log(s);
rm.setStatus(s);
}
rm.done();
}
}, rm);
}
else {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
}
}
public void getTopFrame(final IDMContext execContext, final DataRequestMonitor<IFrameDMContext> rm) {
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(execContext)); }
asyncExec(new Runnable() {
public void run() {
try {
IFrameDMContext[] frames = getFramesForDMC((ExecutionDMC) execContext, 0, 0);
if (frames.length == 0) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE,
"No top stack frame available", null)); //$NON-NLS-1$
rm.done();
return;
}
rm.setData(frames[0]);
} catch (CoreException e) {
Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
EDCDebugger.getMessageLogger().log(s);
rm.setStatus(s);
}
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(rm.getData())); }
rm.done();
}
}, rm);
}
public void getVariableData(IVariableDMContext variableDmc, DataRequestMonitor<IVariableDMData> rm) {
rm.setData(new VariableData((VariableDMC) variableDmc));
rm.done();
}
@SuppressWarnings("unchecked")
public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
if (dmc instanceof IFrameDMContext) {
getFrameData((IFrameDMContext) dmc, (DataRequestMonitor<IFrameDMData>) rm);
} else if (dmc instanceof IVariableDMContext) {
getVariableData((IVariableDMContext) dmc, (DataRequestMonitor<IVariableDMData>) rm);
} else
rm.done();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IStack#getFrames(org.eclipse.cdt.dsf.datamodel.IDMContext, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
public void getFrames(final IDMContext execContext, final int startIndex, final int endIndex, final DataRequestMonitor<IFrameDMContext[]> rm) {
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { execContext, startIndex, endIndex })); }
final ExecutionDMC execDmc = DMContexts.getAncestorOfType(execContext, ExecutionDMC.class);
if (execDmc != null)
{
if (!execDmc.isSuspended())
{
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "Context is running: " + execDmc, null)); //$NON-NLS-1$
rm.done();
return;
}
asyncExec(new Runnable() {
public void run() {
try {
rm.setData(getFramesForDMC((ExecutionDMC) execContext, startIndex, endIndex));
if (rm.getData().length == 0)
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "No stack frame available for: " + execContext, null)); //$NON-NLS-1$
} catch (CoreException e) {
Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
EDCDebugger.getMessageLogger().log(s);
rm.setStatus(s);
}
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(rm.getData())); }
rm.done();
}
}, rm);
}
else {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
}
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(rm.getData())); }
}
public IFrameDMContext[] getFramesForDMC(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException {
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, startIndex, endIndex })); }
if (!context.isSuspended() ||
! RunControl.isNonContainer(context)) // no frames for container context.
{
return new IFrameDMContext[0];
}
boolean needsUpdate = false;
synchronized (stackFrames) {
List<StackFrameDMC> frames = stackFrames.get(context.getID());
// Need to update the frames if there is no cached list for this
// context or if the cached list does not include all of the
// requested frames.
if (frames == null) {
// nothing in the cache so need to update
needsUpdate = true;
} else if (allFramesCached.containsKey(context.getID()) && allFramesCached.get(context.getID())) {
// all frames are cached
needsUpdate = false;
} else if (endIndex == ALL_FRAMES) {
// some but not all frames cached
needsUpdate = true;
} else {
// some but not all requested frames cached
needsUpdate = (frames.get(0).getLevel() > startIndex ||
frames.get(frames.size() - 1).getLevel() < endIndex);
}
if (needsUpdate)
updateFrames(context, startIndex, endIndex);
frames = stackFrames.get(context.getID());
// endIndex is inclusive and may be negative to fetch all frames
if (endIndex >= 0) {
if (startIndex < frames.size() && startIndex <= endIndex) {
frames = frames.subList(startIndex, Math.min(endIndex + 1, frames.size()));
} else {
frames = Collections.emptyList();
}
}
IFrameDMContext[] result = frames.toArray(new IFrameDMContext[frames.size()]);
if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(result)); }
return result;
}
}
private void updateFrames(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException {
ArrayList<StackFrameDMC> frames = new ArrayList<StackFrameDMC>();
List<EdcStackFrame> edcFrames = computeStackFrames(context, startIndex, endIndex);
StackFrameDMC previous = null;
for (EdcStackFrame edcFrame : edcFrames) {
StackFrameDMC frame = new StackFrameDMC(context, edcFrame);
if (previous != null) {
frame.calledFrame = previous;
// note: don't store "callerFrame" since this is missing if only a partial stack was fetched
}
frames.add(frame);
previous = frame;
}
stackFrames.put(context.getID(), frames);
// all frames are cached if we request all frames, or if the returned number of frames was less than
// the requested max number of frames. e.g. if we ask for 10 and they return 9, it's because there
// are only 9 frames. so we have calculated all of them.
allFramesCached.put(context.getID(), startIndex == 0 && ((endIndex == ALL_FRAMES) || (frames.size() <= endIndex)));
}
/**
* A stack frame described as one or more of the following properties, plus
* any additional custom ones.
*
* <ul>
* <li>{@link StackFrameDMC#LEVEL_INDEX}
* <li>{@link StackFrameDMC#ROOT_FRAME}
* <li>{@link StackFrameDMC#BASE_ADDR}
* <li>{@link StackFrameDMC#INSTRUCTION_PTR_ADDR}
* <li>{@link StackFrameDMC#MODULE_NAME}
* <li>{@link StackFrameDMC#SOURCE_FILE}
* <li>{@link StackFrameDMC#FUNCTION_NAME}
* <li>{@link StackFrameDMC#LINE_NUMBER}
* <li>{@link StackFrameDMC#IN_PROLOGUE}
* <li>{@link StackFrameDMC#PRESERVED_REGISTERS}
* </ul>
*
* @since 2.0
*/
public class EdcStackFrame {
public EdcStackFrame(Map<String, Object> props) {
this.props = props;
}
public Map<String, Object> props;
}
protected abstract List<EdcStackFrame> computeStackFrames(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException;
public void loadFramesForContext(IEDCExecutionDMC exeDmc, Element allFrames) throws Exception {
flushCache(null);
List<StackFrameDMC> frames = Collections.synchronizedList(new ArrayList<StackFrameDMC>());
NodeList frameElements = allFrames.getElementsByTagName(STACK_FRAME);
int numFrames = frameElements.getLength();
StackFrameDMC previousFrameDMC = null;
for (int i = 0; i < numFrames; i++) {
Element groupElement = (Element) frameElements.item(i);
Element propElement = (Element) groupElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
HashMap<String, Object> properties = new HashMap<String, Object>();
SnapshotUtils.initializeFromXML(propElement, properties);
// ensure that stack level numbering is canonical:
// we expect level==0 to be the top, but it used to be 1
properties.put(StackFrameDMC.LEVEL_INDEX, i);
StackFrameDMC frameDMC = new StackFrameDMC(exeDmc, new EdcStackFrame(properties));
frameDMC.loadSnapshot(groupElement);
if (previousFrameDMC != null) {
frameDMC.calledFrame = previousFrameDMC;
}
frames.add(frameDMC);
previousFrameDMC = frameDMC;
}
stackFrames.put(exeDmc.getID(), frames);
allFramesCached.put(exeDmc.getID(), true);
}
public void flushCache(IDMContext context) {
if (isSnapshot())
return;
if (context != null && context instanceof IEDCDMContext) {
String contextID = ((IEDCDMContext) context).getID();
stackFrames.remove(contextID);
allFramesCached.remove(contextID);
} else {
stackFrames.clear();
allFramesCached.clear();
}
}
@DsfServiceEventHandler
public void eventDispatched(ISuspendedDMEvent e) {
flushCache(e.getDMContext());
}
@DsfServiceEventHandler
public void eventDispatched(IResumedDMEvent e) {
flushCache(e.getDMContext());
}
}