blob: ffbbbc63302b4c116f8449037fdf3f684d4d5f81 [file] [log] [blame]
/*******************************************************************************
* 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
* Broadcom - Refactored ForwardTypeReference to separate top-level class
* Optimized fetchCompileUnitHeader()
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.WeakHashMap;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.IStreamBuffer;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.symbols.IForwardTypeReference;
import org.eclipse.cdt.debug.edc.internal.symbols.Scope;
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.CommonInformationEntry;
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.FrameDescriptionEntry;
import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider;
import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
import org.eclipse.cdt.debug.edc.symbols.IRangeList;
import org.eclipse.cdt.debug.edc.symbols.IRangeList.Entry;
import org.eclipse.cdt.debug.edc.symbols.IScope;
import org.eclipse.cdt.debug.edc.symbols.IType;
import org.eclipse.cdt.debug.edc.symbols.IVariable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* This class handles the low-level aspects of reading DWARF data.
* There exists one provider per symbol file.
*/
public class DwarfDebugInfoProvider implements IDebugInfoProvider {
static public class CompilationUnitHeader {
int length;
short version;
int abbreviationOffset;
byte addressSize;
int debugInfoOffset;
DwarfCompileUnit scope;
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Offset: " + debugInfoOffset).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("Length: " + length).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("Version: " + version).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("Abbreviation: " + abbreviationOffset).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
sb.append("Address size: " + addressSize).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
return sb.toString();
}
}
static class AbbreviationEntry {
short tag;
ArrayList<Attribute> attributes;
boolean hasChildren;
AbbreviationEntry(long code, short tag, boolean hasChildren) {
// abbreviation code not stored
this.tag = tag;
this.hasChildren = hasChildren;
attributes = new ArrayList<Attribute>();
}
}
static class Attribute {
short tag;
byte form;
Attribute(short tag, byte form) {
this.tag = tag;
this.form = form;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("tag: " + Long.toHexString(tag)); //$NON-NLS-1$
sb.append(" form: " + Long.toHexString(form)); //$NON-NLS-1$
return sb.toString();
}
}
static class AttributeValue {
private Object value;
// for indirect form, this is the actual form
private byte actualForm;
AttributeValue(byte form, IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) {
actualForm = form;
try {
value = readAttribute(in, addressSize, debugStrings);
} catch (IOException e) {
EDCDebugger.getMessageLogger().logError(null, e);
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
if (value != null) {
Class<? extends Object> clazz = value.getClass();
if (clazz.isArray()) {
int len = Array.getLength(value);
sb.append(len).append(' ');
sb.append(clazz.getComponentType().toString());
sb.append(':');
for (int i = 0; i < len; i++) {
byte b = Array.getByte(value, i);
sb.append(' ').append(Integer.toHexString(b));
}
} else {
if (value instanceof Number) {
Number n = (Number) value;
sb.append(Long.toHexString(n.longValue()));
} else if (value instanceof String) {
sb.append(value);
} else {
sb.append(value);
}
}
}
return sb.toString();
}
private Object readAttribute(IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) throws IOException {
Object obj = null;
switch (actualForm) {
case DwarfConstants.DW_FORM_addr:
case DwarfConstants.DW_FORM_ref_addr:
obj = DwarfInfoReader.readAddress(in, addressSize);
break;
case DwarfConstants.DW_FORM_block: {
int size = (int) DwarfInfoReader.read_unsigned_leb128(in);
byte[] bytes = new byte[size];
in.get(bytes);
obj = bytes;
}
break;
case DwarfConstants.DW_FORM_block1: {
int size = in.get() & 0xff;
byte[] bytes = new byte[size];
in.get(bytes);
obj = bytes;
}
break;
case DwarfConstants.DW_FORM_block2: {
int size = in.getShort();
byte[] bytes = new byte[size];
in.get(bytes);
obj = bytes;
}
break;
case DwarfConstants.DW_FORM_block4: {
int size = in.getInt();
byte[] bytes = new byte[size];
in.get(bytes);
obj = bytes;
}
break;
case DwarfConstants.DW_FORM_data1:
obj = new Byte(in.get());
break;
case DwarfConstants.DW_FORM_data2:
obj = new Short(in.getShort());
break;
case DwarfConstants.DW_FORM_data4:
obj = new Integer(in.getInt());
break;
case DwarfConstants.DW_FORM_data8:
obj = new Long(in.getLong());
break;
case DwarfConstants.DW_FORM_sdata:
obj = new Long(DwarfInfoReader.read_signed_leb128(in));
break;
case DwarfConstants.DW_FORM_udata:
obj = new Long(DwarfInfoReader.read_unsigned_leb128(in));
break;
case DwarfConstants.DW_FORM_string: {
int c;
StringBuffer sb = new StringBuffer();
while ((c = (in.get() & 0xff)) != -1) {
if (c == 0) {
break;
}
sb.append((char) c);
}
obj = sb.toString();
}
break;
case DwarfConstants.DW_FORM_flag:
obj = new Byte(in.get());
break;
case DwarfConstants.DW_FORM_strp: {
int offset = in.getInt();
if (debugStrings == null) {
obj = new String();
} else if (offset < 0 || offset > debugStrings.capacity()) {
obj = new String();
} else {
debugStrings.position(offset);
obj = DwarfInfoReader.readString(debugStrings);
}
}
break;
case DwarfConstants.DW_FORM_ref1:
obj = new Integer(in.get() & 0xff);
break;
case DwarfConstants.DW_FORM_ref2:
obj = new Integer(in.getShort() & 0xffff);
break;
case DwarfConstants.DW_FORM_ref4:
obj = new Integer(in.getInt());
break;
case DwarfConstants.DW_FORM_ref8:
obj = new Long(in.getLong());
break;
case DwarfConstants.DW_FORM_ref_udata:
obj = new Long(DwarfInfoReader.read_unsigned_leb128(in));
break;
case DwarfConstants.DW_FORM_indirect: {
actualForm = (byte) DwarfInfoReader.read_unsigned_leb128(in);
return readAttribute(in, addressSize, debugStrings);
}
default:
assert (false);
break;
}
return obj;
}
/**
* @param attr
* @param in
* @param addressSize
* @param debugStrings
*/
public static void skipAttributeValue(short form, IStreamBuffer in,
byte addressSize) throws IOException {
switch (form) {
case DwarfConstants.DW_FORM_addr:
case DwarfConstants.DW_FORM_ref_addr:
in.position(in.position() + addressSize);
break;
case DwarfConstants.DW_FORM_block: {
int size = (int) DwarfInfoReader.read_unsigned_leb128(in);
in.position(in.position() + size);
}
break;
case DwarfConstants.DW_FORM_block1: {
int size = in.get() & 0xff;
in.position(in.position() + size);
}
break;
case DwarfConstants.DW_FORM_block2: {
int size = in.getShort();
in.position(in.position() + size);
}
break;
case DwarfConstants.DW_FORM_block4: {
int size = in.getInt();
in.position(in.position() + size);
}
break;
case DwarfConstants.DW_FORM_data1:
in.position(in.position() + 1);
break;
case DwarfConstants.DW_FORM_data2:
in.position(in.position() + 2);
break;
case DwarfConstants.DW_FORM_data4:
in.position(in.position() + 4);
break;
case DwarfConstants.DW_FORM_data8:
in.position(in.position() + 8);
break;
case DwarfConstants.DW_FORM_sdata:
DwarfInfoReader.read_signed_leb128(in);
break;
case DwarfConstants.DW_FORM_udata:
DwarfInfoReader.read_unsigned_leb128(in);
break;
case DwarfConstants.DW_FORM_string: {
int c;
while ((c = (in.get() & 0xff)) != -1) {
if (c == 0) {
break;
}
}
}
break;
case DwarfConstants.DW_FORM_flag:
in.position(in.position() + 1);
break;
case DwarfConstants.DW_FORM_strp:
in.position(in.position() + 4);
break;
case DwarfConstants.DW_FORM_ref1:
in.position(in.position() + 1);
break;
case DwarfConstants.DW_FORM_ref2:
in.position(in.position() + 2);
break;
case DwarfConstants.DW_FORM_ref4:
in.position(in.position() + 4);
break;
case DwarfConstants.DW_FORM_ref8:
in.position(in.position() + 8);
break;
case DwarfConstants.DW_FORM_ref_udata:
DwarfInfoReader.read_unsigned_leb128(in);
break;
case DwarfConstants.DW_FORM_indirect: {
form = (short) DwarfInfoReader.read_unsigned_leb128(in);
skipAttributeValue(form, in, addressSize);
break;
}
default:
assert (false);
break;
}
}
/**
* Parse attributes and then skip to sibling, if any
*
* @param names array to hold up to two names
* @param entry debug info entry
* @param in buffer stream of debug info
* @param addressSize
* @param debugStrings
* @return DW_AT_name value, or null if there is no or invalid DW_AT_name attribute
*/
public static void skipAttributesToSibling(AbbreviationEntry entry, IStreamBuffer in, byte addressSize) {
long sibling = -1;
// go through the attributes and throw away everything except the sibling
int len = entry.attributes.size();
for (int i = 0; i < len; i++) {
Attribute attr = entry.attributes.get(i);
try {
if (attr.tag == DwarfConstants.DW_AT_sibling) {
if (attr.form == DwarfConstants.DW_FORM_ref_udata) {
sibling = DwarfInfoReader.read_unsigned_leb128(in);
} else if (attr.form == DwarfConstants.DW_FORM_ref4) {
sibling = in.getInt();
} else {
// TODO: allow other forms for sibling value
AttributeValue.skipAttributeValue(attr.form, in, addressSize);
}
} else {
AttributeValue.skipAttributeValue(attr.form, in, addressSize);
}
} catch (IOException e) {
EDCDebugger.getMessageLogger().logError(null, e);
break;
}
}
if (sibling != -1)
in.position(sibling);
}
public byte getActualForm() {
return actualForm;
}
/**
* Get the value as a 64-bit signed long, sign-extending any shorter attribute
* @return value as signed long
*/
public long getValueAsSignedLong() {
if (value instanceof Number) {
return ((Number) value).longValue();
}
return 0;
}
/**
* Get the value as a 64-bit long.
*
* A Byte, Short, or Integer is zero-extended.
*
* @return value as long
*/
public long getValueAsLong() {
if (value instanceof Byte) {
return ((Byte) value).byteValue() & 0xff;
}
if (value instanceof Short) {
return ((Short) value).shortValue() & 0xffff;
}
if (value instanceof Integer) {
return ((Integer) value).intValue() & 0xffffffff;
}
// fallthrough
if (value instanceof Number) {
return ((Number) value).longValue();
}
return 0;
}
/**
* Get the value as a 32-bit int.
*
* A Byte or Short is zero-extended.
*
* @return value as int
*/
public int getValueAsInt() {
if (value instanceof Byte) {
return ((Byte) value).byteValue() & 0xff;
}
if (value instanceof Short) {
return ((Short) value).shortValue() & 0xffff;
}
// fallthrough
if (value instanceof Number) {
return ((Number) value).intValue();
}
return 0;
}
/**
* Get the value as a string
* @return String or "" if not a string
*/
public String getValueAsString() {
if (value != null)
return value.toString();
return null;
}
/**
* Get the byte array value (which is empty if this is not a byte array)
* @return array
*/
public byte[] getValueAsBytes() {
if (value instanceof byte[])
return (byte[]) value;
return new byte[0];
}
}
static public class AttributeList {
Map<Short, AttributeValue> attributeMap;
AttributeList(AbbreviationEntry entry, IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) {
int len = entry.attributes.size();
attributeMap = new HashMap<Short, AttributeValue>(len);
for (int i = 0; i < len; i++) {
Attribute attr = entry.attributes.get(i);
attributeMap.put(Short.valueOf(attr.tag), new AttributeValue(attr.form, in, addressSize, debugStrings));
}
}
public static void skipAttributes(AbbreviationEntry entry, IStreamBuffer in, byte addressSize) {
int len = entry.attributes.size();
for (int i = 0; i < len; i++) {
Attribute attr = entry.attributes.get(i);
try {
AttributeValue.skipAttributeValue(attr.form, in, addressSize);
} catch (IOException e) {
EDCDebugger.getMessageLogger().logError(null, e);
break;
}
}
}
public long getAttributeValueAsLong(short attributeName) {
AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
if (attr != null) {
return attr.getValueAsLong();
}
return 0;
}
public int getAttributeValueAsInt(short attributeName) {
AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
if (attr != null) {
return attr.getValueAsInt();
}
return 0;
}
public long getAttributeValueAsSignedLong(short attributeName) {
AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
if (attr != null) {
return attr.getValueAsSignedLong();
}
return 0;
}
public String getAttributeValueAsString(short attributeName) {
AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
if (attr != null) {
return attr.getValueAsString();
}
return ""; //$NON-NLS-1$
}
public byte[] getAttributeValueAsBytes(short attributeName) {
AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
if (attr != null) {
return attr.getValueAsBytes();
}
return new byte[0];
}
public AttributeValue getAttribute(short attributeName) {
return attributeMap.get(Short.valueOf(attributeName));
}
/**
* Tell whether the attributes do not have a code range.
* <p>
* Note: a singular DW_AT_low_pc means an entry point
* <p>
* Also note: a compile unit can have code represented by DW_AT_stmt_list
* @return true if the attributes represent code
*/
public boolean hasCodeRangeAttributes() {
return attributeMap.containsKey(DwarfConstants.DW_AT_high_pc)
|| attributeMap.containsKey(DwarfConstants.DW_AT_ranges);
}
}
static public class PublicNameInfo {
public final String nameWithNameSpace;
public final CompilationUnitHeader cuHeader;
public final short tag; // DW_TAG_xxx
public PublicNameInfo(String nameWithNameSpace, CompilationUnitHeader cuHeader, short tag) {
this.nameWithNameSpace = nameWithNameSpace;
this.cuHeader = cuHeader;
this.tag = tag;
}
}
// list of compilation units per source file
protected HashMap<IPath, List<ICompileUnitScope>> compileUnitsPerFile
= new HashMap<IPath, List<ICompileUnitScope>>();
// list of compile units in .debug_info order
protected ArrayList<DwarfCompileUnit> compileUnits
= new ArrayList<DwarfCompileUnit>();
// list of compile units with code (non-zero high address), sorted by low address
protected ArrayList<DwarfCompileUnit> sortedCompileUnitsWithCode
= new ArrayList<DwarfCompileUnit>();
// function and type declarations can be referenced by offsets relative to
// the compile unit or to the entire .debug_info section. therefore we keep
// maps by .debug_info offset, and for compile unit relative offsets, we
// just add the compile unit offset into the .debug_info section.
protected Map<Long, AttributeList> functionsByOffset
= new HashMap<Long, AttributeList>();
protected Map<Long, IType> typesByOffset
= Collections.synchronizedMap(new HashMap<Long, IType>());
// These references may eventually become pointless so weak reference them
protected Map<Long, IForwardTypeReference> referenceTypesByOffset
= Collections.synchronizedMap(new WeakHashMap<Long, IForwardTypeReference>());
// for casting to a type, keep certain types by name
protected Map<String, List<IType>> typesByName
= new HashMap<String, List<IType>>();
// for casting to a type, track whether the cast name includes an aggregate designator
enum TypeAggregate { Class, Struct, Union, None };
// map of entities which created scopes
protected Map<Long, Scope> scopesByOffset = new HashMap<Long, Scope>();
// entry points for CUs in the .debug_info section, used to dynamically parse CUs as needed
protected TreeMap<Long, CompilationUnitHeader>
debugOffsetsToCompileUnits = new TreeMap<Long, CompilationUnitHeader>();
// forward references for tags we have not parsed yet.
// (These will go into typesByOffset once handled)
// Map<Long, ForwardDwarfDefinition> forwardDwarfDefinitions
// = new HashMap<Long, ForwardDwarfDefinition>();
// these are just for faster lookups
protected Map<String, List<IFunctionScope>> functionsByName
= new HashMap<String, List<IFunctionScope>>();
protected Map<String, List<PublicNameInfo>> publicFunctions
= new HashMap<String, List<PublicNameInfo>>();
protected Map<String, List<PublicNameInfo>> publicVariables
= new HashMap<String, List<PublicNameInfo>>();
protected Map<String, List<IVariable>> variablesByName
= new HashMap<String, List<IVariable>>();
private List<IVariable> getCachedVariablesByName(String name,
boolean globalsOnly) {
List<IVariable> v = variablesByName.get(name);
return (v != null && globalsOnly) ? new ArrayList<IVariable>(v) : v;
}
// abbreviation tables (lists of abbrev entries), mapped by .debug_abbrev offset
protected Map<Integer, Map<Long, AbbreviationEntry>> abbreviationMaps
= new HashMap<Integer, Map<Long, AbbreviationEntry>>();
// mapping of PC range to frame description entries
protected TreeMap<IRangeList.Entry, FrameDescriptionEntry> frameDescEntries
= new TreeMap<IRangeList.Entry, FrameDescriptionEntry>();
// mapping of CIE offsets to parsed common info entries
protected Map<Long, CommonInformationEntry> commonInfoEntries
= new HashMap<Long, CommonInformationEntry>();
protected Set<String> referencedFiles = new HashSet<String>();
protected boolean buildReferencedFilesList = true;
private IPath symbolFilePath;
private long symbolFileLastModified;
private boolean parsedInitially = false;
private boolean parsedForVarsAndAddresses = false;
private boolean parsedForScopesAndAddresses = false;
private boolean parsedForTypes = false;
private boolean parsedForGlobalVars = false;
private final IExecutableSymbolicsReader exeReader;
private final DwarfModuleScope moduleScope;
final DwarfFileHelper fileHelper;
private IFrameRegisterProvider frameRegisterProvider;
private static String SOURCE_FILES_CACHE = "_source_files"; //$NON-NLS-1$
public DwarfDebugInfoProvider(IExecutableSymbolicsReader exeReader) {
this.exeReader = exeReader;
this.symbolFilePath = exeReader.getSymbolFile();
this.symbolFileLastModified = symbolFilePath.toFile().lastModified();
this.moduleScope = new DwarfModuleScope(this);
this.fileHelper = new DwarfFileHelper(symbolFilePath);
this.frameRegisterProvider = new DwarfFrameRegisterProvider(this);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return DwarfMessages.DwarfDebugInfoProvider_DwarfProviderFor + symbolFilePath;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.files.IDebugInfoProvider#dispose()
*/
public void dispose() {
// several views in DSF hold onto all our debug info,
// so go through and explicitly break links
if (moduleScope != null) {
moduleScope.dispose();
}
// help GC
compileUnitsPerFile.clear();
compileUnits.clear();
functionsByOffset.clear();
typesByOffset.clear();
referenceTypesByOffset.clear();
scopesByOffset.clear();
debugOffsetsToCompileUnits.clear();
functionsByName.clear();
variablesByName.clear();
publicFunctions.clear();
publicVariables.clear();
abbreviationMaps.clear();
referencedFiles.clear();
parsedInitially = false;
parsedForTypes = false;
parsedForVarsAndAddresses = false;
parsedForScopesAndAddresses = false;
fileHelper.dispose();
frameRegisterProvider.dispose();
}
void ensureParsedInitially() {
if (!parsedInitially) {
DwarfInfoReader reader = new DwarfInfoReader(this);
parsedInitially = true;
reader.parseInitial();
}
}
void ensureParsedForScopes() {
if (!parsedForScopesAndAddresses) {
DwarfInfoReader reader = new DwarfInfoReader(this);
if (!parsedInitially) {
parsedInitially = true;
reader.parseInitial();
}
parsedForScopesAndAddresses = true;
reader.parseForAddresses(false);
}
}
void ensureParsedForScope(IAddress linkAddress) {
DwarfInfoReader reader = new DwarfInfoReader(this);
if (!parsedInitially) {
parsedInitially = true;
reader.parseInitial();
}
reader.parseForAddress(linkAddress);
}
void ensureParsedForVariables() {
if (!parsedForVarsAndAddresses) {
DwarfInfoReader reader = new DwarfInfoReader(this);
if (!parsedInitially) {
parsedInitially = true;
reader.parseInitial();
}
parsedForVarsAndAddresses = true;
reader.parseForAddresses(true);
}
}
private void ensureParsedForGlobalVariables() {
if (parsedForGlobalVars)
return;
parsedForGlobalVars = true;
if (publicVariables.size() == 0)
return;
// determine compilation units containing globals
HashSet<CompilationUnitHeader> cuWithGlobalsArray = new HashSet<CompilationUnitHeader>(publicVariables.size());
for (List<PublicNameInfo> infoList : publicVariables.values()) {
for (PublicNameInfo info : infoList) {
cuWithGlobalsArray.add(info.cuHeader);
}
}
// parse compilation units containing global variables
DwarfInfoReader reader = new DwarfInfoReader(this);
for (CompilationUnitHeader cuHeader : cuWithGlobalsArray)
reader.parseCompilationUnitForAddresses(cuHeader.scope);
}
void ensureParsedForTypes() {
if (!parsedForTypes) {
DwarfInfoReader reader = new DwarfInfoReader(this);
if (!parsedInitially) {
parsedInitially = true;
reader.parseInitial();
}
parsedForTypes = true;
reader.parseForTypes();
}
}
public void setParsedInitially() {
parsedInitially = true;
}
public void setParsedForAddresses() {
parsedForVarsAndAddresses = true;
}
public IPath getSymbolFile() {
return symbolFilePath;
}
public IModuleScope getModuleScope() {
return moduleScope;
}
public IAddress getBaseLinkAddress() {
return exeReader.getBaseLinkAddress();
}
public Collection<IFunctionScope> getFunctionsByName(String name) {
List<IFunctionScope> result;
ensureParsedInitially();
String baseName = name;
/* use same semantics as before, where qualified name lookups would fail
// pubnames uses qualified names but is indexed by basename
if (name != null) {
int baseStart = name.lastIndexOf("::");
if (baseStart != -1)
baseName = name.substring(baseStart + 2);
}
*/
// first, match against public function names
if (publicFunctions.size() > 0) {
if (name != null) {
DwarfInfoReader reader = new DwarfInfoReader(this);
List<PublicNameInfo> nameMatches = publicFunctions.get(baseName);
if (nameMatches != null) {
// parse the compilation units that have matches
if (nameMatches.size() == 1) { // quick usual case
reader.parseCompilationUnitForAddresses(nameMatches.get(0).cuHeader.scope);
} else {
ArrayList<DwarfCompileUnit> cuList = new ArrayList<DwarfCompileUnit>();
for (PublicNameInfo info : nameMatches) {
if (!cuList.contains(info.cuHeader.scope)) {
cuList.add(info.cuHeader.scope);
}
}
for (DwarfCompileUnit cu : cuList) {
reader.parseCompilationUnitForAddresses(cu);
}
}
} else {
// not a public name, so parse all compilation units looking for functions
ensureParsedForScopes();
}
} else {
// name is null, so parse all compilation units looking for functions
ensureParsedForScopes();
}
} else {
// no public names, so parse all compilation units looking for functions
ensureParsedForScopes();
}
if (name != null) {
result = functionsByName.get(baseName);
if (result == null)
return new ArrayList<IFunctionScope>(0);
} else {
result = new ArrayList<IFunctionScope>(functionsByName.size()); // at least this big
for (List<IFunctionScope> functions : functionsByName.values())
result.addAll(functions);
((ArrayList<IFunctionScope>) result).trimToSize();
}
return Collections.unmodifiableCollection(result);
}
public Collection<IVariable> getVariablesByName(String name, boolean globalsOnly) {
List<IVariable> result;
ensureParsedInitially();
if (name == null) {
if (publicVariables.size() > 0) {
// name is null, so parse all compilation units looking for variables
if (globalsOnly)
ensureParsedForGlobalVariables();
else
ensureParsedForVariables();
}
result = new ArrayList<IVariable>(variablesByName.size()); // at least this big
for (List<IVariable> variables : variablesByName.values())
result.addAll(variables);
} else {
String baseName = name;
int baseNameStart = name.lastIndexOf("::"); //$NON-NLS-1$
if (baseNameStart != -1)
baseName = name.substring(baseNameStart + 2);
// match against public variable names, which the initial parse populated
if (publicVariables.size() > 0) {
DwarfInfoReader reader = new DwarfInfoReader(this);
List<PublicNameInfo> nameMatches = publicVariables.get(baseName);
if (nameMatches != null) {
// parse the compilation units that have matches
if (nameMatches.size() == 1) { // quick usual case
reader.parseCompilationUnitForAddresses(nameMatches.get(0).cuHeader.scope);
} else {
ArrayList<DwarfCompileUnit> cuList = new ArrayList<DwarfCompileUnit>();
for (PublicNameInfo info : nameMatches) {
if (!cuList.contains(info.cuHeader.scope)) {
cuList.add(info.cuHeader.scope);
}
}
for (DwarfCompileUnit cu : cuList) {
reader.parseCompilationUnitForAddresses(cu);
}
}
} else {
// not a public name, so parse all compilation units looking for variables
if (globalsOnly)
ensureParsedForGlobalVariables();
else
ensureParsedForVariables();
}
}
result = getCachedVariablesByName(name, globalsOnly);
// check against unqualified name because RVCT 2.x did not include namespace
// info for globals that are inside namespaces
if (result == null && baseNameStart != -1)
result = getCachedVariablesByName(baseName, globalsOnly);
if (result == null)
return new ArrayList<IVariable>(0);
}
if (globalsOnly) {
filterOutLocalVariables(result);
}
return Collections.unmodifiableCollection(result);
}
private void filterOutLocalVariables(List<IVariable> variables) {
List<IVariable> allVariables = new ArrayList<IVariable>(variables);
for (IVariable var : allVariables) {
if (!(var.getScope() instanceof ICompileUnitScope)) {
variables.remove(var);
}
}
}
/**
* @return the publicFunctions
*/
public Map<String, List<PublicNameInfo>> getPublicFunctions() {
ensureParsedInitially();
return publicFunctions;
}
/**
* @return the publicVariables
*/
public Map<String, List<PublicNameInfo>> getPublicVariables() {
ensureParsedInitially();
return publicVariables;
}
public ICompileUnitScope getCompileUnitForAddress(IAddress linkAddress) {
ensureParsedForScope(linkAddress);
IScope scope = moduleScope.getScopeAtAddress(linkAddress);
while (scope != null && !(scope instanceof ICompileUnitScope)) {
scope = scope.getParent();
}
return (ICompileUnitScope) scope;
}
public List<ICompileUnitScope> getCompileUnitsForFile(IPath filePath) {
ensureParsedInitially();
List<ICompileUnitScope> cuList = compileUnitsPerFile.get(filePath);
if (cuList != null)
return cuList;
// FIXME: we need a looser check here: on Windows, we added drive letters to all
// paths before populating compileUnitsPerFile, even if there is not really a
// drive (see DwarFileHelper).
for (Map.Entry<IPath, List<ICompileUnitScope>> entry : compileUnitsPerFile.entrySet()) {
if (entry.getKey().setDevice(null).equals(filePath.setDevice(null))) {
return entry.getValue();
}
}
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
public String[] getSourceFiles(IProgressMonitor monitor) {
if (referencedFiles.isEmpty()) {
// Check the persistent cache
String cacheKey = getSymbolFile().toOSString() + SOURCE_FILES_CACHE;
Set<String> cachedFiles = EDCDebugger.getDefault().getCache().getCachedData(cacheKey, Set.class, symbolFileLastModified);
if (cachedFiles == null)
{
DwarfInfoReader reader = new DwarfInfoReader(this);
reader.quickParseDebugInfo(monitor);
assert referencedFiles.size() > 0;
EDCDebugger.getDefault().getCache().putCachedData(cacheKey, new HashSet<String>(referencedFiles), symbolFileLastModified);
}
else
referencedFiles = cachedFiles;
}
return referencedFiles.toArray(new String[referencedFiles.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.symbols.files.IDebugInfoProvider#getTypes()
*/
public Collection<IType> getTypes() {
ensureParsedForTypes();
ArrayList<IType> types = new ArrayList<IType>(typesByOffset.values());
return types;
}
/////////////////
// Lazy evaluation methods
/**
* Fetch a type lazily. Either we've already parsed the type, or we have
* a reference to it, or we can find its compilation unit and parse its types.
* We do not fix up cross references until someone asks for
* it (e.g. from an IType or IVariable implementation).
*/
public IType readType(long offset_) {
Long offset = Long.valueOf(offset_);
IType type = typesByOffset.get(offset);
if (type == null) {
// make sure we've parsed it
CompilationUnitHeader header = fetchCompileUnitHeader(offset_);
if (header != null) {
DwarfInfoReader reader = new DwarfInfoReader(this);
reader.parseCompilationUnitForTypes(header.scope);
type = typesByOffset.get(offset);
// may be unhandled currently
if (type == null) {
// workaround for GCC-E 3.x bug where some, but not all, type offsets are off by 4
// assume if you hit this null case that the problem may be the GCC-E bug
type = typesByOffset.get(offset - 4);
if (type == null)
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfDebugInfoProvider_NotParsingType1 + Long.toHexString(offset_) +
DwarfMessages.DwarfDebugInfoProvider_NotParsingType2 + symbolFilePath, null);
}
} else {
// may be unhandled currently
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfDebugInfoProvider_CannotResolveCompUnit1 + Long.toHexString(offset_) +
DwarfMessages.DwarfDebugInfoProvider_CannotResolveCompUnit2 + symbolFilePath, null);
}
}
return type;
}
/**
* Fetch a referenced type lazily.
* @param scope
*/
IType resolveTypeReference(ForwardTypeReference ref) {
IType type = typesByOffset.get(ref.offset);
if (type == null) {
type = readType(ref.offset);
}
return type;
}
/**
* @return
*/
public IExecutableSymbolicsReader getExecutableSymbolicsReader() {
return exeReader;
}
/**
* Remember where a compilation unit header lives in the debug info.
* @param debugInfoOffset
* @param currentCUHeader
*/
public void registerCompileUnitHeader(int debugInfoOffset,
CompilationUnitHeader currentCUHeader) {
debugOffsetsToCompileUnits.put((long) debugInfoOffset, currentCUHeader);
}
/**
* Get a compilation unit header that contains the given offset.
* @param debugInfoOffset an offset which is on or after a compilation unit's debug offset
* @return {@link CompilationUnitHeader} containing the offset
*/
public CompilationUnitHeader fetchCompileUnitHeader(long debugInfoOffset) {
CompilationUnitHeader match = debugOffsetsToCompileUnits.get(debugInfoOffset);
if (match != null) {
return match;
}
// it's inside one
SortedMap<Long,CompilationUnitHeader> headMap = debugOffsetsToCompileUnits.headMap(debugInfoOffset);
if (!headMap.isEmpty()) {
match = headMap.get(headMap.lastKey());
return match;
}
// shouldn't get here; most callers will handle null rather badly
return null;
}
/**
* Get the frame description entry for the given PC
* @param framePC
* @return FDE or <code>null</code>
*/
public FrameDescriptionEntry findFrameDescriptionEntry(IAddress framePC) {
DwarfInfoReader reader = new DwarfInfoReader(this);
if (frameDescEntries.isEmpty()) {
reader.parseForFrameIndices();
}
long pc = framePC.getValue().longValue();
SortedMap<Entry, FrameDescriptionEntry> tailMap = frameDescEntries.tailMap(new IRangeList.Entry(pc, pc));
if (tailMap.isEmpty())
return null;
FrameDescriptionEntry entry = tailMap.values().iterator().next();
if (entry.getCIE() == null) {
CommonInformationEntry cie = null;
if (!commonInfoEntries.containsKey(entry.ciePtr)) {
try {
cie = reader.parseCommonInfoEntry(entry.ciePtr, entry.addressSize, framePC);
} catch (IOException e) {
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfDebugInfoProvider_FailedToReadCIE + entry.ciePtr, e);
}
commonInfoEntries.put(entry.ciePtr, cie);
} else {
cie = commonInfoEntries.get(entry.ciePtr);
}
entry.setCIE(cie);
}
return entry;
}
/**
* @return
*/
public IFrameRegisterProvider getFrameRegisterProvider() {
return frameRegisterProvider;
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider#getTypesByName(java.lang.String)
*/
public Collection<IType> getTypesByName(String name) {
// is name has "struct", "class" or "union", search without that
name = name.trim();
String baseName = name;
TypeAggregate aggregate = TypeAggregate.None;
if (baseName.startsWith("class ")) { //$NON-NLS-1$
aggregate = TypeAggregate.Class;
baseName = baseName.replace("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
} else if (baseName.startsWith("struct ")) { //$NON-NLS-1$
aggregate = TypeAggregate.Struct;
baseName = baseName.replace("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
} else if (baseName.startsWith("union ")) { //$NON-NLS-1$
aggregate = TypeAggregate.Union;
baseName = baseName.replace("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
Collection<IType> types = typesByName.get(baseName);
String templateName = null;
String templateName2 = null;
if (types == null) {
// if we didn't match and this is a template name,
// remove extra spaces and composite type names
if (baseName.indexOf('<') != -1) {
templateName = baseName;
while (templateName.contains(" ")) //$NON-NLS-1$
templateName = templateName.replaceAll(" ", " "); //$NON-NLS-1$ //$NON-NLS-2$
templateName = templateName.replaceAll(", ", ","); //$NON-NLS-1$ //$NON-NLS-2$
templateName = templateName.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
templateName = templateName.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
templateName = templateName.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
types = typesByName.get(templateName);
// template name without "<...>", rather than with "<...>", might match
if (types == null) {
templateName2 = templateName.substring(0, templateName.indexOf('<'));
types = typesByName.get(templateName2);
// screen out types whose template list does not match
if (types != null) {
ArrayList<IType> matchingTypes = null;
for (Iterator<IType> it = types.iterator(); it.hasNext(); ) {
IType nextType = it.next();
String match = nextType.getName();
// for templates, remove composite type names (e.g., "class")
match = match.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
match = match.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
match = match.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
if (match.equals(templateName)) {
if (matchingTypes == null)
matchingTypes = new ArrayList<IType>(types.size());
matchingTypes.add(nextType);
}
}
types = matchingTypes; // may be null
}
}
}
if (types == null) {
// Maybe we optimistically searched for relevant types;
// if that fails, do the full parse of types now
if (!parsedForTypes) {
ensureParsedForTypes();
types = getTypesByName(baseName);
if (types != null)
return types; // non-template return
if (baseName.indexOf('<') != -1) {
types = typesByName.get(templateName);
if (types == null)
types = typesByName.get(templateName2);
else
templateName2 = null; // did not match name without "<...>"
}
}
if (types == null)
return new ArrayList<IType>(0);
}
}
// screen out types whose template list does not match
if (templateName2 != null) {
ArrayList<IType> matchingTypes = new ArrayList<IType>(types.size());
for (Iterator<IType> it = types.iterator(); it.hasNext(); ) { // types can't be null
IType nextType = it.next();
String match = nextType.getName();
// for templates, remove composite type names (e.g., "class")
match = match.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
match = match.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
match = match.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
if (match.equals(templateName))
matchingTypes.add(nextType);
}
types = matchingTypes;
}
// make sure that the aggregate type matches as well as the name
if (aggregate == TypeAggregate.None)
return Collections.unmodifiableCollection(types);
Iterator<IType> itr = types.iterator();
while (itr.hasNext()) {
IType nextType = itr.next();
if ((aggregate == TypeAggregate.Class && !nextType.getName().contains("class ")) || //$NON-NLS-1$
(aggregate == TypeAggregate.Struct && !nextType.getName().contains("struct ")) || //$NON-NLS-1$
(aggregate == TypeAggregate.Union && !nextType.getName().contains("union "))) //$NON-NLS-1$
types.remove(nextType);
}
if (types.isEmpty())
return new ArrayList<IType>(0);
return Collections.unmodifiableCollection(types);
}
}