//===- llvm/unittest/DebugInfo/LogicalView/WarningInternalTest.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/LVLine.h" #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" #include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" #include "llvm/DebugInfo/LogicalView/Core/LVType.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::logicalview; namespace { class MyLocation : public LVLocation { public: bool validateRanges(); }; // This code emulates the work done by the Readers when processing the // binary files and the creation of the AddressToLine mapping is done // automatically, using the text sections. class MyAddressToLine { using LVAddressToLine = std::map; LVAddressToLine AddressToLineData; public: MyAddressToLine() = default; void insert(LVLine *Line) { AddressToLineData.emplace(Line->getOffset(), Line); } LVLine *lineLowerBound(LVAddress Address); LVLine *lineUpperBound(LVAddress Address); }; LVLine *MyAddressToLine::lineLowerBound(LVAddress Address) { LVAddressToLine::const_iterator Iter = AddressToLineData.lower_bound(Address); return (Iter != AddressToLineData.end()) ? Iter->second : nullptr; } LVLine *MyAddressToLine::lineUpperBound(LVAddress Address) { if (AddressToLineData.empty()) return nullptr; LVAddressToLine::const_iterator Iter = AddressToLineData.upper_bound(Address); if (Iter != AddressToLineData.begin()) Iter = std::prev(Iter); return Iter->second; } MyAddressToLine AddressToLine; class ReaderTestWarningInternal : public LVReader { #define CREATE(VARIABLE, CREATE_FUNCTION) \ VARIABLE = CREATE_FUNCTION(); \ EXPECT_NE(VARIABLE, nullptr); #define CREATE_CUSTOM(VARIABLE, CREATE_FUNCTION) \ VARIABLE = CREATE_FUNCTION(); \ EXPECT_NE(VARIABLE, nullptr); // Types. LVType *IntegerType = nullptr; // Scopes. LVScope *NestedScope = nullptr; LVScopeFunction *Function = nullptr; // Symbols. LVSymbol *LocalVariable = nullptr; LVSymbol *NestedVariable = nullptr; LVSymbol *Parameter = nullptr; // Lines. LVLine *LineOne = nullptr; LVLine *LineTwo = nullptr; LVLine *LineThree = nullptr; LVLine *LineFour = nullptr; LVLine *LineFive = nullptr; LVLine *LineSix = nullptr; // Locations. MyLocation *LocationOne = nullptr; MyLocation *LocationTwo = nullptr; MyLocation *LocationThree = nullptr; MyLocation *LocationFour = nullptr; MyLocation *LocationFive = nullptr; MyLocation *LocationSix = nullptr; llvm::SpecificBumpPtrAllocator AllocatedLocations; protected: MyLocation *createCustomLocation() { return new (AllocatedLocations.Allocate()) MyLocation(); } void add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine); void add(LVScope *Parent, LVElement *Element); void set(LVElement *Element, StringRef Name, LVOffset Offset, uint32_t LineNumber = 0, LVElement *Type = nullptr); void set(MyLocation *Location, LVLine *LowerLine, LVLine *UpperLine, LVAddress LowerAddress, LVAddress UpperAddress); public: ReaderTestWarningInternal(ScopedPrinter &W) : LVReader("", "", W) { setInstance(this); } Error createScopes() { return LVReader::createScopes(); } void setMapping(); void createElements(); void addElements(); void initElements(); void resolveElements(); void checkWarnings(); }; bool MyLocation::validateRanges() { // Traverse the locations and validate them against the address to line // mapping in the current compile unit. Record those invalid ranges. // A valid range must meet the following conditions: // a) line(lopc) <= line(hipc) // b) line(lopc) and line(hipc) are valid. LVLine *LowLine = AddressToLine.lineLowerBound(getLowerAddress()); LVLine *HighLine = AddressToLine.lineUpperBound(getUpperAddress()); if (LowLine) setLowerLine(LowLine); else { setIsInvalidLower(); return false; } if (HighLine) setUpperLine(HighLine); else { setIsInvalidUpper(); return false; } // Check for a valid interval. if (LowLine->getLineNumber() > HighLine->getLineNumber()) { setIsInvalidRange(); return false; } return true; } // Map all logical lines with their addresses. void ReaderTestWarningInternal::setMapping() { AddressToLine.insert(LineOne); AddressToLine.insert(LineTwo); AddressToLine.insert(LineThree); AddressToLine.insert(LineFour); AddressToLine.insert(LineFive); AddressToLine.insert(LineSix); } // Helper function to add a logical element to a given scope. void ReaderTestWarningInternal::add(LVScope *Parent, LVElement *Child) { Parent->addElement(Child); EXPECT_EQ(Child->getParent(), Parent); EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); } // Helper function to set the initial values for a given logical element. void ReaderTestWarningInternal::set(LVElement *Element, StringRef Name, LVOffset Offset, uint32_t LineNumber, LVElement *Type) { Element->setName(Name); Element->setOffset(Offset); Element->setLineNumber(LineNumber); Element->setType(Type); EXPECT_EQ(Element->getName(), Name); EXPECT_EQ(Element->getOffset(), Offset); EXPECT_EQ(Element->getLineNumber(), LineNumber); EXPECT_EQ(Element->getType(), Type); } // Helper function to set the initial values for a given logical location. void ReaderTestWarningInternal::set(MyLocation *Location, LVLine *LowerLine, LVLine *UpperLine, LVAddress LowerAddress, LVAddress UpperAddress) { Location->setLowerLine(LowerLine); Location->setUpperLine(UpperLine); Location->setLowerAddress(LowerAddress); Location->setUpperAddress(UpperAddress); EXPECT_EQ(Location->getLowerLine(), LowerLine); EXPECT_EQ(Location->getUpperLine(), UpperLine); EXPECT_EQ(Location->getLowerAddress(), LowerAddress); EXPECT_EQ(Location->getUpperAddress(), UpperAddress); } // Helper function to add a logical location to a logical symbol. void ReaderTestWarningInternal::add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine) { dwarf::Attribute Attr = dwarf::DW_AT_location; Symbol->addLocation(Attr, LowerLine->getAddress(), UpperLine->getAddress(), /*SectionOffset=*/0, /*LocDesOffset=*/0); } // Create the logical elements. void ReaderTestWarningInternal::createElements() { // Create scope root. Error Err = createScopes(); ASSERT_THAT_ERROR(std::move(Err), Succeeded()); Root = getScopesRoot(); EXPECT_NE(Root, nullptr); // Create the logical types. CREATE(IntegerType, createType); // Create the logical scopes. CREATE(NestedScope, createScope); CREATE(CompileUnit, createScopeCompileUnit); CREATE(Function, createScopeFunction); // Create the logical symbols. CREATE(LocalVariable, createSymbol); CREATE(NestedVariable, createSymbol); CREATE(Parameter, createSymbol); // Create the logical lines. CREATE(LineOne, createLine); CREATE(LineTwo, createLine); CREATE(LineThree, createLine); CREATE(LineFour, createLine); CREATE(LineFive, createLine); CREATE(LineSix, createLine); // Create the logical locations. CREATE_CUSTOM(LocationOne, createCustomLocation); CREATE_CUSTOM(LocationTwo, createCustomLocation); CREATE_CUSTOM(LocationThree, createCustomLocation); CREATE_CUSTOM(LocationFour, createCustomLocation); CREATE_CUSTOM(LocationFive, createCustomLocation); CREATE_CUSTOM(LocationSix, createCustomLocation); } // Create the logical view adding the created logical elements. void ReaderTestWarningInternal::addElements() { setCompileUnit(CompileUnit); // Root // CompileUnit // IntegerType // Function // LocationOne // LocationTwo // LocationFive // LocationSix // Parameter // LocalVariable // LineOne // LineTwo // NestedScope // LocationThree // LocationFour // NestedVariable // LineThree // LineFour // LineFive // LineSix // Add elements to Root. add(Root, CompileUnit); // Add elements to CompileUnit. add(CompileUnit, IntegerType); add(CompileUnit, Function); // Add elements to Function. add(Function, Parameter); add(Function, LocalVariable); add(Function, LineOne); add(Function, LineTwo); add(Function, LineFive); add(Function, LineSix); add(Function, NestedScope); // Add elements to NestedScope. add(NestedScope, NestedVariable); add(NestedScope, LineThree); add(NestedScope, LineFour); } void ReaderTestWarningInternal::resolveElements() { // Traverse the given scope and its children checking for any warnings. std::function TraverseScope = [&](LVScope *Parent) { auto Warnings = [&](auto *Entry) { if (Entry->getIsLine()) { LVLine *Line = (LVLine *)Entry; if (options().getWarningLines() && Line->getIsLineDebug() && !Line->getLineNumber()) CompileUnit->addLineZero(Line); } }; auto Traverse = [&](const auto *Set) { if (Set) for (const auto &Entry : *Set) { Warnings(Entry); } }; Warnings(Parent); Traverse(Parent->getSymbols()); Traverse(Parent->getTypes()); Traverse(Parent->getLines()); if (const LVScopes *Scopes = Parent->getScopes()) for (LVScope *Scope : *Scopes) { Warnings(Scope); TraverseScope(Scope); } }; // Start traversing the scopes root and resolve the elements. TraverseScope(Root); } // Set initial values to logical elements. void ReaderTestWarningInternal::initElements() { // Types. set(IntegerType, "int", 0x1000); // Scopes. set(CompileUnit, "foo.cpp", 0x2000); set(Function, "foo", 0x2010, 100, IntegerType); set(NestedScope, "", 0x2020, 300); // Symbols. set(Parameter, "Param", 0x3000, 110, IntegerType); set(LocalVariable, "LocalVariable", 0x3020, 120, IntegerType); set(NestedVariable, "NestedVariable", 0x3010, 310, IntegerType); // Lines. set(LineOne, "", 0x5000, 100); LineOne->setIsLineDebug(); set(LineTwo, "", 0x5200, 000); LineTwo->setIsLineDebug(); set(LineThree, "", 0x5400, 300); LineThree->setIsLineDebug(); set(LineFour, "", 0x5600, 000); LineFour->setIsLineDebug(); set(LineFive, "", 0x5800, 500); LineOne->setIsLineDebug(); set(LineSix, "", 0x6000, 600); LineSix->setIsLineDebug(); // Locations. set(LocationOne, LineOne, LineOne, 0x5000, 0x5100); EXPECT_STREQ(LocationOne->getIntervalInfo().c_str(), " Lines 100:100 [0x0000005000:0x0000005100]"); // Uses a Line zero. set(LocationTwo, LineTwo, LineTwo, 0x5200, 0x5300); EXPECT_STREQ(LocationTwo->getIntervalInfo().c_str(), " Lines -:- [0x0000005200:0x0000005300]"); set(LocationThree, LineThree, LineThree, 0x5400, 0x5500); EXPECT_STREQ(LocationThree->getIntervalInfo().c_str(), " Lines 300:300 [0x0000005400:0x0000005500]"); // Uses a Line zero. set(LocationFour, LineFour, LineFour, 0x5600, 0x5700); LocationFour->setIsAddressRange(); EXPECT_STREQ(LocationFour->getIntervalInfo().c_str(), "{Range} Lines -:- [0x0000005600:0x0000005700]"); // Invalid range. set(LocationFive, LineFive, LineFive, 0x7800, 0x5900); LocationFive->setIsAddressRange(); EXPECT_STREQ(LocationFive->getIntervalInfo().c_str(), "{Range} Lines 500:500 [0x0000007800:0x0000005900]"); set(LocationSix, LineSix, LineSix, 0x6000, 0x6100); LocationSix->setIsAddressRange(); EXPECT_STREQ(LocationSix->getIntervalInfo().c_str(), "{Range} Lines 600:600 [0x0000006000:0x0000006100]"); // Add ranges to Function. // Function: LocationOne, LocationTwo, LocationFive, LocationSix Function->addObject(LocationOne); Function->addObject(LocationTwo); Function->addObject(LocationFive); Function->addObject(LocationSix); EXPECT_EQ(Function->rangeCount(), 4u); // Add ranges to NestedScope. // NestedScope: LocationThree, LocationFour NestedScope->addObject(LocationThree); NestedScope->addObject(LocationFour); EXPECT_EQ(NestedScope->rangeCount(), 2u); // Get all ranges. LVRange Ranges; CompileUnit->getRanges(Ranges); Ranges.startSearch(); EXPECT_EQ(Ranges.getEntry(0x4000), nullptr); EXPECT_EQ(Ranges.getEntry(0x5060), Function); EXPECT_EQ(Ranges.getEntry(0x5850), nullptr); EXPECT_EQ(Ranges.getEntry(0x5010, 0x5090), Function); EXPECT_EQ(Ranges.getEntry(0x5210, 0x5290), Function); EXPECT_EQ(Ranges.getEntry(0x5810, 0x5890), nullptr); EXPECT_EQ(Ranges.getEntry(0x6010, 0x6090), Function); EXPECT_EQ(Ranges.getEntry(0x5400), NestedScope); EXPECT_EQ(Ranges.getEntry(0x5650), NestedScope); EXPECT_EQ(Ranges.getEntry(0x5410, 0x5490), NestedScope); EXPECT_EQ(Ranges.getEntry(0x5610, 0x5690), NestedScope); EXPECT_EQ(Ranges.getEntry(0x8000), nullptr); Ranges.endSearch(); // Add locations to symbols. // Parameter: [LineOne, LineSix] // LocalVariable: [LineTwo, LineSix], [LineFour, LineFive] // NestedVariable: [LineThree, LineFour] add(Parameter, LineOne, LineSix); add(LocalVariable, LineTwo, LineSix); add(LocalVariable, LineFour, LineFive); add(NestedVariable, LineThree, LineFour); add(NestedVariable, LineOne, LineSix); } // Check logical elements warnigs. void ReaderTestWarningInternal::checkWarnings() { // Map all lines with their addresses. setMapping(); // Check for lines with line zero. resolveElements(); // Check invalid locations and ranges using a customized validation. CompileUnit->processRangeLocationCoverage( (LVValidLocation)(&MyLocation::validateRanges)); // Get lines with line zero. [Parent, Line] // Function, LineTwo // NestedScope, LineFour LVOffsetLinesMap LinesZero = CompileUnit->getLinesZero(); ASSERT_EQ(LinesZero.size(), 2u); LVOffsetLinesMap::iterator IterZero = LinesZero.begin(); EXPECT_EQ(IterZero->first, Function->getOffset()); LVLines *Lines = &IterZero->second; EXPECT_NE(Lines, nullptr); ASSERT_EQ(Lines->size(), 1u); LVLine *Line = *(Lines->begin()); EXPECT_NE(Line, nullptr); EXPECT_EQ(Line, LineTwo); ++IterZero; EXPECT_EQ(IterZero->first, NestedScope->getOffset()); Lines = &IterZero->second; EXPECT_NE(Lines, nullptr); ASSERT_EQ(Lines->size(), 1u); Line = *(Lines->begin()); EXPECT_NE(Line, nullptr); EXPECT_EQ(Line, LineFour); // Elements with invalid offsets. // Function (line zero) // NestedScope (line zero) // NestedVariable (invalid location) LVOffsetElementMap InvalidOffsets = CompileUnit->getWarningOffsets(); ASSERT_EQ(InvalidOffsets.size(), 3u); LVOffsetElementMap::iterator IterOffset = InvalidOffsets.begin(); EXPECT_EQ(IterOffset->second, Function); ++IterOffset; EXPECT_EQ(IterOffset->second, NestedScope); ++IterOffset; EXPECT_EQ(IterOffset->second, NestedVariable); // Invalid ranges. // Function LVOffsetLocationsMap InvalidRanges = CompileUnit->getInvalidRanges(); ASSERT_EQ(InvalidRanges.size(), 1u); LVOffsetLocationsMap::iterator IterRange = InvalidRanges.begin(); EXPECT_EQ(IterRange->first, Function->getOffset()); LVLocations *Locations = &IterRange->second; EXPECT_NE(Locations, nullptr); ASSERT_EQ(Locations->size(), 1u); LVLocation *Location = *(Locations->begin()); EXPECT_NE(Location, nullptr); EXPECT_EQ(Location, LocationFive); // Invalid location. // NestedVariable LVOffsetLocationsMap InvalidLocations = CompileUnit->getInvalidLocations(); ASSERT_EQ(InvalidLocations.size(), 1u); LVOffsetLocationsMap::iterator IterLocations = InvalidLocations.begin(); EXPECT_EQ(IterLocations->first, NestedVariable->getOffset()); Locations = &IterLocations->second; EXPECT_NE(Locations, nullptr); ASSERT_EQ(Locations->size(), 1u); Location = *(Locations->begin()); EXPECT_NE(Location, nullptr); EXPECT_EQ(Location->getLowerAddress(), LocationThree->getLowerAddress()); EXPECT_EQ(Location->getUpperAddress(), LocationFour->getLowerAddress()); EXPECT_EQ(Location->getLowerLine()->getLineNumber(), LineThree->getLineNumber()); EXPECT_EQ(Location->getUpperLine()->getLineNumber(), 0u); // Invalid coverages. // NestedVariable LVOffsetSymbolMap InvalidCoverages = CompileUnit->getInvalidCoverages(); ASSERT_EQ(InvalidCoverages.size(), 1u); LVOffsetSymbolMap::iterator IterCoverages = InvalidCoverages.begin(); EXPECT_EQ(IterCoverages->first, NestedVariable->getOffset()); EXPECT_EQ(IterCoverages->second, NestedVariable); EXPECT_GE((int)NestedVariable->getCoveragePercentage(), 100); EXPECT_EQ((int)NestedVariable->getCoveragePercentage(), 900); EXPECT_EQ(NestedVariable->getCoverageFactor(), 0x1200u); EXPECT_EQ((unsigned)Parameter->getCoveragePercentage(), 100u); EXPECT_EQ(Parameter->getCoverageFactor(), 100u); EXPECT_EQ((unsigned)LocalVariable->getCoveragePercentage(), 47u); EXPECT_EQ(LocalVariable->getCoverageFactor(), LineSix->getAddress() - LineOne->getAddress()); } TEST(LogicalViewTest, WarningInternal) { ScopedPrinter W(outs()); ReaderTestWarningInternal Reader(W); // Reader options. LVOptions ReaderOptions; ReaderOptions.setAttributeOffset(); ReaderOptions.setAttributeRange(); ReaderOptions.setAttributeLocation(); ReaderOptions.setPrintAll(); ReaderOptions.setWarningCoverages(); ReaderOptions.setWarningLines(); ReaderOptions.setWarningLocations(); ReaderOptions.setWarningRanges(); ReaderOptions.resolveDependencies(); options().setOptions(&ReaderOptions); Reader.createElements(); Reader.addElements(); Reader.initElements(); Reader.checkWarnings(); } } // namespace