100 changes: 100 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===-- LVReaderHandler.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 class implements the Reader handler.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVREADERHANDLER_H
#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVREADERHANDLER_H

#include "llvm/ADT/PointerUnion.h"
#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/MachOUniversal.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/ScopedPrinter.h"
#include <string>
#include <vector>

namespace llvm {
namespace logicalview {

using LVReaders = std::vector<LVReader *>;
using ArgVector = std::vector<std::string>;
using PdbOrObj = PointerUnion<object::ObjectFile *, pdb::PDBFile *>;

// This class performs the following tasks:
// - Creates a logical reader for every binary file in the command line,
// that parses the debug information and creates a high level logical
// view representation containing scopes, symbols, types and lines.
// - Prints and compares the logical views.
//
// The supported binary formats are: ELF, Mach-O and CodeView.
class LVReaderHandler {
ArgVector &Objects;
ScopedPrinter &W;
raw_ostream &OS;
LVReaders TheReaders;

Error createReaders();
void destroyReaders();
Error printReaders();
Error compareReaders();

Error handleArchive(LVReaders &Readers, StringRef Filename,
object::Archive &Arch);
Error handleBuffer(LVReaders &Readers, StringRef Filename,
MemoryBufferRef Buffer, StringRef ExePath = {});
Error handleFile(LVReaders &Readers, StringRef Filename,
StringRef ExePath = {});
Error handleMach(LVReaders &Readers, StringRef Filename,
object::MachOUniversalBinary &Mach);
Error handleObject(LVReaders &Readers, StringRef Filename,
object::Binary &Binary);

Error createReader(StringRef Filename, LVReaders &Readers, PdbOrObj &Input,
StringRef FileFormatName, StringRef ExePath = {});

public:
LVReaderHandler() = delete;
LVReaderHandler(ArgVector &Objects, ScopedPrinter &W,
LVOptions &ReaderOptions)
: Objects(Objects), W(W), OS(W.getOStream()) {
setOptions(&ReaderOptions);
}
LVReaderHandler(const LVReaderHandler &) = delete;
LVReaderHandler &operator=(const LVReaderHandler &) = delete;
~LVReaderHandler() { destroyReaders(); }

Error createReader(StringRef Filename, LVReaders &Readers) {
return handleFile(Readers, Filename);
}
Error process();

Expected<LVReader *> createReader(StringRef Pathname) {
LVReaders Readers;
if (Error Err = createReader(Pathname, Readers))
return std::move(Err);
return Readers[0];
}
void deleteReader(LVReader *Reader) { delete Reader; }

void print(raw_ostream &OS) const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void dump() const { print(dbgs()); }
#endif
};

} // end namespace logicalview
} // namespace llvm

#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVREADERHANDLER_H
180 changes: 180 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
//===-- LVBinaryReader.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 LVBinaryReader class, which is used to describe a
// binary reader.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVBINARYREADER_H
#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVBINARYREADER_H

#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/ObjectFile.h"

namespace llvm {
namespace logicalview {

constexpr bool UpdateHighAddress = false;

// Logical scope, Section address, Section index, IsComdat.
struct LVSymbolTableEntry final {
LVScope *Scope = nullptr;
LVAddress Address = 0;
LVSectionIndex SectionIndex = 0;
bool IsComdat = false;
LVSymbolTableEntry() = default;
LVSymbolTableEntry(LVScope *Scope, LVAddress Address,
LVSectionIndex SectionIndex, bool IsComdat)
: Scope(Scope), Address(Address), SectionIndex(SectionIndex),
IsComdat(IsComdat) {}
};

// Function names extracted from the object symbol table.
class LVSymbolTable final {
using LVSymbolNames = std::map<std::string, LVSymbolTableEntry>;
LVSymbolNames SymbolNames;

public:
LVSymbolTable() = default;

void add(StringRef Name, LVScope *Function, LVSectionIndex SectionIndex = 0);
void add(StringRef Name, LVAddress Address, LVSectionIndex SectionIndex,
bool IsComdat);
LVSectionIndex update(LVScope *Function);

const LVSymbolTableEntry &getEntry(StringRef Name);
LVAddress getAddress(StringRef Name);
LVSectionIndex getIndex(StringRef Name);
bool getIsComdat(StringRef Name);

void print(raw_ostream &OS);
};

class LVBinaryReader : public LVReader {
// Function names extracted from the object symbol table.
LVSymbolTable SymbolTable;

// Instruction lines for a logical scope. These instructions are fetched
// during its merge with the debug lines.
LVDoubleMap<LVSectionIndex, LVScope *, LVLines *> ScopeInstructions;

// Links the scope with its first assembler address line.
LVDoubleMap<LVSectionIndex, LVAddress, LVScope *> AssemblerMappings;

// Mapping from virtual address to section.
// The virtual address refers to the address where the section is loaded.
using LVSectionAddresses = std::map<LVSectionIndex, object::SectionRef>;
LVSectionAddresses SectionAddresses;

void addSectionAddress(const object::SectionRef &Section) {
if (SectionAddresses.find(Section.getAddress()) == SectionAddresses.end())
SectionAddresses.emplace(Section.getAddress(), Section);
}

// Scopes with ranges for current compile unit. It is used to find a line
// giving its exact or closest address. To support comdat functions, all
// addresses for the same section are recorded in the same map.
using LVSectionRanges = std::map<LVSectionIndex, LVRange *>;
LVSectionRanges SectionRanges;

// Image base and virtual address for Executable file.
uint64_t ImageBaseAddress = 0;
uint64_t VirtualAddress = 0;

// Object sections with machine code.
using LVSections = std::map<LVSectionIndex, object::SectionRef>;
LVSections Sections;

protected:
// It contains the LVLineDebug elements representing the logical lines for
// the current compile unit, created by parsing the debug line section.
LVLines CULines;

std::unique_ptr<const MCRegisterInfo> MRI;
std::unique_ptr<const MCAsmInfo> MAI;
std::unique_ptr<const MCSubtargetInfo> STI;
std::unique_ptr<const MCInstrInfo> MII;
std::unique_ptr<const MCDisassembler> MD;
std::unique_ptr<MCContext> MC;
std::unique_ptr<MCInstPrinter> MIP;

// Loads all info for the architecture of the provided object file.
Error loadGenericTargetInfo(StringRef TheTriple, StringRef TheFeatures);

virtual void mapRangeAddress(const object::ObjectFile &Obj) {}
virtual void mapRangeAddress(const object::ObjectFile &Obj,
const object::SectionRef &Section,
bool IsComdat) {}

// Create a mapping from virtual address to section.
void mapVirtualAddress(const object::ObjectFile &Obj);
void mapVirtualAddress(const object::COFFObjectFile &COFFObj);

Expected<std::pair<LVSectionIndex, object::SectionRef>>
getSection(LVScope *Scope, LVAddress Address, LVSectionIndex SectionIndex);

void addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope);
void addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope,
LVAddress LowerAddress, LVAddress UpperAddress);
LVRange *getSectionRanges(LVSectionIndex SectionIndex);

Error createInstructions();
Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex);
Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex,
const LVNameInfo &NameInfo);

void processLines(LVLines *DebugLines, LVSectionIndex SectionIndex);
void processLines(LVLines *DebugLines, LVSectionIndex SectionIndex,
LVScope *Function);

public:
LVBinaryReader() = delete;
LVBinaryReader(StringRef Filename, StringRef FileFormatName, ScopedPrinter &W,
LVBinaryType BinaryType)
: LVReader(Filename, FileFormatName, W, BinaryType) {}
LVBinaryReader(const LVBinaryReader &) = delete;
LVBinaryReader &operator=(const LVBinaryReader &) = delete;
virtual ~LVBinaryReader();

void addToSymbolTable(StringRef Name, LVScope *Function,
LVSectionIndex SectionIndex = 0);
void addToSymbolTable(StringRef Name, LVAddress Address,
LVSectionIndex SectionIndex, bool IsComdat);
LVSectionIndex updateSymbolTable(LVScope *Function);

const LVSymbolTableEntry &getSymbolTableEntry(StringRef Name);
LVAddress getSymbolTableAddress(StringRef Name);
LVSectionIndex getSymbolTableIndex(StringRef Name);
bool getSymbolTableIsComdat(StringRef Name);

LVSectionIndex getSectionIndex(LVScope *Scope) override {
return Scope ? getSymbolTableIndex(Scope->getLinkageName())
: DotTextSectionIndex;
}

void print(raw_ostream &OS) const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void dump() const { print(dbgs()); }
#endif
};

} // end namespace logicalview
} // end namespace llvm

#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVBINARYREADER_H
154 changes: 154 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Readers/LVELFReader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//===-- LVELFReader.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 LVELFReader class, which is used to describe a
// debug information (DWARF) reader.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVELFREADER_H
#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVELFREADER_H

#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h"
#include <unordered_set>

