| /******************************************************************************* |
| * 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()); |
| } |
| |
| } |