Files
VladimirMedic d63ecaa12c [llvm-objdump] Add --substitute-path and --source-dir for --source (#201096)
When the code object was compiled on a different machine that does not
have the same directory structure, or the source code has been moved, we
are seeing a warning with the disassembler, stating that the files
embedded in the code object were not found on disk.
 
This patch introduces a command line options for llvm-objdump, which
provide alternate directory locations to locate the source files on
disk. These options are inspired by GDB commands _set directory_ and
_set substitute-path_.
--substitute-path — Takes two strings, _from_ and _to_, and do a simple
string replacement of from with to at the start of the directory part of
the source file name, then use that result instead of the original file
name to look up the sources. A rule applies only if _from_ ends at a
directory separator.
--source-dir — Add directories to the source search path. Directories
are searched in following order: original recorded path, source-dir +
relative recorded path or absolute path without root, source-dir +
basename.
2026-06-24 17:00:23 +01:00

876 lines
32 KiB
C++

//===-- SourcePrinter.cpp - source interleaving utilities ----------------===//
//
// 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 implements the LiveElementPrinter and SourcePrinter classes to
// keep track of DWARF info as the current address is updated, and print out the
// source file line and variable or inlined function liveness as needed.
//
//===----------------------------------------------------------------------===//
#include "SourcePrinter.h"
#include "llvm-objdump.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
#define DEBUG_TYPE "objdump"
namespace llvm {
namespace objdump {
static bool sourceFileExists(StringRef Path) {
if (sys::fs::exists(Path) && !sys::fs::is_directory(Path))
return true;
return false;
}
static void normalizeSourcePath(SmallVectorImpl<char> &Path) {
sys::path::native(Path);
sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
}
static std::optional<std::string> trySourcePath(StringRef Path) {
SmallString<256> Normalized(Path);
normalizeSourcePath(Normalized);
if (sourceFileExists(Normalized))
return std::string(Normalized);
return std::nullopt;
}
static std::optional<std::string>
searchSourceWithDirs(StringRef FileName, ArrayRef<StringRef> SearchDirs,
bool TryLiteralFirst) {
if (TryLiteralFirst)
if (auto Path = trySourcePath(FileName))
return Path;
StringRef PathSuffix = sys::path::relative_path(FileName);
StringRef BaseName = sys::path::filename(FileName);
for (StringRef Dir : SearchDirs) {
SmallString<256> Candidate(Dir);
sys::path::append(Candidate, PathSuffix);
if (auto Path = trySourcePath(Candidate))
return Path;
}
for (StringRef Dir : SearchDirs) {
SmallString<256> Candidate(Dir);
sys::path::append(Candidate, BaseName);
if (auto Path = trySourcePath(Candidate))
return Path;
}
return std::nullopt;
}
static std::optional<std::string>
findSourceFilePath(StringRef FileName, ArrayRef<StringRef> SearchDirs) {
if (FileName.empty() || FileName == DILineInfo::BadString)
return std::nullopt;
if (sys::path::is_absolute_gnu(FileName))
return searchSourceWithDirs(FileName, SearchDirs, /*TryLiteralFirst=*/true);
return searchSourceWithDirs(FileName, SearchDirs, /*TryLiteralFirst=*/false);
}
static std::string applySubstitutePaths(StringRef FileName) {
if (SubstitutePaths.empty())
return FileName.str();
StringRef BaseName = sys::path::filename(FileName);
SmallString<256> Directory(sys::path::parent_path(FileName));
normalizeSourcePath(Directory);
for (const auto &[From, To] : SubstitutePaths) {
SmallString<256> FromPath(From);
normalizeSourcePath(FromPath);
StringRef Dir = Directory;
if (!Dir.starts_with(FromPath))
continue;
if (Dir.size() > FromPath.size() &&
!sys::path::is_separator(Dir[FromPath.size()]))
continue;
SmallString<256> NewDir(To);
StringRef Suffix = Dir.substr(FromPath.size());
while (!Suffix.empty() && sys::path::is_separator(Suffix.front()))
Suffix = Suffix.drop_front();
if (!Suffix.empty())
sys::path::append(NewDir, Suffix);
normalizeSourcePath(NewDir);
if (NewDir.empty())
return BaseName.str();
SmallString<256> Result(NewDir);
sys::path::append(Result, BaseName);
normalizeSourcePath(Result);
return std::string(Result);
}
return FileName.str();
}
bool InlinedFunction::liveAtAddress(object::SectionedAddress Addr) const {
if (!Range.valid())
return false;
return Range.LowPC <= Addr.Address && Range.HighPC > Addr.Address;
}
void InlinedFunction::print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
const char *MangledCallerName = FuncDie.getName(DINameKind::LinkageName);
if (!MangledCallerName)
return;
if (Demangle)
OS << "inlined into " << demangle(MangledCallerName);
else
OS << "inlined into " << MangledCallerName;
}
void InlinedFunction::dump(raw_ostream &OS) const {
OS << Name << " @ " << Range << ": ";
}
void InlinedFunction::printElementLine(raw_ostream &OS,
object::SectionedAddress Addr,
bool IsEnd) const {
uint32_t CallFile, CallLine, CallColumn, CallDiscriminator;
InlinedFuncDie.getCallerFrame(CallFile, CallLine, CallColumn,
CallDiscriminator);
const DWARFDebugLine::LineTable *LineTable =
Unit->getContext().getLineTableForUnit(Unit);
std::string FileName;
if (!LineTable->hasFileAtIndex(CallFile))
return;
if (!LineTable->getFileNameByIndex(
CallFile, Unit->getCompilationDir(),
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
return;
if (FileName.empty())
return;
const char *MangledCallerName = FuncDie.getName(DINameKind::LinkageName);
if (!MangledCallerName)
return;
std::string CallerName = MangledCallerName;
std::string CalleeName = Name;
if (Demangle) {
CallerName = demangle(MangledCallerName);
CalleeName = demangle(Name);
}
OS << "; " << FileName << ":" << CallLine << ":" << CallColumn << ": ";
if (IsEnd)
OS << "end of ";
OS << CalleeName << " inlined into " << CallerName << "\n";
}
bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) const {
if (LocExpr.Range == std::nullopt)
return false;
return LocExpr.Range->SectionIndex == Addr.SectionIndex &&
LocExpr.Range->LowPC <= Addr.Address &&
LocExpr.Range->HighPC > Addr.Address;
}
void LiveVariable::print(raw_ostream &OS, const MCRegisterInfo &MRI) const {
DataExtractor Data(LocExpr.Expr, Unit->getContext().isLittleEndian());
DWARFExpression Expression(Data, Unit->getAddressByteSize());
auto GetRegName = [&MRI, &OS](uint64_t DwarfRegNum, bool IsEH) -> StringRef {
if (std::optional<MCRegister> LLVMRegNum =
MRI.getLLVMRegNum(DwarfRegNum, IsEH))
if (const char *RegName = MRI.getName(*LLVMRegNum))
return StringRef(RegName);
OS << "<unknown register " << DwarfRegNum << ">";
return {};
};
printDwarfExpressionCompact(&Expression, OS, GetRegName);
}
void LiveVariable::dump(raw_ostream &OS) const {
OS << Name << " @ " << LocExpr.Range << ": ";
}
void LiveElementPrinter::addInlinedFunction(DWARFDie FuncDie,
DWARFDie InlinedFuncDie) {
uint64_t FuncLowPC, FuncHighPC, SectionIndex;
if (!InlinedFuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex))
return;
DWARFUnit *U = InlinedFuncDie.getDwarfUnit();
const char *InlinedFuncName = InlinedFuncDie.getName(DINameKind::LinkageName);
DWARFAddressRange Range{FuncLowPC, FuncHighPC, SectionIndex};
// Add the new element to the main vector.
LiveElements.emplace_back(std::make_unique<InlinedFunction>(
InlinedFuncName, U, FuncDie, InlinedFuncDie, Range));
LiveElement *LE = LiveElements.back().get();
// Map the element's low address (LowPC) to its pointer for fast range start
// lookup.
LiveElementsByAddress[FuncLowPC].push_back(LE);
// Map the element's high address (HighPC) to its pointer for fast range end
// lookup.
LiveElementsByEndAddress[FuncHighPC].push_back(LE);
// Map the pointer to its DWARF discovery index for deterministic
// ordering.
ElementPtrToIndex[LE] = LiveElements.size() - 1;
}
/// Registers the most recently added LiveVariable into all data structures.
void LiveElementPrinter::registerNewVariable() {
assert(
!LiveElements.empty() &&
"registerNewVariable called before element was added to LiveElements.");
LiveVariable *CurrentVar =
static_cast<LiveVariable *>(LiveElements.back().get());
assert(ElementPtrToIndex.count(CurrentVar) == 0 &&
"Element already registered!");
// Map from a LiveElement pointer to its index in the LiveElements.
ElementPtrToIndex[CurrentVar] = LiveElements.size() - 1;
if (const std::optional<DWARFAddressRange> &Range =
CurrentVar->getLocExpr().Range) {
// Add the variable to address-based maps.
LiveElementsByAddress[Range->LowPC].push_back(CurrentVar);
LiveElementsByEndAddress[Range->HighPC].push_back(CurrentVar);
}
}
void LiveElementPrinter::addVariable(DWARFDie FuncDie, DWARFDie VarDie) {
uint64_t FuncLowPC, FuncHighPC, SectionIndex;
FuncDie.getLowAndHighPC(FuncLowPC, FuncHighPC, SectionIndex);
const char *VarName = VarDie.getName(DINameKind::ShortName);
DWARFUnit *U = VarDie.getDwarfUnit();
Expected<DWARFLocationExpressionsVector> Locs =
VarDie.getLocations(dwarf::DW_AT_location);
if (!Locs) {
// If the variable doesn't have any locations, just ignore it. We don't
// report an error or warning here as that could be noisy on optimised
// code.
consumeError(Locs.takeError());
return;
}
for (const DWARFLocationExpression &LocExpr : *Locs) {
if (LocExpr.Range) {
LiveElements.emplace_back(
std::make_unique<LiveVariable>(LocExpr, VarName, U, FuncDie));
} else {
// If the LocExpr does not have an associated range, it is valid for
// the whole of the function.
// TODO: technically it is not valid for any range covered by another
// LocExpr, does that happen in reality?
DWARFLocationExpression WholeFuncExpr{
DWARFAddressRange(FuncLowPC, FuncHighPC, SectionIndex), LocExpr.Expr};
LiveElements.emplace_back(
std::make_unique<LiveVariable>(WholeFuncExpr, VarName, U, FuncDie));
}
// Register the new variable with all data structures.
registerNewVariable();
}
}
void LiveElementPrinter::addFunction(DWARFDie D) {
for (const DWARFDie &Child : D.children()) {
if (DbgVariables != DFDisabled &&
(Child.getTag() == dwarf::DW_TAG_variable ||
Child.getTag() == dwarf::DW_TAG_formal_parameter)) {
addVariable(D, Child);
} else if (DbgInlinedFunctions != DFDisabled &&
Child.getTag() == dwarf::DW_TAG_inlined_subroutine) {
addInlinedFunction(D, Child);
addFunction(Child);
} else
addFunction(Child);
}
}
// Get the column number (in characters) at which the first live element
// line should be printed.
unsigned LiveElementPrinter::getIndentLevel() const {
return DbgIndent + getInstStartColumn(STI);
}
// Indent to the first live-range column to the right of the currently
// printed line, and return the index of that column.
// TODO: formatted_raw_ostream uses "column" to mean a number of characters
// since the last \n, and we use it to mean the number of slots in which we
// put live element lines. Pick a less overloaded word.
unsigned LiveElementPrinter::moveToFirstVarColumn(formatted_raw_ostream &OS) {
// Logical column number: column zero is the first column we print in, each
// logical column is 2 physical columns wide.
unsigned FirstUnprintedLogicalColumn =
std::max((int)(OS.getColumn() - getIndentLevel() + 1) / 2, 0);
// Physical column number: the actual column number in characters, with
// zero being the left-most side of the screen.
unsigned FirstUnprintedPhysicalColumn =
getIndentLevel() + FirstUnprintedLogicalColumn * 2;
if (FirstUnprintedPhysicalColumn > OS.getColumn())
OS.PadToColumn(FirstUnprintedPhysicalColumn);
return FirstUnprintedLogicalColumn;
}
unsigned LiveElementPrinter::getOrCreateColumn(unsigned ElementIdx) {
// Check if the element already has an assigned column.
auto it = ElementToColumn.find(ElementIdx);
if (it != ElementToColumn.end())
return it->second;
unsigned ColIdx;
if (!FreeCols.empty()) {
// Get the smallest available index from the set.
ColIdx = *FreeCols.begin();
// Remove the index from the set.
FreeCols.erase(FreeCols.begin());
} else {
// No free columns, so create a new one.
ColIdx = ActiveCols.size();
ActiveCols.emplace_back();
}
// Assign the element to the column and update the map.
ElementToColumn[ElementIdx] = ColIdx;
ActiveCols[ColIdx].ElementIdx = ElementIdx;
return ColIdx;
}
void LiveElementPrinter::freeColumn(unsigned ColIdx) {
unsigned ElementIdx = ActiveCols[ColIdx].ElementIdx;
// Clear the column's data.
ActiveCols[ColIdx].clear();
// Remove the element's entry from the map and add the column to the free
// list.
ElementToColumn.erase(ElementIdx);
FreeCols.insert(ColIdx);
}
std::vector<unsigned>
LiveElementPrinter::getSortedActiveElementIndices() const {
// Get all element indices that currently have an assigned column.
std::vector<unsigned> Indices;
for (const auto &Pair : ElementToColumn)
Indices.push_back(Pair.first);
// Sort by the DWARF discovery order.
llvm::stable_sort(Indices);
return Indices;
}
void LiveElementPrinter::dump() const {
for (const std::unique_ptr<LiveElement> &LE : LiveElements) {
LE->dump(dbgs());
LE->print(dbgs(), MRI);
dbgs() << "\n";
}
}
void LiveElementPrinter::addCompileUnit(DWARFDie D) {
if (D.getTag() == dwarf::DW_TAG_subprogram)
addFunction(D);
else
for (const DWARFDie &Child : D.children())
addFunction(Child);
}
/// Update to match the state of the instruction between ThisAddr and
/// NextAddr. In the common case, any live range active at ThisAddr is
/// live-in to the instruction, and any live range active at NextAddr is
/// live-out of the instruction. If IncludeDefinedVars is false, then live
/// ranges starting at NextAddr will be ignored.
void LiveElementPrinter::update(object::SectionedAddress ThisAddr,
object::SectionedAddress NextAddr,
bool IncludeDefinedVars) {
// Exit early if only printing function limits.
if (DbgInlinedFunctions == DFLimitsOnly)
return;
// Free columns identified in the previous cycle.
for (unsigned ColIdx : ColumnsToFreeNextCycle)
freeColumn(ColIdx);
ColumnsToFreeNextCycle.clear();
// Update status of active columns and collect those to free next cycle.
for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) {
if (!ActiveCols[ColIdx].isActive())
continue;
const std::unique_ptr<LiveElement> &LE =
LiveElements[ActiveCols[ColIdx].ElementIdx];
ActiveCols[ColIdx].LiveIn = LE->liveAtAddress(ThisAddr);
ActiveCols[ColIdx].LiveOut = LE->liveAtAddress(NextAddr);
LLVM_DEBUG({
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
dbgs() << "pass 1, " << ThisAddr.Address << "-" << NextAddr.Address
<< ", " << Name << ", Col " << ColIdx
<< ": LiveIn=" << ActiveCols[ColIdx].LiveIn
<< ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n";
});
// If element is fully dead, deactivate column immediately.
if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) {
ActiveCols[ColIdx].ElementIdx = Column::NullElementIdx;
continue;
}
// Mark for cleanup in the next cycle if range ends here.
if (ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
ColumnsToFreeNextCycle.push_back(ColIdx);
}
// Next, look for variables which don't already have a column, but which
// are now live (those starting at ThisAddr or NextAddr).
if (IncludeDefinedVars) {
// Collect all elements starting at ThisAddr and NextAddr.
std::vector<std::pair<unsigned, LiveElement *>> NewLiveElements;
auto CollectNewElements = [&](const auto &It) {
if (It == LiveElementsByAddress.end())
return;
const std::vector<LiveElement *> &ElementList = It->second;
for (LiveElement *LE : ElementList) {
auto IndexIt = ElementPtrToIndex.find(LE);
assert(IndexIt != ElementPtrToIndex.end() &&
"LiveElement in address map but missing from index map!");
// Get the element index for sorting and column management.
unsigned ElementIdx = IndexIt->second;
// Skip elements that already have a column.
if (ElementToColumn.count(ElementIdx))
continue;
bool LiveIn = LE->liveAtAddress(ThisAddr);
bool LiveOut = LE->liveAtAddress(NextAddr);
if (!LiveIn && !LiveOut)
continue;
NewLiveElements.emplace_back(ElementIdx, LE);
}
};
// Collect elements starting at ThisAddr.
CollectNewElements(LiveElementsByAddress.find(ThisAddr.Address));
// Collect elements starting at NextAddr (the address immediately
// following the instruction).
CollectNewElements(LiveElementsByAddress.find(NextAddr.Address));
// Sort elements by DWARF discovery order for deterministic column
// assignment.
llvm::stable_sort(NewLiveElements, [](const auto &A, const auto &B) {
return A.first < B.first;
});
// Assign columns in deterministic order.
for (const auto &ElementPair : NewLiveElements) {
unsigned ElementIdx = ElementPair.first;
// Skip if element was already added from the first range.
if (ElementToColumn.count(ElementIdx))
continue;
LiveElement *LE = ElementPair.second;
bool LiveIn = LE->liveAtAddress(ThisAddr);
bool LiveOut = LE->liveAtAddress(NextAddr);
// Assign or create a column.
unsigned ColIdx = getOrCreateColumn(ElementIdx);
LLVM_DEBUG({
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
dbgs() << "pass 2, " << ThisAddr.Address << "-" << NextAddr.Address
<< ", " << Name << ", Col " << ColIdx << ": LiveIn=" << LiveIn
<< ", LiveOut=" << LiveOut << "\n";
});
ActiveCols[ColIdx].LiveIn = LiveIn;
ActiveCols[ColIdx].LiveOut = LiveOut;
ActiveCols[ColIdx].MustDrawLabel = true;
// Mark for cleanup next cycle if range ends here.
if (ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut)
ColumnsToFreeNextCycle.push_back(ColIdx);
}
}
}
enum class LineChar {
RangeStart,
RangeMid,
RangeEnd,
LabelVert,
LabelCornerNew,
LabelCornerActive,
LabelHoriz,
};
const char *LiveElementPrinter::getLineChar(LineChar C) const {
bool IsASCII = DbgVariables == DFASCII || DbgInlinedFunctions == DFASCII;
switch (C) {
case LineChar::RangeStart:
return IsASCII ? "^" : (const char *)u8"\u2548";
case LineChar::RangeMid:
return IsASCII ? "|" : (const char *)u8"\u2503";
case LineChar::RangeEnd:
return IsASCII ? "v" : (const char *)u8"\u253b";
case LineChar::LabelVert:
return IsASCII ? "|" : (const char *)u8"\u2502";
case LineChar::LabelCornerNew:
return IsASCII ? "/" : (const char *)u8"\u250c";
case LineChar::LabelCornerActive:
return IsASCII ? "|" : (const char *)u8"\u2520";
case LineChar::LabelHoriz:
return IsASCII ? "-" : (const char *)u8"\u2500";
}
llvm_unreachable("Unhandled LineChar enum");
}
/// Print live ranges to the right of an existing line. This assumes the
/// line is not an instruction, so doesn't start or end any live ranges, so
/// we only need to print active ranges or empty columns. If AfterInst is
/// true, this is being printed after the last instruction fed to update(),
/// otherwise this is being printed before it.
void LiveElementPrinter::printAfterOtherLine(formatted_raw_ostream &OS,
bool AfterInst) {
if (ActiveCols.size()) {
unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
for (size_t ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
ColIdx < End; ++ColIdx) {
if (ActiveCols[ColIdx].isActive()) {
if ((AfterInst && ActiveCols[ColIdx].LiveOut) ||
(!AfterInst && ActiveCols[ColIdx].LiveIn))
OS << getLineChar(LineChar::RangeMid);
else if (!AfterInst && ActiveCols[ColIdx].LiveOut)
OS << getLineChar(LineChar::LabelVert);
else
OS << " ";
}
OS << " ";
}
}
OS << "\n";
}
/// Print any live element range info needed to the right of a
/// non-instruction line of disassembly. This is where we print the element
/// names and expressions, with thin line-drawing characters connecting them
/// to the live range which starts at the next instruction. If MustPrint is
/// true, we have to print at least one line (with the continuation of any
/// already-active live ranges) because something has already been printed
/// earlier on this line.
void LiveElementPrinter::printBetweenInsts(formatted_raw_ostream &OS,
bool MustPrint) {
bool PrintedSomething = false;
// Get all active elements, sorted by discovery order.
std::vector<unsigned> SortedElementIndices = getSortedActiveElementIndices();
// The outer loop iterates over the deterministic DWARF discovery order.
for (unsigned ElementIdx : SortedElementIndices) {
// Look up the physical column index (ColIdx) assigned to this
// element. We use .at() because we are certain the element is active.
unsigned ColIdx = ElementToColumn.at(ElementIdx);
if (ActiveCols[ColIdx].isActive() && ActiveCols[ColIdx].MustDrawLabel) {
// First we need to print the live range markers for any active
// columns to the left of this one.
OS.PadToColumn(getIndentLevel());
for (unsigned ColIdx2 = 0; ColIdx2 < ColIdx; ++ColIdx2) {
if (ActiveCols[ColIdx2].isActive()) {
if (ActiveCols[ColIdx2].MustDrawLabel && !ActiveCols[ColIdx2].LiveIn)
OS << getLineChar(LineChar::LabelVert) << " ";
else
OS << getLineChar(LineChar::RangeMid) << " ";
} else
OS << " ";
}
const std::unique_ptr<LiveElement> &LE = LiveElements[ElementIdx];
// Then print the variable name and location of the new live range,
// with box drawing characters joining it to the live range line.
OS << getLineChar(ActiveCols[ColIdx].LiveIn ? LineChar::LabelCornerActive
: LineChar::LabelCornerNew)
<< getLineChar(LineChar::LabelHoriz) << " ";
std::string Name = Demangle ? demangle(LE->getName()) : LE->getName();
WithColor(OS, raw_ostream::GREEN) << Name;
OS << " = ";
{
WithColor ExprColor(OS, raw_ostream::CYAN);
LE->print(OS, MRI);
}
// If there are any columns to the right of the expression we just
// printed, then continue their live range lines.
unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
for (unsigned ColIdx2 = FirstUnprintedColumn, End = ActiveCols.size();
ColIdx2 < End; ++ColIdx2) {
if (ActiveCols[ColIdx2].isActive() && ActiveCols[ColIdx2].LiveIn)
OS << getLineChar(LineChar::RangeMid) << " ";
else
OS << " ";
}
OS << "\n";
PrintedSomething = true;
}
}
for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx)
if (ActiveCols[ColIdx].isActive())
ActiveCols[ColIdx].MustDrawLabel = false;
// If we must print something (because we printed a line/column number),
// but don't have any new variables to print, then print a line which
// just continues any existing live ranges.
if (MustPrint && !PrintedSomething)
printAfterOtherLine(OS, false);
}
/// Print the live element ranges to the right of a disassembled instruction.
void LiveElementPrinter::printAfterInst(formatted_raw_ostream &OS) {
if (!ActiveCols.size())
return;
unsigned FirstUnprintedColumn = moveToFirstVarColumn(OS);
for (unsigned ColIdx = FirstUnprintedColumn, End = ActiveCols.size();
ColIdx < End; ++ColIdx) {
if (!ActiveCols[ColIdx].isActive())
OS << " ";
else if (ActiveCols[ColIdx].LiveIn && ActiveCols[ColIdx].LiveOut)
OS << getLineChar(LineChar::RangeMid) << " ";
else if (ActiveCols[ColIdx].LiveOut)
OS << getLineChar(LineChar::RangeStart) << " ";
else if (ActiveCols[ColIdx].LiveIn)
OS << getLineChar(LineChar::RangeEnd) << " ";
else
llvm_unreachable("var must be live in or out!");
}
}
void LiveElementPrinter::printBoundaryLine(formatted_raw_ostream &OS,
object::SectionedAddress Addr,
bool IsEnd) {
// Only print the start/end line for inlined functions if DFLimitsOnly is
// enabled.
if (DbgInlinedFunctions != DFLimitsOnly)
return;
// Select the appropriate map based on whether we are checking the start
// (LowPC) or end (HighPC) address.
const auto &AddressMap =
IsEnd ? LiveElementsByEndAddress : LiveElementsByAddress;
// Use the map to find all elements that start/end at the given address.
std::vector<unsigned> ElementIndices;
auto It = AddressMap.find(Addr.Address);
if (It != AddressMap.end()) {
for (LiveElement *LE : It->second) {
// Look up the element index from the pointer.
auto IndexIt = ElementPtrToIndex.find(LE);
assert(IndexIt != ElementPtrToIndex.end() &&
"LiveElement found in address map but missing index!");
ElementIndices.push_back(IndexIt->second);
}
}
// Sort the indices to ensure deterministic output order (by DWARF discovery
// order).
llvm::stable_sort(ElementIndices);
for (unsigned ElementIdx : ElementIndices) {
LiveElement *LE = LiveElements[ElementIdx].get();
LE->printElementLine(OS, Addr, IsEnd);
}
}
bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
std::unique_ptr<MemoryBuffer> Buffer;
if (LineInfo.Source) {
Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source);
} else {
std::string PathToOpen = LineInfo.FileName;
if (!SourceDirs.empty()) {
SmallVector<StringRef, 8> SearchDirs;
for (const std::string &Dir : SourceDirs)
SearchDirs.push_back(Dir);
if (std::optional<std::string> Resolved =
findSourceFilePath(LineInfo.FileName, SearchDirs))
PathToOpen = std::move(*Resolved);
}
auto BufferOrError = MemoryBuffer::getFile(PathToOpen, /*IsText=*/true);
if (!BufferOrError) {
if (MissingSources.insert(LineInfo.FileName).second)
reportWarning("failed to find source " + LineInfo.FileName,
Obj->getFileName());
return false;
}
Buffer = std::move(*BufferOrError);
}
// Chomp the file to get lines
const char *BufferStart = Buffer->getBufferStart(),
*BufferEnd = Buffer->getBufferEnd();
std::vector<StringRef> &Lines = LineCache[LineInfo.FileName];
const char *Start = BufferStart;
for (const char *I = BufferStart; I != BufferEnd; ++I)
if (*I == '\n') {
Lines.emplace_back(Start, I - Start - (BufferStart < I && I[-1] == '\r'));
Start = I + 1;
}
if (Start < BufferEnd)
Lines.emplace_back(Start, BufferEnd - Start);
SourceCache[LineInfo.FileName] = std::move(Buffer);
return true;
}
void SourcePrinter::printSourceLine(formatted_raw_ostream &OS,
object::SectionedAddress Address,
StringRef ObjectFilename,
LiveElementPrinter &LEP,
StringRef Delimiter) {
if (!Symbolizer)
return;
DILineInfo LineInfo = DILineInfo();
Expected<DILineInfo> ExpectedLineInfo =
Symbolizer->symbolizeCode(*Obj, Address);
if (ExpectedLineInfo) {
LineInfo = *ExpectedLineInfo;
} else if (!WarnedInvalidDebugInfo) {
WarnedInvalidDebugInfo = true;
// TODO Untested.
reportWarning("failed to parse debug information: " +
toString(ExpectedLineInfo.takeError()),
ObjectFilename);
}
if (!objdump::SubstitutePaths.empty())
LineInfo.FileName = applySubstitutePaths(LineInfo.FileName);
if (!objdump::Prefix.empty() &&
sys::path::is_absolute_gnu(LineInfo.FileName)) {
// FileName has at least one character since is_absolute_gnu is false for
// an empty string.
assert(!LineInfo.FileName.empty());
if (PrefixStrip > 0) {
uint32_t Level = 0;
auto StrippedNameStart = LineInfo.FileName.begin();
// Path.h iterator skips extra separators. Therefore it cannot be used
// here to keep compatibility with GNU Objdump.
for (auto Pos = StrippedNameStart + 1, End = LineInfo.FileName.end();
Pos != End && Level < PrefixStrip; ++Pos) {
if (sys::path::is_separator(*Pos)) {
StrippedNameStart = Pos;
++Level;
}
}
LineInfo.FileName =
std::string(StrippedNameStart, LineInfo.FileName.end());
}
SmallString<128> FilePath;
sys::path::append(FilePath, Prefix, LineInfo.FileName);
LineInfo.FileName = std::string(FilePath);
}
if (PrintLines)
printLines(OS, Address, LineInfo, Delimiter, LEP);
if (PrintSource)
printSources(OS, LineInfo, ObjectFilename, Delimiter, LEP);
OldLineInfo = std::move(LineInfo);
}
void SourcePrinter::printLines(formatted_raw_ostream &OS,
object::SectionedAddress Address,
const DILineInfo &LineInfo, StringRef Delimiter,
LiveElementPrinter &LEP) {
bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
LineInfo.FunctionName != OldLineInfo.FunctionName;
if (PrintFunctionName) {
OS << Delimiter << LineInfo.FunctionName;
// If demangling is successful, FunctionName will end with "()". Print it
// only if demangling did not run or was unsuccessful.
if (!StringRef(LineInfo.FunctionName).ends_with("()"))
OS << "()";
OS << ":\n";
}
if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
(OldLineInfo.Line != LineInfo.Line ||
OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName)) {
OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line;
LEP.printBetweenInsts(OS, true);
}
}
// Get the source line text for LineInfo:
// - use LineInfo::LineSource if available;
// - use LineCache if LineInfo::Source otherwise.
StringRef SourcePrinter::getLine(const DILineInfo &LineInfo,
StringRef ObjectFilename) {
if (LineInfo.LineSource)
return LineInfo.LineSource.value();
if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
if (!cacheSource(LineInfo))
return {};
auto LineBuffer = LineCache.find(LineInfo.FileName);
if (LineBuffer == LineCache.end())
return {};
if (LineInfo.Line > LineBuffer->second.size()) {
reportWarning(
formatv("debug info line number {0} exceeds the number of lines in {1}",
LineInfo.Line, LineInfo.FileName),
ObjectFilename);
return {};
}
// Vector begins at 0, line numbers are non-zero
return LineBuffer->second[LineInfo.Line - 1];
}
void SourcePrinter::printSources(formatted_raw_ostream &OS,
const DILineInfo &LineInfo,
StringRef ObjectFilename, StringRef Delimiter,
LiveElementPrinter &LEP) {
if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
(OldLineInfo.Line == LineInfo.Line &&
OldLineInfo.FileName == LineInfo.FileName))
return;
StringRef Line = getLine(LineInfo, ObjectFilename);
if (!Line.empty()) {
OS << Delimiter << Line;
LEP.printBetweenInsts(OS, true);
}
}
SourcePrinter::SourcePrinter(const object::ObjectFile *Obj,
StringRef DefaultArch)
: Obj(Obj) {
symbolize::LLVMSymbolizer::Options SymbolizerOpts;
SymbolizerOpts.PrintFunctions =
DILineInfoSpecifier::FunctionNameKind::LinkageName;
SymbolizerOpts.Demangle = Demangle;
SymbolizerOpts.DefaultArch = std::string(DefaultArch);
Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
}
} // namespace objdump
} // namespace llvm