Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h @@ -336,7 +336,6 @@ virtual void resolveReferences() {} void resolveParents(); -public: static LVElementDispatch &getDispatch() { return Dispatch; } }; Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -168,6 +168,9 @@ // copy constructor to create that object; it is used to print a reference // to another object and in the case of templates, to print its encoded args. LVObject(const LVObject &Object) { +#ifndef NDEBUG + incID(); +#endif Properties = Object.Properties; Offset = Object.Offset; LineNumber = Object.LineNumber; @@ -176,6 +179,19 @@ Parent = Object.Parent; } +#ifndef NDEBUG + // This is an internal ID used for debugging logical elements. It is used + // for cases where an unique offset within the binary input file is not + // available. + static uint64_t GID; + uint64_t ID = 0; + + void incID() { + ++GID; + ID = GID; + } +#endif + protected: // Get a string representation for the given number and discriminator. std::string lineAsString(uint32_t LineNumber, LVHalf Discriminator, @@ -190,7 +206,11 @@ virtual void printFileIndex(raw_ostream &OS, bool Full = true) const {} public: - LVObject() = default; + LVObject() { +#ifndef NDEBUG + incID(); +#endif + }; LVObject &operator=(const LVObject &) = delete; virtual ~LVObject() = default; @@ -308,6 +328,15 @@ #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) virtual void dump() const { print(dbgs()); } #endif + + uint64_t getID() const { + return +#ifndef NDEBUG + ID; +#else + 0; +#endif + } }; } // end namespace logicalview Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -161,6 +161,7 @@ static void setInstance(LVReader *Reader); void print(raw_ostream &OS) const; + virtual void printRecords(raw_ostream &OS) const {} #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) void dump() const { print(dbgs()); } Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/LogicalView/Core/LVElement.h" #include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" #include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include #include #include @@ -56,7 +57,12 @@ using LVScopeDispatch = std::map; using LVScopeRequest = std::vector; +using LVOffsetList = std::list; using LVOffsetElementMap = std::map; +using LVOffsetLinesMap = std::map; +using LVOffsetLocationsMap = std::map; +using LVOffsetSymbolMap = std::map; +using LVTagOffsetsMap = std::map; // Class to represent a DWARF Scope. class LVScope : public LVElement { @@ -266,6 +272,7 @@ void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; + virtual void printWarnings(raw_ostream &OS, bool Full = true) const {} virtual void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) {} #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) @@ -358,11 +365,39 @@ using LVAddressToLine = std::map; LVAddressToLine AddressToLine; + // DWARF Tags (Tag, Element list). + LVTagOffsetsMap DebugTags; + + // Offsets associated with objects being flagged as having invalid data + // (ranges, locations, lines zero or coverages). + LVOffsetElementMap WarningOffsets; + + // Symbols with invalid locations. (Symbol, Location List). + LVOffsetLocationsMap InvalidLocations; + + // Symbols with invalid coverage values. + LVOffsetSymbolMap InvalidCoverages; + + // Scopes with invalid ranges (Scope, Range list). + LVOffsetLocationsMap InvalidRanges; + + // Scopes with lines zero (Scope, Line list). + LVOffsetLinesMap LinesZero; + // Record scopes contribution in bytes to the debug information. using LVSizesMap = std::map; LVSizesMap Sizes; LVOffset CUContributionSize = 0; + // Helper function to add an invalid location/range. + void addInvalidLocationOrRange(LVLocation *Location, LVElement *Element, + LVOffsetLocationsMap *Map) { + LVOffset Offset = Element->getOffset(); + addInvalidOffset(Offset, Element); + addItem( + Map, Offset, Location); + } + // Record scope sizes indexed by lexical level. // Setting an initial size that will cover a very deep nested scopes. const size_t TotalInitialSize = 8; @@ -388,7 +423,12 @@ } LVScopeCompileUnit(const LVScopeCompileUnit &) = delete; LVScopeCompileUnit &operator=(const LVScopeCompileUnit &) = delete; - ~LVScopeCompileUnit() = default; + ~LVScopeCompileUnit() { + deleteList(DebugTags); + deleteList(InvalidLocations); + deleteList(InvalidRanges); + deleteList(LinesZero); + } LVScope *getCompileUnitParent() const override { return static_cast(const_cast(this)); @@ -417,6 +457,30 @@ ProducerIndex = getStringPool().getIndex(ProducerName); } + // Record DWARF tags. + void addDebugTag(dwarf::Tag Target, LVOffset Offset); + // Record elements with invalid offsets. + void addInvalidOffset(LVOffset Offset, LVElement *Element); + // Record symbols with invalid coverage values. + void addInvalidCoverage(LVSymbol *Symbol); + // Record symbols with invalid locations. + void addInvalidLocation(LVLocation *Location); + // Record scopes with invalid ranges. + void addInvalidRange(LVLocation *Location); + // Record line zero. + void addLineZero(LVLine *Line); + + const LVTagOffsetsMap getDebugTags() const { return DebugTags; } + const LVOffsetElementMap getWarningOffsets() const { return WarningOffsets; } + const LVOffsetLocationsMap getInvalidLocations() const { + return InvalidLocations; + } + const LVOffsetSymbolMap getInvalidCoverages() const { + return InvalidCoverages; + } + const LVOffsetLocationsMap getInvalidRanges() const { return InvalidRanges; } + const LVOffsetLinesMap getLinesZero() const { return LinesZero; } + // Process ranges, locations and calculate coverage. void processRangeLocationCoverage( LVValidLocation ValidLocation = &LVLocation::validateRanges); @@ -456,6 +520,7 @@ void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; + void printWarnings(raw_ostream &OS, bool Full = true) const override; void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) override; }; Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h @@ -142,6 +142,27 @@ return Stream.str(); } +// Add an item to a map with second being a list. +template +void addItem(MapType *Map, KeyType Key, ValueType Value) { + ListType *List = nullptr; + typename MapType::const_iterator Iter = Map->find(Key); + if (Iter != Map->end()) + List = Iter->second; + else { + List = new ListType(); + Map->emplace(Key, List); + } + List->push_back(Value); +} + +// Delete the map contained list. +template void deleteList(MapType &Map) { + for (typename MapType::const_reference Entry : Map) + delete Entry.second; +} + // Unified and flattened pathnames. std::string transformPath(StringRef Path); std::string flattenedFilePath(StringRef Path); Index: llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -59,6 +59,8 @@ // String used as padding for printing elements with no line number. std::string LVLine::noLineAsString(bool ShowZero) const { + if (options().getInternalNone()) + return LVObject::noLineAsString(ShowZero); return (ShowZero || options().getAttributeZero()) ? (" 0 ") : (" - "); } Index: llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -21,6 +21,10 @@ #define DEBUG_TYPE "Object" +#ifndef NDEBUG +uint64_t LVObject::GID = 0; +#endif + StringRef llvm::logicalview::typeNone() { return StringRef(); } StringRef llvm::logicalview::typeVoid() { return "void"; } StringRef llvm::logicalview::typeInt() { return "int"; } @@ -61,6 +65,9 @@ } else Stream << noLineAsString(ShowZero); + if (options().getInternalNone()) + Stream.str(noLineAsString(ShowZero)); + return Stream.str(); } @@ -118,6 +125,10 @@ } void LVObject::printAttributes(raw_ostream &OS, bool Full) const { +#ifndef NDEBUG + if (options().getInternalID()) + OS << hexSquareString(getID()); +#endif if (options().getAttributeOffset()) OS << hexSquareString(getOffset()); if (options().getAttributeLevel()) { Index: llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -23,6 +23,90 @@ #define DEBUG_TYPE "Reader" +// Detect elements that are inserted more than once at different scopes, +// causing a crash on the reader destruction, as the element is already +// deleted from other scope. Helper for CodeView reader. +bool checkIntegrityScopesTree(LVScope *Root) { + using LVDuplicateEntry = std::tuple; + using LVDuplicate = std::vector; + LVDuplicate Duplicate; + + using LVIntegrity = std::map; + LVIntegrity Integrity; + + // Add the given element to the integrity map. + auto AddElement = [&](LVElement *Element, LVScope *Scope) { + LVIntegrity::iterator Iter = Integrity.find(Element); + if (Iter == Integrity.end()) + Integrity.emplace(Element, Scope); + else + // We found a duplicate. + Duplicate.emplace_back(Element, Scope, Iter->second); + }; + + // Recursively add all the elements in the scope. + std::function TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + AddElement(Entry, Parent); + }; + if (const LVScopes *Scopes = Parent->getScopes()) { + for (LVScope *Scope : *Scopes) { + AddElement(Scope, Parent); + TraverseScope(Scope); + } + } + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + }; + + // Start traversing the scopes root and print any duplicates. + TraverseScope(Root); + bool PassIntegrity = true; + if (Duplicate.size()) { + std::stable_sort(begin(Duplicate), end(Duplicate), + [](const auto &l, const auto &r) { + return std::get<0>(l)->getID() < std::get<0>(r)->getID(); + }); + + auto PrintIndex = [](unsigned Index) { + if (Index) + dbgs() << format("%8d: ", Index); + else + dbgs() << format("%8c: ", ' '); + }; + auto PrintElement = [&](LVElement *Element, unsigned Index = 0) { + PrintIndex(Index); + std::string ElementName(Element->getName()); + dbgs() << format("%15s ID=0x%08x '%s'\n", Element->kind(), + Element->getID(), ElementName.c_str()); + }; + + std::string RootName(Root->getName()); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + dbgs() << format("Root: '%s'\nDuplicated elements: %d\n", RootName.c_str(), + Duplicate.size()); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + + unsigned Index = 0; + for (const LVDuplicateEntry &Entry : Duplicate) { + LVElement *Element; + LVScope *First; + LVScope *Second; + std::tie(Element, First, Second) = Entry; + dbgs() << formatv("\n{0}\n", fmt_repeat('-', 72)); + PrintElement(Element, ++Index); + PrintElement(First); + PrintElement(Second); + dbgs() << formatv("{0}\n", fmt_repeat('-', 72)); + } + PassIntegrity = false; + } + return PassIntegrity; +} + //===----------------------------------------------------------------------===// // Class to represent a split context. //===----------------------------------------------------------------------===// @@ -149,6 +233,10 @@ if (Error Err = createScopes()) return Err; + if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root)) + return llvm::make_error("Duplicated elements in Scopes Tree", + inconvertibleErrorCode()); + // Calculate symbol coverage and detect invalid debug locations and ranges. Root->processRangeInformation(); Index: llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -559,6 +559,12 @@ } bool LVScope::resolvePrinting() const { + // The warnings collected during the scope creation as per compile unit. + // If there is a request for printing warnings, always print its associate + // Compile Unit. + if (options().getPrintWarnings() && (getIsRoot() || getIsCompileUnit())) + return true; + // In selection mode, always print the root scope regardless of the // number of matched elements. If no matches, the root by itself will // indicate no matches. @@ -650,6 +656,10 @@ Line->doPrint(Split, Match, Print, *StreamSplit, Full)) return Err; } + + // Print the warnings. + if (options().getPrintWarnings()) + printWarnings(*StreamSplit, Full); } } @@ -666,6 +676,10 @@ } } + if (getIsRoot() && options().getPrintWarnings()) { + getReader().printRecords(*StreamSplit); + } + return Error::success(); } @@ -1001,15 +1015,25 @@ if (options().getAttributeRange()) { // Traverse the scopes to get scopes that have invalid ranges. LVLocations Locations; - bool RecordInvalid = false; + bool RecordInvalid = options().getWarningRanges(); getRanges(Locations, ValidLocation, RecordInvalid); + + // Validate ranges associated with scopes. + if (RecordInvalid) + for (LVLocation *Location : Locations) + addInvalidRange(Location); } if (options().getAttributeLocation()) { // Traverse the scopes to get locations that have invalid ranges. LVLocations Locations; - bool RecordInvalid = false; + bool RecordInvalid = options().getWarningLocations(); getLocations(Locations, ValidLocation, RecordInvalid); + + // Validate ranges associated with locations. + if (RecordInvalid) + for (LVLocation *Location : Locations) + addInvalidLocation(Location); } } @@ -1019,10 +1043,12 @@ } LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address) const { + if (AddressToLine.empty()) + return nullptr; LVAddressToLine::const_iterator Iter = AddressToLine.upper_bound(Address); if (Iter != AddressToLine.begin()) Iter = std::prev(Iter); - return (Iter != AddressToLine.end()) ? Iter->second : nullptr; + return Iter->second; } StringRef LVScopeCompileUnit::getFilename(size_t Index) const { @@ -1069,6 +1095,46 @@ void LVScopeCompileUnit::addedElement(LVSymbol *Symbol) { increment(Symbol); } void LVScopeCompileUnit::addedElement(LVType *Type) { increment(Type); } +// Record unsuported DWARF tags. +void LVScopeCompileUnit::addDebugTag(dwarf::Tag Target, LVOffset Offset) { + addItem(&DebugTags, + Target, Offset); +} + +// Record elements with invalid offsets. +void LVScopeCompileUnit::addInvalidOffset(LVOffset Offset, LVElement *Element) { + if (WarningOffsets.find(Offset) == WarningOffsets.end()) + WarningOffsets.emplace(Offset, Element); +} + +// Record symbols with invalid coverage values. +void LVScopeCompileUnit::addInvalidCoverage(LVSymbol *Symbol) { + LVOffset Offset = Symbol->getOffset(); + if (InvalidCoverages.find(Offset) == InvalidCoverages.end()) + InvalidCoverages.emplace(Offset, Symbol); +} + +// Record symbols with invalid locations. +void LVScopeCompileUnit::addInvalidLocation(LVLocation *Location) { + addInvalidLocationOrRange(Location, Location->getParentSymbol(), + &InvalidLocations); +} + +// Record scopes with invalid ranges. +void LVScopeCompileUnit::addInvalidRange(LVLocation *Location) { + addInvalidLocationOrRange(Location, Location->getParentScope(), + &InvalidRanges); +} + +// Record line zero. +void LVScopeCompileUnit::addLineZero(LVLine *Line) { + LVScope *Scope = Line->getParentScope(); + LVOffset Offset = Scope->getOffset(); + addInvalidOffset(Offset, Scope); + addItem(&LinesZero, Offset, + Line); +} + void LVScopeCompileUnit::printLocalNames(raw_ostream &OS, bool Full) const { if (!options().getPrintFormatting()) return; @@ -1104,6 +1170,86 @@ PrintNames(Option::File); } +void LVScopeCompileUnit::printWarnings(raw_ostream &OS, bool Full) const { + auto PrintHeader = [&](const char *Header) { OS << "\n" << Header << ":\n"; }; + auto PrintFooter = [&](auto &Set) { + if (Set.empty()) + OS << "None\n"; + }; + auto PrintOffset = [&](unsigned &Count, LVOffset Offset) { + if (Count == 5) { + Count = 0; + OS << "\n"; + } + ++Count; + OS << hexSquareString(Offset) << " "; + }; + auto PrintElement = [&](const LVOffsetElementMap &Map, LVOffset Offset) { + LVOffsetElementMap::const_iterator Iter = Map.find(Offset); + LVElement *Element = Iter != Map.end() ? Iter->second : nullptr; + OS << "[" << hexString(Offset) << "]"; + if (Element) + OS << " " << formattedKind(Element->kind()) << " " + << formattedName(Element->getName()); + OS << "\n"; + }; + auto PrintInvalidLocations = [&](const LVOffsetLocationsMap &Map, + const char *Header) { + PrintHeader(Header); + for (LVOffsetLocationsMap::const_reference Entry : Map) { + PrintElement(WarningOffsets, Entry.first); + for (const LVLocation *Location : *Entry.second) + OS << hexSquareString(Location->getOffset()) << " " + << Location->getIntervalInfo() << "\n"; + } + PrintFooter(Map); + }; + + if (options().getInternalTag() && getReader().isBinaryTypeELF()) { + PrintHeader("Unsupported DWARF Tags"); + for (LVTagOffsetsMap::const_reference Entry : DebugTags) { + OS << format("\n0x%02x", (unsigned)Entry.first) << ", " + << dwarf::TagString(Entry.first) << "\n"; + unsigned Count = 0; + for (const LVOffset &Offset : *Entry.second) + PrintOffset(Count, Offset); + OS << "\n"; + } + PrintFooter(DebugTags); + } + + if (options().getWarningCoverages()) { + PrintHeader("Symbols Invalid Coverages"); + for (LVOffsetSymbolMap::const_reference Entry : InvalidCoverages) { + // Symbol basic information. + LVSymbol *Symbol = Entry.second; + OS << hexSquareString(Entry.first) << " {Coverage} " + << format("%.2f%%", Symbol->getCoveragePercentage()) << " " + << formattedKind(Symbol->kind()) << " " + << formattedName(Symbol->getName()) << "\n"; + } + PrintFooter(InvalidCoverages); + } + + if (options().getWarningLines()) { + PrintHeader("Lines Zero References"); + for (LVOffsetLinesMap::const_reference Entry : LinesZero) { + PrintElement(WarningOffsets, Entry.first); + unsigned Count = 0; + for (const LVLine *Line : *Entry.second) + PrintOffset(Count, Line->getOffset()); + OS << "\n"; + } + PrintFooter(LinesZero); + } + + if (options().getWarningLocations()) + PrintInvalidLocations(InvalidLocations, "Invalid Location Ranges"); + + if (options().getWarningRanges()) + PrintInvalidLocations(InvalidRanges, "Invalid Code Ranges"); +} + void LVScopeCompileUnit::printTotals(raw_ostream &OS) const { OS << "\nTotals by lexical level:\n"; for (size_t Index = 1; Index <= MaxSeenLevel; ++Index) Index: llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -212,6 +212,9 @@ ? rint((double(CoverageFactor) / CoverageParent) * 100.0 * 100.0) / 100.0 : 0; + // Record invalid coverage entry. + if (options().getWarningCoverages() && CoveragePercentage > 100) + getReaderCompileUnit()->addInvalidCoverage(this); } } Index: llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt +++ llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -8,6 +8,7 @@ LocationRangesTest.cpp LogicalElementsTest.cpp StringPoolTest.cpp + WarningInternalTest.cpp ) target_link_libraries(DebugInfoLogicalViewTests PRIVATE LLVMTestingSupport) Index: llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp @@ -0,0 +1,558 @@ +//===- 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; + +// 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 { + // 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; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create() { + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + return 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); + void add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine); + +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(); +}; + +class MyLocation : public LVLocation { +public: + bool validateRanges(); +}; + +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. + IntegerType = create(); + + // Create the logical scopes. + NestedScope = create(); + CompileUnit = create(); + Function = create(); + + // Create the logical symbols. + LocalVariable = create(); + NestedVariable = create(); + Parameter = create(); + + // Create the logical lines. + LineOne = create(); + LineTwo = create(); + LineThree = create(); + LineFour = create(); + LineFive = create(); + LineSix = create(); + + // Create the logical locations. + LocationOne = create(); + LocationTwo = create(); + LocationThree = create(); + LocationFour = create(); + LocationFive = create(); + LocationSix = create(); +} + +// 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