diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLocation.h @@ -0,0 +1,195 @@ +//===-- LVLocation.h --------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVOperation and LVLocation classes, which are used +// to describe variable locations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" + +namespace llvm { +namespace logicalview { + +// The DW_AT_data_member_location attribute is a simple member offset. +const LVSmall LVLocationMemberOffset = 0; + +class LVOperation final { + // To describe an operation: + // OpCode + // Operands[0]: First operand. + // Operands[1]: Second operand. + // OP_bregx, OP_bit_piece, OP_[GNU_]const_type, + // OP_[GNU_]deref_type, OP_[GNU_]entry_value, OP_implicit_value, + // OP_[GNU_]implicit_pointer, OP_[GNU_]regval_type, OP_xderef_type. + LVSmall Opcode = 0; + uint64_t Operands[2]; + +public: + LVOperation() = delete; + LVOperation(LVSmall Opcode, LVUnsigned Operand1, LVUnsigned Operand2) + : Opcode(Opcode) { + Operands[0] = Operand1; + Operands[1] = Operand2; + } + LVOperation(const LVOperation &) = delete; + LVOperation &operator=(const LVOperation &) = delete; + ~LVOperation() = default; + + LVSmall getOpcode() const { return Opcode; } + uint64_t getOperand1() const { return Operands[0]; } + uint64_t getOperand2() const { return Operands[1]; } + std::string getOperandsDWARFInfo(); + std::string getOperandsCodeViewInfo(); + + void print(raw_ostream &OS, bool Full = true) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() { print(dbgs()); } +#endif +}; + +class LVLocation : public LVObject { + enum class Property { + IsAddressRange, + IsBaseClassOffset, + IsBaseClassStep, + IsClassOffset, + IsFixedAddress, + IsLocationSimple, + IsGapEntry, + IsOperation, + IsOperationList, + IsRegister, + IsStackOffset, + IsDiscardedRange, + IsInvalidRange, + IsInvalidLower, + IsInvalidUpper, + IsCallSite, + LastEntry + }; + // Typed bitvector with properties for this location. + LVProperties Properties; + + // True if the location it is associated with a debug range. + bool hasAssociatedRange() const { + return !getIsClassOffset() && !getIsDiscardedRange(); + } + +protected: + // Line numbers associated with locations ranges. + LVLine *LowerLine = nullptr; + LVLine *UpperLine = nullptr; + + // Active range: + // LowPC: an offset from an applicable base address, not a PC value. + // HighPC: an offset from an applicable base address, or a length. + LVAddress LowPC = 0; + LVAddress HighPC = 0; + + void setKind(); + +public: + LVLocation() : LVObject() { setIsLocation(); } + LVLocation(const LVLocation &) = delete; + LVLocation &operator=(const LVLocation &) = delete; + virtual ~LVLocation() = default; + + PROPERTY(Property, IsAddressRange); + PROPERTY(Property, IsBaseClassOffset); + PROPERTY(Property, IsBaseClassStep); + PROPERTY_1(Property, IsClassOffset, IsLocationSimple); + PROPERTY_1(Property, IsFixedAddress, IsLocationSimple); + PROPERTY(Property, IsLocationSimple); + PROPERTY(Property, IsGapEntry); + PROPERTY(Property, IsOperationList); + PROPERTY(Property, IsOperation); + PROPERTY(Property, IsRegister); + PROPERTY_1(Property, IsStackOffset, IsLocationSimple); + PROPERTY(Property, IsDiscardedRange); + PROPERTY(Property, IsInvalidRange); + PROPERTY(Property, IsInvalidLower); + PROPERTY(Property, IsInvalidUpper); + PROPERTY(Property, IsCallSite); + + const char *kind() const override; + // Mark the locations that have only DW_OP_fbreg as stack offset based. + virtual void updateKind() {} + + // Line numbers for locations. + const LVLine *getLowerLine() const { return LowerLine; } + void setLowerLine(LVLine *Line) { LowerLine = Line; } + const LVLine *getUpperLine() const { return UpperLine; } + void setUpperLine(LVLine *Line) { UpperLine = Line; } + + // Addresses for locations. + LVAddress getLowerAddress() const override { return LowPC; } + void setLowerAddress(LVAddress Address) override { LowPC = Address; } + LVAddress getUpperAddress() const override { return HighPC; } + void setUpperAddress(LVAddress Address) override { HighPC = Address; } + + std::string getIntervalInfo() const; + + bool validateRanges(); + + // In order to calculate a symbol coverage (percentage), take the ranges + // and obtain the number of units (bytes) covered by those ranges. We can't + // use the line numbers, because they can be zero or invalid. + // We return: + // false: No locations or multiple locations. + // true: a single location. + static bool calculateCoverage(LVLocations *Locations, unsigned &Factor, + float &Percentage); + + virtual void addObject(LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, uint64_t LocDescOffset) {} + virtual void addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) {} + + static void print(LVLocations *Locations, raw_ostream &OS, bool Full = true); + void printInterval(raw_ostream &OS, bool Full = true) const; + void printRaw(raw_ostream &OS, bool Full = true) const; + virtual void printRawExtra(raw_ostream &OS, bool Full = true) const {} + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +class LVLocationSymbol final : public LVLocation { + // Location descriptors for the active range. + LVAutoOperations *Entries = nullptr; + + void updateKind() override; + +public: + LVLocationSymbol() : LVLocation() {} + LVLocationSymbol(const LVLocationSymbol &) = delete; + LVLocationSymbol &operator=(const LVLocationSymbol &) = delete; + ~LVLocationSymbol() { delete Entries; }; + + void addObject(LVAddress LowPC, LVAddress HighPC, LVUnsigned SectionOffset, + uint64_t LocDescOffset) override; + void addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) override; + + void printRawExtra(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVLOCATION_H diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -102,6 +102,9 @@ enum class LVBinaryType { NONE, ELF, COFF }; +// Validate functions. +using LVValidLocation = bool (LVLocation::*)(); + // Keep counters of objects. struct LVCounter { unsigned Lines = 0; @@ -258,6 +261,11 @@ void setParent(LVSymbol *Symbol); void resetParent() { Parent = {nullptr}; } + virtual LVAddress getLowerAddress() const { return 0; } + virtual void setLowerAddress(LVAddress Address) {} + virtual LVAddress getUpperAddress() const { return 0; } + virtual void setUpperAddress(LVAddress Address) {} + uint32_t getLineNumber() const { return LineNumber; } void setLineNumber(uint32_t Number) { LineNumber = Number; } diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVRange.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVRange.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVRange.h @@ -0,0 +1,98 @@ +//===-- LVRange.h -----------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVRange class, which is used to describe a debug +// information range. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVRANGE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVRANGE_H + +#include "llvm/ADT/IntervalTree.h" +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" + +namespace llvm { +namespace logicalview { + +using LVAddressRange = std::pair; + +class LVRangeEntry final { + LVAddress Lower = 0; + LVAddress Upper = 0; + LVScope *Scope = nullptr; + +public: + using RangeType = LVAddress; + + LVRangeEntry() = delete; + LVRangeEntry(LVAddress LowerAddress, LVAddress UpperAddress, LVScope *Scope) + : Lower(LowerAddress), Upper(UpperAddress), Scope(Scope) {} + + RangeType lower() const { return Lower; } + RangeType upper() const { return Upper; } + LVAddressRange addressRange() const { + return LVAddressRange(lower(), upper()); + } + LVScope *scope() const { return Scope; } +}; + +// Class to represent a list of range addresses associated with a +// scope; the addresses are stored in ascending order and can overlap. +using LVRangeEntries = std::vector; + +class LVRange final : public LVObject { + /// Map of where a user value is live, and its location. + using LVRangesTree = IntervalTree; + using LVAllocator = LVRangesTree::Allocator; + + LVAllocator Allocator; + LVRangesTree RangesTree; + LVRangeEntries RangeEntries; + LVAddress Lower = MaxAddress; + LVAddress Upper = 0; + +public: + LVRange() : LVObject(), RangesTree(Allocator) {} + LVRange(const LVRange &) = delete; + LVRange &operator=(const LVRange &) = delete; + ~LVRange() = default; + + void addEntry(LVScope *Scope, LVAddress LowerAddress, LVAddress UpperAddress); + void addEntry(LVScope *Scope); + LVScope *getEntry(LVAddress Address) const; + LVScope *getEntry(LVAddress LowerAddress, LVAddress UpperAddress) const; + bool hasEntry(LVAddress Low, LVAddress High) const; + LVAddress getLower() const { return Lower; } + LVAddress getUpper() const { return Upper; } + + const LVRangeEntries &getEntries() const { return RangeEntries; } + + void clear() { + RangeEntries.clear(); + Lower = MaxAddress; + Upper = 0; + } + bool empty() const { return RangeEntries.empty(); } + void sort(); + + void startSearch(); + void endSearch() {} + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVRANGE_H diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -15,6 +15,7 @@ #define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" #include "llvm/Support/Error.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/ToolOutputFile.h" @@ -127,6 +128,11 @@ Error doPrint(); Error doLoad(); + virtual std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) { + llvm_unreachable("Invalid instance reader."); + return {}; + } + virtual bool isSystemEntry(LVElement *Element, StringRef Name = {}) { return false; }; @@ -136,6 +142,7 @@ // Conditions to print an object. bool doPrintLine(const LVLine *Line) const { return true; } + bool doPrintLocation(const LVLocation *Location) const { return true; } bool doPrintScope(const LVScope *Scope) const { return true; } bool doPrintSymbol(const LVSymbol *Symbol) const { return true; } bool doPrintType(const LVType *Type) const { return true; } diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h @@ -15,6 +15,7 @@ #define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVSCOPE_H #include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" #include "llvm/DebugInfo/LogicalView/Core/LVSort.h" #include #include @@ -22,6 +23,8 @@ namespace llvm { namespace logicalview { +class LVRange; + enum class LVScopeKind { IsAggregate, IsArray, @@ -76,6 +79,15 @@ LVProperties Kinds; LVProperties Properties; + // Coverage factor in units (bytes). + unsigned CoverageFactor = 0; + + // Calculate coverage factor. + void calculateCoverage() { + float CoveragePercentage = 0; + LVLocation::calculateCoverage(Ranges, CoverageFactor, CoveragePercentage); + } + // Decide if the scope will be printed, using some conditions given by: // only-globals, only-locals, a-pattern. bool resolvePrinting() const; @@ -86,11 +98,12 @@ LVScopeSetFunction SetFunction); protected: - // Types, Symbols, Scopes, Lines in this scope. + // Types, Symbols, Scopes, Lines, Locations in this scope. LVAutoTypes *Types = nullptr; LVAutoSymbols *Symbols = nullptr; LVAutoScopes *Scopes = nullptr; LVAutoLines *Lines = nullptr; + LVAutoLocations *Ranges = nullptr; // Vector of elements (types, scopes and symbols). // It is the union of (*Types, *Symbols and *Scopes) to be used for @@ -104,8 +117,9 @@ void resolveTemplate(); void printEncodedArgs(raw_ostream &OS, bool Full) const; - virtual void printSizes(raw_ostream &OS) const {} - virtual void printSummary(raw_ostream &OS) const {} + void printActiveRanges(raw_ostream &OS, bool Full = true) const; + virtual void printSizes(raw_ostream &OS) const {}; + virtual void printSummary(raw_ostream &OS) const {}; // Encoded template arguments. virtual StringRef getEncodedArgs() const { return StringRef(); } @@ -171,6 +185,7 @@ // Get the specific children. const LVLines *getLines() const { return Lines; } + const LVLocations *getRanges() const { return Ranges; } const LVScopes *getScopes() const { return Scopes; } const LVSymbols *getSymbols() const { return Symbols; } const LVTypes *getTypes() const { return Types; } @@ -181,6 +196,8 @@ void addElement(LVScope *Scope); void addElement(LVSymbol *Symbol); void addElement(LVType *Type); + void addObject(LVLocation *Location); + void addObject(LVAddress LowerAddress, LVAddress UpperAddress); void addToChildren(LVElement *Element); // Add the missing elements from the given 'Reference', which is the @@ -194,10 +211,23 @@ // Get the size of specific children. size_t lineCount() const { return Lines ? Lines->size() : 0; } + size_t rangeCount() const { return Ranges ? Ranges->size() : 0; } size_t scopeCount() const { return Scopes ? Scopes->size() : 0; } size_t symbolCount() const { return Symbols ? Symbols->size() : 0; } size_t typeCount() const { return Types ? Types->size() : 0; } + // Find containing parent for the given address. + LVScope *outermostParent(LVAddress Address); + + // Get all the locations associated with symbols. + void getLocations(LVLocations &LocationList, LVValidLocation ValidLocation, + bool RecordInvalid = false); + void getRanges(LVLocations &LocationList, LVValidLocation ValidLocation, + bool RecordInvalid = false); + void getRanges(LVRange &RangeList); + + unsigned getCoverageFactor() const { return CoverageFactor; } + Error doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, bool Full = true) const override; // Sort the logical elements using the criteria specified by the @@ -378,6 +408,10 @@ ProducerIndex = getStringPool().getIndex(ProducerName); } + // Process ranges, locations and calculate coverage. + void processRangeLocationCoverage( + LVValidLocation ValidLocation = &LVLocation::validateRanges); + void printLocalNames(raw_ostream &OS, bool Full = true) const; void printSummary(raw_ostream &OS, const LVCounter &Counter, const char *Header) const; @@ -563,6 +597,9 @@ FileFormatNameIndex = getStringPool().getIndex(FileFormatName); } + // Process the collected location, ranges and calculate coverage. + void processRangeInformation(); + void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; Error doPrintMatches(bool Split, raw_ostream &OS, diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h @@ -40,6 +40,7 @@ LVSortValue compareLine(const LVObject *LHS, const LVObject *RHS); LVSortValue compareName(const LVObject *LHS, const LVObject *RHS); LVSortValue compareOffset(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareRange(const LVObject *LHS, const LVObject *RHS); LVSortValue sortByKind(const LVObject *LHS, const LVObject *RHS); LVSortValue sortByLine(const LVObject *LHS, const LVObject *RHS); LVSortValue sortByName(const LVObject *LHS, const LVObject *RHS); diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h @@ -158,6 +158,21 @@ return (Twine("'") + Twine(Name1) + Twine(Name2) + Twine("'")).str(); } +// These are the values assigned to the debug location record IDs. +// See DebugInfo/CodeView/CodeViewSymbols.def. +// S_DEFRANGE 0x113f +// S_DEFRANGE_SUBFIELD 0x1140 +// S_DEFRANGE_REGISTER 0x1141 +// S_DEFRANGE_FRAMEPOINTER_REL 0x1142 +// S_DEFRANGE_SUBFIELD_REGISTER 0x1143 +// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE 0x1144 +// S_DEFRANGE_REGISTER_REL 0x1145 +// When recording CodeView debug location, the above values are truncated +// to a uint8_t value in order to fit the 'OpCode' used for the logical +// debug location operations. +// Return the original CodeView enum value. +inline uint16_t getCodeViewOperationCode(uint8_t Code) { return 0x1100 | Code; } + } // end namespace logicalview } // end namespace llvm diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h @@ -43,6 +43,8 @@ // Reference to DW_AT_specification, DW_AT_abstract_origin attribute. LVSymbol *Reference = nullptr; + LVAutoLocations *Locations = nullptr; + LVLocation *CurrentLocation = nullptr; // Bitfields length. uint32_t BitSize = 0; @@ -50,6 +52,14 @@ // Index in the String pool representing any initial value. size_t ValueIndex = 0; + // Coverage factor in units (bytes). + unsigned CoverageFactor = 0; + float CoveragePercentage = 0; + + // Add a location gap into the location list. + LVAutoLocations::iterator addLocationGap(LVAutoLocations::iterator Pos, + LVAddress LowPC, LVAddress HighPC); + public: LVSymbol() : LVElement(LVSubclassID::LV_SYMBOL) { setIsSymbol(); @@ -57,7 +67,7 @@ } LVSymbol(const LVSymbol &) = delete; LVSymbol &operator=(const LVSymbol &) = delete; - ~LVSymbol() = default; + ~LVSymbol() { delete Locations; } static bool classof(const LVElement *Element) { return Element->getSubclassID() == LVSubclassID::LV_SYMBOL; @@ -107,6 +117,34 @@ } size_t getValueIndex() const override { return ValueIndex; } + // Add a Location Entry. + void addLocationConstant(dwarf::Attribute Attr, LVUnsigned Constant, + uint64_t LocDescOffset); + void addLocationOperands(LVSmall Opcode, uint64_t Operand1, + uint64_t Operand2); + void addLocation(dwarf::Attribute Attr, LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, uint64_t LocDescOffset, + bool CallSiteLocation = false); + + // Fill gaps in the location list. + void fillLocationGaps(); + + // Get all the locations associated with symbols. + void getLocations(LVLocations &LocationList, LVValidLocation ValidLocation, + bool RecordInvalid = false); + void getLocations(LVLocations &LocationList) const; + + // Calculate coverage factor. + void calculateCoverage(); + + unsigned getCoverageFactor() const { return CoverageFactor; } + void setCoverageFactor(unsigned Value) { CoverageFactor = Value; } + float getCoveragePercentage() const { return CoveragePercentage; } + void setCoveragePercentage(float Value) { CoveragePercentage = Value; } + + // Print location in raw format. + void printLocations(raw_ostream &OS, bool Full = true) const; + // Follow a chain of references given by DW_AT_abstract_origin and/or // DW_AT_specification and update the symbol name. StringRef resolveReferencesChain(); diff --git a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt --- a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt +++ b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -6,8 +6,10 @@ add_lv_impl_folder(Core Core/LVElement.cpp Core/LVLine.cpp + Core/LVLocation.cpp Core/LVObject.cpp Core/LVOptions.cpp + Core/LVRange.cpp Core/LVReader.cpp Core/LVScope.cpp Core/LVSort.cpp diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp @@ -0,0 +1,675 @@ +//===-- LVLocation.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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVOperation and LVLocation classes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Location" + +void LVOperation::print(raw_ostream &OS, bool Full) const {} + +// Identify the most common type of operations and print them using a high +// level format, trying to isolate the DWARF complexity. +std::string LVOperation::getOperandsDWARFInfo() { + std::string String; + raw_string_ostream Stream(String); + + auto PrintRegisterInfo = [&](LVSmall Code) { + //----------------------------------------------------------------------- + // 2.5.1.1 Literal encodings. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_lit0 <= Code && Code <= dwarf::DW_OP_lit31) { + Stream << format("lit%d", Code - dwarf::DW_OP_lit0); + return; + } + + //----------------------------------------------------------------------- + // 2.5.1.2 Register values. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_breg0 <= Code && Code <= dwarf::DW_OP_breg31) { + std::string RegisterName(getReader().getRegisterName(Code, Operands)); + Stream << format("breg%d+%d%s", Code - dwarf::DW_OP_breg0, Operands[0], + RegisterName.c_str()); + return; + } + + //----------------------------------------------------------------------- + // 2.6.1.1.3 Register location descriptions. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_reg0 <= Code && Code <= dwarf::DW_OP_reg31) { + std::string RegisterName(getReader().getRegisterName(Code, Operands)); + Stream << format("reg%d%s", Code - dwarf::DW_OP_reg0, + RegisterName.c_str()); + return; + } + + Stream << format("#0x%02x ", Code) << hexString(Operands[0]) << " " + << hexString(Operands[1]) << "#"; + }; + + switch (Opcode) { + //------------------------------------------------------------------------- + // 2.5.1.1 Literal encodings. + //------------------------------------------------------------------------- + case dwarf::DW_OP_addr: + Stream << "addr " << hexString(Operands[0]); + break; + case dwarf::DW_OP_constu: + case dwarf::DW_OP_const1u: + case dwarf::DW_OP_const2u: + case dwarf::DW_OP_const4u: + case dwarf::DW_OP_const8u: + Stream << "const_u " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_consts: + case dwarf::DW_OP_const1s: + case dwarf::DW_OP_const2s: + case dwarf::DW_OP_const4s: + case dwarf::DW_OP_const8s: + Stream << "const_s " << int(Operands[0]); + break; + case dwarf::DW_OP_addrx: + Stream << "addrx " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_constx: + Stream << "constx " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_const_type: + Stream << "TODO: DW_OP_const_type"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.2 Register values. + //------------------------------------------------------------------------- + case dwarf::DW_OP_fbreg: + Stream << "fbreg " << int(Operands[0]); + break; + case dwarf::DW_OP_bregx: { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("bregx %d%s+%d", Operands[0], RegisterName.c_str(), + unsigned(Operands[1])); + break; + } + case dwarf::DW_OP_regval_type: { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("regval_type %d%s+%d", Operands[0], RegisterName.c_str(), + unsigned(Operands[1])); + break; + } + + //------------------------------------------------------------------------- + // 2.5.1.3 Stack operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_dup: + Stream << "dup"; + break; + case dwarf::DW_OP_drop: + Stream << "drop"; + break; + case dwarf::DW_OP_pick: + Stream << "pick " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_over: + Stream << "over"; + break; + case dwarf::DW_OP_swap: + Stream << "swap"; + break; + case dwarf::DW_OP_rot: + Stream << "rot"; + break; + case dwarf::DW_OP_deref: + Stream << "deref"; + break; + case dwarf::DW_OP_deref_size: + Stream << "deref_size " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_deref_type: + Stream << "deref_type " << unsigned(Operands[0]) << " DIE offset " + << hexString(Operands[1]); + break; + case dwarf::DW_OP_xderef: + Stream << "xderef"; + break; + case dwarf::DW_OP_xderef_size: + Stream << "xderef_size " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_xderef_type: + Stream << "xderef_type " << unsigned(Operands[0]) << " DIE offset " + << hexString(Operands[1]); + break; + case dwarf::DW_OP_push_object_address: + Stream << "push_object_address"; + break; + case dwarf::DW_OP_form_tls_address: + Stream << "form_tls_address " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call_frame_cfa: + Stream << "call_frame_cfa"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.4 Arithmetic and Logical Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_abs: + Stream << "abs"; + break; + case dwarf::DW_OP_and: + Stream << "and"; + break; + case dwarf::DW_OP_div: + Stream << "div"; + break; + case dwarf::DW_OP_minus: + Stream << "minus"; + break; + case dwarf::DW_OP_mod: + Stream << "mod"; + break; + case dwarf::DW_OP_mul: + Stream << "mul"; + break; + case dwarf::DW_OP_neg: + Stream << "neg"; + break; + case dwarf::DW_OP_not: + Stream << "not"; + break; + case dwarf::DW_OP_or: + Stream << "or"; + break; + case dwarf::DW_OP_plus: + Stream << "plus"; + break; + case dwarf::DW_OP_plus_uconst: + Stream << "plus_uconst " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_shl: + Stream << "shl"; + break; + case dwarf::DW_OP_shr: + Stream << "shr"; + break; + case dwarf::DW_OP_shra: + Stream << "shra"; + break; + case dwarf::DW_OP_xor: + Stream << "xor"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.5 Control Flow Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_le: + Stream << "le"; + break; + case dwarf::DW_OP_ge: + Stream << "ge"; + break; + case dwarf::DW_OP_eq: + Stream << "eq"; + break; + case dwarf::DW_OP_lt: + Stream << "lt"; + break; + case dwarf::DW_OP_gt: + Stream << "gt"; + break; + case dwarf::DW_OP_ne: + Stream << "ne"; + break; + case dwarf::DW_OP_skip: + Stream << "skip " << signed(Operands[0]); + break; + case dwarf::DW_OP_bra: + Stream << "bra " << signed(Operands[0]); + break; + case dwarf::DW_OP_call2: + Stream << "call2 DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call4: + Stream << "call4 DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call_ref: + Stream << "call_ref DIE offset " << hexString(Operands[0]); + break; + + //------------------------------------------------------------------------- + // 2.5.1.6 Type Conversions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_convert: + Stream << "convert DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_reinterpret: + Stream << "reinterpret DIE offset " << hexString(Operands[0]); + break; + + //------------------------------------------------------------------------- + // 2.5.1.7 Special Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_nop: + Stream << "nop"; + break; + case dwarf::DW_OP_entry_value: + Stream << "TODO: DW_OP_entry_value"; + break; + + //------------------------------------------------------------------------- + // 2.6.1.1.3 Register location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_regx: + Stream << "regx" << getReader().getRegisterName(Opcode, Operands); + break; + + //------------------------------------------------------------------------- + // 2.6.1.1.4 Implicit location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_stack_value: + Stream << "stack_value"; + break; + case dwarf::DW_OP_implicit_value: + Stream << "TODO: DW_OP_implicit_value"; + break; + case dwarf::DW_OP_implicit_pointer: + Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " " + << int(Operands[1]); + break; + + //------------------------------------------------------------------------- + // 2.6.1.2 Composite location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_piece: + Stream << "piece " << int(Operands[0]); + break; + case dwarf::DW_OP_bit_piece: + Stream << "bit_piece " << int(Operands[0]) << " offset " + << int(Operands[1]); + break; + + //------------------------------------------------------------------------- + // GNU extensions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_GNU_entry_value: + Stream << "gnu_entry_value "; + PrintRegisterInfo(dwarf::DW_OP_reg0); + break; + case dwarf::DW_OP_GNU_push_tls_address: + Stream << "gnu_push_tls_address " << hexString(Operands[0]); + break; + case dwarf::DW_OP_GNU_addr_index: + Stream << "gnu_addr_index " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_GNU_const_index: + Stream << "gnu_const_index " << unsigned(Operands[0]); + break; + + //------------------------------------------------------------------------- + // Member location. + //------------------------------------------------------------------------- + case LVLocationMemberOffset: + Stream << "offset " << int(Operands[0]); + break; + + //------------------------------------------------------------------------- + // Missing location. + //------------------------------------------------------------------------- + case dwarf::DW_OP_hi_user: + Stream << "missing"; + break; + + //------------------------------------------------------------------------- + // Register values. + //------------------------------------------------------------------------- + default: + PrintRegisterInfo(Opcode); + break; + } + + return String; +} + +// Identify the most common type of operations and print them using a high +// level format, trying to isolate the CodeView complexity. +std::string LVOperation::getOperandsCodeViewInfo() { + std::string String; + raw_string_ostream Stream(String); + + // Get original CodeView operation code. + uint16_t OperationCode = getCodeViewOperationCode(Opcode); + + switch (OperationCode) { + // Operands: [Offset, 0]. + case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL: + Stream << "frame_pointer_rel " << int(Operands[0]); + break; + case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + Stream << "frame_pointer_rel_full_scope " << int(Operands[0]); + break; + + // Operands: [Register, 0]. + case codeview::SymbolKind::S_DEFRANGE_REGISTER: + Stream << "register " << getReader().getRegisterName(Opcode, Operands); + break; + case codeview::SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER: + Stream << "subfield_register " + << getReader().getRegisterName(Opcode, Operands); + break; + + // Operands: [Register, Offset]. + case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL: + Stream << "register_rel " << getReader().getRegisterName(Opcode, Operands) + << " offset " << int(Operands[1]); + break; + + // Operands: [Program, 0]. + case codeview::SymbolKind::S_DEFRANGE: + Stream << "frame " << int(Operands[0]); + break; + case codeview::SymbolKind::S_DEFRANGE_SUBFIELD: + Stream << "subfield " << int(Operands[0]); + break; + + default: + Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << " " + << hexString(Operands[1]) << "#"; + break; + } + + return String; +} + +namespace { +const char *const KindBaseClassOffset = "BaseClassOffset"; +const char *const KindBaseClassStep = "BaseClassStep"; +const char *const KindClassOffset = "ClassOffset"; +const char *const KindFixedAddress = "FixedAddress"; +const char *const KindMissingInfo = "Missing"; +const char *const KindOperation = "Operation"; +const char *const KindOperationList = "OperationList"; +const char *const KindRegister = "Register"; +const char *const KindUndefined = "Undefined"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF location information. +//===----------------------------------------------------------------------===// +const char *LVLocation::kind() const { + const char *Kind = KindUndefined; + if (getIsBaseClassOffset()) + Kind = KindBaseClassOffset; + else if (getIsBaseClassStep()) + Kind = KindBaseClassStep; + else if (getIsClassOffset()) + Kind = KindClassOffset; + else if (getIsFixedAddress()) + Kind = KindFixedAddress; + else if (getIsGapEntry()) + Kind = KindMissingInfo; + else if (getIsOperation()) + Kind = KindOperation; + else if (getIsOperationList()) + Kind = KindOperationList; + else if (getIsRegister()) + Kind = KindRegister; + return Kind; +} + +std::string LVLocation::getIntervalInfo() const { + static const char *const Question = "?"; + std::string String; + raw_string_ostream Stream(String); + if (getIsAddressRange()) + Stream << "{Range}"; + + auto PrintLine = [&](const LVLine *Line) { + if (Line) { + std::string TheLine; + TheLine = Line->lineNumberAsStringStripped(); + Stream << TheLine.c_str(); + } else { + Stream << Question; + } + }; + + Stream << " Lines "; + PrintLine(getLowerLine()); + Stream << ":"; + PrintLine(getUpperLine()); + + if (options().getAttributeOffset()) + // Print the active range (low pc and high pc). + Stream << " [" << hexString(getLowerAddress()) << ":" + << hexString(getUpperAddress()) << "]"; + + return String; +} + +// Validate the ranges associated with the location. +bool LVLocation::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. + + if (!hasAssociatedRange()) + return true; + + LVScopeCompileUnit *Scope = getReader().getCompileUnit(); + LVLine *LowLine = Scope->lineLowerBound(getLowerAddress()); + LVLine *HighLine = Scope->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; +} + +bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor, + float &Percentage) { + if (!options().getAttributeCoverage() && !Locations) + return false; + + // Calculate the coverage depending on the kind of location. We have + // the simple and composed locations. + if (Locations->size() == 1) { + // Simple: fixed address, class offset, stack offset. + LVLocation *Location = Locations->front(); + // Some types of locations do not have specific kind. Now is the time + // to set those types, depending on the operation type. + Location->updateKind(); + if (Location->getIsLocationSimple()) { + Factor = 100; + Percentage = 100; + return true; + } + } + + // Composed locations. + LVAddress LowerAddress = 0; + LVAddress UpperAddress = 0; + for (const LVLocation *Location : *Locations) + // Do not include locations representing a gap. + if (!Location->getIsGapEntry()) { + LowerAddress = Location->getLowerAddress(); + UpperAddress = Location->getUpperAddress(); + Factor += (UpperAddress > LowerAddress) ? UpperAddress - LowerAddress + : LowerAddress - UpperAddress; + } + + Percentage = 0; + return false; +} + +void LVLocation::printRaw(raw_ostream &OS, bool Full) const { + // Print the active range (low pc and high pc). + OS << " [" << hexString(getLowerAddress()) << ":" + << hexString(getUpperAddress()) << "]\n"; + // Print any DWARF operations. + printRawExtra(OS, Full); +} + +void LVLocation::printInterval(raw_ostream &OS, bool Full) const { + if (hasAssociatedRange()) + OS << getIntervalInfo(); +} + +void LVLocation::print(raw_ostream &OS, bool Full) const { + if (getReader().doPrintLocation(this)) { + LVObject::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVLocation::printExtra(raw_ostream &OS, bool Full) const { + printInterval(OS, Full); + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF location for a symbol. +//===----------------------------------------------------------------------===// +// Add a Location Entry. +void LVLocationSymbol::addObject(LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, + uint64_t LocDescOffset) { + setLowerAddress(LowPC); + setUpperAddress(HighPC); + + // Record the offset where the location information begins. + setOffset(LocDescOffset ? LocDescOffset : SectionOffset); + + // A -1 HighPC value, indicates no range. + if (HighPC == LVAddress(UINT64_MAX)) + setIsDiscardedRange(); + + // Update the location kind, using the DWARF attribute. + setKind(); +} + +// Add a Location Record. +void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) { + if (!Entries) + Entries = new LVAutoOperations(); + Entries->emplace_back(new LVOperation(Opcode, Operand1, Operand2)); +} + +// Based on the DWARF attribute, define the location kind. +void LVLocation::setKind() { + switch (getAttr()) { + case dwarf::DW_AT_data_member_location: + setIsClassOffset(); + break; + case dwarf::DW_AT_location: + // Depending on the operand, we have a fixed address. + setIsFixedAddress(); + break; + default: + break; + } + // For those symbols with absolute location information, ignore any + // gaps in their location description; that is the case with absolute + // memory addresses and members located at specific offsets. + if (hasAssociatedRange()) + getParentSymbol()->setFillGaps(); +} + +void LVLocationSymbol::updateKind() { + // Update the location type for simple ones. + if (Entries && Entries->size() == 1) { + LVOperation *Operation = Entries->front(); + if (dwarf::DW_OP_fbreg == Operation->getOpcode()) + setIsStackOffset(); + } +} + +void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const { + if (Entries) + for (const LVOperation *Operation : *Entries) + Operation->print(OS, Full); +} + +// Print location (formatted version). +void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) { + if (!Locations || Locations->empty()) + return; + + // Print the symbol coverage. + if (options().getAttributeCoverage()) { + // The location entries are contained within a symbol. Get a location, + // to access basic information about indentation, parent, etc. + LVLocation *Location = Locations->front(); + LVSymbol *Symbol = Location->getParentSymbol(); + float Percentage = Symbol->getCoveragePercentage(); + + // The coverage is dependent on the kind of location. + std::string String; + raw_string_ostream Stream(String); + Stream << format("%.2f%%", Percentage); + if (!Location->getIsLocationSimple()) + Stream << format(" (%d/%d)", Symbol->getCoverageFactor(), + Symbol->getParentScope()->getCoverageFactor()); + Symbol->printAttributes(OS, Full, "{Coverage} ", Symbol, StringRef(String), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } + + // Print the symbol location, including the missing entries. + if (getReader().doPrintLocation(/*Location=*/nullptr)) + for (const LVLocation *Location : *Locations) + Location->print(OS, Full); +} + +void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const { + OS << "{Location}"; + if (getIsCallSite()) + OS << " -> CallSite"; + printInterval(OS, Full); + OS << "\n"; + + // Print location entries. + if (Full && Entries) { + bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation(); + std::stringstream Stream; + std::string Leading = ""; + for (LVOperation *Operation : *Entries) { + Stream << Leading + << (CodeViewLocation ? Operation->getOperandsCodeViewInfo() + : Operation->getOperandsDWARFInfo()); + Leading = ", "; + } + printAttributes(OS, Full, "{Entry} ", const_cast(this), + StringRef(Stream.str()), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp @@ -0,0 +1,158 @@ +//===-- LVRange.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 +// +//===----------------------------------------------------------------------===// +// +// This implements the LVRange class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Range" + +void LVRange::startSearch() { + RangesTree.clear(); + + LLVM_DEBUG({ dbgs() << "\nRanges Tree entries:\n"; }); + + // Traverse the ranges and store them into the interval tree. + for (LVRangeEntry &RangeEntry : RangeEntries) { + LLVM_DEBUG({ + LVScope *Scope = RangeEntry.scope(); + dbgs() << "Scope: " << format_decimal(Scope->getLevel(), 5) << " " + << "Range: [" << hexValue(RangeEntry.lower()) << ":" + << hexValue(RangeEntry.upper()) << "]\n"; + }); + + RangesTree.insert(RangeEntry.lower(), RangeEntry.upper(), + RangeEntry.scope()); + } + + // Create the interval tree. + RangesTree.create(); + + LLVM_DEBUG({ + dbgs() << "\nRanges Tree:\n"; + RangesTree.print(dbgs()); + }); +} + +// Add the pair in an ascending order, with the smallest ranges at the +// start; in that way, enclosing scopes ranges are at the end of the +// list; we assume that low <= high. +void LVRange::addEntry(LVScope *Scope, LVAddress LowerAddress, + LVAddress UpperAddress) { + // We assume the low <= high. + if (LowerAddress > UpperAddress) + std::swap(LowerAddress, UpperAddress); + + // Record the lowest and highest seen addresses. + if (LowerAddress < Lower) + Lower = LowerAddress; + if (UpperAddress > Upper) + Upper = UpperAddress; + + // Just add the scope and range pair, in no particular order. + RangeEntries.emplace_back(LowerAddress, UpperAddress, Scope); +} + +void LVRange::addEntry(LVScope *Scope) { + assert(Scope && "Scope must not be nullptr"); + // Traverse the ranges and update the ranges set only if the ranges + // values are not already recorded. + if (const LVLocations *Locations = Scope->getRanges()) + for (const LVLocation *Location : *Locations) { + LVAddress LowPC = Location->getLowerAddress(); + LVAddress HighPC = Location->getUpperAddress(); + if (!hasEntry(LowPC, HighPC)) + // Add the pair of addresses. + addEntry(Scope, LowPC, HighPC); + } +} + +// Get the scope associated with the input address. +LVScope *LVRange::getEntry(LVAddress Address) const { + LLVM_DEBUG({ dbgs() << format("Searching: 0x%08x\nFound: ", Address); }); + + LVScope *Target = nullptr; + LVLevel TargetLevel = 0; + LVLevel Level = 0; + LVScope *Scope = nullptr; + for (LVRangesTree::find_iterator Iter = RangesTree.find(Address), + End = RangesTree.find_end(); + Iter != End; ++Iter) { + LLVM_DEBUG( + { dbgs() << format("[0x%08x,0x%08x] ", Iter->left(), Iter->right()); }); + Scope = Iter->value(); + Level = Scope->getLevel(); + if (Level > TargetLevel) { + TargetLevel = Level; + Target = Scope; + } + } + + LLVM_DEBUG({ dbgs() << (Scope ? "\n" : "None\n"); }); + + return Target; +} + +// Find the associated Scope for the given ranges values. +LVScope *LVRange::getEntry(LVAddress LowerAddress, + LVAddress UpperAddress) const { + for (const LVRangeEntry &RangeEntry : RangeEntries) + if (LowerAddress >= RangeEntry.lower() && UpperAddress < RangeEntry.upper()) + return RangeEntry.scope(); + return nullptr; +} + +// True if the range addresses contain the pair [LowerAddress, UpperAddress]. +bool LVRange::hasEntry(LVAddress LowerAddress, LVAddress UpperAddress) const { + for (const LVRangeEntry &RangeEntry : RangeEntries) + if (LowerAddress == RangeEntry.lower() && + UpperAddress == RangeEntry.upper()) + return true; + return false; +} + +// Sort the range elements for the whole Compile Unit. +void LVRange::sort() { + auto CompareRangeEntry = [](const LVRangeEntry &lhs, + const LVRangeEntry &rhs) -> bool { + if (lhs.lower() < rhs.lower()) + return true; + + // If the lower address is the same, use the upper address value in + // order to put first the smallest interval. + if (lhs.lower() == rhs.lower()) + return lhs.upper() < rhs.upper(); + + return false; + }; + + // Sort the ranges using low address and range size. + std::stable_sort(RangeEntries.begin(), RangeEntries.end(), CompareRangeEntry); +} + +void LVRange::print(raw_ostream &OS, bool Full) const { + size_t Indentation = 0; + for (const LVRangeEntry &RangeEntry : RangeEntries) { + LVScope *Scope = RangeEntry.scope(); + Scope->printAttributes(OS, Full); + Indentation = options().indentationSize(); + if (Indentation) + OS << " "; + OS << format("[0x%08x,0x%08x] ", RangeEntry.lower(), RangeEntry.upper()) + << formattedKind(Scope->kind()) << " " << formattedName(Scope->getName()) + << "\n"; + } + printExtra(OS, Full); +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -13,7 +13,6 @@ #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" -#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" @@ -134,6 +133,9 @@ if (Error Err = createScopes()) return Err; + // Calculate symbol coverage and detect invalid debug locations and ranges. + Root->processRangeInformation(); + // As the elements can depend on elements from a different compile unit, // information such as name and file/line source information needs to be // updated. diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -12,7 +12,8 @@ #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" -#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" #include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" #include "llvm/DebugInfo/LogicalView/Core/LVType.h" @@ -48,6 +49,7 @@ delete Symbols; delete Scopes; delete Lines; + delete Ranges; delete Children; } @@ -129,6 +131,21 @@ traverseParents(&LVScope::getHasLines, &LVScope::setHasLines); } +// Add a location. +void LVScope::addObject(LVLocation *Location) { + assert(Location && "Invalid location."); + assert(!Location->getParent() && "Location already inserted"); + if (!Ranges) + Ranges = new LVAutoLocations(); + + // Add it to parent. + Location->setParent(this); + Location->setOffset(getOffset()); + + Ranges->push_back(Location); + setHasRanges(); +} + // Adds the scope to the child scopes and sets the parent in the child. void LVScope::addElement(LVScope *Scope) { assert(Scope && "Invalid scope."); @@ -210,6 +227,17 @@ traverseParents(&LVScope::getHasTypes, &LVScope::setHasTypes); } +// Add a pair of ranges. +void LVScope::addObject(LVAddress LowerAddress, LVAddress UpperAddress) { + // Pack the ranges into a Location object. + LVLocation *Location = new LVLocation(); + Location->setLowerAddress(LowerAddress); + Location->setUpperAddress(UpperAddress); + Location->setIsAddressRange(); + + addObject(Location); +} + bool LVScope::removeElement(LVElement *Element) { auto Predicate = [Element](LVElement *Item) -> bool { return Item == Element; @@ -618,6 +646,7 @@ Traverse(Parent->Types, SortFunction); Traverse(Parent->Symbols, SortFunction); Traverse(Parent->Scopes, SortFunction); + Traverse(Parent->Ranges, compareRange); Traverse(Parent->Children, SortFunction); if (Parent->Scopes) @@ -679,6 +708,83 @@ TraverseChildren(this); } +// Traverse the symbol location ranges and for each range: +// - Apply the 'ValidLocation' validation criteria. +// - Add any failed range to the 'LocationList'. +// - Calculate location coverage. +void LVScope::getLocations(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + // Traverse scopes and symbols. + if (Symbols) + for (LVSymbol *Symbol : *Symbols) + Symbol->getLocations(LocationList, ValidLocation, RecordInvalid); + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getLocations(LocationList, ValidLocation, RecordInvalid); +} + +// Traverse the scope ranges and for each range: +// - Apply the 'ValidLocation' validation criteria. +// - Add any failed range to the 'LocationList'. +// - Calculate location coverage. +void LVScope::getRanges(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + + // Process the ranges for current scope. + if (Ranges) { + for (LVLocation *Location : *Ranges) { + // Add the invalid location object. + if (!(Location->*ValidLocation)() && RecordInvalid) + LocationList.push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); + } + + // Traverse the scopes. + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getRanges(LocationList, ValidLocation, RecordInvalid); +} + +// Get all the ranges associated with scopes. +void LVScope::getRanges(LVRange &RangeList) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + + if (Ranges) + RangeList.addEntry(this); + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getRanges(RangeList); +} + +LVScope *LVScope::outermostParent(LVAddress Address) { + LVScope *Parent = this; + while (Parent) { + const LVLocations *ParentRanges = Parent->getRanges(); + if (ParentRanges) + for (const LVLocation *Location : *ParentRanges) + if (Location->getLowerAddress() <= Address) + return Parent; + Parent = Parent->getParentScope(); + } + return Parent; +} + +void LVScope::printActiveRanges(raw_ostream &OS, bool Full) const { + if (options().getPrintFormatting() && options().getAttributeRange() && + Ranges) { + for (const LVLocation *Location : *Ranges) + Location->print(OS, Full); + } +} + void LVScope::printEncodedArgs(raw_ostream &OS, bool Full) const { if (options().getPrintFormatting() && options().getAttributeEncoded()) printAttributes(OS, Full, "{Encoded} ", const_cast(this), @@ -705,6 +811,10 @@ << formattedNames(getTypeQualifiedName(), typeAsString()); } OS << "\n"; + + // Print any active ranges. + if (Full && getIsBlock()) + printActiveRanges(OS, Full); } //===----------------------------------------------------------------------===// @@ -829,6 +939,24 @@ CUContributionSize = Size; } +void LVScopeCompileUnit::processRangeLocationCoverage( + LVValidLocation ValidLocation) { + + if (options().getAttributeRange()) { + // Traverse the scopes to get scopes that have invalid ranges. + LVLocations Locations; + bool RecordInvalid = false; + getRanges(Locations, ValidLocation, RecordInvalid); + } + + if (options().getAttributeLocation()) { + // Traverse the scopes to get locations that have invalid ranges. + LVLocations Locations; + bool RecordInvalid = false; + getLocations(Locations, ValidLocation, RecordInvalid); + } +} + LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address) const { LVAddressToLine::const_iterator Iter = AddressToLine.lower_bound(Address); return (Iter != AddressToLine.end()) ? Iter->second : nullptr; @@ -1103,9 +1231,10 @@ // Reset file index, to allow its children to print the correct filename. options().resetFilenameIndex(); - // Print any files, directories, public names. + // Print any files, directories, public names and active ranges. if (Full) { printLocalNames(OS, Full); + printActiveRanges(OS, Full); } } @@ -1209,6 +1338,7 @@ if (Full) { if (getIsTemplateResolved()) printEncodedArgs(OS, Full); + printActiveRanges(OS, Full); if (Reference) Reference->printReference(OS, Full, const_cast(this)); } @@ -1268,12 +1398,31 @@ void LVScopeNamespace::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; + // Print any active ranges. if (Full) { + printActiveRanges(OS, Full); + if (LVScope *Reference = getReference()) Reference->printReference(OS, Full, const_cast(this)); } } +//===----------------------------------------------------------------------===// +// An object file (single or multiple CUs). +//===----------------------------------------------------------------------===// +void LVScopeRoot::processRangeInformation() { + if (!options().getAttributeAnyLocation()) + return; + + if (Scopes) + for (LVScope *Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = + static_cast(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->processRangeLocationCoverage(); + } +} + void LVScopeRoot::print(raw_ostream &OS, bool Full) const { OS << "\nLogical View:\n"; LVScope::print(OS, Full); diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp @@ -47,6 +47,20 @@ return LHS->getOffset() < RHS->getOffset(); } +// Callback comparator for Range compare. +LVSortValue llvm::logicalview::compareRange(const LVObject *LHS, + const LVObject *RHS) { + if (LHS->getLowerAddress() < RHS->getLowerAddress()) + return true; + + // If the lower address is the same, use the upper address value in + // order to put first the smallest interval. + if (LHS->getLowerAddress() == RHS->getLowerAddress()) + return LHS->getUpperAddress() < RHS->getUpperAddress(); + + return false; +} + // Callback comparator based on multiple keys (First: Kind). LVSortValue llvm::logicalview::sortByKind(const LVObject *LHS, const LVObject *RHS) { diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" @@ -50,6 +51,161 @@ return Kind; } +// Add a Location Entry. +void LVSymbol::addLocation(dwarf::Attribute Attr, LVAddress LowPC, + LVAddress HighPC, LVUnsigned SectionOffset, + uint64_t LocDescOffset, bool CallSiteLocation) { + if (!Locations) + Locations = new LVAutoLocations(); + + // Create the location entry. + CurrentLocation = new LVLocationSymbol(); + CurrentLocation->setParent(this); + CurrentLocation->setAttr(Attr); + if (CallSiteLocation) + CurrentLocation->setIsCallSite(); + CurrentLocation->addObject(LowPC, HighPC, SectionOffset, LocDescOffset); + Locations->push_back(CurrentLocation); + + // Mark the symbol as having location information. + setHasLocation(); +} + +// Add a Location Record. +void LVSymbol::addLocationOperands(LVSmall Opcode, uint64_t Operand1, + uint64_t Operand2) { + if (CurrentLocation) + CurrentLocation->addObject(Opcode, Operand1, Operand2); +} + +// Add a Location Entry. +void LVSymbol::addLocationConstant(dwarf::Attribute Attr, LVUnsigned Constant, + uint64_t LocDescOffset) { + // Create a Location Entry, with the global information. + addLocation(Attr, + /*LowPC=*/0, /*HighPC=*/-1, + /*SectionOffset=*/0, LocDescOffset); + + // Add records to Location Entry. + addLocationOperands(/*Opcode=*/LVLocationMemberOffset, + /*Operand1=*/Constant, /*Operand2=*/0); +} + +LVLocations::iterator LVSymbol::addLocationGap(LVLocations::iterator Pos, + LVAddress LowPC, + LVAddress HighPC) { + // Create a location entry for the gap. + LVLocation *Gap = new LVLocationSymbol(); + Gap->setParent(this); + Gap->setAttr(dwarf::DW_AT_location); + Gap->addObject(LowPC, HighPC, + /*section_offset=*/0, + /*locdesc_offset=*/0); + + LVLocations::iterator Iter = Locations->insert(Pos, Gap); + + // Add gap to Location Entry. + Gap->addObject(/*op=*/dwarf::DW_OP_hi_user, + /*opd1=*/0, /*opd2=*/0); + + // Mark the entry as a gap. + Gap->setIsGapEntry(); + + return Iter; +} + +void LVSymbol::fillLocationGaps() { + // The symbol has locations records. Fill gaps in the location list. + if (!getHasLocation() || !getFillGaps()) + return; + + // Get the parent range information and add dummy location entries. + const LVLocations *Ranges = getParentScope()->getRanges(); + if (!Ranges) + return; + + for (const LVLocation *Entry : *Ranges) { + LVAddress ParentLowPC = Entry->getLowerAddress(); + LVAddress ParentHighPC = Entry->getUpperAddress(); + + // Traverse the symbol locations and for each location contained in + // the current parent range, insert locations for any existing gap. + LVLocation *Location; + LVAddress LowPC = 0; + LVAddress Marker = ParentLowPC; + for (LVLocations::iterator Iter = Locations->begin(); + Iter != Locations->end(); ++Iter) { + Location = *Iter; + LowPC = Location->getLowerAddress(); + if (LowPC != Marker) { + // We have a gap at [Marker,LowPC - 1]. + Iter = addLocationGap(Iter, Marker, LowPC - 1); + ++Iter; + } + + // Move to the next item in the location list. + Marker = Location->getUpperAddress() + 1; + } + + // Check any gap at the end. + if (Marker < ParentHighPC) + // We have a gap at [Marker,ParentHighPC]. + addLocationGap(Locations->end(), Marker, ParentHighPC); + } +} + +// Get all the locations based on the valid function. +void LVSymbol::getLocations(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + if (!Locations) + return; + + for (LVLocation *Location : *Locations) { + // Add the invalid location object. + if (!(Location->*ValidLocation)() && RecordInvalid) + LocationList.push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); +} + +void LVSymbol::getLocations(LVLocations &LocationList) const { + if (!Locations) + return; + + for (LVLocation *Location : *Locations) + LocationList.push_back(Location); +} + +// Calculate coverage factor. +void LVSymbol::calculateCoverage() { + if (!LVLocation::calculateCoverage(Locations, CoverageFactor, + CoveragePercentage)) { + LVScope *Parent = getParentScope(); + if (Parent->getIsInlinedFunction()) { + // For symbols representing the inlined function parameters and its + // variables, get the outer most parent that contains their location + // lower address. + // The symbol can have a set of non-contiguous locations. We are using + // only the first location entry to get the outermost parent. + // If no scope contains the location, assume its enclosing parent. + LVScope *Scope = + Parent->outermostParent(Locations->front()->getLowerAddress()); + if (Scope) + Parent = Scope; + } + unsigned CoverageParent = Parent->getCoverageFactor(); + // Get a percentage rounded to two decimal digits. This avoids + // implementation-defined rounding inside printing functions. + CoveragePercentage = + CoverageParent + ? rint((double(CoverageFactor) / CoverageParent) * 100.0 * 100.0) / + 100.0 + : 0; + } +} + void LVSymbol::resolveName() { if (getIsResolvedName()) return; @@ -108,6 +264,12 @@ return getName(); } +void LVSymbol::printLocations(raw_ostream &OS, bool Full) const { + if (Locations) + for (const LVLocation *Location : *Locations) + Location->printRaw(OS, Full); +} + void LVSymbol::print(raw_ostream &OS, bool Full) const { if (getIncludeInPrint() && getReader().doPrintSymbol(this)) { getReaderCompileUnit()->incrementPrintedSymbols(); @@ -160,5 +322,8 @@ printLinkageName(OS, Full, const_cast(this)); if (LVSymbol *Reference = getReference()) Reference->printReference(OS, Full, const_cast(this)); + + // Print location information. + LVLocation::print(Locations, OS, Full); } } diff --git a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt --- a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -4,6 +4,7 @@ add_llvm_unittest(DebugInfoLogicalViewTests CommandLineOptionsTest.cpp + LocationRangesTest.cpp LogicalElementsTest.cpp StringPoolTest.cpp ) diff --git a/llvm/unittests/DebugInfo/LogicalView/LocationRangesTest.cpp b/llvm/unittests/DebugInfo/LogicalView/LocationRangesTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/LogicalView/LocationRangesTest.cpp @@ -0,0 +1,587 @@ +//===- llvm/unittest/DebugInfo/LogicalView/LocationRangesTest.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 ReaderTest : public LVReader { +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(LVLocation *Location, LVLine *LowerLine, LVLine *UpperLine, + LVAddress LowerAddress, LVAddress UpperAddress); + void add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine); + +public: + ReaderTest(ScopedPrinter &W) : LVReader("", "", W) { setInstance(this); } + + Error createScopes() { return LVReader::createScopes(); } +}; + +// Helper function to add a logical element to a given scope. +void ReaderTest::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 ReaderTest::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 ReaderTest::set(LVLocation *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 ReaderTest::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); +} + +class ReaderTestLocations : public ReaderTest { + // 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. + LVLocation *LocationOne = nullptr; + LVLocation *LocationTwo = nullptr; + LVLocation *LocationThree = nullptr; + LVLocation *LocationFour = nullptr; + LVLocation *LocationFive = nullptr; + LVLocation *LocationSix = nullptr; + +public: + ReaderTestLocations(ScopedPrinter &W) : ReaderTest(W) {} + + void createElements(); + void addElements(); + void initElements(); +}; + +// Create the logical elements. +void ReaderTestLocations::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 ReaderTestLocations::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); +} + +// Set initial values to logical elements. +void ReaderTestLocations::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", 0x3000, 120, IntegerType); + set(NestedVariable, "NestedVariable", 0x3010, 310, IntegerType); + + // Lines. + set(LineOne, "", 0x5000, 100); + set(LineTwo, "", 0x5200, 200); + set(LineThree, "", 0x5400, 300); + set(LineFour, "", 0x5600, 400); + set(LineFive, "", 0x5800, 500); + set(LineSix, "", 0x6000, 600); + + // Locations. + set(LocationOne, LineOne, LineOne, 0x5000, 0x5100); + EXPECT_STREQ(LocationOne->getIntervalInfo().c_str(), + " Lines 100:100 [0x0000005000:0x0000005100]"); + + set(LocationTwo, LineTwo, LineTwo, 0x5200, 0x5300); + EXPECT_STREQ(LocationTwo->getIntervalInfo().c_str(), + " Lines 200:200 [0x0000005200:0x0000005300]"); + + set(LocationThree, LineThree, LineThree, 0x5400, 0x5500); + EXPECT_STREQ(LocationThree->getIntervalInfo().c_str(), + " Lines 300:300 [0x0000005400:0x0000005500]"); + + set(LocationFour, LineFour, LineFour, 0x5600, 0x5700); + LocationFour->setIsAddressRange(); + EXPECT_STREQ(LocationFour->getIntervalInfo().c_str(), + "{Range} Lines 400:400 [0x0000005600:0x0000005700]"); + + set(LocationFive, LineFive, LineFive, 0x5800, 0x5900); + LocationFive->setIsAddressRange(); + EXPECT_STREQ(LocationFive->getIntervalInfo().c_str(), + "{Range} Lines 500:500 [0x0000005800: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), Function); + EXPECT_EQ(Ranges.getEntry(0x5010, 0x5090), Function); + EXPECT_EQ(Ranges.getEntry(0x5210, 0x5290), Function); + EXPECT_EQ(Ranges.getEntry(0x5810, 0x5890), Function); + 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); + + LVLocation *Location; + LVLocations Locations; + Parameter->getLocations(Locations); + ASSERT_EQ(Locations.size(), 1u); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineOne->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineSix->getAddress()); + + Locations.clear(); + LocalVariable->getLocations(Locations); + ASSERT_EQ(Locations.size(), 2u); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineTwo->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineSix->getAddress()); + Location = Locations[1]; + EXPECT_EQ(Location->getLowerAddress(), LineFour->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineFive->getAddress()); + + Locations.clear(); + NestedVariable->getLocations(Locations); + ASSERT_EQ(Locations.size(), 1u); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineThree->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineFour->getAddress()); +} + +class ReaderTestCoverage : public ReaderTest { + // Types. + LVType *IntegerType = nullptr; + + // Scopes. + LVScopeFunction *Function = nullptr; + LVScopeFunctionInlined *InlinedFunction = nullptr; + + // Symbols. + LVSymbol *Variable = nullptr; + LVSymbol *Parameter = nullptr; + + // Lines. + LVLine *LineOne = nullptr; + LVLine *LineTwo = nullptr; + LVLine *LineThree = nullptr; + LVLine *LineFour = nullptr; + LVLine *LineFive = nullptr; + LVLine *LineSix = nullptr; + + // Locations. + LVLocation *LocationOne = nullptr; + LVLocation *LocationTwo = nullptr; + LVLocation *LocationThree = nullptr; + LVLocation *LocationFour = nullptr; + LVLocation *LocationFive = nullptr; + LVLocation *LocationSix = nullptr; + +public: + ReaderTestCoverage(ScopedPrinter &W) : ReaderTest(W) {} + + void createElements(); + void addElements(); + void initElements(); +}; + +// Create the logical elements. +void ReaderTestCoverage::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. + CompileUnit = create(); + Function = create(); + InlinedFunction = create(); + + // Create the logical symbols. + Variable = 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 ReaderTestCoverage::addElements() { + setCompileUnit(CompileUnit); + + // Root + // CompileUnit + // IntegerType + // Function + // Ranges + // [LineOne, LineOne] + // [LineTwo, LineSix] + // [LineSix, LineSix] + // LineOne + // LineTwo + // InlinedFunction + // Ranges + // [LineFive, LineFive] + // Parameter + // Location + // [LineThree, LineThree] + // Variable + // Location + // [LineFour, LineFive] + // [LineFive, LineSix] + // 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, InlinedFunction); + add(Function, LineOne); + add(Function, LineTwo); + add(Function, LineSix); + + // Add elements to function InlinedFunction. + add(InlinedFunction, Parameter); + add(InlinedFunction, Variable); + add(InlinedFunction, LineThree); + add(InlinedFunction, LineFour); + add(InlinedFunction, LineFive); +} + +// Set initial values to logical elements. +void ReaderTestCoverage::initElements() { + // Types. + set(IntegerType, "int", 0x1000); + + // Scopes. + set(CompileUnit, "foo.cpp", 0x2000); + set(Function, "foo", 0x2500, 100, IntegerType); + set(InlinedFunction, "InlinedFunction", 0x3000, 300); + + // Symbols. + set(Parameter, "Parameter", 0x3100, 310, IntegerType); + set(Variable, "Variable", 0x3200, 320, IntegerType); + + // Lines. + set(LineOne, "", 0x5000, 100); + set(LineTwo, "", 0x5200, 200); + set(LineThree, "", 0x5400, 300); + set(LineFour, "", 0x5600, 400); + set(LineFive, "", 0x5800, 500); + set(LineSix, "", 0x6000, 600); + + // Locations. + set(LocationOne, LineOne, LineOne, 0x5000, 0x5199); + EXPECT_STREQ(LocationOne->getIntervalInfo().c_str(), + " Lines 100:100 [0x0000005000:0x0000005199]"); + + set(LocationTwo, LineTwo, LineSix, 0x5200, 0x6100); + EXPECT_STREQ(LocationTwo->getIntervalInfo().c_str(), + " Lines 200:600 [0x0000005200:0x0000006100]"); + + set(LocationThree, LineThree, LineFive, 0x5400, 0x5800); + EXPECT_STREQ(LocationThree->getIntervalInfo().c_str(), + " Lines 300:500 [0x0000005400:0x0000005800]"); + + set(LocationFour, LineFour, LineFour, 0x5600, 0x5700); + EXPECT_STREQ(LocationFour->getIntervalInfo().c_str(), + " Lines 400:400 [0x0000005600:0x0000005700]"); + + set(LocationFive, LineFive, LineFive, 0x5800, 0x5900); + EXPECT_STREQ(LocationFive->getIntervalInfo().c_str(), + " Lines 500:500 [0x0000005800:0x0000005900]"); + + set(LocationSix, LineSix, LineSix, 0x6000, 0x6100); + EXPECT_STREQ(LocationSix->getIntervalInfo().c_str(), + " Lines 600:600 [0x0000006000:0x0000006100]"); + + // Add ranges to Function. + // Function: LocationOne, LocationTwo, LocationSix + Function->addObject(LocationOne); + Function->addObject(LocationTwo); + Function->addObject(LocationSix); + EXPECT_EQ(Function->rangeCount(), 3u); + + // Add ranges to Inlined. + // Inlined: LocationFive + InlinedFunction->addObject(LocationFive); + EXPECT_EQ(InlinedFunction->rangeCount(), 1u); + + // Add locations to symbols. + // Parameter: [LineThree, LineThree] + // Variable: [LineFour, LineFive], [LineFive, LineSix] + add(Parameter, LineThree, LineThree); + add(Variable, LineFour, LineFive); + add(Variable, LineFive, LineSix); + + CompileUnit->processRangeLocationCoverage(); + + LVLocation *Location; + LVLocations Locations; + Parameter->getLocations(Locations); + ASSERT_EQ(Locations.size(), 1u); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineThree->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineThree->getAddress()); + + Locations.clear(); + Variable->getLocations(Locations); + ASSERT_EQ(Locations.size(), 2u); + Location = Locations[0]; + EXPECT_EQ(Location->getLowerAddress(), LineFour->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineFive->getAddress()); + Location = Locations[1]; + EXPECT_EQ(Location->getLowerAddress(), LineFive->getAddress()); + EXPECT_EQ(Location->getUpperAddress(), LineSix->getAddress()); + + // Test the changes done to 'LVScope::outermostParent' to use the + // ranges allocated int the current scope during the scopes traversal. + // These are the pre-conditions for the symbol: + // - Its parent must be an inlined function. + // - Have more than one location description. + + // Before the changes: Parameter: CoveragePercentage = 100.00 + // After the changes: Parameter: CoveragePercentage = 100.00 + EXPECT_FLOAT_EQ(Parameter->getCoveragePercentage(), 100.00); + + // Before the changes: Variable: CoveragePercentage = 1000.00 + // After the changes: Variable: CoveragePercentage = 56.83 + EXPECT_FLOAT_EQ(Variable->getCoveragePercentage(), 56.83); +} + +TEST(LogicalViewTest, LocationRanges) { + ScopedPrinter W(outs()); + ReaderTestLocations Reader(W); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintAll(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); +} + +TEST(LogicalViewTest, LocationCoverage) { + ScopedPrinter W(outs()); + ReaderTestCoverage Reader(W); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setAttributeRange(); + ReaderOptions.setAttributeLocation(); + ReaderOptions.setPrintAll(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); +} + +} // namespace