namespace llvm {
namespace logicalview {

class LVElement;
class LVLine;
class LVScopeCompileUnit;
class LVSymbol;
class LVType;

using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec;

class LVELFReader final : public LVBinaryReader {
object::ObjectFile &Obj;

// Indicates if ranges data are available; in the case of split DWARF any
// reference to ranges is valid only if the skeleton DIE has been loaded.
bool RangesDataAvailable = false;
LVAddress CUBaseAddress = 0;
LVAddress CUHighAddress = 0;

// Current elements during the processing of a DIE.
LVElement *CurrentElement = nullptr;
LVScope *CurrentScope = nullptr;
LVSymbol *CurrentSymbol = nullptr;
LVType *CurrentType = nullptr;
LVOffset CurrentOffset = 0;
LVOffset CurrentEndOffset = 0;

// In DWARF v4, the files are 1-indexed.
// In DWARF v5, the files are 0-indexed.
// The ELF reader expects the indexes as 1-indexed.
bool IncrementFileIndex = false;

// Address ranges collected for current DIE.
std::vector<LVAddressRange> CurrentRanges;

// Symbols with locations for current compile unit.
LVSymbols SymbolsWithLocations;

// Global Offsets (Offset, Element).
LVOffsetElementMap GlobalOffsets;

// Low PC and High PC values for DIE being processed.
LVAddress CurrentLowPC = 0;
LVAddress CurrentHighPC = 0;
bool FoundLowPC = false;
bool FoundHighPC = false;

// Cross references (Elements).
using LVElementSet = std::unordered_set<LVElement *>;
using LVElementEntry = std::pair<LVElement *, LVElementSet>;
using LVElementReference = std::unordered_map<LVOffset, LVElementEntry>;
LVElementReference ElementTable;

Error loadTargetInfo(const object::ObjectFile &Obj);

void mapRangeAddress(const object::ObjectFile &Obj) override;

LVElement *createElement(dwarf::Tag Tag);
void traverseDieAndChildren(DWARFDie &DIE, LVScope *Parent,
DWARFDie &SkeletonDie);
// Process the attributes for the given DIE.
LVScope *processOneDie(const DWARFDie &InputDIE, LVScope *Parent,
DWARFDie &SkeletonDie);
void processOneAttribute(const DWARFDie &Die, LVOffset *OffsetPtr,
const AttributeSpec &AttrSpec);
void createLineAndFileRecords(const DWARFDebugLine::LineTable *Lines);
void processLocationGaps();

// Add offset to global map.
void addGlobalOffset(LVOffset Offset) {
if (GlobalOffsets.find(Offset) == GlobalOffsets.end())
// Just associate the DIE offset with a null element, as we do not
// know if the referenced element has been created.
GlobalOffsets.emplace(Offset, nullptr);
}

// Remove offset from global map.
void removeGlobalOffset(LVOffset Offset) {
LVOffsetElementMap::iterator Iter = GlobalOffsets.find(Offset);
if (Iter != GlobalOffsets.end())
GlobalOffsets.erase(Iter);
}

// Get the location information for DW_AT_data_member_location.
void processLocationMember(dwarf::Attribute Attr,
const DWARFFormValue &FormValue,
const DWARFDie &Die, uint64_t OffsetOnEntry);
void processLocationList(dwarf::Attribute Attr,
const DWARFFormValue &FormValue, const DWARFDie &Die,
uint64_t OffsetOnEntry,
bool CallSiteLocation = false);
void updateReference(dwarf::Attribute Attr, const DWARFFormValue &FormValue);

// Get an element given the DIE offset.
LVElement *getElementForOffset(LVOffset offset, LVElement *Element);

protected:
Error createScopes() override;
void sortScopes() override;

public:
LVELFReader() = delete;
LVELFReader(StringRef Filename, StringRef FileFormatName,
object::ObjectFile &Obj, ScopedPrinter &W)
: LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::ELF),
Obj(Obj) {}
LVELFReader(const LVELFReader &) = delete;
LVELFReader &operator=(const LVELFReader &) = delete;
~LVELFReader() = default;

LVAddress getCUBaseAddress() const { return CUBaseAddress; }
void setCUBaseAddress(LVAddress Address) { CUBaseAddress = Address; }
LVAddress getCUHighAddress() const { return CUHighAddress; }
void setCUHighAddress(LVAddress Address) { CUHighAddress = Address; }

const LVSymbols &GetSymbolsWithLocations() const {
return SymbolsWithLocations;
}

std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) override;

void print(raw_ostream &OS) const;

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
void dump() const { print(dbgs()); }
#endif
};

} // end namespace logicalview
} // end namespace llvm

#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_LVELFREADER_H
18 changes: 18 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,24 @@ bool DWARFDebugLine::LineTable::getFileLineInfoForAddress(
return true;
}

bool DWARFDebugLine::LineTable::getDirectoryForEntry(
const FileNameEntry &Entry, std::string &Directory) const {
if (Prologue.getVersion() >= 5) {
if (Entry.DirIdx < Prologue.IncludeDirectories.size()) {
Directory =
dwarf::toString(Prologue.IncludeDirectories[Entry.DirIdx], "");
return true;
}
return false;
}
if (0 < Entry.DirIdx && Entry.DirIdx <= Prologue.IncludeDirectories.size()) {
Directory =
dwarf::toString(Prologue.IncludeDirectories[Entry.DirIdx - 1], "");
return true;
}
return false;
}

// We want to supply the Unit associated with a .debug_line[.dwo] table when
// we dump it, if possible, but still dump the table even if there isn't a Unit.
// Therefore, collect up handles on all the Units that point into the
Expand Down
7 changes: 3 additions & 4 deletions llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,9 @@ static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS,
}
}

static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS,
DIDumpOptions DumpOpts, uint8_t Opcode,
const uint64_t Operands[2],
const MCRegisterInfo *MRI, bool isEH) {
bool DWARFExpression::prettyPrintRegisterOp(
DWARFUnit *U, raw_ostream &OS, DIDumpOptions DumpOpts, uint8_t Opcode,
const uint64_t Operands[2], const MCRegisterInfo *MRI, bool isEH) {
if (!MRI)
return false;

Expand Down
15 changes: 12 additions & 3 deletions llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) {
return Error::success();
}

bool DWARFUnit::parseDWO() {
bool DWARFUnit::parseDWO(StringRef DWOAlternativeLocation) {
if (IsDWO)
return false;
if (DWO.get())
Expand All @@ -611,8 +611,17 @@ bool DWARFUnit::parseDWO() {
if (!DWOId)
return false;
auto DWOContext = Context.getDWOContext(AbsolutePath);
if (!DWOContext)
return false;
if (!DWOContext) {
// Use the alternative location to get the DWARF context for the DWO object.
if (DWOAlternativeLocation.empty())
return false;
// If the alternative context does not correspond to the original DWO object
// (different hashes), the below 'getDWOCompileUnitForHash' call will catch
// the issue, with a returned null context.
DWOContext = Context.getDWOContext(DWOAlternativeLocation);
if (!DWOContext)
return false;
}

DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId);
if (!DWOCU)
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/DebugInfo/LogicalView/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ add_lv_impl_folder(Core
Core/LVType.cpp
)

add_lv_impl_folder(Readers
LVReaderHandler.cpp
Readers/LVBinaryReader.cpp
Readers/LVELFReader.cpp
)

list(APPEND LIBLV_ADDITIONAL_HEADER_DIRS
"${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/LogicalView"
"${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/LogicalView/Core"
"${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/LogicalView/Readers"
)

add_llvm_component_library(LLVMDebugInfoLogicalView
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,15 @@ void LVElement::printLinkageName(raw_ostream &OS, bool Full,
/*UseQuotes=*/true, /*PrintRef=*/false);
}
}

void LVElement::printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent,
LVScope *Scope) const {
if (options().getPrintFormatting() && options().getAttributeLinkage()) {
LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
std::string Text = (Twine(" 0x") + Twine::utohexstr(SectionIndex) +
Twine(" '") + Twine(getLinkageName()) + Twine("'"))
.str();
printAttributes(OS, Full, "{Linkage} ", Parent, Text,
/*UseQuotes=*/false, /*PrintRef=*/false);
}
}
6 changes: 3 additions & 3 deletions llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,9 @@ bool LVLocation::validateRanges() {
if (!hasAssociatedRange())
return true;

LVScopeCompileUnit *Scope = getReader().getCompileUnit();
LVLine *LowLine = Scope->lineLowerBound(getLowerAddress());
LVLine *HighLine = Scope->lineUpperBound(getUpperAddress());
LVLineRange Range = getReaderCompileUnit()->lineRange(this);
LVLine *LowLine = Range.first;
LVLine *HighLine = Range.second;
if (LowLine)
setLowerLine(LowLine);
else {
Expand Down
64 changes: 57 additions & 7 deletions llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,20 +1252,43 @@ void LVScopeCompileUnit::processRangeLocationCoverage(
}
}

LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address) const {
LVAddressToLine::const_iterator Iter = AddressToLine.lower_bound(Address);
return (Iter != AddressToLine.end()) ? Iter->second : nullptr;
void LVScopeCompileUnit::addMapping(LVLine *Line, LVSectionIndex SectionIndex) {
LVAddress Address = Line->getOffset();
SectionMappings.add(SectionIndex, Address, Line);
}

LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address) const {
if (AddressToLine.empty())
LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address,
LVScope *Scope) const {
LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
LVAddressToLine *Map = SectionMappings.findMap(SectionIndex);
if (!Map || Map->empty())
return nullptr;
LVAddressToLine::const_iterator Iter = AddressToLine.upper_bound(Address);
if (Iter != AddressToLine.begin())
LVAddressToLine::const_iterator Iter = Map->lower_bound(Address);
return (Iter != Map->end()) ? Iter->second : nullptr;
}

LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address,
LVScope *Scope) const {
LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
LVAddressToLine *Map = SectionMappings.findMap(SectionIndex);
if (!Map || Map->empty())
return nullptr;
LVAddressToLine::const_iterator Iter = Map->upper_bound(Address);
if (Iter != Map->begin())
Iter = std::prev(Iter);
return Iter->second;
}

LVLineRange LVScopeCompileUnit::lineRange(LVLocation *Location) const {
// The parent of a location can be a symbol or a scope.
LVElement *Element = Location->getParent();
LVScope *Parent = Element->getIsScope() ? static_cast<LVScope *>(Element)
: Element->getParentScope();
LVLine *LowLine = lineLowerBound(Location->getLowerAddress(), Parent);
LVLine *HighLine = lineUpperBound(Location->getUpperAddress(), Parent);
return LVLineRange(LowLine, HighLine);
}

