/******************************************************************************* | |
* Copyright (c) 2009, 2010, 2011 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.internal.services.dsf; | |
import java.lang.ref.WeakReference; | |
import java.text.MessageFormat; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import org.eclipse.cdt.core.IAddress; | |
import org.eclipse.cdt.debug.edc.internal.EDCDebugger; | |
import org.eclipse.cdt.debug.edc.internal.EDCTrace; | |
import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleDMC; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.DebugInfoProviderFactory; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.ExecutableSymbolicsReaderFactory; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.PEFileExecutableSymbolicsReader; | |
import org.eclipse.cdt.debug.edc.services.AbstractEDCService; | |
import org.eclipse.cdt.debug.edc.services.IEDCDMContext; | |
import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC; | |
import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext; | |
import org.eclipse.cdt.debug.edc.services.IEDCModules; | |
import org.eclipse.cdt.debug.edc.services.IEDCSymbols; | |
import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider; | |
import org.eclipse.cdt.debug.edc.services.IFrameRegisters; | |
import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC; | |
import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider; | |
import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader; | |
import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader; | |
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.ISymbol; | |
import org.eclipse.cdt.debug.edc.symbols.IType; | |
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; | |
import org.eclipse.cdt.dsf.concurrent.RequestMonitor; | |
import org.eclipse.cdt.dsf.datamodel.IDMContext; | |
import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext; | |
import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext; | |
import org.eclipse.cdt.dsf.debug.service.ISymbols; | |
import org.eclipse.cdt.dsf.service.DsfSession; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.SubMonitor; | |
import org.eclipse.debug.core.model.ISourceLocator; | |
public class Symbols extends AbstractEDCService implements ISymbols, IEDCSymbols { | |
private static Map<IPath, WeakReference<IEDCSymbolReader>> readerCache = new HashMap<IPath, WeakReference<IEDCSymbolReader>>(); | |
private ISourceLocator sourceLocator; | |
public ISourceLocator getSourceLocator() { | |
return sourceLocator; | |
} | |
public void setSourceLocator(ISourceLocator sourceLocator) { | |
this.sourceLocator = sourceLocator; | |
} | |
public Symbols(DsfSession session) { | |
super(session, new String[] { IEDCSymbols.class.getName(), ISymbols.class.getName(), Symbols.class.getName() }); | |
} | |
public void getSymbols(ISymbolDMContext symCtx, DataRequestMonitor<Iterable<ISymbolObjectDMContext>> rm) { | |
// TODO Auto-generated method stub | |
} | |
public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) { | |
// TODO Auto-generated method stub | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getFunctionAtAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress) | |
*/ | |
public IFunctionScope getFunctionAtAddress(ISymbolDMContext context, IAddress runtimeAddress) { | |
IEDCModules modulesService = getService(Modules.class); | |
IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress); | |
if (module != null) { | |
IEDCSymbolReader reader = module.getSymbolReader(); | |
if (reader != null) { | |
IScope scope = reader.getModuleScope().getScopeAtAddress(module.toLinkAddress(runtimeAddress)); | |
while (scope != null && !(scope instanceof IFunctionScope)) { | |
scope = scope.getParent(); | |
} | |
return (IFunctionScope) scope; | |
} | |
} | |
return null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getFunctionAtAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress) | |
*/ | |
public String getSymbolNameAtAddress(ISymbolDMContext context, IAddress runtimeAddress) { | |
IEDCModules modulesService = getService(Modules.class); | |
IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress); | |
if (module != null) { | |
IEDCSymbolReader reader = module.getSymbolReader(); | |
if (reader != null) { | |
ISymbol symbol = reader.getSymbolAtAddress(module.toLinkAddress(runtimeAddress)); | |
if (symbol != null) | |
return symbol.getName(); | |
} | |
} | |
return null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getLineEntryForAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress) | |
*/ | |
public ILineEntry getLineEntryForAddress(ISymbolDMContext context, IAddress runtimeAddress) { | |
IEDCModules modulesService = getService(Modules.class); | |
IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress); | |
if (module != null) { | |
IEDCSymbolReader reader = module.getSymbolReader(); | |
if (reader != null) { | |
IAddress linkAddress = module.toLinkAddress(runtimeAddress); | |
IModuleLineEntryProvider lineEntryProvider = reader.getModuleScope().getModuleLineEntryProvider(); | |
return lineEntryProvider.getLineEntryAtAddress(linkAddress); | |
} | |
} | |
return null; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getLineEntriesForAddressRange(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress, org.eclipse.cdt.core.IAddress) | |
*/ | |
public List<ILineEntry> getLineEntriesForAddressRange(ISymbolDMContext context, IAddress start, IAddress end) { | |
List<ILineEntry> lineEntries = new ArrayList<ILineEntry>(); | |
IEDCModules modulesService = getService(Modules.class); | |
IEDCModuleDMContext module = modulesService.getModuleByAddress(context, start); | |
if (module == null) | |
return lineEntries; | |
IAddress linkStartAddress = module.toLinkAddress(start); | |
IAddress linkEndAddress = module.toLinkAddress(end); | |
IEDCSymbolReader reader = module.getSymbolReader(); | |
if (reader != null) { | |
if (linkStartAddress == null) | |
linkStartAddress = module.getSymbolReader().getModuleScope().getLowAddress(); | |
if (linkEndAddress == null) | |
linkEndAddress = module.getSymbolReader().getModuleScope().getHighAddress(); | |
IModuleScope moduleScope = reader.getModuleScope(); | |
if (linkEndAddress.compareTo(moduleScope.getHighAddress()) > 0) { | |
// end address is out of the module sections. | |
// we'll keep getting source lines until we reach an address | |
// point where no source line is available. | |
linkEndAddress = moduleScope.getHighAddress(); | |
} | |
IModuleLineEntryProvider lineEntryProvider = moduleScope.getModuleLineEntryProvider(); | |
ILineEntry entry = lineEntryProvider.getLineEntryAtAddress(linkStartAddress); | |
while (entry != null && entry.getLowAddress().compareTo(linkEndAddress) < 0) { | |
lineEntries.add(entry); | |
// FIXME: this shouldn't happen | |
if (entry.getLowAddress().compareTo(entry.getHighAddress()) >= 0) | |
entry = lineEntryProvider.getLineEntryAtAddress(entry.getHighAddress().add(1)); | |
else | |
entry = lineEntryProvider.getLineEntryAtAddress(entry.getHighAddress()); | |
} | |
} | |
return lineEntries; | |
} | |
/** | |
* Get an accessor for registers in stack frames other than the current one. | |
* <p> | |
* Note: this is not meaningful by itselfis typically used from {@link StackFrameDMC#getFrameRegisters()}. | |
* @param context | |
* @param runtimeAddress | |
* @return {@link IFrameRegisters} or <code>null</code> | |
*/ | |
public IFrameRegisterProvider getFrameRegisterProvider(ISymbolDMContext context, IAddress runtimeAddress) { | |
Modules modulesService = getService(Modules.class); | |
IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress); | |
if (module != null) { | |
IEDCSymbolReader reader = module.getSymbolReader(); | |
if (reader != null) { | |
IFrameRegisterProvider frameRegisterProvider = reader.getModuleScope().getFrameRegisterProvider(); | |
return frameRegisterProvider; | |
} | |
} | |
return null; | |
} | |
public static IEDCSymbolReader getSymbolReader(IPath modulePath) { | |
IEDCSymbolReader reader = null; | |
WeakReference<IEDCSymbolReader> cacheEntry = readerCache.get(modulePath); | |
if (cacheEntry != null) | |
reader = cacheEntry.get(); | |
if (reader != null) { | |
if (reader.getSymbolFile() != null | |
&& reader.getSymbolFile().toFile().exists() | |
&& reader.getSymbolFile().toFile().lastModified() == reader.getModificationDate()) { | |
return reader; | |
} | |
// it's been deleted or modified. remove it from the cache | |
readerCache.remove(modulePath); | |
} | |
IExecutableSymbolicsReader exeReader = ExecutableSymbolicsReaderFactory.createFor(modulePath); | |
if (exeReader != null) { | |
IDebugInfoProvider debugProvider = DebugInfoProviderFactory.createFor(modulePath, exeReader); // may be null | |
if (debugProvider != null) { | |
// if debug info came from a different file, the "executable" may be that symbol file too | |
if (!exeReader.getSymbolFile().equals(debugProvider.getExecutableSymbolicsReader().getSymbolFile())) { | |
exeReader.dispose(); | |
exeReader = debugProvider.getExecutableSymbolicsReader(); | |
} | |
} | |
reader = new EDCSymbolReader(exeReader, debugProvider); | |
} | |
if (reader != null) { | |
readerCache.put(modulePath, new WeakReference<IEDCSymbolReader>(reader)); | |
} | |
return reader; | |
} | |
@Override | |
public void shutdown(RequestMonitor rm) { | |
super.shutdown(rm); | |
} | |
/** | |
* This is exposed only for testing. | |
*/ | |
public static void releaseReaderCache() { | |
Collection<WeakReference<IEDCSymbolReader>> readers = readerCache.values(); | |
for (WeakReference<IEDCSymbolReader> readerRef : readers) { | |
IEDCSymbolReader reader = readerRef.get(); | |
if (reader != null) | |
reader.shutDown(); | |
} | |
readerCache.clear(); | |
} | |
/** | |
* Given an opaque composite type, search the symbol context for definition | |
* of the type. | |
* | |
* A C/C++ opaque type is usually used in opaque pointer, e.g. | |
* | |
* <pre> | |
* typedef class PrivateType* OpaquePTR; | |
* </pre> | |
* | |
* The actual definition of the "PrivateType" is usually in another | |
* library/DLL with or without debug info.<br> | |
* <br> | |
* This method will search all modules loaded in the symbol context (usually | |
* a process) until it finds a non-opaque type with the same name as the | |
* given opaque type. If a loaded module has no debug info, it will just be | |
* skipped. | |
* | |
* @param symCtx | |
* the symbol context (usually a process) | |
* @param type | |
* the opaque composite type | |
* @return a defined type; null if no defined type is found or the given | |
* type is not opaque. | |
*/ | |
public ICompositeType resolveOpaqueType(ISymbolDMContext symCtx, ICompositeType type, | |
IProgressMonitor monitor) { | |
if (type == null || ! type.isOpaque()) | |
return null; | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { | |
EDCTrace.getTrace().traceEntry(null, "Resolve opaque type \"" + type.getName() + "\" in " + EDCTrace.fixArg(symCtx)); | |
} | |
IModuleDMContext[] moduleList = null; | |
Modules modulesService = getService(Modules.class); | |
assert(modulesService != null); | |
if (symCtx instanceof IEDCExecutionDMC) { | |
String symContextID = ((IEDCDMContext) symCtx).getID(); | |
moduleList = modulesService.getModulesForContext(symContextID); | |
} else if (symCtx instanceof IModuleDMContext) { | |
moduleList = new IModuleDMContext[1]; | |
moduleList[0] = (IModuleDMContext) symCtx; | |
} else { | |
// should not happen | |
Status s = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, MessageFormat.format( | |
"Unknown class implementing ISymbolDMContext : {0}", symCtx.getClass().getName()), null); | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) | |
EDCTrace.getTrace().traceExit(null, s); | |
return null; | |
} | |
SubMonitor searchMonitor = SubMonitor.convert(monitor, moduleList.length); | |
searchMonitor.subTask("Searching modules list: 0/" + String.valueOf(moduleList.length)); | |
int count = 0; | |
for (IModuleDMContext module : moduleList) { | |
ModuleDMC mdmc = (ModuleDMC) module; | |
searchMonitor.worked(1); | |
searchMonitor.subTask("Searching modules list: " + String.valueOf(++count) | |
+ "/" + String.valueOf(moduleList.length)); | |
EDCSymbolReader reader = (EDCSymbolReader)mdmc.getSymbolReader(); | |
if (reader == null) | |
continue; | |
searchMonitor.subTask("Searching modules list: " + String.valueOf(count) | |
+ "/" + String.valueOf(moduleList.length) + " " | |
+ reader.getSymbolFile().toOSString()); | |
IDebugInfoProvider provider = reader.getDebugInfoProvider(); | |
if (provider == null) | |
continue; | |
searchMonitor.subTask("Searching modules list: " + String.valueOf(count) | |
+ "/" + String.valueOf(moduleList.length) + " " | |
+ provider.getSymbolFile().toOSString()); | |
Collection<IType> types = provider.getTypesByName(type.getName()); | |
if (types == null) | |
continue; | |
for (IType t : types) | |
if (t instanceof ICompositeType && ! ((ICompositeType)t).isOpaque()) | |
// return immediately as soon as we have something useful | |
return (ICompositeType)t; | |
if (searchMonitor.isCanceled()) | |
break; | |
} | |
return null; | |
} | |
/** | |
* A wrapper method that calls into symbol reader to get runtime address(es) | |
* for a given function name. | |
* For executables that are not PE-COFF, this method only uses the symbol table instead of | |
* also debug info (e.g. Dwarf3) to get function address. See reason in the implementation. | |
* | |
* @param module where the runtime address is. | |
* @param functionName | |
* @return list of runtime addresses. | |
*/ | |
public List<IAddress> getFunctionAddress(IEDCModuleDMContext module, String functionName) { | |
List<IAddress> ret = new ArrayList<IAddress>(2); | |
IEDCSymbolReader symReader = module.getSymbolReader(); | |
if (symReader == null) | |
return ret; | |
// Remove "()" if any | |
int parenIndex = functionName.indexOf('('); | |
if (parenIndex >= 0) | |
functionName = functionName.substring(0, parenIndex); | |
// Supposedly we could call this to get what we need, but this may cause full parse of | |
// debug info and lead to heap overflow for a large symbol file (over one gigabytes of | |
// memory required in parsing a 180M ELF symbol file) and chokes the debugger. | |
// So before a good solution is available, we resort to symbol table of the executable. | |
// ................04/02/10 | |
// Collection<IFunctionScope> functions = symReader.getModuleScope().getFunctionsByName(functionName); | |
// for (IFunctionScope f : functions) { | |
// IAddress breakAddr = f.getLowAddress(); | |
// ... | |
// assume it's the human-readable name first | |
Collection<ISymbol> symbols = symReader.findUnmangledSymbols(functionName); | |
if (symbols.isEmpty()) { | |
// else look for a raw symbol | |
symbols = symReader.findSymbols(functionName); | |
} | |
if (symbols.isEmpty() && symReader.getSymbolicsReader() instanceof PEFileExecutableSymbolicsReader) { | |
// for PE-COFF files, if the name is not in the symbol list, search the debug info | |
IFunctionScope function = symReader.getModuleScope().getFunctionByName(functionName); | |
if (function != null) | |
ret.add(function.getLowAddress()); | |
} else for (ISymbol symbol : symbols) { | |
// don't consider zero sized symbol. | |
if (symbol.getSize() == 0) | |
continue; | |
IAddress addr = symbol.getAddress(); | |
// convert from link to runtime address | |
addr = module.toRuntimeAddress(addr); | |
ret.add(addr); | |
} | |
return ret; | |
} | |
} |