From 2c155d37996066af4b392aab435884ee919ade8d Mon Sep 17 00:00:00 2001 From: Carlos Alberto Enciso Date: Fri, 21 Oct 2022 06:08:48 +0100 Subject: [PATCH] [llvm-debuginfo-analyzer] (06/09) - Warning and internal options MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit llvm-debuginfo-analyzer is a command line tool that processes debug info contained in a binary file and produces a debug information format agnostic “Logical View”, which is a high-level semantic representation of the debug info, independent of the low-level format. The code has been divided into the following patches: 1) Interval tree 2) Driver and documentation 3) Logical elements 4) Locations and ranges 5) Select elements 6) Warning and internal options 7) Compare elements 8) ELF Reader 9) CodeView Reader Full details: https://discourse.llvm.org/t/llvm-dev-rfc-llvm-dva-debug-information-visual-analyzer/62570 This patch: Warning and internal options - Support for '--warning' options. - Support for '--internal' options. Reviewed By: psamolysov, probinson Differential Revision: https://reviews.llvm.org/D125781 --- .../llvm/DebugInfo/LogicalView/Core/LVElement.h | 1 - .../llvm/DebugInfo/LogicalView/Core/LVObject.h | 31 +- .../llvm/DebugInfo/LogicalView/Core/LVReader.h | 1 + .../llvm/DebugInfo/LogicalView/Core/LVScope.h | 67 ++- .../llvm/DebugInfo/LogicalView/Core/LVSupport.h | 21 + llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp | 2 + llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp | 11 + llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp | 88 ++++ llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp | 152 +++++- llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp | 3 + .../unittests/DebugInfo/LogicalView/CMakeLists.txt | 1 + .../DebugInfo/LogicalView/WarningInternalTest.cpp | 558 +++++++++++++++++++++ 12 files changed, 930 insertions(+), 6 deletions(-) create mode 100644 llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h index 7bac7a3..bde8f67 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h @@ -336,7 +336,6 @@ public: virtual void resolveReferences() {} void resolveParents(); -public: static LVElementDispatch &getDispatch() { return Dispatch; } }; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h index b44b706..e081118 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -168,6 +168,9 @@ class LVObject { // 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 @@ class LVObject { 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 @@ protected: 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 @@ public: #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 diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h index 1a1ee19..c5d7186 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -161,6 +161,7 @@ public: 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()); } diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h index 860ed3c..a3268d2 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h +++ b/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 LVScopeKindSet = std::set; 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 @@ public: 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 @@ class LVScopeCompileUnit final : public LVScope { 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 @@ public: } 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 @@ public: 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 @@ public: 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; }; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h index cfa0c7c..15caa8f 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h @@ -142,6 +142,27 @@ std::string formatAttributes(const StringRef First, Args... Others) { 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); diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp index cc36e6d..ace1cef 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -59,6 +59,8 @@ LVLineDispatch LVLine::Dispatch = { // 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 ") : (" - "); } diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp index 79eea66..e312ad4 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -21,6 +21,10 @@ using namespace llvm::logicalview; #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 @@ std::string LVObject::lineAsString(uint32_t LineNumber, LVHalf Discriminator, } 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, StringRef Name, } 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()) { diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp index 5209823..5d74013 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -23,6 +23,90 @@ using namespace llvm::logicalview; #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 @@ Error LVReader::doLoad() { 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(); diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp index 0228be7..56d5740 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -559,6 +559,12 @@ void LVScope::encodeTemplateArguments(std::string &Name, } 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 @@ Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, Line->doPrint(Split, Match, Print, *StreamSplit, Full)) return Err; } + + // Print the warnings. + if (options().getPrintWarnings()) + printWarnings(*StreamSplit, Full); } } @@ -666,6 +676,10 @@ Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, } } + if (getIsRoot() && options().getPrintWarnings()) { + getReader().printRecords(*StreamSplit); + } + return Error::success(); } @@ -1001,15 +1015,25 @@ void LVScopeCompileUnit::processRangeLocationCoverage( 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::lineLowerBound(LVAddress Address) const { } 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(LVScope *Scope) { increment(Scope); } 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 @@ void LVScopeCompileUnit::printLocalNames(raw_ostream &OS, bool Full) const { 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) diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp index 05d6b84..a467b3d 100644 --- a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -212,6 +212,9 @@ void LVSymbol::calculateCoverage() { ? rint((double(CoverageFactor) / CoverageParent) * 100.0 * 100.0) / 100.0 : 0; + // Record invalid coverage entry. + if (options().getWarningCoverages() && CoveragePercentage > 100) + getReaderCompileUnit()->addInvalidCoverage(this); } } diff --git a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt index 1b66e05..8512fd1 100644 --- a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -8,6 +8,7 @@ add_llvm_unittest(DebugInfoLogicalViewTests LocationRangesTest.cpp LogicalElementsTest.cpp StringPoolTest.cpp + WarningInternalTest.cpp ) target_link_libraries(DebugInfoLogicalViewTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp b/llvm/unittests/DebugInfo/LogicalView/WarningInternalTest.cpp new file mode 100644 index 0000000..e2444e3 --- /dev/null +++ b/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 -- 2.7.4