StringRef LVScopeCompileUnit::getFilename(size_t Index) const {
if (Index <= 0 || Index > Filenames.size())
return StringRef();
Expand Down Expand Up @@ -1403,6 +1426,30 @@ void LVScopeCompileUnit::printLocalNames(raw_ostream &OS, bool Full) const {
PrintNames(Option::Directory);
if (options().getAttributeFiles())
PrintNames(Option::File);
if (options().getAttributePublics()) {
StringRef Kind = "Public";
// The public names are indexed by 'LVScope *'. We want to print
// them by logical element address, to show the scopes layout.
using OffsetSorted = std::map<LVAddress, LVPublicNames::const_iterator>;
OffsetSorted SortedNames;
for (LVPublicNames::const_iterator Iter = PublicNames.begin();
Iter != PublicNames.end(); ++Iter)
SortedNames.emplace(Iter->first->getOffset(), Iter);

LVPublicNames::const_iterator Iter;
for (OffsetSorted::reference Entry : SortedNames) {
Iter = Entry.second;
OS << std::string(Indentation, ' ') << formattedKind(Kind) << " "
<< formattedName((*Iter).first->getName());
if (options().getAttributeOffset()) {
LVAddress Address = (*Iter).second.first;
size_t Size = (*Iter).second.second;
OS << " [" << hexString(Address) << ":" << hexString(Address + Size)
<< "]";
}
OS << "\n";
}
}
}

void LVScopeCompileUnit::printWarnings(raw_ostream &OS, bool Full) const {
Expand Down Expand Up @@ -1848,6 +1895,9 @@ void LVScopeFunction::printExtra(raw_ostream &OS, bool Full) const {
if (getIsTemplateResolved())
printEncodedArgs(OS, Full);
printActiveRanges(OS, Full);
if (getLinkageNameIndex())
printLinkageName(OS, Full, const_cast<LVScopeFunction *>(this),
const_cast<LVScopeFunction *>(this));
if (Reference)
Reference->printReference(OS, Full, const_cast<LVScopeFunction *>(this));
}
Expand Down
194 changes: 194 additions & 0 deletions llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
//===-- LVReaderHandler.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 class implements the Reader Handler.
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
#include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h"

using namespace llvm;
using namespace llvm::object;
using namespace llvm::pdb;
using namespace llvm::logicalview;

#define DEBUG_TYPE "ReaderHandler"

Error LVReaderHandler::process() {
if (Error Err = createReaders())
return Err;
if (Error Err = printReaders())
return Err;
if (Error Err = compareReaders())
return Err;

return Error::success();
}

void LVReaderHandler::destroyReaders() {
LLVM_DEBUG(dbgs() << "destroyReaders\n");
for (const LVReader *Reader : TheReaders)
delete Reader;
}

Error LVReaderHandler::createReader(StringRef Filename, LVReaders &Readers,
PdbOrObj &Input, StringRef FileFormatName,
StringRef ExePath) {
auto CreateOneReader = [&]() -> LVReader * {
if (Input.is<ObjectFile *>()) {
ObjectFile &Obj = *Input.get<ObjectFile *>();
if (Obj.isELF() || Obj.isMachO())
return new LVELFReader(Filename, FileFormatName, Obj, W);
}
return nullptr;
};

LVReader *Reader = CreateOneReader();
if (!Reader)
return createStringError(errc::invalid_argument,
"unable to create reader for: '%s'",
Filename.str().c_str());

Readers.push_back(Reader);
return Reader->doLoad();
}

Error LVReaderHandler::handleArchive(LVReaders &Readers, StringRef Filename,
Archive &Arch) {
Error Err = Error::success();
for (const Archive::Child &Child : Arch.children(Err)) {
Expected<MemoryBufferRef> BuffOrErr = Child.getMemoryBufferRef();
if (Error Err = BuffOrErr.takeError())
return createStringError(errorToErrorCode(std::move(Err)), "%s",
Filename.str().c_str());
Expected<StringRef> NameOrErr = Child.getName();
if (Error Err = NameOrErr.takeError())
return createStringError(errorToErrorCode(std::move(Err)), "%s",
Filename.str().c_str());
std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
if (Error Err = handleBuffer(Readers, Name, BuffOrErr.get()))
return createStringError(errorToErrorCode(std::move(Err)), "%s",
Filename.str().c_str());
}

return Error::success();
}

Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename,
MemoryBufferRef Buffer, StringRef ExePath) {
Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buffer);
if (errorToErrorCode(BinOrErr.takeError())) {
return createStringError(errc::not_supported,
"Binary object format in '%s' is not supported.",
Filename.str().c_str());
}
return handleObject(Readers, Filename, *BinOrErr.get());
}

Error LVReaderHandler::handleFile(LVReaders &Readers, StringRef Filename,
StringRef ExePath) {
// Convert any Windows backslashes into forward slashes to get the path.
std::string ConvertedPath =
sys::path::convert_to_slash(Filename, sys::path::Style::windows);
ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
MemoryBuffer::getFileOrSTDIN(ConvertedPath);
if (BuffOrErr.getError()) {
return createStringError(errc::bad_file_descriptor,
"File '%s' does not exist.",
ConvertedPath.c_str());
}
std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
return handleBuffer(Readers, ConvertedPath, *Buffer, ExePath);
}

Error LVReaderHandler::handleMach(LVReaders &Readers, StringRef Filename,
MachOUniversalBinary &Mach) {
for (const MachOUniversalBinary::ObjectForArch &ObjForArch : Mach.objects()) {
std::string ObjName = (Twine(Filename) + Twine("(") +
Twine(ObjForArch.getArchFlagName()) + Twine(")"))
.str();
if (Expected<std::unique_ptr<MachOObjectFile>> MachOOrErr =
ObjForArch.getAsObjectFile()) {
MachOObjectFile &Obj = **MachOOrErr;
PdbOrObj Input = &Obj;
if (Error Err =
createReader(Filename, Readers, Input, Obj.getFileFormatName()))
return Err;
continue;
} else
consumeError(MachOOrErr.takeError());
if (Expected<std::unique_ptr<Archive>> ArchiveOrErr =
ObjForArch.getAsArchive()) {
if (Error Err = handleArchive(Readers, ObjName, *ArchiveOrErr.get()))
return Err;
continue;
} else
consumeError(ArchiveOrErr.takeError());
}
return Error::success();
}

Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename,
Binary &Binary) {
if (PdbOrObj Input = dyn_cast<ObjectFile>(&Binary))
return createReader(Filename, Readers, Input,
Input.get<ObjectFile *>()->getFileFormatName());

if (MachOUniversalBinary *Fat = dyn_cast<MachOUniversalBinary>(&Binary))
return handleMach(Readers, Filename, *Fat);

if (Archive *Arch = dyn_cast<Archive>(&Binary))
return handleArchive(Readers, Filename, *Arch);

return createStringError(errc::not_supported,
"Binary object format in '%s' is not supported.",
Filename.str().c_str());
}

Error LVReaderHandler::createReaders() {
LLVM_DEBUG(dbgs() << "createReaders\n");
for (std::string &Object : Objects) {
LVReaders Readers;
if (Error Err = createReader(Object, Readers))
return Err;
TheReaders.insert(TheReaders.end(), Readers.begin(), Readers.end());
}

return Error::success();
}

Error LVReaderHandler::printReaders() {
LLVM_DEBUG(dbgs() << "printReaders\n");
if (options().getPrintExecute())
for (LVReader *Reader : TheReaders)
if (Error Err = Reader->doPrint())
return Err;

return Error::success();
}

Error LVReaderHandler::compareReaders() {
LLVM_DEBUG(dbgs() << "compareReaders\n");
size_t ReadersCount = TheReaders.size();
if (options().getCompareExecute() && ReadersCount >= 2) {
// If we have more than 2 readers, compare them by pairs.
size_t ViewPairs = ReadersCount / 2;
LVCompare Compare(OS);
for (size_t Pair = 0, Index = 0; Pair < ViewPairs; ++Pair) {
if (Error Err = Compare.execute(TheReaders[Index], TheReaders[Index + 1]))
return Err;
Index += 2;
}
}

return Error::success();
}

void LVReaderHandler::print(raw_ostream &OS) const { OS << "ReaderHandler\n"; }
816 changes: 816 additions & 0 deletions llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp

Large diffs are not rendered by default.

1,235 changes: 1,235 additions & 0 deletions llvm/lib/DebugInfo/LogicalView/Readers/LVELFReader.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
; Test case 1 - General options

; test.cpp
; 1 using INTPTR = const int *;
; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
; 3 if (ParamBool) {
; 4 typedef int INTEGER;
; 5 const INTEGER CONSTANT = 7;
; 6 return CONSTANT;
; 7 }
; 8 return ParamUnsigned;
; 9 }

