| //===- llvm/unittest/DebugInfo/LogicalView/CodeViewReaderTest.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 *CodeViewClang = "test-codeview-clang.o"; |
| const char *CodeViewMsvc = "test-codeview-msvc.o"; |
| const char *CodeViewPdbMsvc = "test-codeview-pdb-msvc.o"; |
| |
| // Helper function to get the first scope child from the given parent. |
| LVScope *getFirstScopeChild(LVScope *Parent) { |
| EXPECT_NE(Parent, nullptr); |
| const LVScopes *Scopes = Parent->getScopes(); |
| EXPECT_NE(Scopes, nullptr); |
| EXPECT_EQ(Scopes->size(), 1u); |
| |
| LVScopes::const_iterator Iter = Scopes->begin(); |
| LVScope *Child = *Iter; |
| EXPECT_NE(Child, nullptr); |
| return Child; |
| } |
| |
| // 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 (Clang - Codeview). |
| void checkElementPropertiesClangCodeview(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = |
| static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root)); |
| LVScopeFunction *Function = |
| static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit)); |
| |
| EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); |
| EXPECT_EQ(Root->getName(), CodeViewClang); |
| |
| EXPECT_EQ(CompileUnit->getBaseAddress(), 0u); |
| EXPECT_TRUE(CompileUnit->getProducer().startswith("clang")); |
| EXPECT_EQ(CompileUnit->getName(), "test.cpp"); |
| |
| EXPECT_EQ(Function->lineCount(), 16u); |
| EXPECT_EQ(Function->scopeCount(), 1u); |
| EXPECT_EQ(Function->symbolCount(), 3u); |
| EXPECT_EQ(Function->typeCount(), 1u); |
| EXPECT_EQ(Function->rangeCount(), 1u); |
| |
| const LVLocations *Ranges = Function->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:0x0000000046]"); |
| |
| LVRange RangeList; |
| Function->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(), 0x46u); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u); |
| |
| ++IterRanges; |
| RangeEntry = *IterRanges; |
| EXPECT_EQ(RangeEntry.lower(), 0x21u); |
| EXPECT_EQ(RangeEntry.upper(), 0x35u); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u); |
| |
| const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); |
| ASSERT_EQ(PublicNames.size(), 1u); |
| LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); |
| LVScope *Foo = (*IterNames).first; |
| EXPECT_EQ(Foo->getName(), "foo"); |
| EXPECT_EQ(Foo->getLineNumber(), 0u); |
| LVNameInfo NameInfo = (*IterNames).second; |
| EXPECT_EQ(NameInfo.first, 0u); |
| EXPECT_EQ(NameInfo.second, 0x46u); |
| |
| // Lines (debug and assembler) for 'foo'. |
| const LVLines *Lines = Foo->getLines(); |
| ASSERT_NE(Lines, nullptr); |
| EXPECT_EQ(Lines->size(), 0x10u); |
| } |
| |
| // Check the logical elements basic properties (MSVC - Codeview). |
| void checkElementPropertiesMsvcCodeview(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = |
| static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root)); |
| LVScopeFunction *Function = |
| static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit)); |
| |
| EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); |
| EXPECT_EQ(Root->getName(), CodeViewMsvc); |
| |
| EXPECT_EQ(CompileUnit->getBaseAddress(), 0u); |
| EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft")); |
| EXPECT_EQ(CompileUnit->getName(), "test.cpp"); |
| |
| EXPECT_EQ(Function->lineCount(), 14u); |
| EXPECT_EQ(Function->scopeCount(), 1u); |
| EXPECT_EQ(Function->symbolCount(), 3u); |
| EXPECT_EQ(Function->typeCount(), 0u); |
| EXPECT_EQ(Function->rangeCount(), 1u); |
| |
| const LVLocations *Ranges = Function->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:0x0000000031]"); |
| |
| LVRange RangeList; |
| Function->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(), 0x31u); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u); |
| |
| ++IterRanges; |
| RangeEntry = *IterRanges; |
| EXPECT_EQ(RangeEntry.lower(), 0x1bu); |
| EXPECT_EQ(RangeEntry.upper(), 0x28u); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u); |
| |
| const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); |
| ASSERT_EQ(PublicNames.size(), 1u); |
| LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); |
| LVScope *Foo = (*IterNames).first; |
| EXPECT_EQ(Foo->getName(), "foo"); |
| EXPECT_EQ(Foo->getLineNumber(), 0u); |
| LVNameInfo NameInfo = (*IterNames).second; |
| EXPECT_EQ(NameInfo.first, 0u); |
| EXPECT_EQ(NameInfo.second, 0x31u); |
| |
| // Lines (debug and assembler) for 'foo'. |
| const LVLines *Lines = Foo->getLines(); |
| ASSERT_NE(Lines, nullptr); |
| EXPECT_EQ(Lines->size(), 0x0eu); |
| } |
| |
| // Check the logical elements basic properties (MSVC - PDB). |
| void checkElementPropertiesMsvcCodeviewPdb(LVReader *Reader) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = |
| static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root)); |
| LVScopeFunction *Function = |
| static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit)); |
| |
| EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); |
| EXPECT_EQ(Root->getName(), CodeViewPdbMsvc); |
| |
| EXPECT_EQ(CompileUnit->getBaseAddress(), 0u); |
| EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft")); |
| EXPECT_EQ(CompileUnit->getName(), "test.cpp"); |
| |
| EXPECT_EQ(Function->lineCount(), 14u); |
| EXPECT_EQ(Function->scopeCount(), 1u); |
| EXPECT_EQ(Function->symbolCount(), 3u); |
| EXPECT_EQ(Function->typeCount(), 0u); |
| EXPECT_EQ(Function->rangeCount(), 1u); |
| |
| const LVLocations *Ranges = Function->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:0x0000000031]"); |
| |
| LVRange RangeList; |
| Function->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(), 0x31u); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u); |
| |
| ++IterRanges; |
| RangeEntry = *IterRanges; |
| EXPECT_EQ(RangeEntry.lower(), 0x1bu); |
| EXPECT_EQ(RangeEntry.upper(), 0x28u); |
| EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u); |
| EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); |
| EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u); |
| |
| const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); |
| ASSERT_EQ(PublicNames.size(), 1u); |
| LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); |
| LVScope *Foo = (*IterNames).first; |
| EXPECT_EQ(Foo->getName(), "foo"); |
| EXPECT_EQ(Foo->getLineNumber(), 0u); |
| LVNameInfo NameInfo = (*IterNames).second; |
| EXPECT_EQ(NameInfo.first, 0u); |
| EXPECT_EQ(NameInfo.second, 0x31u); |
| |
| // Lines (debug and assembler) for 'foo'. |
| const LVLines *Lines = Foo->getLines(); |
| ASSERT_NE(Lines, nullptr); |
| EXPECT_EQ(Lines->size(), 0x0eu); |
| } |
| |
| struct SelectionInfo { |
| const char *Name; |
| LVElementGetFunction Function; |
| }; |
| |
| // Check the logical elements selection. |
| void checkElementSelection(LVReader *Reader, std::vector<SelectionInfo> &Data, |
| size_t Size) { |
| LVScopeRoot *Root = Reader->getScopesRoot(); |
| LVScopeCompileUnit *CompileUnit = |
| static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root)); |
| |
| // Get the matched elements. |
| LVElements MatchedElements = CompileUnit->getMatchedElements(); |
| std::map<StringRef, LVElement *> MapElements; |
| for (LVElement *Element : MatchedElements) |
| MapElements[Element->getName()] = Element; |
| ASSERT_EQ(MapElements.size(), Size); |
| |
| std::map<StringRef, LVElement *>::iterator Iter = MapElements.begin(); |
| for (const SelectionInfo &Entry : Data) { |
| // Get matched element. |
| EXPECT_NE(Iter, MapElements.end()); |
| LVElement *Element = Iter->second; |
| ASSERT_NE(Element, nullptr); |
| EXPECT_NE(Element->getName().find(Entry.Name), StringRef::npos); |
| EXPECT_EQ((Element->*Entry.Function)(), 1u); |
| ++Iter; |
| } |
| |
| // Get the parents for the matched elements. |
| LVScopes MatchedScopes = CompileUnit->getMatchedScopes(); |
| std::set<StringRef> SetScopes; |
| for (LVScope *Scope : MatchedScopes) |
| SetScopes.insert(Scope->getName()); |
| ASSERT_EQ(SetScopes.size(), 3u); |
| |
| // Parents of selected elements. |
| std::set<StringRef>::iterator IterScope; |
| IterScope = SetScopes.find("foo"); |
| EXPECT_NE(IterScope, SetScopes.end()); |
| IterScope = SetScopes.find("foo::?"); |
| EXPECT_NE(IterScope, SetScopes.end()); |
| IterScope = SetScopes.find("test.cpp"); |
| EXPECT_NE(IterScope, 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(), 2u); |
| |
| LVReader *Reader; |
| LVElement *Element; |
| LVComparePass Pass; |
| |
| // Reference: Missing TypeDefinition 'INTEGER' |
| std::tie(Reader, Element, Pass) = PassTable[0]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Reference); |
| EXPECT_EQ(Element->getLevel(), 3u); |
| EXPECT_EQ(Element->getLineNumber(), 0u); |
| EXPECT_EQ(Element->getName(), "INTEGER"); |
| EXPECT_EQ(Pass, LVComparePass::Missing); |
| |
| // Target: Added TypeDefinition 'INTEGER' |
| std::tie(Reader, Element, Pass) = PassTable[1]; |
| ASSERT_NE(Reader, nullptr); |
| ASSERT_NE(Element, nullptr); |
| EXPECT_EQ(Reader, Target); |
| EXPECT_EQ(Element->getLevel(), 4u); |
| EXPECT_EQ(Element->getLineNumber(), 0u); |
| 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<std::string> Objects; |
| ScopedPrinter W(outs()); |
| LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); |
| |
| // Check logical elements properties. |
| { |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, CodeViewClang); |
| checkElementPropertiesClangCodeview(Reader.get()); |
| } |
| { |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, CodeViewMsvc); |
| checkElementPropertiesMsvcCodeview(Reader.get()); |
| } |
| { |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, CodeViewPdbMsvc); |
| checkElementPropertiesMsvcCodeviewPdb(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::vector<SelectionInfo> DataClang = { |
| {"* const int", &LVElement::getIsType}, |
| {"CONSTANT", &LVElement::getIsSymbol}, |
| {"INTEGER", &LVElement::getIsType}, |
| {"INTPTR", &LVElement::getIsType}, |
| {"ParamPtr", &LVElement::getIsSymbol}, |
| {"const int", &LVElement::getIsType}, |
| {"foo", &LVElement::getIsScope}, |
| {"foo::?", &LVElement::getIsScope}, |
| {"int", &LVElement::getIsType}, |
| {"movl", &LVElement::getIsLine}, |
| {"movl", &LVElement::getIsLine}}; |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, CodeViewClang); |
| checkElementSelection(Reader.get(), DataClang, DataClang.size()); |
| } |
| { |
| std::vector<SelectionInfo> DataMsvc = { |
| {"* const int", &LVElement::getIsType}, |
| {"CONSTANT", &LVElement::getIsSymbol}, |
| {"INTEGER", &LVElement::getIsType}, |
| {"INTPTR", &LVElement::getIsType}, |
| {"ParamPtr", &LVElement::getIsSymbol}, |
| {"const int", &LVElement::getIsType}, |
| {"foo", &LVElement::getIsScope}, |
| {"foo::?", &LVElement::getIsScope}, |
| {"int", &LVElement::getIsType}, |
| {"movl", &LVElement::getIsLine}}; |
| std::unique_ptr<LVReader> Reader = |
| createReader(ReaderHandler, InputsDir, CodeViewMsvc); |
| checkElementSelection(Reader.get(), DataMsvc, DataMsvc.size()); |
| } |
| } |
| |
| // 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, CodeViewClang); |
| std::unique_ptr<LVReader> Target = |
| createReader(ReaderHandler, InputsDir, CodeViewMsvc); |
| checkElementComparison(Reference.get(), Target.get()); |
| } |
| |
| TEST(LogicalViewTest, CodeViewReader) { |
| // 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)) |
| return; |
| |
| SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0); |
| |
| // Logical elements general properties and selection. |
| elementProperties(InputsDir); |
| elementSelection(InputsDir); |
| |
| // Compare logical elements. |
| compareElements(InputsDir); |
| } |
| |
| } // namespace |