//===- 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"; 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_EQ(CompileUnits->size(), 1u); LVScopes::const_iterator Iter = CompileUnits->begin(); EXPECT_NE(Iter, nullptr); LVScopeCompileUnit *CompileUnit = static_cast(*Iter); EXPECT_NE(CompileUnit, nullptr); return CompileUnit; } // Helper function to create a reader. std::unique_ptr createReader(LVReaderHandler &ReaderHandler, SmallString<128> &InputsDir, StringRef Filename) { SmallString<128> ObjectName(InputsDir); llvm::sys::path::append(ObjectName, Filename); Expected> ReaderOrErr = ReaderHandler.createReader(std::string(ObjectName)); EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded()); std::unique_ptr 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"); 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 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 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 SetScopes; for (LVScope *Scope : MatchedScopes) SetScopes.insert(Scope->getOffset()); std::set::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.setAttributeLocation(); ReaderOptions.setPrintAll(); ReaderOptions.resolveDependencies(); std::vector Objects; ScopedPrinter W(outs()); LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); // Check logical elements properties. std::unique_ptr Reader = createReader(ReaderHandler, InputsDir, DwarfClang); checkElementProperties(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 Objects; ScopedPrinter W(outs()); LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); // Check logical elements selection. std::unique_ptr 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 Objects; ScopedPrinter W(outs()); LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); // Check logical comparison. std::unique_ptr Reference = createReader(ReaderHandler, InputsDir, DwarfClang); std::unique_ptr 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(std::string(TT.str()), 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