; Compare mode - Logical view.
; The output shows in view form the 'missing (-), added (+)' elements,
; giving more context by swapping the reference and target object files.

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --compare=types \
; RUN: --report=view \
; RUN: --print=symbols,types \
; RUN: %p/Inputs/test-dwarf-clang.o \
; RUN: %p/Inputs/test-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Reference: 'test-dwarf-clang.o'
; ONE-NEXT: Target: 'test-dwarf-gcc.o'
; ONE-EMPTY:
; ONE-NEXT: Logical View:
; ONE-NEXT: [000] {File} 'test-dwarf-clang.o'
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'test.cpp'
; ONE-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int'
; ONE-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; ONE-NEXT: +[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; ONE-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool'
; ONE-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR'
; ONE-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int'
; ONE-NEXT: -[003] 4 {TypeAlias} 'INTEGER' -> 'int'

; Compare mode - Logical elements.
; The output shows in tabular form the 'missing (-), added (+)' elements,
; giving more context by swapping the reference and target object files.

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --compare=types \
; RUN: --report=list \
; RUN: --print=symbols,types,summary \
; RUN: %p/Inputs/test-dwarf-clang.o \
; RUN: %p/Inputs/test-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Reference: 'test-dwarf-clang.o'
; TWO-NEXT: Target: 'test-dwarf-gcc.o'
; TWO-EMPTY:
; TWO-NEXT: (1) Missing Types:
; TWO-NEXT: -[003] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-EMPTY:
; TWO-NEXT: (1) Added Types:
; TWO-NEXT: +[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-EMPTY:
; TWO-NEXT: ----------------------------------------
; TWO-NEXT: Element Expected Missing Added
; TWO-NEXT: ----------------------------------------
; TWO-NEXT: Scopes 4 0 0
; TWO-NEXT: Symbols 0 0 0
; TWO-NEXT: Types 2 1 1
; TWO-NEXT: Lines 0 0 0
; TWO-NEXT: ----------------------------------------
; TWO-NEXT: Total 6 1 1

; Changing the 'Reference' and 'Target' order:

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --compare=types \
; RUN: --report=list \
; RUN: --print=symbols,types,summary \
; RUN: %p/Inputs/test-dwarf-gcc.o \
; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=THR %s

; THR: Reference: 'test-dwarf-gcc.o'
; THR-NEXT: Target: 'test-dwarf-clang.o'
; THR-EMPTY:
; THR-NEXT: (1) Missing Types:
; THR-NEXT: -[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; THR-EMPTY:
; THR-NEXT: (1) Added Types:
; THR-NEXT: +[003] 4 {TypeAlias} 'INTEGER' -> 'int'
; THR-EMPTY:
; THR-NEXT: ----------------------------------------
; THR-NEXT: Element Expected Missing Added
; THR-NEXT: ----------------------------------------
; THR-NEXT: Scopes 4 0 0
; THR-NEXT: Symbols 0 0 0
; THR-NEXT: Types 2 1 1
; THR-NEXT: Lines 0 0 0
; THR-NEXT: ----------------------------------------
; THR-NEXT: Total 6 1 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
; Test case 1 - General options.

; test.cpp
; 1 using INTPTR = const int *;
; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
; 3 if (ParamBool) {
; 4 typedef int INTEGER;
; 5 const INTEGER CONSTANT = 7;
; 6 return CONSTANT;
; 7 }
; 8 return ParamUnsigned;
; 9 }

; Print basic details.
; The following command prints basic details for all the logical elements
; sorted by the debug information internal offset; it includes its lexical
; level and debug info format.

; RUN: llvm-debuginfo-analyzer --attribute=level,format \
; RUN: --output-sort=offset \
; RUN: --print=scopes,symbols,types,lines,instructions \
; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; RUN: llvm-debuginfo-analyzer --attribute=level,format \
; RUN: --output-sort=offset \
; RUN: --print=elements \
; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'test-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'test.cpp'
; ONE-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int'
; ONE-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR'
; ONE-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int'
; ONE-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; ONE-NEXT: [004] 5 {Line}
; ONE-NEXT: [004] {Code} 'movl $0x7, -0x1c(%rbp)'
; ONE-NEXT: [004] 6 {Line}
; ONE-NEXT: [004] {Code} 'movl $0x7, -0x4(%rbp)'
; ONE-NEXT: [004] {Code} 'jmp 0x6'
; ONE-NEXT: [004] 8 {Line}
; ONE-NEXT: [004] {Code} 'movl -0x14(%rbp), %eax'
; ONE-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int'
; ONE-NEXT: [003] 2 {Line}
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] {Code} 'movb %dl, %al'
; ONE-NEXT: [003] {Code} 'movq %rdi, -0x10(%rbp)'
; ONE-NEXT: [003] {Code} 'movl %esi, -0x14(%rbp)'
; ONE-NEXT: [003] {Code} 'andb $0x1, %al'
; ONE-NEXT: [003] {Code} 'movb %al, -0x15(%rbp)'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] {Code} 'testb $0x1, -0x15(%rbp)'
; ONE-NEXT: [003] {Code} 'je 0x13'
; ONE-NEXT: [003] 8 {Line}
; ONE-NEXT: [003] {Code} 'movl %eax, -0x4(%rbp)'
; ONE-NEXT: [003] 9 {Line}
; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [003] 9 {Line}
; ONE-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int'
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
; Test case 1 - General options

; test.cpp
; 1 using INTPTR = const int *;
; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
; 3 if (ParamBool) {
; 4 typedef int INTEGER;
; 5 const INTEGER CONSTANT = 7;
; 6 return CONSTANT;
; 7 }
; 8 return ParamUnsigned;
; 9 }

; Select logical elements.
; The following prints all 'instructions', 'symbols' and 'types' that
; contain 'inte' or 'movl' in their names or types, using a tab layout
; and given the number of matches.

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --select-nocase --select-regex \
; RUN: --select=INTe --select=movl \
; RUN: --report=list \
; RUN: --print=symbols,types,instructions,summary \
; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'test-dwarf-clang.o'
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'test.cpp'
; ONE-NEXT: [004] {Code} 'movl $0x7, -0x1c(%rbp)'
; ONE-NEXT: [004] {Code} 'movl $0x7, -0x4(%rbp)'
; ONE-NEXT: [003] {Code} 'movl %eax, -0x4(%rbp)'
; ONE-NEXT: [003] {Code} 'movl %esi, -0x14(%rbp)'
; ONE-NEXT: [004] {Code} 'movl -0x14(%rbp), %eax'
; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int'
; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; ONE-EMPTY:
; ONE-NEXT: -----------------------------
; ONE-NEXT: Element Total Printed
; ONE-NEXT: -----------------------------
; ONE-NEXT: Scopes 3 0
; ONE-NEXT: Symbols 4 1
; ONE-NEXT: Types 2 1
; ONE-NEXT: Lines 17 6
; ONE-NEXT: -----------------------------
; ONE-NEXT: Total 26 8

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --select-regex --select-nocase \
; RUN: --select=INTe \
; RUN: --report=list \
; RUN: --print=symbols,types \
; RUN: %p/Inputs/test-dwarf-clang.o \
; RUN: %p/Inputs/test-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Logical View:
; TWO-NEXT: [000] {File} 'test-dwarf-clang.o'
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'test.cpp'
; TWO-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; TWO-EMPTY:
; TWO-NEXT: Logical View:
; TWO-NEXT: [000] {File} 'test-dwarf-gcc.o'
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'test.cpp'
; TWO-NEXT: [004] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
; Test case 2 - Assembler instructions.

; hello-world.cpp
; 1 extern int printf(const char * format, ... );
; 2
; 3 int main()
; 4 {
; 5 printf("Hello, World\n");
; 6 return 0;
; 7 }

; Logical lines.
; The logical views shows the intermixed lines and assembler instructions,
; allowing to compare the code generated by the different toolchains.

; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
; RUN: --print=lines,instructions \
; RUN: %p/Inputs/hello-world-dwarf-clang.o \
; RUN: %p/Inputs/hello-world-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'hello-world-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp'
; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}'
; ONE-NEXT: [002] 3 {Function} extern not_inlined 'main' -> 'int'
; ONE-NEXT: [003] 4 {Line}
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] {Code} 'subq $0x10, %rsp'
; ONE-NEXT: [003] {Code} 'movl $0x0, -0x4(%rbp)'
; ONE-NEXT: [003] 5 {Line}
; ONE-NEXT: [003] {Code} 'leaq (%rip), %rdi'
; ONE-NEXT: [003] {Code} 'movb $0x0, %al'
; ONE-NEXT: [003] {Code} 'callq 0x0'
; ONE-NEXT: [003] 6 {Line}
; ONE-NEXT: [003] {Code} 'xorl %eax, %eax'
; ONE-NEXT: [003] {Code} 'addq $0x10, %rsp'
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [003] 6 {Line}
; ONE-EMPTY:
; ONE-NEXT: Logical View:
; ONE-NEXT: [000] {File} 'hello-world-dwarf-gcc.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp'
; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}'
; ONE-NEXT: [002] 3 {Function} extern not_inlined 'main' -> 'int'
; ONE-NEXT: [003] 4 {Line}
; ONE-NEXT: [003] {Code} 'endbr64'
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] 5 {Line}
; ONE-NEXT: [003] {Code} 'leaq (%rip), %rdi'
; ONE-NEXT: [003] {Code} 'movl $0x0, %eax'
; ONE-NEXT: [003] {Code} 'callq 0x0'
; ONE-NEXT: [003] 6 {Line}
; ONE-NEXT: [003] {Code} 'movl $0x0, %eax'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [003] 7 {Line}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
; Test case 3 - Incorrect lexical scope for typedef.

; pr-44884.cpp
; 1 int bar(float Input) { return (int)Input; }
; 2
; 3 unsigned foo(char Param) {
; 4 typedef int INT; // ** Definition for INT **
; 5 INT Value = Param;
; 6 {
; 7 typedef float FLOAT; // ** Definition for FLOAT **
; 8 {
; 9 FLOAT Added = Value + Param;
; 10 Value = bar(Added);
; 11 }
; 12 }
; 13 return Value + Param;
; 14 }

; The lines 4 and 7 contains 2 typedefs, defined at different lexical
; scopes.

; The above test is used to illustrates a scope issue found in the
; Clang compiler.
; PR44884: https://bugs.llvm.org/show_bug.cgi?id=44884
; PR44229: https://github.com/llvm/llvm-project/issues/44229

; In the following logical views, we can see that the Clang compiler
; emits both typedefs at the same lexical scope (3), which is wrong.
; GCC emit correct lexical scope for both typedefs.

; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
; RUN: --output-sort=kind \
; RUN: --print=symbols,types,lines \
; RUN: %p/Inputs/pr-44884-dwarf-clang.o \
; RUN: %p/Inputs/pr-44884-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp'
; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}'
; ONE-NEXT: [002] 1 {Function} extern not_inlined 'bar' -> 'int'
; ONE-NEXT: [003] 1 {Parameter} 'Input' -> 'float'
; ONE-NEXT: [003] 1 {Line}
; ONE-NEXT: [003] 1 {Line}
; ONE-NEXT: [003] 1 {Line}
; ONE-NEXT: [002] 3 {Function} extern not_inlined 'foo' -> 'unsigned int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 9 {Variable} 'Added' -> 'FLOAT'
; ONE-NEXT: [004] 9 {Line}
; ONE-NEXT: [004] 9 {Line}
; ONE-NEXT: [004] 9 {Line}
; ONE-NEXT: [004] 9 {Line}
; ONE-NEXT: [004] 9 {Line}
; ONE-NEXT: [004] 10 {Line}
; ONE-NEXT: [004] 10 {Line}
; ONE-NEXT: [004] 10 {Line}
; ONE-NEXT: [004] 13 {Line}
; ONE-NEXT: [003] 3 {Parameter} 'Param' -> 'char'
; ONE-NEXT: [003] 7 {TypeAlias} 'FLOAT' -> 'float'
; ONE-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int'
; ONE-NEXT: [003] 5 {Variable} 'Value' -> 'INT'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] 5 {Line}
; ONE-NEXT: [003] 5 {Line}
; ONE-NEXT: [003] 13 {Line}
; ONE-NEXT: [003] 13 {Line}
; ONE-NEXT: [003] 13 {Line}
; ONE-NEXT: [003] 13 {Line}
; ONE-EMPTY:
; ONE-NEXT: Logical View:
; ONE-NEXT: [000] {File} 'pr-44884-dwarf-gcc.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp'
; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}'
; ONE-NEXT: [002] 1 {Function} extern not_inlined 'bar' -> 'int'
; ONE-NEXT: [003] 1 {Parameter} 'Input' -> 'float'
; ONE-NEXT: [003] 1 {Line}
; ONE-NEXT: [003] 1 {Line}
; ONE-NEXT: [003] 1 {Line}
; ONE-NEXT: [002] 3 {Function} extern not_inlined 'foo' -> 'unsigned int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] {Block}
; ONE-NEXT: [005] 9 {Variable} 'Added' -> 'FLOAT'
; ONE-NEXT: [005] 9 {Line}
; ONE-NEXT: [005] 9 {Line}
; ONE-NEXT: [005] 9 {Line}
; ONE-NEXT: [005] 10 {Line}
; ONE-NEXT: [005] 13 {Line}
; ONE-NEXT: [004] 7 {TypeAlias} 'FLOAT' -> 'float'
; ONE-NEXT: [003] 3 {Parameter} 'Param' -> 'char'
; ONE-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int'
; ONE-NEXT: [003] 5 {Variable} 'Value' -> 'INT'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] 5 {Line}
; ONE-NEXT: [003] 13 {Line}
; ONE-NEXT: [003] 14 {Line}
; ONE-NEXT: [003] 14 {Line}

; Using the selection facilities, we can produce a simple tabular
; output showing just the logical types that are 'Typedef'.

; RUN: llvm-debuginfo-analyzer --attribute=level,format \
; RUN: --output-sort=name \
; RUN: --select-types=Typedef \
; RUN: --report=list \
; RUN: --print=types \
; RUN: %p/Inputs/pr-44884-*.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Logical View:
; TWO-NEXT: [000] {File} 'pr-44884-dwarf-clang.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp'
; TWO-NEXT: [003] 7 {TypeAlias} 'FLOAT' -> 'float'
; TWO-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int'
; TWO-EMPTY:
; TWO-NEXT: Logical View:
; TWO-NEXT: [000] {File} 'pr-44884-dwarf-gcc.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp'
; TWO-NEXT: [004] 7 {TypeAlias} 'FLOAT' -> 'float'
; TWO-NEXT: [003] 4 {TypeAlias} 'INT' -> 'int'
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
; Test case 4 - Missing nested enumerations.

; pr-46466.cpp
; 1 struct Struct {
; 2 union Union {
; 3 enum NestedEnum { RED, BLUE };
; 4 };
; 5 Union U;
; 6 };
; 7
; 8 Struct S;
; 9 int test() {
; 10 return S.U.BLUE;
; 11 }

; The above test is used to illustrate a scope issue found in the Clang
; compiler.
; PR46466: https://bugs.llvm.org/show_bug.cgi?id=46466
; PR45811: https://github.com/llvm/llvm-project/issues/45811

; In the following logical views, we can see that the DWARF debug
; information generated by the Clang compiler does not include any
; references to the enumerators 'RED' and 'BLUE'. The DWARF generated
; by GCC, does include such references.

; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
; RUN: --output-sort=name \
; RUN: --print=symbols,types \
; RUN: %p/Inputs/pr-46466-dwarf-clang.o \
; RUN: %p/Inputs/pr-46466-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp'
; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}'
; ONE-NEXT: [002] 8 {Variable} extern 'S' -> 'Struct'
; ONE-NEXT: [002] 1 {Struct} 'Struct'
; ONE-NEXT: [003] 5 {Member} public 'U' -> 'Union'
; ONE-EMPTY:
; ONE-NEXT: Logical View:
; ONE-NEXT: [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp'
; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}'
; ONE-NEXT: [002] 8 {Variable} extern 'S' -> 'Struct'
; ONE-NEXT: [002] 1 {Struct} 'Struct'
; ONE-NEXT: [003] 5 {Member} public 'U' -> 'Union'
; ONE-NEXT: [003] 2 {Union} 'Union'
; ONE-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'unsigned int'
; ONE-NEXT: [005] {Enumerator} 'BLUE' = '0x1'
; ONE-NEXT: [005] {Enumerator} 'RED' = '0x0'

; Using the selection facilities, we can produce a logical view
; showing just the logical types that are 'Enumerator' and its
; parents. The logical view is sorted by the types name.

