| //===- llvm/unittest/DebugInfo/LogicalView/DWARFReaderTest.cpp ------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVType.h" |
| #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Support/COM.h" |
| #include "llvm/Support/InitLLVM.h" |
| #include "llvm/Support/ScopedPrinter.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include "llvm/Testing/Support/Error.h" |
| |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| using namespace llvm::logicalview; |
| |
| extern const char *TestMainArgv0; |
| |
| namespace { |
| |
| const char *DwarfClang = "test-dwarf-clang.o"; |
| // Two compile units: one declares `extern int foo_printf(const char *, ...);` |
| // and another one that defines the function. |
| const char *DwarfClangUnspecParams = "test-dwarf-clang-unspec-params.elf"; |
| const char *DwarfClangModule = "test-dwarf-clang-module.o"; |
| const char *DwarfGcc = "test-dwarf-gcc.o"; |
| |
| // Helper function to get the first compile unit. |
| LVScopeCompileUnit *getFirstCompileUnit(LVScopeRoot *Root) { |
| EXPECT_NE(Root, nullptr); |
| const LVScopes *CompileUnits = Root->getScopes(); |
| EXPECT_NE(CompileUnits, nullptr); |
| EXPECT_GT(CompileUnits->size(), 0u); |
| |
| LVScopes::const_iterator Iter = CompileUnits->begin(); |
| EXPECT_NE(Iter, nullptr); |
| LVScopeCompileUnit *CompileUnit = static_cast<LVScopeCompileUnit *>(*Iter); |
| EXPECT_NE(CompileUnit, nullptr); |
| return CompileUnit; |
| } |
| |
| // Helper function to create a reader. |
| std::unique_ptr<LVReader> createReader(LVReaderHandler &ReaderHandler, |
| SmallString<128> &InputsDir, |
| StringRef Filename) { |
| SmallString<128> ObjectName(InputsDir); |
| llvm::sys::path::append(ObjectName, Filename); |
| |
| Expected<std::unique_ptr<LVReader>> ReaderOrErr = |
| ReaderHandler.createReader(std::string(ObjectName)); |
| EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded()); |
| std::unique_ptr<LVReader> Reader = std::move(*ReaderOrErr); |
| EXPECT_NE(Reader, nullptr); |
| return Reader; |
| } |
| |
| // Check the logical elements basic properties. |
| void checkElementProperties(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root); |
| |
| EXPECT_EQ(Root->getFileFormatName(), "elf64-x86-64"); |
| EXPECT_EQ(Root->getName(), DwarfClang); |
| |
| EXPECT_EQ(CompileUnit->getBaseAddress(), 0u); |
| EXPECT_TRUE(CompileUnit->getProducer().starts_with("clang")); |
| EXPECT_EQ(CompileUnit->getName(), "test.cpp"); |
| LVSourceLanguage Language = CompileUnit->getSourceLanguage(); |
| EXPECT_TRUE(Language.isValid()); |
| EXPECT_EQ(Language, LVSourceLanguage::DW_LANG_C_plus_plus_14); |
| EXPECT_EQ(Language.getName(), "DW_LANG_C_plus_plus_14"); |
| |
| EXPECT_EQ(CompileUnit->lineCount(), 0u); |
| EXPECT_EQ(CompileUnit->scopeCount(), 1u); |
| EXPECT_EQ(CompileUnit->symbolCount(), 0u); |
| EXPECT_EQ(CompileUnit->typeCount(), 7u); |
| EXPECT_EQ(CompileUnit->rangeCount(), 1u); |
| |
| const LVLocations *Ranges = CompileUnit->getRanges(); |
| ASSERT_NE(Ranges, nullptr); |
| ASSERT_EQ(Ranges->size(), 1u); |
| LVLocations::const_iterator IterLocation = Ranges->begin(); |
| LVLocation *Location = (*IterLocation); |
| EXPECT_STREQ(Location->getIntervalInfo().c_str(), |
| "{Range} Lines 2:9 [0x0000000000:0x000000003a]"); |
| |
| LVRange RangeList; |
| CompileUnit->getRanges(RangeList); |
| |
| const LVRangeEntries &RangeEntries = RangeList.getEntries(); |
| ASSERT_EQ(RangeEntries.size(), 2u); |
| LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); |
| LVRangeEntry RangeEntry = *IterRanges; |
| EXPECT_EQ(RangeEntry.lower(), 0u); |
| EXPECT_EQ(RangeEntry.upper(), 0x3au); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "test.cpp"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0x0bu); |
| |
| ++IterRanges; |
| RangeEntry = *IterRanges; |
| EXPECT_EQ(RangeEntry.lower(), 0x1cu); |
| EXPECT_EQ(RangeEntry.upper(), 0x2fu); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0x71u); |
| |
| const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); |
| ASSERT_EQ(PublicNames.size(), 1u); |
| LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); |
| LVScope *Function = (*IterNames).first; |
| EXPECT_EQ(Function->getName(), "foo"); |
| EXPECT_EQ(Function->getLineNumber(), 2u); |
| LVNameInfo NameInfo = (*IterNames).second; |
| EXPECT_EQ(NameInfo.first, 0u); |
| EXPECT_EQ(NameInfo.second, 0x3au); |
| |
| // Lines (debug and assembler) for 'foo'. |
| const LVLines *Lines = Function->getLines(); |
| ASSERT_NE(Lines, nullptr); |
| ASSERT_EQ(Lines->size(), 0x12u); |
| |
| // Check size of types in CompileUnit. |
| const LVTypes *Types = CompileUnit->getTypes(); |
| ASSERT_NE(Types, nullptr); |
| EXPECT_EQ(Types->size(), 7u); |
| |
| const auto BoolType = llvm::find_if( |
| *Types, [](const LVElement *elt) { return elt->getName() == "bool"; }); |
| ASSERT_NE(BoolType, Types->end()); |
| const auto IntType = llvm::find_if( |
| *Types, [](const LVElement *elt) { return elt->getName() == "int"; }); |
| ASSERT_NE(IntType, Types->end()); |
| EXPECT_EQ(static_cast<LVType *>(*BoolType)->getBitSize(), 8u); |
| EXPECT_EQ(static_cast<LVType *>(*BoolType)->getStorageSizeInBytes(), 1u); |
| EXPECT_EQ(static_cast<LVType *>(*IntType)->getBitSize(), 32u); |
| EXPECT_EQ(static_cast<LVType *>(*IntType)->getStorageSizeInBytes(), 4u); |
| } |
| |
| // Check proper handling of DW_AT_unspecified_parameters in |
| // LVScope::addMissingElements(). |
| void checkUnspecifiedParameters(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root); |
| |
| EXPECT_EQ(Root->getFileFormatName(), "elf64-x86-64"); |
| EXPECT_EQ(Root->getName(), DwarfClangUnspecParams); |
| |
| const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); |
| ASSERT_EQ(PublicNames.size(), 1u); |
| |
| LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); |
| LVScope *Function = (*IterNames).first; |
| EXPECT_EQ(Function->getName(), "foo_printf"); |
| const LVElements *Elements = Function->getChildren(); |
| ASSERT_NE(Elements, nullptr); |
| // foo_printf is a variadic function whose prototype is |
| // `int foo_printf(const char *, ...)`, where the '...' is represented by a |
| // DW_TAG_unspecified_parameters, i.e. we expect to find at least one child |
| // for which getIsUnspecified() returns true. |
| EXPECT_TRUE(llvm::any_of(*Elements, [](const LVElement *elt) { |
| return elt->getIsSymbol() && |
| static_cast<const LVSymbol *>(elt)->getIsUnspecified(); |
| })); |
| } |
| |
| // Check the basic properties on parsed DW_TAG_module. |
| void checkScopeModule(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root); |
| |
| EXPECT_EQ(Root->getFileFormatName(), "Mach-O 64-bit x86-64"); |
| EXPECT_EQ(Root->getName(), DwarfClangModule); |
| |
| ASSERT_NE(CompileUnit->getChildren(), nullptr); |
| LVElement *FirstChild = *(CompileUnit->getChildren()->begin()); |
| EXPECT_EQ(FirstChild->getIsScope(), 1); |
| LVScopeModule *Module = static_cast<LVScopeModule *>(FirstChild); |
| EXPECT_EQ(Module->getIsModule(), 1); |
| EXPECT_EQ(Module->getName(), "DebugModule"); |
| } |
| |
| // Check the logical elements selection. |
| void checkElementSelection(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root); |
| |
| // Get the matched elements. |
| LVElements MatchedElements = CompileUnit->getMatchedElements(); |
| std::map<LVOffset, LVElement *> MapElements; |
| for (LVElement *Element : MatchedElements) |
| MapElements[Element->getOffset()] = Element; |
| ASSERT_EQ(MapElements.size(), 0xeu); |
| |
| LVElement *Element = MapElements[0x000000004b]; // 'foo' |
| ASSERT_NE(Element, nullptr); |
| EXPECT_NE(Element->getName().find("foo"), StringRef::npos); |
| EXPECT_EQ(Element->getIsScope(), 1); |
| |
| Element = MapElements[0x00000000c0]; // 'CONSTANT' |
| ASSERT_NE(Element, nullptr); |
| EXPECT_NE(Element->getName().find("CONSTANT"), StringRef::npos); |
| EXPECT_EQ(Element->getIsSymbol(), 1); |
| |
| Element = MapElements[0x000000002d]; // 'INTPTR' |
| ASSERT_NE(Element, nullptr); |
| EXPECT_NE(Element->getName().find("INTPTR"), StringRef::npos); |
| EXPECT_EQ(Element->getIsType(), 1); |
| |
| Element = MapElements[0x00000000af]; // 'INTEGER' |
| ASSERT_NE(Element, nullptr); |
| EXPECT_NE(Element->getName().find("INTEGER"), StringRef::npos); |
| EXPECT_EQ(Element->getIsType(), 1); |
| |
| Element = MapElements[0x000000000f]; // 'movl %edx, %eax' |
| ASSERT_NE(Element, nullptr); |
| EXPECT_NE(Element->getName().find("movl"), StringRef::npos); |
| EXPECT_EQ(Element->getIsLine(), 1); |
| |
| // Get the parents for the matched elements. |
| LVScopes MatchedScopes = CompileUnit->getMatchedScopes(); |
| std::set<LVOffset> SetScopes; |
| for (LVScope *Scope : MatchedScopes) |
| SetScopes.insert(Scope->getOffset()); |
| std::set<LVOffset>::iterator Iter; |
| ASSERT_EQ(SetScopes.size(), 3u); |
| |
| Iter = SetScopes.find(0x000000000b); // CompileUnit <- 'foo' |
| EXPECT_NE(Iter, SetScopes.end()); |
| Iter = SetScopes.find(0x000000009e); // Function <- 'movl %edx, %eax' |
| EXPECT_NE(Iter, SetScopes.end()); |
| Iter = SetScopes.find(0x000000009e); // LexicalScope <- 'INTEGER' |
| EXPECT_NE(Iter, SetScopes.end()); |
| } |
| |
| // Check the logical elements comparison. |
| void checkElementComparison(LVReader *Reference, LVReader *Target) { |
| LVCompare Compare(nulls()); |
| Error Err = Compare.execute(Reference, Target); |
| ASSERT_THAT_ERROR(std::move(Err), Succeeded()); |
| |
| // Get comparison table. |
| LVPassTable PassTable = Compare.getPassTable(); |
| ASSERT_EQ(PassTable.size(), 5u); |
| |
| LVReader *Reader; |
| LVElement *Element; |
| LVComparePass Pass; |
| |
| // Reference: Missing Variable 'CONSTANT' |
| std::tie(Reader, Element, Pass) = PassTable[0]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Reference); |
| EXPECT_EQ(Element->getLevel(), 4u); |
| EXPECT_EQ(Element->getLineNumber(), 5u); |
| EXPECT_EQ(Element->getName(), "CONSTANT"); |
| EXPECT_EQ(Pass, LVComparePass::Missing); |
| |
| // Reference: Missing TypeDefinition 'INTEGER' |
| std::tie(Reader, Element, Pass) = PassTable[1]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Reference); |
| EXPECT_EQ(Element->getLevel(), 3u); |
| EXPECT_EQ(Element->getLineNumber(), 4u); |
| EXPECT_EQ(Element->getName(), "INTEGER"); |
| EXPECT_EQ(Pass, LVComparePass::Missing); |
| |
| // Reference: Missing DebugLine |
| std::tie(Reader, Element, Pass) = PassTable[2]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Reference); |
| EXPECT_EQ(Element->getLevel(), 3u); |
| EXPECT_EQ(Element->getLineNumber(), 8u); |
| EXPECT_EQ(Element->getName(), ""); |
| EXPECT_EQ(Pass, LVComparePass::Missing); |
| |
| // Target: Added Variable 'CONSTANT' |
| std::tie(Reader, Element, Pass) = PassTable[3]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Target); |
| EXPECT_EQ(Element->getLevel(), 4u); |
| EXPECT_EQ(Element->getLineNumber(), 5u); |
| EXPECT_EQ(Element->getName(), "CONSTANT"); |
| EXPECT_EQ(Pass, LVComparePass::Added); |
| |
| // Target: Added TypeDefinition 'INTEGER' |
| std::tie(Reader, Element, Pass) = PassTable[4]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Target); |
| EXPECT_EQ(Element->getLevel(), 4u); |
| EXPECT_EQ(Element->getLineNumber(), 4u); |
| EXPECT_EQ(Element->getName(), "INTEGER"); |
| EXPECT_EQ(Pass, LVComparePass::Added); |
| } |
| |
| // Logical elements properties. |
| void elementProperties(SmallString<128> &InputsDir) { |
| // Reader options. |
| LVOptions ReaderOptions; |
| ReaderOptions.setAttributeOffset(); |
| ReaderOptions.setAttributeFormat(); |
| ReaderOptions.setAttributeFilename(); |
| ReaderOptions.setAttributeProducer(); |
| ReaderOptions.setAttributePublics(); |
| ReaderOptions.setAttributeRange(); |
| ReaderOptions.setAttributeLanguage(); |
| ReaderOptions.setAttributeLocation(); |
| ReaderOptions.setAttributeInserted(); |
| ReaderOptions.setAttributeSize(); |
| ReaderOptions.setPrintAll(); |
| ReaderOptions.resolveDependencies(); |
| |
| std::vector<std::string> Objects; |
| ScopedPrinter W(outs()); |
| LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); |
| |
| // Check logical elements properties. |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, DwarfClang); |
| checkElementProperties(Reader.get()); |
| |
| Reader = createReader(ReaderHandler, InputsDir, DwarfClangUnspecParams); |
| checkUnspecifiedParameters(Reader.get()); |
| |
| Reader = createReader(ReaderHandler, InputsDir, DwarfClangModule); |
| checkScopeModule(Reader.get()); |
| } |
| |
| // Logical elements selection. |
| void elementSelection(SmallString<128> &InputsDir) { |
| // Reader options. |
| LVOptions ReaderOptions; |
| ReaderOptions.setAttributeOffset(); |
| ReaderOptions.setPrintAll(); |
| |
| ReaderOptions.setSelectIgnoreCase(); |
| ReaderOptions.setSelectUseRegex(); |
| |
| ReaderOptions.setReportList(); // Matched elements. |
| ReaderOptions.setReportView(); // Parents for matched elements. |
| |
| // Add patterns. |
| ReaderOptions.Select.Generic.insert("foo"); |
| ReaderOptions.Select.Generic.insert("movl[ \t]?%"); |
| ReaderOptions.Select.Generic.insert("INT[a-z]*"); |
| ReaderOptions.Select.Generic.insert("CONSTANT"); |
| |
| ReaderOptions.resolveDependencies(); |
| |
| std::vector<std::string> Objects; |
| ScopedPrinter W(outs()); |
| LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); |
| |
| // Check logical elements selection. |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, DwarfGcc); |
| checkElementSelection(Reader.get()); |
| } |
| |
| // Compare logical elements. |
| void compareElements(SmallString<128> &InputsDir) { |
| // Reader options. |
| LVOptions ReaderOptions; |
| ReaderOptions.setAttributeOffset(); |
| ReaderOptions.setPrintLines(); |
| ReaderOptions.setPrintSymbols(); |
| ReaderOptions.setPrintTypes(); |
| ReaderOptions.setCompareLines(); |
| ReaderOptions.setCompareSymbols(); |
| ReaderOptions.setCompareTypes(); |
| |
| ReaderOptions.resolveDependencies(); |
| |
| std::vector<std::string> Objects; |
| ScopedPrinter W(outs()); |
| LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); |
| |
| // Check logical comparison. |
| std::unique_ptr<LVReader> Reference = |
| createReader(ReaderHandler, InputsDir, DwarfClang); |
| std::unique_ptr<LVReader> Target = |
| createReader(ReaderHandler, InputsDir, DwarfGcc); |
| checkElementComparison(Reference.get(), Target.get()); |
| } |
| |
| TEST(LogicalViewTest, DWARFReader) { |
| // Initialize targets and assembly printers/parsers. |
| llvm::InitializeAllTargetInfos(); |
| llvm::InitializeAllTargetMCs(); |
| InitializeAllDisassemblers(); |
| |
| llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); |
| |
| // This test requires a x86-registered-target. |
| Triple TT; |
| TT.setArch(Triple::x86_64); |
| TT.setVendor(Triple::UnknownVendor); |
| TT.setOS(Triple::UnknownOS); |
| |
| std::string TargetLookupError; |
| if (!TargetRegistry::lookupTarget(TT, TargetLookupError)) |
| GTEST_SKIP(); |
| |
| SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0); |
| |
| // Logical elements general properties and selection. |
| elementProperties(InputsDir); |
| elementSelection(InputsDir); |
| |
| // Compare logical elements. |
| compareElements(InputsDir); |
| } |
| |
| } // namespace |