| /******************************************************************************* |
| * 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.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.IStatus; |
| import org.eclipse.core.runtime.Status; |
| 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) { |
| ICompositeType result = null; |
| if (type == null || ! type.isOpaque()) { |
| return result; |
| } |
| |
| 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 result; |
| } |
| |
| for (IModuleDMContext module : moduleList) { |
| ModuleDMC mdmc = (ModuleDMC) module; |
| EDCSymbolReader reader = (EDCSymbolReader)mdmc.getSymbolReader(); |
| |
| if (reader == null || reader.getDebugInfoProvider() == null) // no debug info |
| continue; |
| |
| Collection<IType> types = reader.getDebugInfoProvider().getTypesByName(type.getName()); |
| if (types == null) |
| return result; |
| |
| for (IType t : types) { |
| if (t instanceof ICompositeType && ! ((ICompositeType)t).isOpaque()) { |
| result = (ICompositeType)t; |
| break; |
| } |
| } |
| |
| } |
| |
| return result; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| } |