; RUN: llvm-debuginfo-analyzer --attribute=level,format \
; RUN: --output-sort=name \
; RUN: --select-types=Enumerator \
; RUN: --report=parents \
; RUN: --print=types \
; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Logical View:
; TWO-NEXT: [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp'
; TWO-EMPTY:
; TWO-NEXT: Logical View:
; TWO-NEXT: [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp'
; TWO-NEXT: [002] 1 {Struct} 'Struct'
; TWO-NEXT: [003] 2 {Union} 'Union'
; TWO-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'unsigned int'
; TWO-NEXT: [005] {Enumerator} 'BLUE' = '0x1'
; TWO-NEXT: [005] {Enumerator} 'RED' = '0x0'

; Using the selection facilities, we can produce a simple tabular output
; including a summary for the logical types that are 'Enumerator'. The
; logical view is sorted by the types name.

; RUN: llvm-debuginfo-analyzer --attribute=level,format \
; RUN: --output-sort=name \
; RUN: --select-types=Enumerator \
; RUN: --print=types,summary \
; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=THR %s

; THR: Logical View:
; THR-NEXT: [000] {File} 'pr-46466-dwarf-clang.o' -> elf64-x86-64
; THR-EMPTY:
; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp'
; THR-EMPTY:
; THR-NEXT: -----------------------------
; THR-NEXT: Element Total Printed
; THR-NEXT: -----------------------------
; THR-NEXT: Scopes 4 0
; THR-NEXT: Symbols 0 0
; THR-NEXT: Types 0 0
; THR-NEXT: Lines 0 0
; THR-NEXT: -----------------------------
; THR-NEXT: Total 4 0
; THR-EMPTY:
; THR-NEXT: Logical View:
; THR-NEXT: [000] {File} 'pr-46466-dwarf-gcc.o' -> elf64-x86-64
; THR-EMPTY:
; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp'
; THR-NEXT: [005] {Enumerator} 'BLUE' = '0x1'
; THR-NEXT: [005] {Enumerator} 'RED' = '0x0'
; THR-EMPTY:
; THR-NEXT: -----------------------------
; THR-NEXT: Element Total Printed
; THR-NEXT: -----------------------------
; THR-NEXT: Scopes 5 0
; THR-NEXT: Symbols 0 0
; THR-NEXT: Types 2 2
; THR-NEXT: Lines 0 0
; THR-NEXT: -----------------------------
; THR-NEXT: Total 7 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
; Test case 5 - Incorrect lexical scope variable.

; pr-43860.cpp
; 1 #include "definitions.h"
; 2 forceinline int InlineFunction(int Param) {
; 3 int Var_1 = Param;
; 4 {
; 5 int Var_2 = Param + Var_1;
; 6 Var_1 = Var_2;
; 7 }
; 8 return Var_1;
; 9 }
; 10
; 11 int test(int Param_1, int Param_2) {
; 12 int A = Param_1;
; 13 A += InlineFunction(Param_2);
; 14 return A;
; 15 }

; The above test is used to illustrate a variable issue found in the
; Clang compiler.
; PR43860: https://bugs.llvm.org/show_bug.cgi?id=43860
; PR43205: https://github.com/llvm/llvm-project/issues/43205

; In the following logical views, we can see that the DWARF debug
; information generated by the Clang compiler shows the variables
; 'Var_1' and 'Var_2' are at the same lexical scope (4) in the function
; 'InlineFuction'.
; The DWARF generated by GCC/Clang show those variables at the correct
; lexical scope: '3' and '4' respectively.

; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
; RUN: --output-sort=name \
; RUN: --print=symbols \
; RUN: %p/Inputs/pr-43860-dwarf-clang.o \
; RUN: %p/Inputs/pr-43860-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp'
; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}'
; ONE-NEXT: [002] 2 {Function} extern not_inlined 'InlineFunction' -> 'int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 5 {Variable} 'Var_2' -> 'int'
; ONE-NEXT: [003] 2 {Parameter} 'Param' -> 'int'
; ONE-NEXT: [003] 3 {Variable} 'Var_1' -> 'int'
; ONE-NEXT: [002] 11 {Function} extern not_inlined 'test' -> 'int'
; ONE-NEXT: [003] 12 {Variable} 'A' -> 'int'
; ONE-NEXT: [003] 14 {InlinedFunction} not_inlined 'InlineFunction' -> 'int'
; ONE-NEXT: [004] {Block}
; ONE-NEXT: [005] {Variable} 'Var_2' -> 'int'
; ONE-NEXT: [004] {Parameter} 'Param' -> 'int'
; ONE-NEXT: [004] {Variable} 'Var_1' -> 'int'
; ONE-NEXT: [003] 11 {Parameter} 'Param_1' -> 'int'
; ONE-NEXT: [003] 11 {Parameter} 'Param_2' -> 'int'
; ONE-EMPTY:
; ONE-NEXT: Logical View:
; ONE-NEXT: [000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp'
; ONE-NEXT: [002] {Producer} 'GNU C++14 10.3.0 {{.*}}'
; ONE-NEXT: [002] 2 {Function} extern declared_inlined 'InlineFunction' -> 'int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 5 {Variable} 'Var_2' -> 'int'
; ONE-NEXT: [003] 2 {Parameter} 'Param' -> 'int'
; ONE-NEXT: [003] 3 {Variable} 'Var_1' -> 'int'
; ONE-NEXT: [002] 11 {Function} extern not_inlined 'test' -> 'int'
; ONE-NEXT: [003] 12 {Variable} 'A' -> 'int'
; ONE-NEXT: [003] 13 {InlinedFunction} declared_inlined 'InlineFunction' -> 'int'
; ONE-NEXT: [004] {Block}
; ONE-NEXT: [005] {Variable} 'Var_2' -> 'int'
; ONE-NEXT: [004] {Parameter} 'Param' -> 'int'
; ONE-NEXT: [004] {Variable} 'Var_1' -> 'int'
; ONE-NEXT: [003] 11 {Parameter} 'Param_1' -> 'int'
; ONE-NEXT: [003] 11 {Parameter} 'Param_2' -> 'int'

; Using the selection facilities, we can produce a simple tabular output
; showing just the logical elements that have in their name the 'var'
; pattern. The logical view is sorted by the variables name.

; RUN: llvm-debuginfo-analyzer --attribute=level,format \
; RUN: --output-sort=name \
; RUN: --select-regex --select-nocase \
; RUN: --select=Var \
; RUN: --report=list \
; RUN: --print=symbols \
; RUN: %p/Inputs/pr-43860-*.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Logical View:
; TWO-NEXT: [000] {File} 'pr-43860-dwarf-clang.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp'
; TWO-NEXT: [004] {Variable} 'Var_1' -> 'int'
; TWO-NEXT: [003] 3 {Variable} 'Var_1' -> 'int'
; TWO-NEXT: [005] {Variable} 'Var_2' -> 'int'
; TWO-NEXT: [004] 5 {Variable} 'Var_2' -> 'int'
; TWO-EMPTY:
; TWO-NEXT: Logical View:
; TWO-NEXT: [000] {File} 'pr-43860-dwarf-gcc.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp'
; TWO-NEXT: [004] {Variable} 'Var_1' -> 'int'
; TWO-NEXT: [003] 3 {Variable} 'Var_1' -> 'int'
; TWO-NEXT: [005] {Variable} 'Var_2' -> 'int'
; TWO-NEXT: [004] 5 {Variable} 'Var_2' -> 'int'
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
; Test case 6 - Full logical view

; test.cpp
; 1 using INTPTR = const int *;
; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
; 3 if (ParamBool) {
; 4 typedef int INTEGER;
; 5 const INTEGER CONSTANT = 7;
; 6 return CONSTANT;
; 7 }
; 8 return ParamUnsigned;
; 9 }

; Print low level details.
; The following command prints low level information that includes
; offsets within the debug information section, debug location
; operands, linkage names, etc.

; RUN: llvm-debuginfo-analyzer --attribute=all \
; RUN: --print=all \
; RUN: %p/Inputs/test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [0x0000000000][000] {File} '{{.*}}test-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [0x000000000b][001] {CompileUnit} 'test.cpp'
; ONE-NEXT: [0x000000000b][002] {Producer} 'clang version 15.0.0 {{.*}}'
; ONE-NEXT: {Directory} '/data/projects/tests/input/general'
; ONE-NEXT: {File} 'test.cpp'
; ONE-NEXT: {Public} 'foo' [0x0000000000:0x000000003a]
; ONE-NEXT: [0x000000000b][002] {Range} Lines 2:9 [0x0000000000:0x000000003a]
; ONE-NEXT: [0x00000000bc][002] {BaseType} 'bool'
; ONE-NEXT: [0x0000000099][002] {BaseType} 'int'
; ONE-NEXT: [0x00000000b5][002] {BaseType} 'unsigned int'
; ONE-EMPTY:
; ONE-NEXT: [0x00000000a0][002] {Source} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x00000000a0][002] 1 {TypeAlias} 'INTPTR' -> [0x00000000ab]'* const int'
; ONE-NEXT: [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
; ONE-NEXT: [0x000000002a][003] {Range} Lines 2:9 [0x0000000000:0x000000003a]
; ONE-NEXT: [0x000000002a][003] {Linkage} 0x2 '_Z3fooPKijb'
; ONE-NEXT: [0x0000000071][003] {Block}
; ONE-NEXT: [0x0000000071][004] {Range} Lines 5:8 [0x000000001c:0x000000002f]
; ONE-NEXT: [0x000000007e][004] 5 {Variable} 'CONSTANT' -> [0x00000000c3]'const INTEGER'
; ONE-NEXT: [0x000000007e][005] {Coverage} 100.00%
; ONE-NEXT: [0x000000007f][005] {Location}
; ONE-NEXT: [0x000000007f][006] {Entry} fbreg -28
; ONE-NEXT: [0x000000001c][004] 5 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x000000001c][004] {Code} 'movl $0x7, -0x1c(%rbp)'
; ONE-NEXT: [0x0000000023][004] 6 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x0000000023][004] {Code} 'movl $0x7, -0x4(%rbp)'
; ONE-NEXT: [0x000000002a][004] {Code} 'jmp 0x6'
; ONE-NEXT: [0x000000002f][004] 8 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x000000002f][004] {Code} 'movl -0x14(%rbp), %eax'
; ONE-NEXT: [0x0000000063][003] 2 {Parameter} 'ParamBool' -> [0x00000000bc]'bool'
; ONE-NEXT: [0x0000000063][004] {Coverage} 100.00%
; ONE-NEXT: [0x0000000064][004] {Location}
; ONE-NEXT: [0x0000000064][005] {Entry} fbreg -21
; ONE-NEXT: [0x0000000047][003] 2 {Parameter} 'ParamPtr' -> [0x00000000a0]'INTPTR'
; ONE-NEXT: [0x0000000047][004] {Coverage} 100.00%
; ONE-NEXT: [0x0000000048][004] {Location}
; ONE-NEXT: [0x0000000048][005] {Entry} fbreg -16
; ONE-NEXT: [0x0000000055][003] 2 {Parameter} 'ParamUnsigned' -> [0x00000000b5]'unsigned int'
; ONE-NEXT: [0x0000000055][004] {Coverage} 100.00%
; ONE-NEXT: [0x0000000056][004] {Location}
; ONE-NEXT: [0x0000000056][005] {Entry} fbreg -20
; ONE-NEXT: [0x000000008d][003] 4 {TypeAlias} 'INTEGER' -> [0x0000000099]'int'
; ONE-NEXT: [0x0000000000][003] 2 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x0000000000][003] {Code} 'pushq %rbp'
; ONE-NEXT: [0x0000000001][003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [0x0000000004][003] {Code} 'movb %dl, %al'
; ONE-NEXT: [0x0000000006][003] {Code} 'movq %rdi, -0x10(%rbp)'
; ONE-NEXT: [0x000000000a][003] {Code} 'movl %esi, -0x14(%rbp)'
; ONE-NEXT: [0x000000000d][003] {Code} 'andb $0x1, %al'
; ONE-NEXT: [0x000000000f][003] {Code} 'movb %al, -0x15(%rbp)'
; ONE-NEXT: [0x0000000012][003] 3 {Line} {NewStatement} {PrologueEnd} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x0000000012][003] {Code} 'testb $0x1, -0x15(%rbp)'
; ONE-NEXT: [0x0000000016][003] {Code} 'je 0x13'
; ONE-NEXT: [0x0000000032][003] 8 {Line} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x0000000032][003] {Code} 'movl %eax, -0x4(%rbp)'
; ONE-NEXT: [0x0000000035][003] 9 {Line} {NewStatement} '/data/projects/tests/input/general/test.cpp'
; ONE-NEXT: [0x0000000035][003] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [0x0000000038][003] {Code} 'popq %rbp'
; ONE-NEXT: [0x0000000039][003] {Code} 'retq'
; ONE-NEXT: [0x000000003a][003] 9 {Line} {NewStatement} {EndSequence} '/data/projects/tests/input/general/test.cpp'
; ONE-EMPTY:
; ONE-NEXT: -----------------------------
; ONE-NEXT: Element Total Printed
; ONE-NEXT: -----------------------------
; ONE-NEXT: Scopes 3 3
; ONE-NEXT: Symbols 4 4
; ONE-NEXT: Types 5 5
; ONE-NEXT: Lines 25 25
; ONE-NEXT: -----------------------------
; ONE-NEXT: Total 37 37
; ONE-EMPTY:
; ONE-NEXT: Scope Sizes:
; ONE-NEXT: 189 (100.00%) : [0x000000000b][001] {CompileUnit} 'test.cpp'
; ONE-NEXT: 110 ( 58.20%) : [0x000000002a][002] 2 {Function} extern not_inlined 'foo' -> [0x0000000099]'int'
; ONE-NEXT: 27 ( 14.29%) : [0x0000000071][003] {Block}
; ONE-EMPTY:
; ONE-NEXT: Totals by lexical level:
; ONE-NEXT: [001]: 189 (100.00%)
; ONE-NEXT: [002]: 110 ( 58.20%)
; ONE-NEXT: [003]: 27 ( 14.29%)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
; Ignored attributes with DW_FORM_implicit_const.
; https://github.com/llvm/llvm-project/issues/57040

; Output generated by g++ (Debian 11.3.0-3) 11.3.0

; .debug_abbrev contents:
; [1] DW_TAG_formal_parameter DW_CHILDREN_no
; DW_AT_decl_file DW_FORM_implicit_const 1
; DW_AT_decl_line DW_FORM_implicit_const 2

; [2] DW_TAG_typedef DW_CHILDREN_no
; DW_AT_decl_file DW_FORM_implicit_const 1
; DW_AT_decl_line DW_FORM_data1

; Attributes with DW_FORM_implicit_const being ignored by the ELFReader,
; causing {Parameter} and {TypeAlias} to omit line numbers.

; test.cpp
; 1 using INTPTR = const int *;
; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
; 3 if (ParamBool) {
; 4 typedef int INTEGER;
; 5 const INTEGER CONSTANT = 7;
; 6 return CONSTANT;
; 7 }
; 8 return ParamUnsigned;
; 9 }

; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
; RUN: --print=scopes,symbols,types \
; RUN: %p/Inputs/pr-57040-test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'pr-57040-test-dwarf-clang.o' -> elf64-x86-64
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'test.cpp'
; ONE-NEXT: [002] {Producer} 'clang version 14.0.6'
; ONE-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int'
; ONE-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; ONE-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool'
; ONE-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR'
; ONE-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int'
; ONE-NEXT: [003] 4 {TypeAlias} 'INTEGER' -> 'int'

; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \
; RUN: --print=scopes,symbols,types \
; RUN: %p/Inputs/pr-57040-test-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Logical View:
; TWO-NEXT: [000] {File} 'pr-57040-test-dwarf-gcc.o' -> elf64-x86-64
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'test.cpp'
; TWO-NEXT: [002] {Producer} 'GNU C++17 11.3.0 {{.*}}'
; TWO-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int'
; TWO-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int'
; TWO-NEXT: [003] {Block}
; TWO-NEXT: [004] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; TWO-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool'
; TWO-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR'
; TWO-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int'
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
; Incorrect function matching during comparison.
; https://github.com/llvm/llvm-project/issues/57040

; Output generated by g++ (Debian 11.3.0-3) 11.3.0

; .debug_info contents:
; format = DWARF32, version = 0x0005, unit_type = DW_UT_compile
;
; DW_TAG_compile_unit
; DW_TAG_subprogram ("foo")
; DW_AT_decl_file (1)
;
; DW_TAG_formal_parameter ("ParamPtr")
; DW_AT_decl_file (1)
;
; .debug_line contents:
; Line table prologue:
; format: DWARF32, version: 5
; include_directories[0] = "/usr/local/google/home/aheejin/test/llvm-dva"
; file_names[0]: name: "test.cpp" dir_index: 0
; file_names[1]: name: "test.cpp" dir_index: 0

; The values for DW_AT_decl_file are 1-indexed.

; test.cpp
; 1 using INTPTR = const int *;
; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) {
; 3 if (ParamBool) {
; 4 typedef int INTEGER;
; 5 const INTEGER CONSTANT = 7;
; 6 return CONSTANT;
; 7 }
; 8 return ParamUnsigned;
; 9 }

; RUN: llvm-debuginfo-analyzer --attribute=level,producer \
; RUN: --compare=types \
; RUN: --report=view \
; RUN: --print=symbols,types \
; RUN: %p/Inputs/pr-57040-test-dwarf-clang.o \
; RUN: %p/Inputs/pr-57040-test-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Reference: 'pr-57040-test-dwarf-clang.o'
; ONE-NEXT: Target: 'pr-57040-test-dwarf-gcc.o'
; ONE-EMPTY:
; ONE-NEXT: Logical View:
; ONE-NEXT: [000] {File} 'pr-57040-test-dwarf-clang.o'
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'test.cpp'
; ONE-NEXT: [002] {Producer} 'clang version 14.0.6'
; ONE-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int'
; ONE-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; ONE-NEXT: +[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; ONE-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool'
; ONE-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR'
; ONE-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int'
; ONE-NEXT: -[003] 4 {TypeAlias} 'INTEGER' -> 'int'

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --compare=types \
; RUN: --report=list \
; RUN: --print=symbols,types,summary \
; RUN: %p/Inputs/pr-57040-test-dwarf-clang.o \
; RUN: %p/Inputs/pr-57040-test-dwarf-gcc.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Reference: 'pr-57040-test-dwarf-clang.o'
; TWO-NEXT: Target: 'pr-57040-test-dwarf-gcc.o'
; TWO-EMPTY:
; TWO-NEXT: (1) Missing Types:
; TWO-NEXT: -[003] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-EMPTY:
; TWO-NEXT: (1) Added Types:
; TWO-NEXT: +[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; TWO-EMPTY:
; TWO-NEXT: ----------------------------------------
; TWO-NEXT: Element Expected Missing Added
; TWO-NEXT: ----------------------------------------
; TWO-NEXT: Scopes 4 0 0
; TWO-NEXT: Symbols 0 0 0
; TWO-NEXT: Types 2 1 1
; TWO-NEXT: Lines 0 0 0
; TWO-NEXT: ----------------------------------------
; TWO-NEXT: Total 6 1 1

; Changing the 'Reference' and 'Target' order:

; RUN: llvm-debuginfo-analyzer --attribute=level,producer \
; RUN: --compare=types \
; RUN: --report=view \
; RUN: --print=symbols,types \
; RUN: %p/Inputs/pr-57040-test-dwarf-gcc.o \
; RUN: %p/Inputs/pr-57040-test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=THR %s

; THR: Reference: 'pr-57040-test-dwarf-gcc.o'
; THR-NEXT: Target: 'pr-57040-test-dwarf-clang.o'
; THR-EMPTY:
; THR-NEXT: Logical View:
; THR-NEXT: [000] {File} 'pr-57040-test-dwarf-gcc.o'
; THR-EMPTY:
; THR-NEXT: [001] {CompileUnit} 'test.cpp'
; THR-NEXT: [002] {Producer} 'GNU C++17 11.3.0 {{.*}}'
; THR-NEXT: [002] 1 {TypeAlias} 'INTPTR' -> '* const int'
; THR-NEXT: [002] 2 {Function} extern not_inlined 'foo' -> 'int'
; THR-NEXT: [003] {Block}
; THR-NEXT: -[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; THR-NEXT: [004] 5 {Variable} 'CONSTANT' -> 'const INTEGER'
; THR-NEXT: [003] 2 {Parameter} 'ParamBool' -> 'bool'
; THR-NEXT: [003] 2 {Parameter} 'ParamPtr' -> 'INTPTR'
; THR-NEXT: [003] 2 {Parameter} 'ParamUnsigned' -> 'unsigned int'
; THR-NEXT: +[003] 4 {TypeAlias} 'INTEGER' -> 'int'

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --compare=types \
; RUN: --report=list \
; RUN: --print=symbols,types,summary \
; RUN: %p/Inputs/pr-57040-test-dwarf-gcc.o \
; RUN: %p/Inputs/pr-57040-test-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=FOU %s

; FOU: Reference: 'pr-57040-test-dwarf-gcc.o'
; FOU-NEXT: Target: 'pr-57040-test-dwarf-clang.o'
; FOU-EMPTY:
; FOU-NEXT: (1) Missing Types:
; FOU-NEXT: -[004] 4 {TypeAlias} 'INTEGER' -> 'int'
; FOU-EMPTY:
; FOU-NEXT: (1) Added Types:
; FOU-NEXT: +[003] 4 {TypeAlias} 'INTEGER' -> 'int'
; FOU-EMPTY:
; FOU-NEXT: ----------------------------------------
; FOU-NEXT: Element Expected Missing Added
; FOU-NEXT: ----------------------------------------
; FOU-NEXT: Scopes 4 0 0
; FOU-NEXT: Symbols 0 0 0
; FOU-NEXT: Types 2 1 1
; FOU-NEXT: Lines 0 0 0
; FOU-NEXT: ----------------------------------------
; FOU-NEXT: Total 6 1 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
; * Added incorrect logical instructions for: --print=lines,instructions
; 'bar' and 'foo' showing extra instruction from compiler generated functions:
; '_cxx_global_var_init' and '_GLOBAL_sub_l_suite_lexical_01.cpp'
;
; * Missing logical instructions for: --print=instructions
; Only 'foo' showing logical instructions.

; pr-incorrect-instructions-dwarf-clang.cpp
; 1 int ABCDE = 56; int XYZ = ABCDE * 65;
; 2 int bar(int Param) {
; 3 return Param + 999999 * Param - 66;
; 4 }
; 5
; 6 int foo(int Param) {
; 7 return Param - bar(Param) / Param * 66 + ABCDE;
; 8 }
; 9
; 10 int test(int P1) {
; 11 int Local_1 = P1 - ABCDE;
; 12 {
; 13 int Local_A = 0;
; 14 Local_A = P1 + foo(Local_1);
; 15 ++Local_1;
; 16 }
; 17 return Local_1;
; 18 }
; 19
; 20 int main() {
; 21 return 0;
; 22 }

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --print=lines,instructions \
; RUN: %p/Inputs/pr-incorrect-instructions-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

; ONE: Logical View:
; ONE-NEXT: [000] {File} 'pr-incorrect-instructions-dwarf-clang.o'
; ONE-EMPTY:
; ONE-NEXT: [001] {CompileUnit} 'pr-incorrect-instructions-dwarf-clang.cpp'
; ONE-NEXT: [002] 2 {Function} extern not_inlined 'bar' -> 'int'
; ONE-NEXT: [003] 2 {Line}
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] {Code} 'movl %edi, -0x4(%rbp)'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] {Code} 'imull $0xf423f, -0x4(%rbp), %ecx'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] {Code} 'addl %ecx, %eax'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] {Code} 'subl $0x42, %eax'
; ONE-NEXT: [003] 3 {Line}
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [002] 6 {Function} extern not_inlined 'foo' -> 'int'
; ONE-NEXT: [003] 6 {Line}
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] {Code} 'subq $0x10, %rsp'
; ONE-NEXT: [003] {Code} 'movl %edi, -0x4(%rbp)'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [003] {Code} 'movl %eax, -0x8(%rbp)'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %edi'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'callq 0x0'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'cltd'
; ONE-NEXT: [003] {Code} 'idivl -0x4(%rbp)'
; ONE-NEXT: [003] {Code} 'movl %eax, %ecx'
; ONE-NEXT: [003] {Code} 'movl -0x8(%rbp), %eax'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'imull $0x42, %ecx, %ecx'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'subl %ecx, %eax'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'addl (%rip), %eax'
; ONE-NEXT: [003] 7 {Line}
; ONE-NEXT: [003] {Code} 'addq $0x10, %rsp'
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [003] {Code} 'data16'
; ONE-NEXT: [002] 10 {Function} extern not_inlined 'test' -> 'int'
; ONE-NEXT: [003] {Block}
; ONE-NEXT: [004] 13 {Line}
; ONE-NEXT: [004] {Code} 'movl $0x0, -0xc(%rbp)'
; ONE-NEXT: [004] 14 {Line}
; ONE-NEXT: [004] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [004] {Code} 'movl %eax, -0x10(%rbp)'
; ONE-NEXT: [004] 14 {Line}
; ONE-NEXT: [004] {Code} 'movl -0x8(%rbp), %edi'
; ONE-NEXT: [004] 14 {Line}
; ONE-NEXT: [004] {Code} 'callq 0x0'
; ONE-NEXT: [004] {Code} 'movl %eax, %ecx'
; ONE-NEXT: [004] {Code} 'movl -0x10(%rbp), %eax'
; ONE-NEXT: [004] 14 {Line}
; ONE-NEXT: [004] {Code} 'addl %ecx, %eax'
; ONE-NEXT: [004] 14 {Line}
; ONE-NEXT: [004] {Code} 'movl %eax, -0xc(%rbp)'
; ONE-NEXT: [004] 15 {Line}
; ONE-NEXT: [004] {Code} 'movl -0x8(%rbp), %eax'
; ONE-NEXT: [004] {Code} 'addl $0x1, %eax'
; ONE-NEXT: [004] {Code} 'movl %eax, -0x8(%rbp)'
; ONE-NEXT: [004] 17 {Line}
; ONE-NEXT: [004] {Code} 'movl -0x8(%rbp), %eax'
; ONE-NEXT: [003] 10 {Line}
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] {Code} 'subq $0x10, %rsp'
; ONE-NEXT: [003] {Code} 'movl %edi, -0x4(%rbp)'
; ONE-NEXT: [003] 11 {Line}
; ONE-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; ONE-NEXT: [003] 11 {Line}
; ONE-NEXT: [003] {Code} 'subl (%rip), %eax'
; ONE-NEXT: [003] 11 {Line}
; ONE-NEXT: [003] {Code} 'movl %eax, -0x8(%rbp)'
; ONE-NEXT: [003] 17 {Line}
; ONE-NEXT: [003] {Code} 'addq $0x10, %rsp'
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [002] 20 {Function} extern not_inlined 'main' -> 'int'
; ONE-NEXT: [003] 20 {Line}
; ONE-NEXT: [003] {Code} 'pushq %rbp'
; ONE-NEXT: [003] {Code} 'movq %rsp, %rbp'
; ONE-NEXT: [003] {Code} 'movl $0x0, -0x4(%rbp)'
; ONE-NEXT: [003] 21 {Line}
; ONE-NEXT: [003] {Code} 'xorl %eax, %eax'
; ONE-NEXT: [003] {Code} 'popq %rbp'
; ONE-NEXT: [003] {Code} 'retq'
; ONE-NEXT: [003] 21 {Line}

