| /******************************************************************************* |
| * 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.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 = EDCDebugger.getPrefs(EDCDebugger.PLUGIN_ID); |
| showAllVariablesEnabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, false); |
| } |
| |
| boolean enabled = showAllVariablesEnabled.booleanValue(); |
| if (locals != null) { |
| IEclipsePreferences scope = EDCDebugger.getPrefs(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()); |
| } |
| |
| } |