| /******************************************************************************* |
| * 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.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 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 { |
| |
| /** |
| * This represents a forward type reference, which is a type |
| * that resolves itself when referenced. |
| */ |
| static public class ForwardTypeReference implements IType, IForwardTypeReference { |
| |
| static public final IType NULL_TYPE_ENTRY = new IType() { |
| |
| public int getByteSize() { |
| return 0; |
| } |
| |
| public String getName() { |
| return DwarfMessages.DwarfDebugInfoProvider_UnhandledType; |
| } |
| |
| public Map<Object, Object> getProperties() { |
| return Collections.emptyMap(); |
| } |
| |
| public IScope getScope() { |
| return null; |
| } |
| |
| public IType getType() { |
| return null; |
| } |
| |
| public void setType(IType type) { |
| throw new IllegalStateException(); |
| } |
| |
| public void dispose() { |
| } |
| }; |
| |
| private DwarfDebugInfoProvider provider; |
| private IType type = null; |
| |
| private final long offset; |
| |
| public ForwardTypeReference(DwarfDebugInfoProvider provider, long offset) { |
| this.provider = provider; |
| this.offset = offset; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.dwarf.IForwardTypeReference#getReferencedType() |
| */ |
| public IType getReferencedType() { |
| if (type == null) { |
| // to prevent recursion |
| IType newType = NULL_TYPE_ENTRY; |
| newType = provider.resolveTypeReference(this); |
| if (newType == null) { |
| // FIXME |
| newType = NULL_TYPE_ENTRY; |
| } |
| type = newType; |
| } |
| return type; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getByteSize() |
| */ |
| public int getByteSize() { |
| return getReferencedType().getByteSize(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getName() |
| */ |
| public String getName() { |
| return getReferencedType().getName(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getProperties() |
| */ |
| public Map<Object, Object> getProperties() { |
| return getReferencedType().getProperties(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getScope() |
| */ |
| public IScope getScope() { |
| return getReferencedType().getScope(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getType() |
| */ |
| public IType getType() { |
| return getReferencedType().getType(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#setType(org.eclipse.cdt.debug.edc.internal.symbols.IType) |
| */ |
| public void setType(IType type_) { |
| getReferencedType().setType(type_); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#dispose() |
| */ |
| public void dispose() { |
| type = null; |
| provider = null; |
| } |
| } |
| |
| 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>()); |
| |
| // 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<IVariable>> variablesByName = new HashMap<String, List<IVariable>>(); |
| protected Map<String, List<PublicNameInfo>> publicFunctions = new HashMap<String, List<PublicNameInfo>>(); |
| protected Map<String, List<PublicNameInfo>> publicVariables = new HashMap<String, List<PublicNameInfo>>(); |
| |
| // 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(); |
| scopesByOffset.clear(); |
| debugOffsetsToCompileUnits.clear(); |
| functionsByName.clear(); |
| variablesByName.clear(); |
| publicFunctions.clear(); |
| publicVariables.clear(); |
| abbreviationMaps.clear(); |
| referencedFiles.clear(); |
| moduleScope.dispose(); |
| 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); |
| |
| return Collections.unmodifiableCollection(result); |
| } |
| |
| 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) |
| ensureParsedForVariables(); |
| } |
| } |
| |
| result = variablesByName.get(name); |
| |
| // 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 = variablesByName.get(baseName); |
| |
| if (result == null) |
| return new ArrayList<IVariable>(0); |
| |
| return Collections.unmodifiableCollection(result); |
| } |
| |
| /** |
| * @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); |
| // urgh, sorted map... no easy way to get to the end |
| for (CompilationUnitHeader header : headMap.values()) { |
| match = header; |
| } |
| return match; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| } |