/************************************************************************* | |
* Copyright (c) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies). | |
* All rights reserved. | |
* This component and the accompanying materials are made available | |
* under the terms of the License "Eclipse Public License v1.0" | |
* which accompanies this distribution, and is available | |
* at the URL "http://www.eclipse.org/legal/epl-v10.html". | |
* | |
* Initial Contributors: | |
* Nokia Corporation - initial contribution. | |
* | |
* Contributors: | |
* Broadcom - use newly created top-level ForwardReference type | |
* | |
* Description: | |
* | |
*************************************************************************/ | |
package org.eclipse.cdt.debug.edc.tests; | |
import java.math.BigInteger; | |
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 java.util.Random; | |
import org.eclipse.cdt.core.IAddress; | |
import org.eclipse.cdt.debug.edc.arm.IARMSymbol; | |
import org.eclipse.cdt.debug.edc.debugger.tests.SimpleDebuggerTest; | |
import org.eclipse.cdt.debug.edc.internal.EDCDebugger; | |
import org.eclipse.cdt.debug.edc.internal.PathUtils; | |
import org.eclipse.cdt.debug.edc.internal.services.dsf.EDCSymbolReader; | |
import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ArrayType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ConstType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.FieldType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IArrayBoundType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IBasicType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IField; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IForwardTypeReference; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IInheritance; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ILexicalBlockScope; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IMayBeQualifedType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IQualifierType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ITemplateParam; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ITypedef; | |
import org.eclipse.cdt.debug.edc.internal.symbols.InheritanceType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.SubroutineType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.TypedefType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfCompileUnit; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeList; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.PublicNameInfo; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.DefCFAExpressionInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.DefCFAInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.ExpressionInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.FrameDescriptionEntry; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.InstructionState; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.NopInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.RegisterInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.RememberStateInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.RestoreInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.RestoreStateInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.SameValueInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.UndefinedInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.ValueExpressionInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.ValueOffsetInstruction; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfInfoReader; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfMessages; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfVariable; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.ForwardTypeReference; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.LocationEntry; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.LocationExpression; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.LocationList; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.RangeList; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.ExecutableSymbolicsReaderFactory; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.FileStatistics; | |
import org.eclipse.cdt.debug.edc.launch.EDCLaunch; | |
import org.eclipse.cdt.debug.edc.services.EDCServicesTracker; | |
import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope; | |
import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader; | |
import org.eclipse.cdt.debug.edc.symbols.IEnumerator; | |
import org.eclipse.cdt.debug.edc.symbols.IExecutableSection; | |
import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader; | |
import org.eclipse.cdt.debug.edc.symbols.IFunctionScope; | |
import org.eclipse.cdt.debug.edc.symbols.ILocationProvider; | |
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.debug.edc.symbols.IVariable; | |
import org.eclipse.cdt.debug.edc.symbols.TypeUtils; | |
import org.eclipse.cdt.dsf.service.DsfSession; | |
import org.eclipse.cdt.utils.Addr32; | |
import org.eclipse.cdt.utils.Addr64; | |
import org.eclipse.core.runtime.IPath; | |
import org.junit.Assert; | |
import org.junit.Test; | |
/** | |
* | |
*/ | |
public class TestDwarfReader extends AbstractDwarfReaderTest { | |
private static class DwarfFrameRegisterAlbum extends SimpleDebuggerTest { | |
/* (non-Javadoc) | |
* @see org.eclipse.cdt.debug.edc.debugger.tests.SimpleDebuggerTest#getRequiredLaunchConfigurationType() | |
*/ | |
@Override | |
protected String getRequiredLaunchConfigurationType() { | |
return "com.nokia.cdt.debug.launch.systemTRKLaunch"; | |
} | |
@Override | |
public String getAlbumName() { | |
return "RegisterFrameTestsBlackFlagRVCT.dsa"; | |
} | |
public EDCLaunch getLaunch() { | |
return launch; | |
} | |
public DsfSession getSession() { | |
return session; | |
} | |
static DwarfFrameRegisterAlbum openAlbum() { | |
return new DwarfFrameRegisterAlbum(); | |
} | |
} | |
private DwarfFrameRegisterAlbum dwarfAlbum; | |
@Test | |
public void testSymFromExeDetect() throws Exception { | |
for (TestInfo info : testInfos.values()) { | |
if (info.exeFile == null) continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.exeFile); | |
assertNotNull(symbolReader); | |
System.out.println("Sym for exe " + info.exeFile + " is " + symbolReader.getSymbolFile()); | |
assertEquals(info.symFile, symbolReader.getSymbolFile()); | |
assertNotNull(symbolReader.getExecutableSections()); | |
IExecutableSection dsect = symbolReader.findExecutableSection(DwarfInfoReader.DWARF_DEBUG_INFO); | |
assertNotNull(dsect); | |
assertEquals(".debug_info", dsect.getName()); | |
System.out.println(" Dwarf section: " + dsect.toString()); | |
dsect.dispose(); | |
} | |
} | |
/** | |
* This should be a quick check, not a slow one | |
* @throws Exception | |
*/ | |
@Test | |
public void testSymDetect() throws Exception { | |
long time = System.currentTimeMillis(); | |
_testSymDetect(); | |
long span = System.currentTimeMillis() - time; | |
System.out.println(span + " ms (testSymDetect)"); | |
} | |
private void _testSymDetect() { | |
for (TestInfo info : testInfos.values()) { | |
System.out.println("Checking sym for " + info.symFile); | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
assertNotNull(info.symFile.lastSegment(), symbolReader); | |
assertTrue(info.symFile.lastSegment(), symbolReader.hasRecognizedDebugInformation()); | |
} | |
} | |
@Test | |
public void testSourceFiles() throws Exception { | |
long time = System.currentTimeMillis(); | |
_testSourceFiles(); | |
long span = System.currentTimeMillis() - time; | |
System.out.println(span + " ms (testSourceFiles)"); | |
} | |
private void _testSourceFiles() { | |
for (TestInfo info : testInfos.values()) { | |
if (info.numberOfSources == 0) continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
String[] sources= symbolReader.getSourceFiles(); | |
assertNotNull(sources); | |
//System.out.println(info.symFile.lastSegment() + " : " + sources.length); | |
assertEquals(info.symFile.lastSegment(), info.numberOfSources, sources.length); | |
} | |
} | |
@Test | |
public void testCompileUnits() throws Exception { | |
long time = System.currentTimeMillis(); | |
_testCompileUnits(); | |
long span = System.currentTimeMillis() - time; | |
System.out.println(span + " ms (testCompileUnits)"); | |
// This is for information. | |
new FileStatistics(); // nothing but for code coverage. | |
FileStatistics.dump(); | |
} | |
private void _testCompileUnits() { | |
for (TestInfo info : testInfos.values()) { | |
if (info.numberOfModuleScopeChildren == 0) continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
Collection<IScope> scopes= symbolReader.getModuleScope().getChildren(); | |
assertNotNull(scopes); | |
//System.out.println(info.symFile.lastSegment() + ": " + scopes.size()); | |
//for (IScope kid : scopes) | |
// System.out.println("\t" + kid.getName()); | |
assertEquals(info.symFile.lastSegment(), info.numberOfModuleScopeChildren, scopes.size()); | |
} | |
} | |
@Test | |
public void testGlobalVariables() throws Exception { | |
long time = System.currentTimeMillis(); | |
_testGlobalVariables(); | |
long span = System.currentTimeMillis() - time; | |
System.out.println(span + " ms (testGlobalVariables)"); | |
} | |
private void _testGlobalVariables() { | |
for (TestInfo info : testInfos.values()) { | |
if (info.numberOfVariables == 0) continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
Collection<IVariable> variables = symbolReader.getModuleScope().getVariables(); | |
assertNotNull(variables); | |
//System.out.println(info.symFile.lastSegment() + ": " + variables.size()); | |
assertEquals(info.symFile.lastSegment(), info.numberOfVariables, variables.size()); | |
} | |
} | |
/** | |
* Test that we can find and get types for globals in compilation units | |
* @throws Exception | |
*/ | |
@Test | |
public void testPerCUGlobals() throws Exception { | |
long time = System.currentTimeMillis(); | |
_testPerCUGlobals(); | |
long span = System.currentTimeMillis() - time; | |
System.out.println(span + " ms (testPerCUGlobals)"); | |
} | |
private void _testPerCUGlobals() { | |
// check all globals | |
for (TestInfo info : testInfos.values()) { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
boolean discover = false; | |
if (discover) { | |
// DISCOVERY (rerun if reader gets better and paste into static block at top) | |
//System.out.println(info.symFile); | |
boolean any = false; | |
for (String srcFile : symbolReader.getSourceFiles()) { | |
List<ICompileUnitScope> cusList = symbolReader.getModuleScope().getCompileUnitsForFile(PathUtils.createPath(srcFile)); | |
any = true; | |
for (ICompileUnitScope cus : cusList) { | |
Collection<IVariable> vars = cus.getVariables(); | |
if (!vars.isEmpty()) { | |
//System.out.println(srcFile +" : # vars = " + vars.size()); | |
for (IVariable var : vars) { | |
//System.out.println(var.getName() + " : " + getTypeName(var.getType())); | |
System.out.println(MessageFormat.format( | |
"setCUVariableInfo(\"{0}\", \"{1}\", \"{2}\", \"{3}\");", | |
info.symFile.lastSegment(), | |
srcFile, | |
var.getName(), | |
getTypeName(var.getType()))); | |
} | |
} | |
} | |
} | |
assertTrue("Any CUs in " + info.symFile, any); | |
} else { | |
if (info.cuVarMap == null) | |
continue; | |
for (Map.Entry<String, Map<String, VariableInfo>> entry : info.cuVarMap.entrySet()) { | |
String cu = entry.getKey(); | |
List<ICompileUnitScope> cusList = symbolReader.getModuleScope().getCompileUnitsForFile(PathUtils.createPath(cu)); | |
assertNotNull(info.symFile + " : " + cu, cusList); | |
for (Map.Entry<String, VariableInfo> varEntry : entry.getValue().entrySet()) { | |
// TODO: getter by name | |
boolean found = false; | |
for (ICompileUnitScope cus : cusList) { | |
for (IVariable var : cus.getVariables()) { | |
if (var.getName().equals(varEntry.getKey())) { | |
found = true; | |
VariableInfo varInfo = varEntry.getValue(); | |
assertNotNull(var.getType()); | |
String theTypeName = getTypeName(var.getType()); | |
System.out.println(info.symFile + " : " + cu + " : " + var.getName() + " = " + theTypeName); | |
assertEquals(info.symFile + " : " + cu + " : " + var.getName(), | |
varInfo.typeName, theTypeName); | |
} | |
} | |
} | |
assertTrue(info.symFile + " : " + cu + " : " + entry.getKey(), found); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Test that we can find and resolve all types. The lazy type evaluator only | |
* is lazy as far as dereferenced types go, so we don't check anything but names | |
* and type trees here. | |
* | |
* This test isn't exhaustive; it just ferrets out assertion errors and null pointer references. | |
* @throws Exception | |
*/ | |
@Test | |
public void testTypes() throws Exception { | |
for (TestInfo info : testInfos.values()) { | |
if (info.numberOfTypes == 0) continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
boolean discover = false; | |
if (discover) { | |
// DISCOVERY | |
//System.out.println(info.symFile); | |
int cnt = symbolReader.getModuleScope().getTypes().size(); | |
//for (IType type : symbolReader.getModuleScope().getTypes()) { | |
// System.out.println(getTypeName(type)); | |
//} | |
System.out.println(info.symFile + " : " + cnt); | |
} | |
else { | |
Collection<IType> types = symbolReader.getModuleScope().getTypes(); | |
assertNotNull(types); | |
System.out.println("Initial: " + types.size()); | |
// this should trigger expansion of subtypes | |
int idx = 1; | |
for (IType type : types) { | |
doTestType(idx, type); | |
idx++; | |
} | |
types = symbolReader.getModuleScope().getTypes(); | |
System.out.println("After: " + types.size()); | |
assertEquals(info.symFile.lastSegment(), info.numberOfTypes, types.size()); | |
// now, ensure the types are still there in a new reader | |
symbolReader = Symbols.getSymbolReader(info.symFile); | |
assertEquals(info.symFile.lastSegment(), info.numberOfTypes, symbolReader.getModuleScope().getTypes().size()); | |
} | |
} | |
} | |
/** | |
* @param idx | |
* @param type | |
*/ | |
private void doTestType(int idx, IType type) { | |
String name = getTypeName(type); | |
if (type.getByteSize() == 0) { | |
IType checkType = type; | |
while (checkType != null) { | |
if (checkType instanceof IQualifierType || checkType instanceof TypedefType) | |
checkType = checkType.getType(); | |
else | |
break; | |
} | |
if (checkType == null) | |
return; | |
if (checkType instanceof ICompositeType) { | |
return; // this is allowed, even though the spec says it should be here. | |
// we can't fix it up, because even if we sum up the field sizes, we can't predict the extra space used by alignment | |
} | |
if (checkType instanceof SubroutineType || checkType instanceof InheritanceType) | |
return; // this is allowed | |
if (checkType instanceof FieldType) { | |
doTestType(idx, ((FieldType) checkType).getType()); | |
return; | |
} | |
if (checkType instanceof ArrayType) { | |
for (IArrayBoundType bound : ((ArrayType) checkType).getBounds()) { | |
if (bound.getElementCount() == 0) | |
return; | |
} | |
// else, should have more | |
} | |
if (checkType == ForwardTypeReference.NULL_TYPE_ENTRY) | |
return; // should not get here, but something else tests this | |
if (checkType instanceof ICPPBasicType && ((ICPPBasicType) checkType).getBaseType() == ICPPBasicType.t_void) | |
return; // yup | |
if (checkType instanceof ITemplateParam) { | |
if (checkType.getType().getName().equals("class TModelEntry")) { | |
assertNull(((ITemplateParam)checkType).getProperties()); | |
assertNull(((ITemplateParam)checkType).getScope()); | |
assertTrue(((ITemplateParam)checkType).getType() instanceof ICompositeType); | |
// following is not quite meaningful but to beef up method coverage. | |
checkType.setType(null); | |
checkType.dispose(); | |
} | |
return; // no inherent size | |
} | |
if (checkType instanceof IArrayBoundType) | |
return; // no inherent size | |
fail(name + " has zero size"); | |
} | |
if (idx % 1000 == 0) System.out.print("."); | |
} | |
/** | |
* This method is useful for dumping the types actually parsed and comparing them with each other. | |
*/ | |
/* | |
public void testTypesXX() throws Exception { | |
for (TestInfo info : testInfos.values()) { | |
if (!info.symFile.lastSegment().contains("gcce")) | |
continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
Collection<IType> types = symbolReader.getModuleScope().getTypes(); | |
assertNotNull(types); | |
// force expansion | |
for (IType type : types) { | |
//String name = | |
getTypeName(type); | |
} | |
// start again | |
types = symbolReader.getModuleScope().getTypes(); | |
List<String> names = new ArrayList<String>(); | |
for (IType type : types) { | |
names.add(getTypeName(type)); | |
} | |
//Collections.sort(names); | |
String fname = "true".equals(System.getProperty(Symbols.DWARF_USE_NEW_READER)) ? "new.txt" : "old.txt"; | |
PrintStream dump = new PrintStream(new File("/tmp/" + fname)); | |
for (String name : names) { | |
dump.println(name); | |
} | |
dump.close(); | |
} | |
} | |
*/ | |
/** | |
* Test case(s) for specific type parses | |
* @throws Exception | |
*/ | |
@Test | |
public void testSpecificTypes1a() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
List<ICompileUnitScope> cuList = getCompileUnitsFor(symbolReader, "dbg_multipleInheritance.cpp"); | |
if (cuList.isEmpty()) | |
cuList = getCompileUnitsFor(symbolReader, "dbg_multipleinheritance.cpp"); | |
assertFalse(cuList.isEmpty()); | |
List<ICompileUnitScope> cuhList = getCompileUnitsFor(symbolReader, "dbg_multipleInheritance.h"); | |
assertFalse(cuhList.isEmpty()); | |
// multipleInheritance | |
IFunctionScope function = null; | |
for (ICompileUnitScope cu : cuList) { | |
function = cu.getFunctionAtAddress(new Addr32(0xa940)); | |
if (function != null) | |
break; | |
} | |
assertNotNull(function); | |
assertEquals("multipleInheritance", function.getName()); | |
for (IVariable var : function.getVariablesInTree()) { | |
if (var.getName().equals("pdv1")) { | |
// check forward reference behavior | |
if ( var instanceof DwarfVariable | |
&& (((DwarfVariable)var).getRawType() instanceof IForwardTypeReference)) { | |
IForwardTypeReference forward = (IForwardTypeReference)(((DwarfVariable)var).getRawType()); | |
assertEquals("", forward.getName()); | |
assertEquals(4, forward.getByteSize()); | |
assertNull(forward.getProperties()); | |
assertNotNull(forward.getScope()); | |
IType resolvedType = forward.getType(); | |
assertEquals("class Derv1", resolvedType.getName()); | |
forward.setType(resolvedType); | |
assertEquals("class Derv1", forward.getType().getName()); | |
} | |
IType type = var.getType(); | |
assertFalse(type instanceof IForwardTypeReference); // should not peek through interface | |
assertNotNull(type); | |
assertEquals(function, var.getScope()); | |
_testSpecificTypePointerToDerv1(type); | |
} | |
} | |
// okay place to check accessibility of base link address & provider string | |
DwarfDebugInfoProvider provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
// ensure placeholder null type has correct fields | |
IType nullEntryType = ForwardTypeReference.NULL_TYPE_ENTRY; | |
assertEquals(0, nullEntryType.getByteSize()); | |
assertEquals(DwarfMessages.DwarfDebugInfoProvider_UnhandledType, nullEntryType.getName()); | |
assertEquals(0, nullEntryType.getProperties().size()); | |
assertNull(nullEntryType.getScope()); | |
assertNull(nullEntryType.getType()); | |
boolean throwException = false; | |
try { | |
nullEntryType.setType(null); | |
} catch (IllegalStateException ise) { | |
throwException = true; | |
} | |
assertTrue(throwException); | |
nullEntryType.dispose(); // should do nothing | |
assertEquals(0, nullEntryType.getProperties().size()); // still alive | |
assertEquals(provider.getBaseLinkAddress().toString(), "32768"); | |
IModuleScope moduleScope = provider.getModuleScope(); | |
assertNotNull(moduleScope); | |
assertTrue(moduleScope.getEnumerators().size() == 0); | |
assertTrue(moduleScope.getSymbolFile().toPortableString().contains("BlackFlag_rvct.sym")); | |
ICompileUnitScope cuScope = provider.getCompileUnitForAddress(new Addr64("32768")); | |
assertNotNull(cuScope); | |
assertTrue(cuScope instanceof DwarfCompileUnit); | |
DwarfCompileUnit cu = (DwarfCompileUnit)cuScope; | |
cu.setParsedForVariables(false); | |
assertFalse(cu.isParsedForVariables()); | |
cu.setParsedForVariables(true); | |
assertTrue(cu.isParsedForVariables()); | |
AttributeList attrList = cu.getAttributeList(); | |
cu.setAttributes(null); | |
assertNull(cu.getAttributeList()); | |
cu.setAttributes(attrList); | |
assertNotNull(cu.getAttributeList()); | |
assertNotNull(cu.toString()); | |
assertNotNull(provider.toString()); | |
} | |
/** | |
* Test case(s) for specific type parses, from a focused parse | |
* @throws Exception | |
*/ | |
@Test | |
public void testSpecificTypes1b() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
DwarfDebugInfoProvider provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
// DW_TAG_pointer_type -> Derv1 | |
IType type = provider.readType(0x1cb139); | |
_testSpecificTypePointerToDerv1(type); | |
} | |
/** | |
* @param type | |
*/ | |
private void _testSpecificTypePointerToDerv1(IType type) { | |
assertTrue(type.getName(), type instanceof IPointerType); | |
IType derv1 = ((IPointerType) type).getType(); | |
assertFalse(derv1 instanceof IForwardTypeReference); // should not peek through interface | |
assertNotNull(derv1); | |
assertTrue(derv1.getName(), derv1 instanceof ICompositeType); | |
ICompositeType derv1Comp = (ICompositeType) derv1; | |
assertEquals("class Derv1", derv1Comp.getName()); | |
assertEquals(1, derv1Comp.fieldCount()); | |
assertEquals(2, derv1Comp.inheritanceCount()); | |
IField vptrField = derv1Comp.findFields("__vptr")[0]; | |
assertNotNull("__vptr", vptrField); | |
assertEquals(0, derv1Comp.getTemplateParams().length); | |
assertEquals(ICompositeType.k_class, derv1Comp.getKey()); | |
// inherited fields also visible | |
IField xField = derv1Comp.findFields("x")[0]; | |
assertNotNull("x", xField); | |
// x is in an inherited type | |
IInheritance[] inhs = derv1Comp.getInheritances(); | |
assertEquals(2, inhs.length); | |
IType base1 = inhs[1].getType(); | |
assertNotNull(base1); | |
assertEquals("class Base1", base1.getName()); | |
assertTrue(base1.getName(), base1 instanceof ICompositeType); | |
ICompositeType base1Comp = (ICompositeType) base1; | |
assertEquals(1, base1Comp.fieldCount()); | |
assertEquals(0, base1Comp.inheritanceCount()); | |
xField = base1Comp.findFields("x")[0]; | |
assertNotNull("x", xField); | |
IType base2 = inhs[0].getType(); | |
assertNotNull(base2); | |
assertEquals("class Base2", base2.getName()); | |
assertTrue(base2.getName(), base2 instanceof ICompositeType); | |
ICompositeType base2Comp = (ICompositeType) base2; | |
assertEquals(0, base2Comp.fieldCount()); | |
assertEquals(0, base2Comp.inheritanceCount()); | |
// watch for side effects (late adding of inherited fields) | |
assertEquals(1, derv1Comp.fieldCount()); | |
assertEquals(2, derv1Comp.inheritanceCount()); | |
assertEquals(1, base1Comp.fieldCount()); | |
assertEquals(0, base1Comp.inheritanceCount()); | |
assertEquals(0, base2Comp.fieldCount()); | |
assertEquals(0, base2Comp.inheritanceCount()); | |
// the class is in the header | |
IScope classScope = ((IPointerType)type).getType().getScope(); | |
assertTrue(classScope instanceof ICompileUnitScope); | |
IPath path = ((ICompileUnitScope) classScope).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equalsIgnoreCase("dbg_multipleInheritance.h")); | |
// the pointer type is declared in a function (either "show3", due to "this", or the one we looked in) | |
assertTrue(type.getScope() instanceof IFunctionScope); | |
//assertEquals(((IFunctionScope) type.getScope()).getName(), "show3"); | |
IScope fileScope = type.getScope().getParent(); | |
assertTrue(fileScope instanceof ICompileUnitScope); | |
path = ((ICompileUnitScope) fileScope).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equalsIgnoreCase("dbg_multipleInheritance.cpp")); | |
} | |
/** | |
* Test case(s) for specific type parses | |
* @throws Exception | |
*/ | |
@Test | |
public void testSpecificTypes2() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
List<ICompileUnitScope> cuList = getCompileUnitsFor(symbolReader, "dbg_multipleInheritance.cpp"); | |
if (cuList.isEmpty()) | |
cuList = getCompileUnitsFor(symbolReader, "dbg_multipleinheritance.cpp"); | |
assertFalse(cuList.isEmpty()); | |
List<ICompileUnitScope> cuhList = getCompileUnitsFor(symbolReader, "dbg_multipleInheritance.h"); | |
assertFalse(cuhList.isEmpty()); | |
// Base2::show2 | |
IFunctionScope function = null; | |
for (ICompileUnitScope cu : cuList) { | |
function = cu.getFunctionAtAddress(new Addr32(0xa916)); | |
if (function != null) | |
break; | |
} | |
if (function == null) | |
for (ICompileUnitScope cu : cuhList) { | |
function = cu.getFunctionAtAddress(new Addr32(0xa916)); | |
if (function != null) | |
break; | |
} | |
assertNotNull(function); | |
assertEquals("show2", function.getName()); | |
for (IVariable var : function.getVariablesInTree()) { | |
if (var.getName().equals("Base2Show")) | |
_testSpecificTypeCharArray(function, cuhList.get(0), var); | |
} | |
} | |
/** | |
* @param function | |
* @param headerScope | |
* @param var | |
*/ | |
private void _testSpecificTypeCharArray(IFunctionScope function, ICompileUnitScope headerScope, IVariable var) { | |
IType type = var.getType(); | |
assertFalse(type instanceof IForwardTypeReference); // should not peek through interface | |
assertNotNull(type); | |
assertTrue(type.getName(), type instanceof IArrayType); | |
assertEquals(function, var.getScope()); | |
IType baseType = ((IArrayType) type).getType(); | |
assertNotNull(baseType); | |
assertTrue(baseType.getName(), baseType instanceof IBasicType); | |
// FIXME: this should be null for a primitive type, but is the previous function in the old reader | |
//assertEquals(randomFunction, baseType.getScope()); | |
IBasicType charType = (IBasicType) baseType; | |
assertEquals("char", charType.getName()); | |
assertEquals(1, charType.getByteSize()); | |
} | |
/** | |
* Test case(s) for specific type parses, from the whole module scope | |
* @throws Exception | |
*/ | |
@Test | |
public void testSpecificTypes3a() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlagMinGW.exe")); | |
List<ICompileUnitScope> cuList = getCompileUnitsFor(symbolReader, "dbg_expressions.cpp"); | |
// TODO: lookup by name | |
for (ICompileUnitScope cu : cuList) { | |
for (IVariable var : cu.getVariables()) { | |
if (var.getName().equals("genum")) { | |
assertEquals(cu, var.getScope()); | |
_testSpecificTypeEnum(var.getType(), "dbg_typedefs.h"); | |
break; | |
} | |
} | |
} | |
//////// | |
symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
cuList = getCompileUnitsFor(symbolReader, "dbg_typedefs.h"); | |
// TODO: lookup by name | |
for (ICompileUnitScope cu : cuList) { | |
for (IVariable var : cu.getVariables()) { | |
if (var.getName().equals("genum")) { | |
assertEquals(cu, var.getScope()); | |
_testSpecificTypeEnum(var.getType(), "dbg_typedefs.h"); | |
break; | |
} | |
} | |
} | |
} | |
/** | |
* Test case(s) for specific type parses, from a focused scope | |
* @throws Exception | |
*/ | |
@Test | |
public void testSpecificTypes3b() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlagMinGW.exe")); | |
DwarfDebugInfoProvider provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
// DW_TAG_enumeration_type : enum_type | |
IType type = provider.readType(0x6a65); | |
_testSpecificTypeEnum(type, "dbg_expressions.cpp"); | |
/////// | |
// defined in different place here | |
symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
// DW_TAG_enumeration_type : enum_type | |
type = provider.readType(0x1a6a3d); | |
_testSpecificTypeEnum(type, "dbg_typedefs.h"); | |
} | |
private void _testSpecificTypeEnum(IType type, String file) { | |
assertTrue(getTypeName(type), type instanceof IEnumeration); | |
IEnumeration enumType = (IEnumeration) type; | |
assertEquals("enum_type", enumType.getName()); | |
assertEquals(5, enumType.enumeratorCount()); | |
assertEquals("zero", enumType.getEnumerators()[0].getName()); | |
assertEquals("one", enumType.getEnumerators()[1].getName()); | |
assertEquals("two", enumType.getEnumerators()[2].getName()); | |
assertEquals("three", enumType.getEnumerators()[3].getName()); | |
assertEquals("four", enumType.getEnumerators()[4].getName()); | |
IScope scope = type.getScope(); | |
assertTrue(scope instanceof ICompileUnitScope); | |
ICompileUnitScope cus = (ICompileUnitScope) scope; | |
assertTrue(cus.getFilePath().toString(), cus.getFilePath().lastSegment().equals(file)); | |
} | |
@Test | |
public void testStaticLocals1() throws Exception { | |
for (TestInfo info : testInfos.values()) { | |
if (info.blackFlagMainFilePath != null) { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
List<ICompileUnitScope> cuList = getCompileUnitsFor(symbolReader, info.blackFlagMainFilePath.lastSegment()); | |
assertFalse(cuList.isEmpty()); | |
for (ICompileUnitScope cu : cuList) { | |
for (IFunctionScope func : cu.getFunctions()) { | |
if (func.getName().equals("doExampleL")) { | |
for (IVariable var : func.getVariablesInTree()) { | |
if (var.getName().equals("KHelloWorldText")) { | |
IScope vScope = var.getScope(); | |
if (vScope instanceof ILexicalBlockScope) | |
vScope = vScope.getParent(); | |
assertEquals(func, vScope); | |
_testStaticLocal(var.getType()); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
@Test | |
public void testStaticLocals2() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_gcce.sym")); | |
DwarfDebugInfoProvider provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
IType type = provider.readType(0x7e11); | |
_testStaticLocal(type); | |
/////////// | |
symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
type = provider.readType(0x1dc89a); | |
_testStaticLocal(type); | |
} | |
/** | |
* @param type | |
*/ | |
private void _testStaticLocal(IType type) { | |
assertTrue(getTypeName(type), type instanceof ConstType); | |
type = type.getType(); | |
assertTrue(getTypeName(type), type instanceof ICompositeType); | |
ICompositeType comp = (ICompositeType) type; | |
// differs in RVCT and GCCE | |
assertTrue(getTypeName(type), "struct TLitC<14>".equals(comp.getName()) | |
|| "class TLitC".equals(comp.getName())); | |
assertEquals(2, comp.fieldCount()); | |
assertEquals(0, comp.inheritanceCount()); | |
IField f = comp.getFields()[0]; | |
assertEquals("iTypeLength", f.getName()); | |
assertTrue(getTypeName(f.getType()), f.getType() instanceof ITypedef); | |
type = f.getType().getType(); | |
assertTrue(getTypeName(type), type instanceof IBasicType); | |
f = comp.getFields()[1]; | |
assertEquals("iBuf", f.getName()); | |
assertTrue(getTypeName(f.getType()), f.getType() instanceof IArrayType); | |
IArrayType arr = (IArrayType) f.getType(); | |
assertNotNull(arr.getBounds()); | |
assertEquals(1, arr.getBoundsCount()); | |
IArrayBoundType bound = arr.getBound(0); | |
assertEquals(0, bound.getDimensionIndex()); | |
assertEquals(14, bound.getBoundCount()); | |
assertEquals(1, bound.getElementCount()); | |
type = arr.getType(); | |
assertTrue(getTypeName(type), type instanceof ITypedef); | |
type = type.getType(); | |
assertTrue(getTypeName(type), type instanceof ICPPBasicType); | |
assertEquals(8, ((ICPPBasicType)type).getQualifierBits()); | |
assertTrue(((ICPPBasicType)type).isUnsigned()); | |
assertTrue(type instanceof IMayBeQualifedType); | |
assertFalse(((IMayBeQualifedType)type).isConst()); | |
assertFalse(((IMayBeQualifedType)type).isVolatile()); | |
} | |
/** | |
* Discovered while testing GCC-E | |
* @throws Exception | |
*/ | |
@Test | |
public void testSpecificTypes4() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_gcce.sym")); | |
DwarfDebugInfoProvider provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
IType type = provider.readType(0x36104); | |
_testSpecificTypeGCCEDerv1(type); | |
} | |
/** | |
* @param type | |
*/ | |
private void _testSpecificTypeGCCEDerv1(IType type) { | |
assertTrue(type.getName(), type instanceof ICompositeType); | |
ICompositeType derv1Comp = (ICompositeType) type; | |
assertEquals("struct Derv1", derv1Comp.getName()); | |
assertEquals(1, derv1Comp.fieldCount()); | |
assertEquals(2, derv1Comp.inheritanceCount()); | |
IField vptrField = derv1Comp.findFields("_vptr$Derv1")[0]; // renamed by parser to avoid having an invalid expression-like field | |
assertNotNull("_vptr$Derv1", vptrField); | |
// inherited fields also visible | |
IField xField = derv1Comp.findFields("x")[0]; | |
assertNotNull("x", xField); | |
// x is in an inherited type | |
IInheritance[] inhs = derv1Comp.getInheritances(); | |
assertEquals(2, inhs.length); | |
IType base1 = inhs[1].getType(); | |
assertNotNull(base1); | |
assertEquals("struct Base1", base1.getName()); | |
assertTrue(base1.getName(), base1 instanceof ICompositeType); | |
ICompositeType base1Comp = (ICompositeType) base1; | |
assertEquals(1, base1Comp.fieldCount()); | |
assertEquals(0, base1Comp.inheritanceCount()); | |
xField = base1Comp.findFields("x")[0]; | |
assertNotNull("x", xField); | |
IType base2 = inhs[0].getType(); | |
assertNotNull(base2); | |
assertEquals("struct Base2", base2.getName()); | |
assertTrue(base2.getName(), base2 instanceof ICompositeType); | |
ICompositeType base2Comp = (ICompositeType) base2; | |
assertEquals(0, base2Comp.fieldCount()); | |
assertEquals(0, base2Comp.inheritanceCount()); | |
// watch for side effects (late adding of inherited fields) | |
assertEquals(1, derv1Comp.fieldCount()); | |
assertEquals(2, derv1Comp.inheritanceCount()); | |
assertEquals(1, base1Comp.fieldCount()); | |
assertEquals(0, base1Comp.inheritanceCount()); | |
assertEquals(0, base2Comp.fieldCount()); | |
assertEquals(0, base2Comp.inheritanceCount()); | |
IScope scope = type.getScope(); | |
assertTrue(scope instanceof ICompileUnitScope); | |
IPath path = ((ICompileUnitScope) scope).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equalsIgnoreCase("dbg_multipleInheritance.cpp")); | |
} | |
private boolean isExecutable(ISymbol symbol) { | |
Boolean executable = (Boolean)symbol.getProperties().get(ISymbol.PROPERTY_EXECUTABLE); | |
// for testing purposes, if the property doesn't exist, assume true | |
return executable == null || executable; | |
} | |
/** | |
* Test that we do not have multiple entries for the same symbol with a zero size. | |
* @throws Exception | |
*/ | |
@Test | |
public void testSymbols() throws Exception { | |
for (TestInfo info : testInfos.values()) { | |
if (info.numberOfSymbols == 0) continue; | |
IEDCSymbolReader reader = Symbols.getSymbolReader(info.symFile); | |
Collection<ISymbol> symbols = reader.getSymbols(); | |
int numSymbols = symbols.size(); | |
//System.out.println(info.symFile.lastSegment() + " : " + numSymbols); | |
assertEquals(info.symFile.lastSegment(), info.numberOfSymbols, numSymbols); | |
Map<IAddress, List<ISymbol>> zeroSymbols = new HashMap<IAddress, List<ISymbol>>(); | |
for (ISymbol symbol : symbols) { | |
if (symbol.getSize() == 0) { | |
IAddress symAddr = symbol.getAddress(); | |
List<ISymbol> sameAddrList = zeroSymbols.get(symAddr); | |
if (sameAddrList != null && !symAddr.getValue().equals(BigInteger.ZERO)) { | |
// with the addition of multiple zero-sized symbols, | |
// the criterion is that there should not now be | |
// duplicate executable symbols at the same address | |
if (isExecutable(symbol)) { | |
for (ISymbol sameAddr : sameAddrList) { | |
if (symbol.getClass().equals(sameAddr) && isExecutable(sameAddr)) { | |
if (symbol instanceof IARMSymbol) { | |
// you may have more than one zero-sized executable | |
// symbol at the same address in ARM. But they should | |
// not be the same mode, e.g. both ARM or both Thumb. | |
assertFalse("multiple zero-size symbols" + symbol.getName() + " & "+ sameAddr.getName(), | |
((IARMSymbol)symbol).isThumbAddress() == ((IARMSymbol)sameAddr).isThumbAddress()); | |
} else { | |
assertFalse("multiple zero-size symbols" + symbol.getName() + " & "+ sameAddr.getName(), true); | |
} | |
} | |
} | |
} | |
} else { | |
sameAddrList = new ArrayList<ISymbol>(); | |
} | |
sameAddrList.add(symbol); | |
zeroSymbols.put(symbol.getAddress(), sameAddrList); | |
} | |
} | |
} | |
} | |
/** | |
* Test some type lookup edge cases | |
*/ | |
@Test | |
public void testSpecificTypes4a() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
List<ICompileUnitScope> scopes = getCompileUnitsFor(symbolReader, "dbg_multipleInheritance.cpp"); | |
if (scopes.isEmpty()) | |
scopes = getCompileUnitsFor(symbolReader, "dbg_multipleinheritance.cpp"); | |
assertFalse(scopes.isEmpty()); | |
/* | |
if (Symbols.useNewReader()) | |
// TODO: along with other filepath lookup issues: | |
// what happens here is, there are three CUs for dbg_multipleInheritance.cpp, | |
// but one has no DW_AT_comp_dir. All of their names are \BlackFlags\SRC\dbg_multipleInheritance.cpp, | |
// which (once canonicalized) looks like an absolute path in Linux, thus comes in as | |
// three distinct CUs for the same path. In Win32, though, the comp dir is prepended | |
// in two cases, since the name is not considered absolute. | |
assertEquals(HostOS.IS_WIN32 ? 2 : 3, scopes.size()); | |
else | |
assertEquals(1, scopes.size()); | |
*/ | |
IFunctionScope functionScope = null; | |
for (ICompileUnitScope scope : scopes) { | |
functionScope = scope.getFunctionAtAddress(new Addr32(0xaa4e)); | |
if (functionScope != null) | |
break; | |
} | |
assertNotNull(functionScope); | |
Collection<IVariable> vars = functionScope.getVariablesInTree(); | |
assertEquals(2, vars.size()); | |
java.util.Iterator<IVariable> vit = vars.iterator(); | |
assertEquals("FromBase2", vit.next().getName()); | |
assertEquals("a", vit.next().getName()); | |
vars = functionScope.getParameters(); | |
assertEquals(1, vars.size()); | |
vit = vars.iterator(); | |
IVariable thisVar = vit.next(); | |
assertEquals("this", thisVar.getName()); | |
assertNotNull(thisVar.getType()); | |
_testSpecificTypes4(thisVar.getType()); | |
} | |
/** | |
* Test some type lookup edge cases | |
*/ | |
@Test | |
public void testSpecificTypes4b() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
DwarfDebugInfoProvider provider = getDwarfDebugInfoProvider(symbolReader); | |
if (provider == null) | |
return; | |
// 0x1cb0c6: subprogram | |
// 0x1cb0de: this | |
// 0x1cb098: Base2* | |
IType type = provider.readType(0x1cb098); | |
assertNotNull(type); | |
_testSpecificTypes4(type); | |
} | |
/** | |
* @param type | |
*/ | |
private void _testSpecificTypes4(IType type) { | |
assertTrue(type.getName(), type instanceof IPointerType); | |
IType base2 = ((IPointerType) type).getType(); | |
assertFalse(base2 instanceof IForwardTypeReference); // should not peek through interface | |
assertNotNull(base2); | |
assertTrue(base2.getName(), base2 instanceof ICompositeType); | |
ICompositeType base2Comp = (ICompositeType) base2; | |
assertEquals("class Base2", base2Comp.getName()); | |
assertEquals(0, base2Comp.fieldCount()); | |
assertEquals(0, base2Comp.inheritanceCount()); | |
// the class is in the header | |
IScope classScope = ((IPointerType)type).getType().getScope(); | |
assertTrue(classScope instanceof ICompileUnitScope); | |
IPath path = ((ICompileUnitScope) classScope).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equalsIgnoreCase("dbg_multipleInheritance.h")); | |
// the pointer type is declared in a function | |
assertTrue(type.getScope() instanceof IFunctionScope); | |
IScope fileScope = type.getScope().getParent(); | |
assertTrue(fileScope instanceof ICompileUnitScope); | |
path = ((ICompileUnitScope) fileScope).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equalsIgnoreCase("dbg_multipleInheritance.cpp")); | |
} | |
/** | |
* Test some type lookup edge cases | |
*/ | |
@Test | |
public void testSpecificTypes5a() throws Exception { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_rvct.sym")); | |
// inlined Der1::Der1() | |
IScope scope = symbolReader.getModuleScope().getScopeAtAddress(new Addr32(0x8cda)); | |
assertTrue(scope instanceof IFunctionScope); | |
IFunctionScope functionScope = (IFunctionScope) scope; | |
// two locals __func_local__0 and __result are optimized out | |
Collection<IVariable> vars = functionScope.getVariablesInTree(); | |
assertEquals(0, vars.size()); | |
java.util.Iterator<IVariable> vit; | |
/* | |
vit = vars.iterator(); | |
IVariable var = vit.next(); | |
assertEquals("__func_local__0", var.getName()); | |
var = vit.next(); | |
assertEquals("__result", var.getName()); | |
*/ | |
vars = functionScope.getParameters(); | |
assertEquals(1, vars.size()); | |
vit = vars.iterator(); | |
IVariable thisVar = vit.next(); | |
assertEquals("this", thisVar.getName()); | |
assertNotNull(thisVar.getType()); | |
_testSpecificTypes5(thisVar.getType()); | |
} | |
/** | |
* @param type | |
*/ | |
private void _testSpecificTypes5(IType type) { | |
assertTrue(type.getName(), type instanceof IPointerType); | |
IType der1 = ((IPointerType) type).getType(); | |
assertFalse(der1 instanceof IForwardTypeReference); // should not peek through interface | |
assertNotNull(der1); | |
assertTrue(der1.getName(), der1 instanceof ICompositeType); | |
ICompositeType der1Comp = (ICompositeType) der1; | |
assertEquals("struct Der1", der1Comp.getName()); | |
assertEquals(1, der1Comp.fieldCount()); | |
assertEquals(1, der1Comp.inheritanceCount()); | |
IField field = der1Comp.findFields("b1")[0]; | |
assertNotNull("b1", field); | |
field = der1Comp.findFields("__vptr")[0]; | |
assertNotNull("__vptr", field); | |
// inherited fields also visible | |
field = der1Comp.findFields("a")[0]; | |
assertNotNull("a", field); | |
// x is in an inherited type | |
IInheritance[] inhs = der1Comp.getInheritances(); | |
assertEquals(1, inhs.length); | |
IType base01 = inhs[0].getType(); | |
assertNotNull(base01); | |
assertEquals("struct Base01", base01.getName()); | |
assertTrue(base01.getName(), base01 instanceof ICompositeType); | |
ICompositeType base01Comp = (ICompositeType) base01; | |
assertEquals(2, base01Comp.fieldCount()); | |
assertEquals(0, base01Comp.inheritanceCount()); | |
field = base01Comp.findFields("a")[0]; | |
assertNotNull("a", field); | |
field = der1Comp.findFields("__vptr")[0]; | |
assertNotNull("__vptr", field); | |
// the class is in the header | |
IScope classScope = ((IPointerType)type).getType().getScope(); | |
assertTrue(classScope instanceof ICompileUnitScope); | |
IPath path = ((ICompileUnitScope) classScope).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equals("dbg_rtti.cpp")); | |
// the pointer type is declared in a function | |
assertTrue(type.getScope() instanceof ICompileUnitScope); | |
path = ((ICompileUnitScope) type.getScope()).getFilePath(); | |
assertTrue(path.toString(), path.lastSegment().equals("dbg_rtti.cpp")); | |
} | |
/** | |
* Look for DWARF files with bad scopes and make sure we fix them up properly. | |
* Use full scanning to populate the content. | |
*/ | |
@Test | |
public void testScopes1a() { | |
for (TestInfo info : testInfos.values()) { | |
System.out.println("Scopes for " + info.symFile.lastSegment()); | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
if (symbolReader != null) { | |
readFully(symbolReader); | |
doTestScopes(symbolReader); | |
} | |
} | |
} | |
/** | |
* Look for DWARF files with bad scopes and make sure we fix them up properly. | |
* Use random scanning to populate the content. | |
*/ | |
@Test | |
public void testScopes1b() { | |
for (TestInfo info : testInfos.values()) { | |
System.out.println("Scopes for " + info.symFile.lastSegment()); | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
if (symbolReader != null) { | |
readRandomly(symbolReader); | |
doTestScopes(symbolReader); | |
} | |
} | |
} | |
/** | |
* @param symbolReader | |
*/ | |
private void doTestScopes(IEDCSymbolReader symbolReader) { | |
IModuleScope moduleScope = symbolReader.getModuleScope(); | |
IAddress low = moduleScope.getLowAddress(); | |
IAddress high = moduleScope.getHighAddress(); | |
assertTrue(low.compareTo(high) < 0); | |
checkChildScopes(moduleScope, low, high); | |
} | |
/** | |
* @param scope | |
* @param low | |
* @param high | |
*/ | |
private void checkChildScopes(IScope scope, IAddress low, IAddress high) { | |
for (IScope kid : scope.getChildren()) { | |
IAddress kidlo = kid.getLowAddress(); | |
IAddress kidhi = kid.getHighAddress(); | |
if (!kid.hasEmptyRange()) { | |
if (!(kidlo.compareTo(kidhi) <= 0)) { | |
fail(describeScope(kid)); | |
} | |
if (!(low.compareTo(kidlo) <= 0)) { | |
fail(describeScope(kid)); | |
} | |
if (!(kidhi.compareTo(high) <= 0)) { | |
fail(describeScope(kid)); | |
} | |
} | |
if (!(kid instanceof ILexicalBlockScope) && !(scope instanceof ILexicalBlockScope)) { | |
checkChildScopes(kid, kidlo, kidhi); | |
} else { | |
// lexical blocks are not constrained to be within other lexical blocks, | |
// but they should be within the function. | |
checkChildScopes(kid, low, high); | |
} | |
} | |
} | |
/** | |
* Make sure our fixing up of CU scopes makes sense. In old GCC-E (e.g. 3.4.3), the | |
* compile units do not have low_pc and high_pc, which makes it hard to find functions. | |
*/ | |
@Test | |
public void testScopes3() { | |
for (TestInfo info : testInfos.values()) { | |
String label = info.symFile.lastSegment(); | |
System.out.println("Address->Function mapping for "+ label); | |
// explicitly read the functions | |
IExecutableSymbolicsReader exeReader = ExecutableSymbolicsReaderFactory.createFor(info.symFile); | |
if (exeReader == null) | |
continue; | |
DwarfDebugInfoProvider debugInfoProvider = new DwarfDebugInfoProvider(exeReader); | |
IEDCSymbolReader explicitSymbolReader = new EDCSymbolReader( | |
exeReader, | |
debugInfoProvider); | |
// this is NOT guaranteed: GCC-E at link time will share function definitions among CUs, | |
// so two CUs can "overlap" if you consider only their raw Lo and Hi PCs. | |
/* | |
// make sure the CUs are proper | |
ArrayList<IScope> cuList= new ArrayList<IScope>(explicitSymbolReader.getModuleScope().getChildren()); | |
Collections.sort(cuList); | |
long low = 0, high = 0; | |
for (IScope cu : cuList) { | |
String culabel = label + ":" + cu; | |
long start = cu.getLowAddress().getValue().longValue(); | |
long end = cu.getHighAddress().getValue().longValue(); | |
if (start != end) { | |
assertTrue(culabel, start < end); | |
assertTrue(culabel, start >= low); | |
if (high > 0) | |
assertTrue(culabel, end > high); | |
high = end; | |
} | |
} | |
*/ | |
// remember all the functions | |
List<IFunctionScope> allFuncs = new ArrayList<IFunctionScope>( | |
explicitSymbolReader.getModuleScope().getFunctionsByName(null)); | |
assertTrue(allFuncs.size() >= 10); | |
debugInfoProvider.dispose(); | |
// now, make sure we can find all those functions with a random scan | |
debugInfoProvider = new DwarfDebugInfoProvider(exeReader); | |
IEDCSymbolReader testSymbolReader = new EDCSymbolReader( | |
exeReader, | |
debugInfoProvider); | |
StringBuilder missing = new StringBuilder(); | |
Random random = new Random(0x12145895); | |
int count = allFuncs.size(); | |
while (count-- > 0) { | |
IFunctionScope allFunc = allFuncs.get(random.nextInt(allFuncs.size())); | |
doTestScope3Func(label, missing, testSymbolReader, allFunc); | |
} | |
debugInfoProvider.dispose(); | |
if(missing.length() > 0) | |
fail(missing.toString()); | |
} | |
// as good a place as any to test the fields of the static EDCSymbolReader.EMPTY_MODULE_SCOPE | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.compareTo(new Object()) == 0); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.equals(new Object()) == false); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getChildren().isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getCompileUnitForAddress(null) == null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getCompileUnitsForFile(null).isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getEnumerators().isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getFrameRegisterProvider() == null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getFunctionsByName(null).isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getHighAddress().isZero()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getLowAddress().isZero()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getModuleLineEntryProvider() != null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getName().length() == 0); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getParent() == null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getRangeList() == null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getScopeAtAddress(null) == null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getSymbolFile() == null); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getTypes().isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getVariables().isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getVariablesByName(null, false).isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.getVariablesByName(null, true).isEmpty()); | |
assertTrue(EDCSymbolReader.EMPTY_MODULE_SCOPE.hasEmptyRange()); | |
} | |
/** | |
* Make sure our fixing up of CU scopes makes sense. In old GCC-E (e.g. 3.4.3), the | |
* compile units do not have low_pc and high_pc, which makes it hard to find functions. | |
*/ | |
@Test | |
public void testScopes3b() { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(getFile("BlackFlag_gcce_343.sym")); | |
IScope scope = symbolReader.getModuleScope().getScopeAtAddress(new Addr32(0xad32)); | |
assertTrue(scope instanceof IFunctionScope || scope instanceof ILexicalBlockScope); | |
} | |
/** | |
* @param label | |
* @param missing | |
* @param symbolReader | |
* @param allFunc | |
*/ | |
private void doTestScope3Func(String label, StringBuilder missing, | |
IEDCSymbolReader symbolReader, IFunctionScope allFunc) { | |
IScope scope1 = symbolReader.getModuleScope().getScopeAtAddress(allFunc.getLowAddress().add(1)); | |
IScope scope2 = symbolReader.getModuleScope().getScopeAtAddress(allFunc.getHighAddress().add(-1)); | |
if (scope1 == null || scope2 == null) { | |
missing.append(label + ":"+ allFunc +" missing\n"); | |
} | |
// skip if the function is defined over a range, because this may mean interleaved runtime code | |
else if (allFunc.getRangeList() == null){ | |
// one of both may be inlined | |
while (scope1 instanceof ILexicalBlockScope || scope1.getParent() instanceof IFunctionScope) | |
scope1 = scope1.getParent(); | |
while (scope2 instanceof ILexicalBlockScope || scope2.getParent() instanceof IFunctionScope) | |
scope2 = scope2.getParent(); | |
if (scope1 != scope2) { | |
missing.append(label + ":"+ allFunc +" did not find same function at low and high: " + scope1 + " / "+ scope2 + "\n"); | |
} | |
} | |
} | |
@Test | |
public void testRangeList() { | |
TestInfo info = testInfos.get("BlackFlagMinGW.exe"); | |
// explicitly read the functions | |
IExecutableSymbolicsReader exeReader = ExecutableSymbolicsReaderFactory.createFor(info.symFile); | |
DwarfDebugInfoProvider debugInfoProvider = new DwarfDebugInfoProvider(exeReader); | |
IEDCSymbolReader reader = new EDCSymbolReader(exeReader, debugInfoProvider); | |
List<ICompileUnitScope> scopes = new ArrayList<ICompileUnitScope>(); | |
for (IScope scope : reader.getModuleScope().getChildren()) { | |
if (scope instanceof ICompileUnitScope) { | |
scopes.add((ICompileUnitScope) scope); | |
} | |
} | |
// check and manipulate range list | |
for (ICompileUnitScope cuScope : scopes) { | |
if (cuScope.getRangeList() != null) { | |
RangeList rangeList = (RangeList) cuScope.getRangeList(); | |
long low = rangeList.getLowAddress(); | |
long high = rangeList.getHighAddress(); | |
rangeList.addLowRange(low - 1); | |
rangeList.addHighRange(high + 1); | |
assertTrue(rangeList.isInRange(low)); | |
assertTrue(low > rangeList.getLowAddress()); | |
assertTrue(high < rangeList.getHighAddress()); | |
assertTrue(rangeList.toString(). | |
contains(Long.toHexString(rangeList.getLowAddress()))); | |
break; | |
} | |
} | |
} | |
@Test | |
public void testFrameRegInstructions() { | |
EDCServicesTracker edcTracker = null; | |
try { | |
TestUtils.showDebugPerspective(); | |
dwarfAlbum = DwarfFrameRegisterAlbum.openAlbum(); | |
dwarfAlbum.launchAndWaitForSuspendedContext(); | |
if (dwarfAlbum.getLaunch() == null) return; | |
edcTracker = new EDCServicesTracker(EDCDebugger.getBundleContext(), dwarfAlbum.getSession().getId()); | |
Assert.assertNotNull(edcTracker); | |
} catch (Exception e) { | |
Assert.fail(e.getLocalizedMessage()); | |
} | |
TestInfo info = testInfos.get("BlackFlag_rvct.sym"); | |
// explicitly read the types to parse the file | |
IExecutableSymbolicsReader exeReader = ExecutableSymbolicsReaderFactory.createFor(info.symFile); | |
DwarfDebugInfoProvider provider = new DwarfDebugInfoProvider(exeReader); | |
provider.getTypes(); | |
FrameDescriptionEntry fde = provider.findFrameDescriptionEntry(new Addr64("32768")); | |
InstructionState dummyState; | |
// Can remove try blocks when all AbstractInstruction#applyInstruction()'s are implemented | |
dummyState = new InstructionState(edcTracker, null, null, fde); | |
try { | |
DefCFAExpressionInstruction defCFAEI = new DefCFAExpressionInstruction(null); | |
assertNotNull(defCFAEI.toString()); | |
defCFAEI.applyInstruction(dummyState); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// save an expression | |
ExpressionInstruction ei = new ExpressionInstruction(0, null); | |
assertNotNull(ei.toString()); | |
ei.applyInstruction(dummyState); | |
assertTrue(dummyState.regRules.containsKey(0)); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// save a register | |
RegisterInstruction re = new RegisterInstruction(0, 1); | |
assertNotNull(re.toString()); | |
re.applyInstruction(dummyState); | |
assertTrue(dummyState.regRules.containsKey(0)); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// nop | |
NopInstruction nop = new NopInstruction(); | |
assertNotNull(nop.toString()); | |
int hash = dummyState.hashCode(); | |
nop.applyInstruction(dummyState); | |
assertTrue(dummyState.hashCode() == hash); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// save an expression for a register | |
ValueExpressionInstruction vei = new ValueExpressionInstruction(0, null); | |
assertNotNull(vei.toString()); | |
vei.applyInstruction(dummyState); | |
assertTrue(dummyState.regRules.containsKey(0)); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// save a register offset | |
ValueOffsetInstruction voi = new ValueOffsetInstruction(0, 12); | |
assertNotNull(voi.toString()); | |
voi.applyInstruction(dummyState); | |
assertTrue(dummyState.regRules.containsKey(0)); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// push a state on the state stack | |
RememberStateInstruction rsi = new RememberStateInstruction(); | |
assertNotNull(rsi.toString()); | |
int i = dummyState.stateStack.size(); | |
rsi.applyInstruction(dummyState); | |
assertTrue(dummyState.stateStack.size() == i + 1); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
// remove rule for instruction | |
RestoreInstruction ri = new RestoreInstruction(0); | |
assertNotNull(ri.toString()); | |
ri.applyInstruction(dummyState); | |
assertFalse(dummyState.regRules.containsKey(0)); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
RememberStateInstruction rsi1 = new RememberStateInstruction(); | |
rsi1.applyInstruction(dummyState); | |
// remove rule for instruction | |
RestoreStateInstruction rsi = new RestoreStateInstruction(); | |
assertNotNull(rsi.toString()); | |
rsi.applyInstruction(dummyState); | |
assertFalse(dummyState.regRules.isEmpty()); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
DefCFAInstruction defCFA = new DefCFAInstruction(0, 12); | |
assertNotNull(defCFA.toString()); | |
defCFA.applyInstruction(dummyState); | |
assertTrue(dummyState.getCFARegister() == 0); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
SameValueInstruction svi = new SameValueInstruction(0); | |
assertNotNull(svi.toString()); | |
svi.applyInstruction(dummyState); | |
assertFalse(dummyState.regRules.isEmpty()); | |
} catch (Throwable t) { | |
} | |
dummyState = new InstructionState(null, null, null, fde); | |
try { | |
UndefinedInstruction svi = new UndefinedInstruction(0); | |
assertNotNull(svi.toString()); | |
svi.applyInstruction(dummyState); | |
assertFalse(dummyState.regRules.isEmpty()); | |
} catch (Throwable t) { | |
} | |
} | |
@Test | |
public void testModuleScopeStartEnd() { | |
for (TestInfo info : testInfos.values()) { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
assertNotNull(symbolReader); | |
IScope moduleScope = symbolReader.getModuleScope(); | |
if (info.lowAddress != null) { | |
assertEquals(info.lowAddress, moduleScope.getLowAddress()); | |
assertEquals(info.highAddress, moduleScope.getHighAddress()); | |
} | |
} | |
} | |
@Test | |
public void testPubnames1() { | |
for (TestInfo info : testInfos.values()) { | |
if (info.numberOfPubFuncNames == 0) continue; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
DwarfDebugInfoProvider debugInfoProvider = (DwarfDebugInfoProvider) ((EDCSymbolReader) symbolReader).getDebugInfoProvider(); | |
Map<String, List<PublicNameInfo>> pubs; | |
pubs = debugInfoProvider.getPublicFunctions(); | |
boolean discover = false; | |
int total = 0; | |
for (String pub : pubs.keySet()) { | |
total += pubs.get(pub).size(); | |
} | |
if (discover) { | |
System.out.println(info.symFile + ": " + pubs.size() + " / "+ total); | |
} else { | |
assertEquals(info.numberOfPubFuncNames, pubs.size()); | |
assertEquals(info.numberOfPubFuncEntries, total); | |
} | |
pubs = debugInfoProvider.getPublicVariables(); | |
total = 0; | |
for (String pub : pubs.keySet()) { | |
total += pubs.get(pub).size(); | |
} | |
if (discover) { | |
System.out.println(info.symFile + ": " + pubs.size() + " / "+ total); | |
} else { | |
assertEquals(info.numberOfPubVarNames, pubs.size()); | |
assertEquals(info.numberOfPubVarEntries, total); | |
} | |
} | |
} | |
@Test | |
public void testPubnames2() { | |
for (TestInfo info : testInfos.values()) { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
for (String name : info.pubFuncs) { | |
String simpleName = stripName(name); | |
Collection<IFunctionScope> funcs = symbolReader.getModuleScope().getFunctionsByName(simpleName); | |
assertNotNull(info.symFile.lastSegment() + ":" + name, funcs); | |
assertTrue(info.symFile.lastSegment() + ":" + name, !funcs.isEmpty()); | |
for (IFunctionScope func : funcs) { | |
assertEquals(func.getName(), simpleName); | |
} | |
} | |
for (String name : info.pubVars) { | |
String simpleName = stripName(name); | |
Collection<IVariable> vars = symbolReader.getModuleScope().getVariablesByName(simpleName, false); | |
assertNotNull(info.symFile.lastSegment() + ":" + name, vars); | |
assertTrue(info.symFile.lastSegment() + ":" + name, !vars.isEmpty()); | |
for (IVariable var : vars) { | |
assertEquals(var.getName(), simpleName); | |
} | |
} | |
} | |
} | |
/** | |
* Test that, for a given PC, we know which locals are in scope. | |
*/ | |
@Test | |
public void testLocalScopes1() { | |
StringBuilder errors = new StringBuilder(); | |
for (TestInfo info : testInfos.values()) { | |
for (String key : info.scopeInfos.keySet()) { | |
String[] split = key.split("\\|"); | |
String file = split[0]; | |
String func = split[1]; | |
String className = split[2]; | |
if (className.equals("null")) | |
className = null; | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
List<ICompileUnitScope> cuList = getCompileUnitsFor(symbolReader, file); | |
boolean found = false; | |
for (ICompileUnitScope cu : cuList) { | |
for (IFunctionScope scope : cu.getFunctions()) { | |
if (scope.getName().equals(func)) { | |
if (className != null) { | |
String theClassName = getClassFor(scope); | |
if (!theClassName.matches("(class|struct) " +className + "(<.*>)?")) | |
continue; | |
} | |
found = true; | |
String label = info.symFile.lastSegment() + ":" + file + (className!= null ? ":" + className : "") + ":" + func; | |
boolean discover = false; | |
if (discover) { | |
System.out.println(label); | |
Collection<IVariable> vars = scope.getVariables(); | |
for (IVariable var : vars) System.out.println(var.getName()); | |
vars = scope.getParameters(); | |
for (IVariable var : vars) System.out.println(var.getName()); | |
} else { | |
doTestLocalScopes1(errors, label, info.scopeInfos.get(key), scope); | |
} | |
} | |
} | |
} | |
assertTrue(info.symFile.lastSegment() + ":" + key, found); | |
} | |
} | |
if (errors.length() > 0) | |
fail(errors.toString()); | |
} | |
/** | |
* @param errors | |
* @param scopeInfos | |
* @param scope | |
*/ | |
private void doTestLocalScopes1(StringBuilder errors, String label, List<ScopeInfo> scopeInfos, IFunctionScope scope) { | |
// TODO: watch out for template functions, which are named the same | |
Addr32 startAddr = new Addr32(scopeInfos.get(0).address); | |
if (scope.getLowAddress().compareTo(startAddr) > 0 | |
|| scope.getHighAddress().compareTo(startAddr) <= 0) { | |
return; | |
} | |
for (ScopeInfo scopeInfo : scopeInfos) { | |
try { | |
doTestScopeInfo(label, scope, scopeInfo); | |
} catch (AssertionError e) { | |
errors.append(e.getMessage()); | |
errors.append('\n'); | |
} | |
} | |
} | |
private void doTestScopeInfo(String label, IFunctionScope scope, | |
ScopeInfo scopeInfo) { | |
label += ":" + Integer.toHexString(scopeInfo.address); | |
Collection<IVariable> vars = scope.getScopedVariables(new Addr32(scopeInfo.address)); | |
StringBuilder missing = new StringBuilder(); | |
for (String name : scopeInfo.names) { | |
boolean found = false; | |
for (IVariable var : vars) { | |
if (var.getName().equals(name)) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) | |
missing.append(name + ", "); | |
} | |
if (missing.length() > 0) | |
fail(label + ": missing "+ missing); | |
StringBuilder extra = new StringBuilder(); | |
for (IVariable var : vars) { | |
boolean found = false; | |
for (String name : scopeInfo.names) { | |
if (name.equals(var.getName())) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
extra.append(var.getName()); | |
extra.append(", "); | |
} | |
} | |
if (extra.length() > 0) | |
fail(label +": found extra variables: " + extra); | |
} | |
/** | |
* Test that, for a given PC, we know which locals are in scope. | |
*/ | |
@Test | |
public void testLocalLocations() { | |
StringBuilder errors = new StringBuilder(); | |
for (TestInfo info : testInfos.values()) { | |
IEDCSymbolReader symbolReader = Symbols.getSymbolReader(info.symFile); | |
for (IScope cuscope : symbolReader.getModuleScope().getChildren()) { | |
ICompileUnitScope cu = (ICompileUnitScope) cuscope; | |
for (IFunctionScope scope : cu.getFunctions()) { | |
String label = info.symFile.lastSegment() + ":" + scope; | |
for (IVariable var : scope.getVariablesInTree()) { | |
checkVariableLocation(errors, label, scope, var); | |
} | |
} | |
} | |
} | |
if (errors.length() > 0) | |
fail(errors.toString()); | |
} | |
/** | |
* @param errors | |
* @param label | |
* @param scope | |
* @param var | |
*/ | |
private void checkVariableLocation(StringBuilder errors, String label, | |
IScope scope, IVariable var) { | |
ILocationProvider locationProvider = var.getLocationProvider(); | |
if (locationProvider instanceof LocationExpression) { | |
LocationExpression expr = (LocationExpression) locationProvider; | |
IScope varScope = expr.getScope(); | |
while (varScope instanceof ILexicalBlockScope || | |
(varScope != null && varScope.getParent() instanceof IFunctionScope)) | |
varScope = varScope.getParent(); | |
if (!(varScope instanceof IFunctionScope)) { | |
errors.append("Wrong scope for " + var + ": " + varScope + "\n"); | |
} | |
} | |
} | |
/** | |
* Be sure we zero extend when reading short DWARF attributes. | |
* One place this is quite important is in array bounds. | |
* Other cases in the reader are line tables, scope ranges, etc. which | |
* are unsigned. Enum values, though, must remain signed. | |
* @throws Exception | |
*/ | |
@Test | |
public void testAttributePromotion() throws Exception { | |
// In here, we have a tmp[256] array, with DW_AT_upper_bound == 255. | |
// This validates some lazy code that always cast this stuff to a long | |
// without considering the sign extension. | |
// (BTW, this bug was logged for something else, and is not the bug being tested here) | |
IEDCSymbolReader reader = Symbols.getSymbolReader(getFile("bug303066.exe")); | |
assertNotNull(reader); | |
Collection<IFunctionScope> scopes = reader.getModuleScope().getFunctionsByName("main"); | |
assertNotNull(scopes); | |
assertEquals(1, scopes.size()); | |
boolean found = false; | |
int enumcnt = 0; | |
for (IVariable var : scopes.iterator().next().getVariables()) { | |
if (var.getName().equals("tmp")) { | |
IType type = var.getType(); | |
assertTrue(type instanceof IArrayType); | |
IArrayBoundType bound = ((IArrayType) type).getBound(0); | |
assertEquals(256, bound.getBoundCount()); | |
found = true; | |
} | |
// these attributes *should* be sign extended | |
if (var.getName().equals("val")) { | |
IType type = TypeUtils.getBaseType(var.getType()); | |
for (IEnumerator enumr : ((IEnumeration)type).getEnumerators()) { | |
if (enumr.getName().equals("Val1")) { | |
assertEquals(255, enumr.getValue()); | |
enumcnt++; | |
} else if (enumr.getName().equals("Val2")) { | |
assertEquals(65535, enumr.getValue()); | |
enumcnt++; | |
} else if (enumr.getName().equals("Val3")) { | |
assertEquals(-255, enumr.getValue()); | |
enumcnt++; | |
} else if (enumr.getName().equals("Val4")) { | |
assertEquals(-65535, enumr.getValue()); | |
enumcnt++; | |
} | |
} | |
} | |
} | |
assertEquals("Found all Val enums", 4, enumcnt); | |
assertTrue("Did not find 'tmp'", found); | |
found = false; | |
for (IVariable var : reader.getModuleScope().getVariablesByName("bigbuffer", false)) { | |
IType type = var.getType(); | |
assertTrue(type instanceof IArrayType); | |
IArrayBoundType bound = ((IArrayType) type).getBound(0); | |
assertEquals(65536, bound.getBoundCount()); | |
found = true; | |
break; | |
} | |
assertTrue("Did not find 'bigbuffer'", found); | |
} | |
/** | |
* RVCT generates location lists with two duplicate address ranges, where | |
* the latter is the one to trust. Be sure we filter out the earlier ones. | |
* @throws Exception | |
*/ | |
@Test | |
public void testBrokenLocationLists() throws Exception { | |
IPath file = getFile("BlackFlag_rvct.sym"); | |
IEDCSymbolReader reader = Symbols.getSymbolReader(file); | |
// show_Const_Arguments | |
IFunctionScope func = (IFunctionScope) reader.getModuleScope().getScopeAtAddress(new Addr32(0x9648)); | |
Collection<IVariable> variables = func.getParameters(); | |
boolean found = false; | |
for (IVariable var : variables) { | |
if (var.getName().equals("aArg1")) { | |
found = true; | |
LocationEntry[] entries = ((LocationList)var.getLocationProvider()).getLocationEntries(); | |
assertEquals(2, entries.length); | |
assertEquals(0x9648, entries[0].getLowPC()); | |
assertEquals(1, entries[0].getBytes().length); | |
assertEquals((byte)0x50, entries[0].getBytes()[0]); | |
assertEquals("LocationEntry [bytes=[80], highPC=38474, lowPC=38472]", entries[0].toString()); | |
break; | |
} | |
} | |
assertTrue(found); | |
} | |
} |