; RUN: llvm-debuginfo-analyzer --attribute=level \
; RUN: --print=instructions \
; RUN: %p/Inputs/pr-incorrect-instructions-dwarf-clang.o 2>&1 | \
; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s

; TWO: Logical View:
; TWO-NEXT: [000] {File} 'pr-incorrect-instructions-dwarf-clang.o'
; TWO-EMPTY:
; TWO-NEXT: [001] {CompileUnit} 'pr-incorrect-instructions-dwarf-clang.cpp'
; TWO-NEXT: [002] 2 {Function} extern not_inlined 'bar' -> 'int'
; TWO-NEXT: [003] {Code} 'pushq %rbp'
; TWO-NEXT: [003] {Code} 'movq %rsp, %rbp'
; TWO-NEXT: [003] {Code} 'movl %edi, -0x4(%rbp)'
; TWO-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; TWO-NEXT: [003] {Code} 'imull $0xf423f, -0x4(%rbp), %ecx'
; TWO-NEXT: [003] {Code} 'addl %ecx, %eax'
; TWO-NEXT: [003] {Code} 'subl $0x42, %eax'
; TWO-NEXT: [003] {Code} 'popq %rbp'
; TWO-NEXT: [003] {Code} 'retq'
; TWO-NEXT: [002] 6 {Function} extern not_inlined 'foo' -> 'int'
; TWO-NEXT: [003] {Code} 'pushq %rbp'
; TWO-NEXT: [003] {Code} 'movq %rsp, %rbp'
; TWO-NEXT: [003] {Code} 'subq $0x10, %rsp'
; TWO-NEXT: [003] {Code} 'movl %edi, -0x4(%rbp)'
; TWO-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; TWO-NEXT: [003] {Code} 'movl %eax, -0x8(%rbp)'
; TWO-NEXT: [003] {Code} 'movl -0x4(%rbp), %edi'
; TWO-NEXT: [003] {Code} 'callq 0x0'
; TWO-NEXT: [003] {Code} 'cltd'
; TWO-NEXT: [003] {Code} 'idivl -0x4(%rbp)'
; TWO-NEXT: [003] {Code} 'movl %eax, %ecx'
; TWO-NEXT: [003] {Code} 'movl -0x8(%rbp), %eax'
; TWO-NEXT: [003] {Code} 'imull $0x42, %ecx, %ecx'
; TWO-NEXT: [003] {Code} 'subl %ecx, %eax'
; TWO-NEXT: [003] {Code} 'addl (%rip), %eax'
; TWO-NEXT: [003] {Code} 'addq $0x10, %rsp'
; TWO-NEXT: [003] {Code} 'popq %rbp'
; TWO-NEXT: [003] {Code} 'retq'
; TWO-NEXT: [003] {Code} 'data16'
; TWO-NEXT: [002] 10 {Function} extern not_inlined 'test' -> 'int'
; TWO-NEXT: [003] {Block}
; TWO-NEXT: [004] {Code} 'movl $0x0, -0xc(%rbp)'
; TWO-NEXT: [004] {Code} 'movl -0x4(%rbp), %eax'
; TWO-NEXT: [004] {Code} 'movl %eax, -0x10(%rbp)'
; TWO-NEXT: [004] {Code} 'movl -0x8(%rbp), %edi'
; TWO-NEXT: [004] {Code} 'callq 0x0'
; TWO-NEXT: [004] {Code} 'movl %eax, %ecx'
; TWO-NEXT: [004] {Code} 'movl -0x10(%rbp), %eax'
; TWO-NEXT: [004] {Code} 'addl %ecx, %eax'
; TWO-NEXT: [004] {Code} 'movl %eax, -0xc(%rbp)'
; TWO-NEXT: [004] {Code} 'movl -0x8(%rbp), %eax'
; TWO-NEXT: [004] {Code} 'addl $0x1, %eax'
; TWO-NEXT: [004] {Code} 'movl %eax, -0x8(%rbp)'
; TWO-NEXT: [004] {Code} 'movl -0x8(%rbp), %eax'
; TWO-NEXT: [003] {Code} 'pushq %rbp'
; TWO-NEXT: [003] {Code} 'movq %rsp, %rbp'
; TWO-NEXT: [003] {Code} 'subq $0x10, %rsp'
; TWO-NEXT: [003] {Code} 'movl %edi, -0x4(%rbp)'
; TWO-NEXT: [003] {Code} 'movl -0x4(%rbp), %eax'
; TWO-NEXT: [003] {Code} 'subl (%rip), %eax'
; TWO-NEXT: [003] {Code} 'movl %eax, -0x8(%rbp)'
; TWO-NEXT: [003] {Code} 'addq $0x10, %rsp'
; TWO-NEXT: [003] {Code} 'popq %rbp'
; TWO-NEXT: [003] {Code} 'retq'
; TWO-NEXT: [002] 20 {Function} extern not_inlined 'main' -> 'int'
; TWO-NEXT: [003] {Code} 'pushq %rbp'
; TWO-NEXT: [003] {Code} 'movq %rsp, %rbp'
; TWO-NEXT: [003] {Code} 'movl $0x0, -0x4(%rbp)'
; TWO-NEXT: [003] {Code} 'xorl %eax, %eax'
; TWO-NEXT: [003] {Code} 'popq %rbp'
; TWO-NEXT: [003] {Code} 'retq'
21 changes: 0 additions & 21 deletions llvm/tools/llvm-debuginfo-analyzer/LLVMBuild.txt

This file was deleted.

18 changes: 16 additions & 2 deletions llvm/tools/llvm-debuginfo-analyzer/llvm-debuginfo-analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@

#include "Options.h"
#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
#include "llvm/Object/Archive.h"
#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/WithColor.h"
Expand All @@ -39,6 +38,16 @@ static void error(std::error_code EC, char const *Fmt, const Ts &...Vals) {
exit(1);
}

static void error(Error EC) {
if (!EC)
return;
handleAllErrors(std::move(EC), [&](const ErrorInfoBase &EI) {
errs() << "\n";
WithColor::error(errs(), ToolName) << EI.message() << ".\n";
exit(1);
});
}

/// If the input path is a .dSYM bundle (as created by the dsymutil tool),
/// replace it with individual entries for each of the object files inside the
/// bundle otherwise return the input path.
Expand Down Expand Up @@ -113,6 +122,7 @@ int main(int argc, char **argv) {

propagateOptions();
ScopedPrinter W(OutputFile.os());
LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);

// Print the command line.
if (options().getInternalCmdline()) {
Expand All @@ -123,5 +133,9 @@ int main(int argc, char **argv) {
Stream << "\n";
}

// Create readers and perform requested tasks on them.
if (Error Err = ReaderHandler.process())
error(std::move(Err));

return EXIT_SUCCESS;
}
7 changes: 6 additions & 1 deletion llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
set(LLVM_LINK_COMPONENTS
AllTargetsDescs
AllTargetsInfos
AllTargetsDisassemblers
DebugInfoLogicalView
MCDisassembler
)

add_llvm_unittest(DebugInfoLogicalViewTests
add_llvm_unittest_with_input_files(DebugInfoLogicalViewTests
CommandLineOptionsTest.cpp
CompareElementsTest.cpp
ELFReaderTest.cpp
SelectElementsTest.cpp
LocationRangesTest.cpp
LogicalElementsTest.cpp
Expand Down
Loading