420 lines · cpp
1//===- llvm/unittest/DebugInfo/LogicalView/DWARFReaderTest.cpp ------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"10#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"11#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"12#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"13#include "llvm/DebugInfo/LogicalView/Core/LVType.h"14#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"15#include "llvm/MC/TargetRegistry.h"16#include "llvm/Support/COM.h"17#include "llvm/Support/InitLLVM.h"18#include "llvm/Support/ScopedPrinter.h"19#include "llvm/Support/TargetSelect.h"20#include "llvm/Support/ToolOutputFile.h"21#include "llvm/Testing/Support/Error.h"22 23#include "gtest/gtest.h"24 25using namespace llvm;26using namespace llvm::logicalview;27 28extern const char *TestMainArgv0;29 30namespace {31 32const char *DwarfClang = "test-dwarf-clang.o";33// Two compile units: one declares `extern int foo_printf(const char *, ...);`34// and another one that defines the function.35const char *DwarfClangUnspecParams = "test-dwarf-clang-unspec-params.elf";36const char *DwarfClangModule = "test-dwarf-clang-module.o";37const char *DwarfGcc = "test-dwarf-gcc.o";38 39// Helper function to get the first compile unit.40LVScopeCompileUnit *getFirstCompileUnit(LVScopeRoot *Root) {41 EXPECT_NE(Root, nullptr);42 const LVScopes *CompileUnits = Root->getScopes();43 EXPECT_NE(CompileUnits, nullptr);44 EXPECT_GT(CompileUnits->size(), 0u);45 46 LVScopes::const_iterator Iter = CompileUnits->begin();47 EXPECT_NE(Iter, nullptr);48 LVScopeCompileUnit *CompileUnit = static_cast<LVScopeCompileUnit *>(*Iter);49 EXPECT_NE(CompileUnit, nullptr);50 return CompileUnit;51}52 53// Helper function to create a reader.54std::unique_ptr<LVReader> createReader(LVReaderHandler &ReaderHandler,55 SmallString<128> &InputsDir,56 StringRef Filename) {57 SmallString<128> ObjectName(InputsDir);58 llvm::sys::path::append(ObjectName, Filename);59 60 Expected<std::unique_ptr<LVReader>> ReaderOrErr =61 ReaderHandler.createReader(std::string(ObjectName));62 EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded());63 std::unique_ptr<LVReader> Reader = std::move(*ReaderOrErr);64 EXPECT_NE(Reader, nullptr);65 return Reader;66}67 68// Check the logical elements basic properties.69void checkElementProperties(LVReader *Reader) {70 LVScopeRoot *Root = Reader->getScopesRoot();71 LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root);72 73 EXPECT_EQ(Root->getFileFormatName(), "elf64-x86-64");74 EXPECT_EQ(Root->getName(), DwarfClang);75 76 EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);77 EXPECT_TRUE(CompileUnit->getProducer().starts_with("clang"));78 EXPECT_EQ(CompileUnit->getName(), "test.cpp");79 LVSourceLanguage Language = CompileUnit->getSourceLanguage();80 EXPECT_TRUE(Language.isValid());81 EXPECT_EQ(Language, LVSourceLanguage::DW_LANG_C_plus_plus_14);82 EXPECT_EQ(Language.getName(), "DW_LANG_C_plus_plus_14");83 84 EXPECT_EQ(CompileUnit->lineCount(), 1u);85 EXPECT_EQ(CompileUnit->scopeCount(), 1u);86 EXPECT_EQ(CompileUnit->symbolCount(), 0u);87 EXPECT_EQ(CompileUnit->typeCount(), 7u);88 EXPECT_EQ(CompileUnit->rangeCount(), 1u);89 90 const LVLocations *Ranges = CompileUnit->getRanges();91 ASSERT_NE(Ranges, nullptr);92 ASSERT_EQ(Ranges->size(), 1u);93 LVLocations::const_iterator IterLocation = Ranges->begin();94 LVLocation *Location = (*IterLocation);95 EXPECT_STREQ(Location->getIntervalInfo().c_str(),96 "{Range} Lines 2:9 [0x0000000000:0x000000003a]");97 98 LVRange RangeList;99 CompileUnit->getRanges(RangeList);100 101 const LVRangeEntries &RangeEntries = RangeList.getEntries();102 ASSERT_EQ(RangeEntries.size(), 2u);103 LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();104 LVRangeEntry RangeEntry = *IterRanges;105 EXPECT_EQ(RangeEntry.lower(), 0u);106 EXPECT_EQ(RangeEntry.upper(), 0x3au);107 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);108 EXPECT_EQ(RangeEntry.scope()->getName(), "test.cpp");109 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0x0bu);110 111 ++IterRanges;112 RangeEntry = *IterRanges;113 EXPECT_EQ(RangeEntry.lower(), 0x1cu);114 EXPECT_EQ(RangeEntry.upper(), 0x2fu);115 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);116 EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");117 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0x71u);118 119 const LVPublicNames &PublicNames = CompileUnit->getPublicNames();120 ASSERT_EQ(PublicNames.size(), 1u);121 LVPublicNames::const_iterator IterNames = PublicNames.cbegin();122 LVScope *Function = (*IterNames).first;123 EXPECT_EQ(Function->getName(), "foo");124 EXPECT_EQ(Function->getLineNumber(), 2u);125 LVNameInfo NameInfo = (*IterNames).second;126 EXPECT_EQ(NameInfo.first, 0u);127 EXPECT_EQ(NameInfo.second, 0x3au);128 129 // Lines (debug and assembler) for 'foo'.130 const LVLines *Lines = Function->getLines();131 ASSERT_NE(Lines, nullptr);132 ASSERT_EQ(Lines->size(), 19u);133 134 // Check size of types in CompileUnit.135 const LVTypes *Types = CompileUnit->getTypes();136 ASSERT_NE(Types, nullptr);137 EXPECT_EQ(Types->size(), 7u);138 139 const auto BoolType = llvm::find_if(140 *Types, [](const LVElement *elt) { return elt->getName() == "bool"; });141 ASSERT_NE(BoolType, Types->end());142 const auto IntType = llvm::find_if(143 *Types, [](const LVElement *elt) { return elt->getName() == "int"; });144 ASSERT_NE(IntType, Types->end());145 EXPECT_EQ(static_cast<LVType *>(*BoolType)->getBitSize(), 8u);146 EXPECT_EQ(static_cast<LVType *>(*BoolType)->getStorageSizeInBytes(), 1u);147 EXPECT_EQ(static_cast<LVType *>(*IntType)->getBitSize(), 32u);148 EXPECT_EQ(static_cast<LVType *>(*IntType)->getStorageSizeInBytes(), 4u);149}150 151// Check proper handling of DW_AT_unspecified_parameters in152// LVScope::addMissingElements().153void checkUnspecifiedParameters(LVReader *Reader) {154 LVScopeRoot *Root = Reader->getScopesRoot();155 LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root);156 157 EXPECT_EQ(Root->getFileFormatName(), "elf64-x86-64");158 EXPECT_EQ(Root->getName(), DwarfClangUnspecParams);159 160 const LVPublicNames &PublicNames = CompileUnit->getPublicNames();161 ASSERT_EQ(PublicNames.size(), 1u);162 163 LVPublicNames::const_iterator IterNames = PublicNames.cbegin();164 LVScope *Function = (*IterNames).first;165 EXPECT_EQ(Function->getName(), "foo_printf");166 const LVElementsView Elements = Function->getChildren();167 // foo_printf is a variadic function whose prototype is168 // `int foo_printf(const char *, ...)`, where the '...' is represented by a169 // DW_TAG_unspecified_parameters, i.e. we expect to find at least one child170 // for which getIsUnspecified() returns true.171 EXPECT_TRUE(llvm::any_of(Elements, [](const LVElement *elt) {172 return elt->getIsSymbol() &&173 static_cast<const LVSymbol *>(elt)->getIsUnspecified();174 }));175}176 177// Check the basic properties on parsed DW_TAG_module.178void checkScopeModule(LVReader *Reader) {179 LVScopeRoot *Root = Reader->getScopesRoot();180 LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root);181 182 EXPECT_EQ(Root->getFileFormatName(), "Mach-O 64-bit x86-64");183 EXPECT_EQ(Root->getName(), DwarfClangModule);184 185 LVElement *FirstChild = *(CompileUnit->getChildren().begin());186 ASSERT_NE(FirstChild, nullptr);187 EXPECT_EQ(FirstChild->getIsScope(), 1);188 LVScopeModule *Module = static_cast<LVScopeModule *>(FirstChild);189 EXPECT_EQ(Module->getIsModule(), 1);190 EXPECT_EQ(Module->getName(), "DebugModule");191}192 193// Check the logical elements selection.194void checkElementSelection(LVReader *Reader) {195 LVScopeRoot *Root = Reader->getScopesRoot();196 LVScopeCompileUnit *CompileUnit = getFirstCompileUnit(Root);197 198 // Get the matched elements.199 LVElements MatchedElements = CompileUnit->getMatchedElements();200 std::map<LVOffset, LVElement *> MapElements;201 for (LVElement *Element : MatchedElements)202 MapElements[Element->getOffset()] = Element;203 ASSERT_EQ(MapElements.size(), 0xeu);204 205 LVElement *Element = MapElements[0x000000004b]; // 'foo'206 ASSERT_NE(Element, nullptr);207 EXPECT_NE(Element->getName().find("foo"), StringRef::npos);208 EXPECT_EQ(Element->getIsScope(), 1);209 210 Element = MapElements[0x00000000c0]; // 'CONSTANT'211 ASSERT_NE(Element, nullptr);212 EXPECT_NE(Element->getName().find("CONSTANT"), StringRef::npos);213 EXPECT_EQ(Element->getIsSymbol(), 1);214 215 Element = MapElements[0x000000002d]; // 'INTPTR'216 ASSERT_NE(Element, nullptr);217 EXPECT_NE(Element->getName().find("INTPTR"), StringRef::npos);218 EXPECT_EQ(Element->getIsType(), 1);219 220 Element = MapElements[0x00000000af]; // 'INTEGER'221 ASSERT_NE(Element, nullptr);222 EXPECT_NE(Element->getName().find("INTEGER"), StringRef::npos);223 EXPECT_EQ(Element->getIsType(), 1);224 225 Element = MapElements[0x000000000f]; // 'movl %edx, %eax'226 ASSERT_NE(Element, nullptr);227 EXPECT_NE(Element->getName().find("movl"), StringRef::npos);228 EXPECT_EQ(Element->getIsLine(), 1);229 230 // Get the parents for the matched elements.231 LVScopes MatchedScopes = CompileUnit->getMatchedScopes();232 std::set<LVOffset> SetScopes;233 for (LVScope *Scope : MatchedScopes)234 SetScopes.insert(Scope->getOffset());235 std::set<LVOffset>::iterator Iter;236 ASSERT_EQ(SetScopes.size(), 3u);237 238 Iter = SetScopes.find(0x000000000b); // CompileUnit <- 'foo'239 EXPECT_NE(Iter, SetScopes.end());240 Iter = SetScopes.find(0x000000009e); // Function <- 'movl %edx, %eax'241 EXPECT_NE(Iter, SetScopes.end());242 Iter = SetScopes.find(0x000000009e); // LexicalScope <- 'INTEGER'243 EXPECT_NE(Iter, SetScopes.end());244}245 246// Check the logical elements comparison.247void checkElementComparison(LVReader *Reference, LVReader *Target) {248 LVCompare Compare(nulls());249 Error Err = Compare.execute(Reference, Target);250 ASSERT_THAT_ERROR(std::move(Err), Succeeded());251 252 // Get comparison table.253 LVPassTable PassTable = Compare.getPassTable();254 ASSERT_EQ(PassTable.size(), 4u);255 256 LVReader *Reader;257 LVElement *Element;258 LVComparePass Pass;259 260 // Reference: Missing Variable 'CONSTANT'261 std::tie(Reader, Element, Pass) = PassTable[0];262 ASSERT_NE(Reader, nullptr);263 ASSERT_NE(Element, nullptr);264 EXPECT_EQ(Reader, Reference);265 EXPECT_EQ(Element->getLevel(), 4u);266 EXPECT_EQ(Element->getLineNumber(), 5u);267 EXPECT_EQ(Element->getName(), "CONSTANT");268 EXPECT_EQ(Pass, LVComparePass::Missing);269 270 // Reference: Missing TypeDefinition 'INTEGER'271 std::tie(Reader, Element, Pass) = PassTable[1];272 ASSERT_NE(Reader, nullptr);273 ASSERT_NE(Element, nullptr);274 EXPECT_EQ(Reader, Reference);275 EXPECT_EQ(Element->getLevel(), 3u);276 EXPECT_EQ(Element->getLineNumber(), 4u);277 EXPECT_EQ(Element->getName(), "INTEGER");278 EXPECT_EQ(Pass, LVComparePass::Missing);279 280 // Target: Added Variable 'CONSTANT'281 std::tie(Reader, Element, Pass) = PassTable[2];282 ASSERT_NE(Reader, nullptr);283 ASSERT_NE(Element, nullptr);284 EXPECT_EQ(Reader, Target);285 EXPECT_EQ(Element->getLevel(), 4u);286 EXPECT_EQ(Element->getLineNumber(), 5u);287 EXPECT_EQ(Element->getName(), "CONSTANT");288 EXPECT_EQ(Pass, LVComparePass::Added);289 290 // Target: Added TypeDefinition 'INTEGER'291 std::tie(Reader, Element, Pass) = PassTable[3];292 ASSERT_NE(Reader, nullptr);293 ASSERT_NE(Element, nullptr);294 EXPECT_EQ(Reader, Target);295 EXPECT_EQ(Element->getLevel(), 4u);296 EXPECT_EQ(Element->getLineNumber(), 4u);297 EXPECT_EQ(Element->getName(), "INTEGER");298 EXPECT_EQ(Pass, LVComparePass::Added);299}300 301// Logical elements properties.302void elementProperties(SmallString<128> &InputsDir) {303 // Reader options.304 LVOptions ReaderOptions;305 ReaderOptions.setAttributeOffset();306 ReaderOptions.setAttributeFormat();307 ReaderOptions.setAttributeFilename();308 ReaderOptions.setAttributeProducer();309 ReaderOptions.setAttributePublics();310 ReaderOptions.setAttributeRange();311 ReaderOptions.setAttributeLanguage();312 ReaderOptions.setAttributeLocation();313 ReaderOptions.setAttributeInserted();314 ReaderOptions.setAttributeSize();315 ReaderOptions.setPrintAll();316 ReaderOptions.resolveDependencies();317 318 std::vector<std::string> Objects;319 ScopedPrinter W(outs());320 LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);321 322 // Check logical elements properties.323 std::unique_ptr<LVReader> Reader =324 createReader(ReaderHandler, InputsDir, DwarfClang);325 checkElementProperties(Reader.get());326 327 Reader = createReader(ReaderHandler, InputsDir, DwarfClangUnspecParams);328 checkUnspecifiedParameters(Reader.get());329 330 Reader = createReader(ReaderHandler, InputsDir, DwarfClangModule);331 checkScopeModule(Reader.get());332}333 334// Logical elements selection.335void elementSelection(SmallString<128> &InputsDir) {336 // Reader options.337 LVOptions ReaderOptions;338 ReaderOptions.setAttributeOffset();339 ReaderOptions.setPrintAll();340 341 ReaderOptions.setSelectIgnoreCase();342 ReaderOptions.setSelectUseRegex();343 344 ReaderOptions.setReportList(); // Matched elements.345 ReaderOptions.setReportView(); // Parents for matched elements.346 347 // Add patterns.348 ReaderOptions.Select.Generic.insert("foo");349 ReaderOptions.Select.Generic.insert("movl[ \t]?%");350 ReaderOptions.Select.Generic.insert("INT[a-z]*");351 ReaderOptions.Select.Generic.insert("CONSTANT");352 353 ReaderOptions.resolveDependencies();354 355 std::vector<std::string> Objects;356 ScopedPrinter W(outs());357 LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);358 359 // Check logical elements selection.360 std::unique_ptr<LVReader> Reader =361 createReader(ReaderHandler, InputsDir, DwarfGcc);362 checkElementSelection(Reader.get());363}364 365// Compare logical elements.366void compareElements(SmallString<128> &InputsDir) {367 // Reader options.368 LVOptions ReaderOptions;369 ReaderOptions.setAttributeOffset();370 ReaderOptions.setPrintLines();371 ReaderOptions.setPrintSymbols();372 ReaderOptions.setPrintTypes();373 ReaderOptions.setCompareLines();374 ReaderOptions.setCompareSymbols();375 ReaderOptions.setCompareTypes();376 377 ReaderOptions.resolveDependencies();378 379 std::vector<std::string> Objects;380 ScopedPrinter W(outs());381 LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);382 383 // Check logical comparison.384 std::unique_ptr<LVReader> Reference =385 createReader(ReaderHandler, InputsDir, DwarfClang);386 std::unique_ptr<LVReader> Target =387 createReader(ReaderHandler, InputsDir, DwarfGcc);388 checkElementComparison(Reference.get(), Target.get());389}390 391TEST(LogicalViewTest, DWARFReader) {392 // Initialize targets and assembly printers/parsers.393 llvm::InitializeAllTargetInfos();394 llvm::InitializeAllTargetMCs();395 InitializeAllDisassemblers();396 397 llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);398 399 // This test requires a x86-registered-target.400 Triple TT;401 TT.setArch(Triple::x86_64);402 TT.setVendor(Triple::UnknownVendor);403 TT.setOS(Triple::UnknownOS);404 405 std::string TargetLookupError;406 if (!TargetRegistry::lookupTarget(TT, TargetLookupError))407 GTEST_SKIP();408 409 SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);410 411 // Logical elements general properties and selection.412 elementProperties(InputsDir);413 elementSelection(InputsDir);414 415 // Compare logical elements.416 compareElements(InputsDir);417}418 419} // namespace420