blob: ed3bb40b802c9a3fbc7b7aadfe9e92b95cef2ec6 [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.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.IEDCTraceOptions;
import org.eclipse.cdt.debug.edc.internal.launch.CSourceLookup;
import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules;
import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleDMC;
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.dwarf.EDCSymbolReader;
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.IModuleScope;
import org.eclipse.cdt.debug.edc.symbols.IScope;
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.DsfServicesTracker;
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.getIPAddress();
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;
}
}
/**
* 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)
registers.writeRegister(executionDMC, id, value.toString(16));
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 DsfServicesTracker dsfServicesTracker;
private final StackFrameDMC context;
/**
* @param preservedRegisters map of register number to the address
* where the register is saved
*/
public PreservedFrameRegisters(DsfServicesTracker 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";
public static final String IP_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";
private final DsfServicesTracker dsfServicesTracker = getServicesTracker();
private final IEDCExecutionDMC executionDMC;
private final int level;
private IAddress baseAddress;
private IAddress ipAddress;
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 IFunctionScope functionScope;
private IFrameRegisters frameRegisters;
public StackFrameDMC calledFrame;
private TypeEngine typeEngine;
private IEDCModuleDMContext module;
@SuppressWarnings("unchecked")
public StackFrameDMC(final IEDCExecutionDMC executionDMC, Map<String, Object> frameProperties) {
super(Stack.this, new IDMContext[] { executionDMC }, createFrameID(executionDMC, frameProperties), frameProperties);
this.executionDMC = executionDMC;
this.level = (Integer) frameProperties.get(LEVEL_INDEX);
this.moduleName = (String) frameProperties.get(MODULE_NAME);
Object base = frameProperties.get(BASE_ADDR);
if (base instanceof Integer)
this.baseAddress = new Addr64(base.toString());
if (base instanceof Long)
this.baseAddress = new Addr64(base.toString());
if (base instanceof String) // the string should be hex string
this.baseAddress = new Addr64((String) base, 16);
Object ipAddr = frameProperties.get(IP_ADDR);
if (ipAddr instanceof Integer)
this.ipAddress = new Addr64(ipAddr.toString());
if (ipAddr instanceof Long)
this.ipAddress = new Addr64(ipAddr.toString());
if (ipAddr instanceof String)
this.ipAddress = new Addr64((String) ipAddr, 16);
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(), ipAddress);
if (module != null) {
IEDCSymbolReader reader = module.getSymbolReader();
if (reader != null)
{
IPath symbolFile = 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(module.toLinkAddress(ipAddress));
if (cachedProperties != null)
{
if (cachedProperties.containsKey(SOURCE_FILE))
frameProperties.put(SOURCE_FILE, cachedProperties.get(SOURCE_FILE));
if (cachedProperties.containsKey(FUNCTION_NAME))
frameProperties.put(FUNCTION_NAME, cachedProperties.get(FUNCTION_NAME));
if (cachedProperties.containsKey(LINE_NUMBER))
frameProperties.put(LINE_NUMBER, cachedProperties.get(LINE_NUMBER));
usingCachedProperties = true;
}
}
}
}
}
}
if (frameProperties.containsKey(SOURCE_FILE)) {
this.sourceFile = (String) frameProperties.get(SOURCE_FILE);
this.functionName = (String) frameProperties.get(FUNCTION_NAME);
this.lineNumber = (Integer) frameProperties.get(LINE_NUMBER);
} else {
if (!usingCachedProperties)
{
// compute the source location
IEDCSymbols symbolsService = getServicesTracker().getService(Symbols.class);
ILineEntry line = symbolsService.getLineEntryForAddress(executionDMC.getSymbolDMContext(), ipAddress);
if (line != null) {
sourceFile = line.getFilePath().toOSString();
frameProperties.put(SOURCE_FILE, sourceFile);
lineNumber = line.getLineNumber();
frameProperties.put(LINE_NUMBER, lineNumber);
}
functionScope = symbolsService
.getFunctionAtAddress(executionDMC.getSymbolDMContext(), ipAddress);
if (functionScope != null) {
// ignore inlined functions
while (functionScope.getParent() instanceof IFunctionScope) {
functionScope = (IFunctionScope) functionScope.getParent();
}
functionName = functionScope.getName();
frameProperties.put(FUNCTION_NAME, functionName);
}
}
}
properties.putAll(frameProperties);
// get the type engine
IDebugInfoProvider debugInfoProvider = null;
if (modules != null) {
module = modules.getModuleByAddress(executionDMC.getSymbolDMContext(), ipAddress);
if (module != null) {
IEDCSymbolReader symbolReader = module.getSymbolReader();
if (symbolReader != null)
{
if (symbolReader instanceof EDCSymbolReader) {
debugInfoProvider = ((EDCSymbolReader) symbolReader).getDebugInfoProvider();
}
if (symbolReader.getSymbolFile() != null)
{
String cacheKey = symbolReader.getSymbolFile().toOSString() + FRAME_PROPERTY_CACHE;
cachedFrameProperties.put(module.toLinkAddress(ipAddress), frameProperties);
EDCDebugger.getDefault().getCache().putCachedData(cacheKey, (Serializable) cachedFrameProperties, symbolReader.getModificationDate());
}
}
}
}
typeEngine = new TypeEngine(dsfServicesTracker, debugInfoProvider);
}
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;
}
public IAddress getIPAddress() {
return ipAddress;
}
public int getLevel() {
return level;
}
public DsfServicesTracker getDsfServicesTracker() {
return dsfServicesTracker;
}
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="
+ ipAddress.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
+ ((ipAddress == null) ? 0 : ipAddress.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 (ipAddress == null) {
if (other.ipAddress != null)
return false;
} else if (!ipAddress.equals(other.ipAddress))
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 = getServicesTracker().getService(CSourceLookup.class);
RunControl runControl = getServicesTracker().getService(RunControl.class);
CSourceLookupDirector director = lookup.getSourceLookupDirector(runControl.getRootDMC());
try {
Object[] elements = director.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());
}
}
} catch (CoreException e1) {
EDCDebugger.getMessageLogger().logError(sourceFile, e1);
}
return result;
}
public Element takeShapshot(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() {
// may need to refresh the locals list because "Show All Variables"
// toggle has changed
if (showAllVariablesEnabled == null) {
IEclipsePreferences scope = new InstanceScope().getNode(EDCDebugger.PLUGIN_ID);
showAllVariablesEnabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, false);
}
Boolean enabled = showAllVariablesEnabled;
if (locals != null) {
IEclipsePreferences scope = new InstanceScope().getNode(EDCDebugger.PLUGIN_ID);
enabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, showAllVariablesEnabled);
}
if (locals == null || (locals != null && enabled != showAllVariablesEnabled)) {
showAllVariablesEnabled = enabled;
locals = new ArrayList<VariableDMC>();
IEDCSymbols symbolsService = getServicesTracker().getService(Symbols.class);
IFunctionScope scope = symbolsService
.getFunctionAtAddress(executionDMC.getSymbolDMContext(), ipAddress);
if (scope != null) {
this.variableScope = scope;
}
// TODO: we fetch ModuleDMC a whole lot; it could be saved in a StackFrameDMC
IEDCModules modulesService = getServicesTracker().getService(Modules.class);
IEDCModuleDMContext module = modulesService.getModuleByAddress(executionDMC.getSymbolDMContext(), ipAddress);
IAddress linkAddress = null;
if (module != null) {
linkAddress = module.toLinkAddress(ipAddress);
}
while (scope != null) {
Collection<IVariable> scopedVariables = scope.getScopedVariables(linkAddress);
for (IVariable variable : scopedVariables) {
VariableDMC var = new VariableDMC(Stack.this, this, variable);
locals.add(var);
localsByName.put(var.getName(), 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);
// there may be multiple compile unit scopes for the same source file,
// so look for a debug info provider to find multiples
IDebugInfoProvider debugInfoProvider = null;
IEDCSymbolReader symbolReader = module.getSymbolReader();
if (symbolReader instanceof EDCSymbolReader) {
debugInfoProvider = ((EDCSymbolReader) symbolReader).getDebugInfoProvider();
}
List<ICompileUnitScope> cuScopes = null;
if (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
for (ICompileUnitScope nextCuScope : cuScopes) {
Collection<IVariable> globals = nextCuScope.getVariables();
if (globals != null) {
for (IVariable variable : globals) {
VariableDMC var = new VariableDMC(Stack.this, this, variable);
locals.add(var);
localsByName.put(var.getName(), var);
}
}
}
}
parentScope = parentScope.getParent();
}
if (!(scope.getParent() instanceof IFunctionScope))
break;
scope = (IFunctionScope) scope.getParent();
}
}
return locals.toArray(new VariableDMC[locals.size()]);
}
/**
* Find a variable or enumerator by name
*
* @param name 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
*/
public IVariableEnumeratorContext findVariableOrEnumeratorByName(String name, boolean localsOnly) {
if (locals == null)
getLocals();
// quickly check for a local variable or enumerator
IVariableEnumeratorContext variableOrEnumerator;
variableOrEnumerator = localsByName.get(name);
if (variableOrEnumerator != null)
return variableOrEnumerator;
if (enumerators == null)
getEnumerators();
variableOrEnumerator = enumeratorsByName.get(name);
if (variableOrEnumerator != null)
return variableOrEnumerator;
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();
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(name, true);
if (variables.size() > 0) {
Object[] variableArray = variables.toArray();
if (variableArray[0] instanceof IVariable)
variableOrEnumerator = new VariableDMC(Stack.this, this, (IVariable)variableArray[0]);
}
// module scope has no parent with variables
break;
}
for (IVariable scopeVariable : variableScope.getVariables()) {
if (scopeVariable.getName().equals(name)) {
variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
break;
}
}
if (variableOrEnumerator == null && variableScope instanceof IFunctionScope) {
IFunctionScope functionScope = (IFunctionScope)variableScope;
for (IVariable scopeVariable : functionScope.getParameters()) {
if (scopeVariable.getName().equals(name)) {
variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
break;
}
}
}
if (variableOrEnumerator == null) {
for (IEnumerator scopeEnumerator : variableScope.getEnumerators()) {
if (scopeEnumerator.getName().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 = getServicesTracker().getService(Symbols.class);
if (executionDMC != null && symbolsService != null) {
IFunctionScope scope = symbolsService.getFunctionAtAddress(executionDMC.getSymbolDMContext(),
ipAddress);
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 = getDsfServicesTracker().getService(Registers.class);
frameRegisters = new CurrentFrameRegisters(executionDMC, registers);
} else {
// see if symbolics can provide unwinding support
Modules modulesService = getServicesTracker().getService(Modules.class);
ModuleDMC module = modulesService.getModuleByAddress(executionDMC.getSymbolDMContext(), ipAddress);
if (module != null) {
Symbols symbolsService = getServicesTracker().getService(Symbols.class);
IFrameRegisterProvider frameRegisterProvider = symbolsService.getFrameRegisterProvider(
executionDMC.getSymbolDMContext(), ipAddress);
if (frameRegisterProvider != null) {
try {
frameRegisters = frameRegisterProvider.getFrameRegisters(
getSession(), getServicesTracker(), 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;
}
public String getValue() {
return "0";
}
}
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;
}
}
public Stack(DsfSession session, String[] classNames) {
super(session, classNames);
}
public static String createFrameID(IEDCExecutionDMC executionDMC, Map<String, Object> frameProperties) {
int level = (Integer) frameProperties.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) {
EDCDebugger.getDefault().getTrace().traceEntry(IEDCTraceOptions.STACK_TRACE, frameDmc);
rm.setData(new StackFrameData((StackFrameDMC) frameDmc));
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, rm.getData());
rm.done();
}
public void getFrames(IDMContext execContext, DataRequestMonitor<IFrameDMContext[]> rm) {
EDCDebugger.getDefault().getTrace().traceEntry(IEDCTraceOptions.STACK_TRACE, 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;
}
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$
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, rm.getData());
rm.done();
}
else {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
}
}
public void getLocals(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm) {
StackFrameDMC frameContext = (StackFrameDMC) frameCtx;
rm.setData(frameContext.getLocals());
rm.done();
}
public void getStackDepth(IDMContext dmc, int maxDepth, DataRequestMonitor<Integer> rm) {
EDCDebugger.getDefault().getTrace().traceEntry(IEDCTraceOptions.STACK_TRACE, 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;
}
int startFrame = 0;
int endFrame = ALL_FRAMES;
if (maxDepth > 0)
endFrame = maxDepth - 1;
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$
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, rm.getData());
rm.done();
}
else {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
}
}
public void getTopFrame(IDMContext execContext, DataRequestMonitor<IFrameDMContext> rm) {
EDCDebugger.getDefault().getTrace().traceEntry(IEDCTraceOptions.STACK_TRACE, execContext);
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]);
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, rm.getData());
rm.done();
}
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(IDMContext execContext, int startIndex, int endIndex, DataRequestMonitor<IFrameDMContext[]> rm) {
EDCDebugger.getDefault().getTrace().traceEntry(IEDCTraceOptions.STACK_TRACE,
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;
}
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$
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, rm.getData());
rm.done();
}
else {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
}
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, rm.getData());
}
public IFrameDMContext[] getFramesForDMC(IEDCExecutionDMC context, int startIndex, int endIndex) {
EDCDebugger.getDefault().getTrace().traceEntry(IEDCTraceOptions.STACK_TRACE,
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.
needsUpdate = frames == null ||
(frames.get(0).getLevel() > startIndex ||
frames.get(frames.size() - 1).getLevel() < endIndex) ||
(endIndex == ALL_FRAMES && !allFramesCached.get(context.getID()));
}
if (needsUpdate)
updateFrames(context, startIndex, endIndex);
synchronized (stackFrames) {
List<StackFrameDMC> 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()]);
EDCDebugger.getDefault().getTrace().traceExit(IEDCTraceOptions.STACK_TRACE, result);
return result;
}
}
private void updateFrames(IEDCExecutionDMC context, int startIndex, int endIndex) {
ArrayList<StackFrameDMC> frames = new ArrayList<StackFrameDMC>();
List<Map<String, Object>> frameProperties = computeStackFrames(context, startIndex, endIndex);
StackFrameDMC previous = null;
for (Map<String, Object> props : frameProperties) {
StackFrameDMC frame = new StackFrameDMC(context, props);
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);
allFramesCached.put(context.getID(), startIndex == 0 && endIndex == ALL_FRAMES);
}
protected abstract List<Map<String, Object>> computeStackFrames(IEDCExecutionDMC context, int startIndex, int endIndex);
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, 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());
}
}