blob: 36c6e16a78dfad953e42815c4fdc8bfaa62eab49 [file] [log] [blame]
//===- llvm/unittest/DebugInfo/LogicalView/WarningInternalTest.cpp --------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::logicalview;
namespace {
class MyLocation : public LVLocation {
public:
bool validateRanges();
};
// This code emulates the work done by the Readers when processing the
// binary files and the creation of the AddressToLine mapping is done
// automatically, using the text sections.
class MyAddressToLine {
using LVAddressToLine = std::map<LVAddress, LVLine *>;
LVAddressToLine AddressToLineData;
public:
MyAddressToLine() = default;
void insert(LVLine *Line) {
AddressToLineData.emplace(Line->getOffset(), Line);
}
LVLine *lineLowerBound(LVAddress Address);
LVLine *lineUpperBound(LVAddress Address);
};
LVLine *MyAddressToLine::lineLowerBound(LVAddress Address) {
LVAddressToLine::const_iterator Iter = AddressToLineData.lower_bound(Address);
return (Iter != AddressToLineData.end()) ? Iter->second : nullptr;
}
LVLine *MyAddressToLine::lineUpperBound(LVAddress Address) {
if (AddressToLineData.empty())
return nullptr;
LVAddressToLine::const_iterator Iter = AddressToLineData.upper_bound(Address);
if (Iter != AddressToLineData.begin())
Iter = std::prev(Iter);
return Iter->second;
}
MyAddressToLine AddressToLine;
class ReaderTestWarningInternal : public LVReader {
#define CREATE(VARIABLE, CREATE_FUNCTION) \
VARIABLE = CREATE_FUNCTION(); \
EXPECT_NE(VARIABLE, nullptr);
#define CREATE_CUSTOM(VARIABLE, CREATE_FUNCTION) \
VARIABLE = CREATE_FUNCTION(); \
EXPECT_NE(VARIABLE, nullptr);
// Types.
LVType *IntegerType = nullptr;
// Scopes.
LVScope *NestedScope = nullptr;
LVScopeFunction *Function = nullptr;
// Symbols.
LVSymbol *LocalVariable = nullptr;
LVSymbol *NestedVariable = nullptr;
LVSymbol *Parameter = nullptr;
// Lines.
LVLine *LineOne = nullptr;
LVLine *LineTwo = nullptr;
LVLine *LineThree = nullptr;
LVLine *LineFour = nullptr;
LVLine *LineFive = nullptr;
LVLine *LineSix = nullptr;
// Locations.
MyLocation *LocationOne = nullptr;
MyLocation *LocationTwo = nullptr;
MyLocation *LocationThree = nullptr;
MyLocation *LocationFour = nullptr;
MyLocation *LocationFive = nullptr;
MyLocation *LocationSix = nullptr;
llvm::SpecificBumpPtrAllocator<MyLocation> AllocatedLocations;
protected:
MyLocation *createCustomLocation() {
return new (AllocatedLocations.Allocate()) MyLocation();
}
void add(LVSymbol *Symbol, LVLine *LowerLine, LVLine *UpperLine);
void add(LVScope *Parent, LVElement *Element);
void set(LVElement *Element, StringRef Name, LVOffset Offset,
uint32_t LineNumber = 0, LVElement *Type = nullptr);
void set(MyLocation *Location, LVLine *LowerLine, LVLine *UpperLine,
LVAddress LowerAddress, LVAddress UpperAddress);
public:
ReaderTestWarningInternal(ScopedPrinter &W) : LVReader("", "", W) {
setInstance(this);
}
Error createScopes() { return LVReader::createScopes(); }
void setMapping();
void createElements();
void addElements();
void initElements();
void resolveElements();
void checkWarnings();
};
bool MyLocation::validateRanges() {
// Traverse the locations and validate them against the address to line
// mapping in the current compile unit. Record those invalid ranges.
// A valid range must meet the following conditions:
// a) line(lopc) <= line(hipc)
// b) line(lopc) and line(hipc) are valid.
LVLine *LowLine = AddressToLine.lineLowerBound(getLowerAddress());
LVLine *HighLine = AddressToLine.lineUpperBound(getUpperAddress());
if (LowLine)
setLowerLine(LowLine);
else {
setIsInvalidLower();
return false;
}
if (HighLine)
setUpperLine(HighLine);
else {
setIsInvalidUpper();
return false;
}
// Check for a valid interval.
if (LowLine->getLineNumber() > HighLine->getLineNumber()) {
setIsInvalidRange();
return false;
}
return true;
}
// Map all logical lines with their addresses.
void ReaderTestWarningInternal::setMapping() {
AddressToLine.insert(LineOne);
AddressToLine.insert(LineTwo);
AddressToLine.insert(LineThree);
AddressToLine.insert(LineFour);
AddressToLine.insert(LineFive);
AddressToLine.insert(LineSix);
}
// Helper function to add a logical element to a given scope.
void ReaderTestWarningInternal::add(LVScope *Parent, LVElement *Child) {
Parent->addElement(Child);
EXPECT_EQ(Child->getParent(), Parent);
EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1);
}
// Helper function to set the initial values for a given logical element.
void ReaderTestWarningInternal::set(LVElement *Element, StringRef Name,
LVOffset Offset, uint32_t LineNumber,
LVElement *Type) {
Element->setName(Name);
Element->setOffset(Offset);
Element->setLineNumber(LineNumber);
Element->setType(Type);
EXPECT_EQ(Element->getName(), Name);
EXPECT_EQ(Element->getOffset(), Offset);
EXPECT_EQ(Element->getLineNumber(), LineNumber);
EXPECT_EQ(Element->getType(), Type);
}
// Helper function to set the initial values for a given logical location.
void ReaderTestWarningInternal::set(MyLocation *Location, LVLine *LowerLine,
LVLine *UpperLine, LVAddress LowerAddress,
LVAddress UpperAddress) {
Location->setLowerLine(LowerLine);
Location->setUpperLine(UpperLine);
Location->setLowerAddress(LowerAddress);
Location->setUpperAddress(UpperAddress);
EXPECT_EQ(Location->getLowerLine(), LowerLine);
EXPECT_EQ(Location->getUpperLine(), UpperLine);
EXPECT_EQ(Location->getLowerAddress(), LowerAddress);
EXPECT_EQ(Location->getUpperAddress(), UpperAddress);
}
// Helper function to add a logical location to a logical symbol.
void ReaderTestWarningInternal::add(LVSymbol *Symbol, LVLine *LowerLine,
LVLine *UpperLine) {
dwarf::Attribute Attr = dwarf::DW_AT_location;
Symbol->addLocation(Attr, LowerLine->getAddress(), UpperLine->getAddress(),
/*SectionOffset=*/0, /*LocDesOffset=*/0);
}
// Create the logical elements.
void ReaderTestWarningInternal::createElements() {
// Create scope root.
Error Err = createScopes();
ASSERT_THAT_ERROR(std::move(Err), Succeeded());
Root = getScopesRoot();
EXPECT_NE(Root, nullptr);
// Create the logical types.
CREATE(IntegerType, createType);
// Create the logical scopes.
CREATE(NestedScope, createScope);
CREATE(CompileUnit, createScopeCompileUnit);
CREATE(Function, createScopeFunction);
// Create the logical symbols.
CREATE(LocalVariable, createSymbol);
CREATE(NestedVariable, createSymbol);
CREATE(Parameter, createSymbol);
// Create the logical lines.
CREATE(LineOne, createLine);
CREATE(LineTwo, createLine);
CREATE(LineThree, createLine);
CREATE(LineFour, createLine);
CREATE(LineFive, createLine);
CREATE(LineSix, createLine);
// Create the logical locations.
CREATE_CUSTOM(LocationOne, createCustomLocation);
CREATE_CUSTOM(LocationTwo, createCustomLocation);
CREATE_CUSTOM(LocationThree, createCustomLocation);
CREATE_CUSTOM(LocationFour, createCustomLocation);
CREATE_CUSTOM(LocationFive, createCustomLocation);
CREATE_CUSTOM(LocationSix, createCustomLocation);
}
// Create the logical view adding the created logical elements.
void ReaderTestWarningInternal::addElements() {
setCompileUnit(CompileUnit);
// Root
// CompileUnit
// IntegerType
// Function
// LocationOne
// LocationTwo
// LocationFive
// LocationSix
// Parameter
// LocalVariable
// LineOne
// LineTwo
// NestedScope
// LocationThree
// LocationFour
// NestedVariable
// LineThree
// LineFour
// LineFive
// LineSix
// Add elements to Root.
add(Root, CompileUnit);
// Add elements to CompileUnit.
add(CompileUnit, IntegerType);
add(CompileUnit, Function);
// Add elements to Function.
add(Function, Parameter);
add(Function, LocalVariable);
add(Function, LineOne);
add(Function, LineTwo);
add(Function, LineFive);
add(Function, LineSix);
add(Function, NestedScope);
// Add elements to NestedScope.
add(NestedScope, NestedVariable);
add(NestedScope, LineThree);
add(NestedScope, LineFour);
}
void ReaderTestWarningInternal::resolveElements() {
// Traverse the given scope and its children checking for any warnings.
std::function<void(LVScope * Parent)> 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