Index: llvm/tools/llvm-objdump/CMakeLists.txt =================================================================== --- llvm/tools/llvm-objdump/CMakeLists.txt +++ llvm/tools/llvm-objdump/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_tool(llvm-objdump llvm-objdump.cpp + SourcePrinter.cpp COFFDump.cpp ELFDump.cpp MachODump.cpp Index: llvm/tools/llvm-objdump/SourcePrinter.h =================================================================== --- /dev/null +++ llvm/tools/llvm-objdump/SourcePrinter.h @@ -0,0 +1,163 @@ +//===-- SourcePrinter.h - source interleaving utilities --------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H +#define LLVM_TOOLS_LLVM_OBJDUMP_SOURCEPRINTER_H + +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Support/FormattedStream.h" +#include +#include + +namespace llvm { +/// Stores a single expression representing the location of a source-level +/// variable, along with the PC range for which that expression is valid. +struct LiveVariable { + DWARFLocationExpression LocExpr; + const char *VarName; + DWARFUnit *Unit; + const DWARFDie FuncDie; + + LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, + DWARFUnit *Unit, const DWARFDie FuncDie) + : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} + + bool liveAtAddress(object::SectionedAddress Addr); + + void print(raw_ostream &OS, const MCRegisterInfo &MRI) const; +}; + +/// Helper class for printing source variable locations alongside disassembly. +class LiveVariablePrinter { + // Information we want to track about one column in which we are printing a + // variable live range. + struct Column { + unsigned VarIdx = NullVarIdx; + bool LiveIn = false; + bool LiveOut = false; + bool MustDrawLabel = false; + + bool isActive() const { return VarIdx != NullVarIdx; } + + static constexpr unsigned NullVarIdx = std::numeric_limits::max(); + }; + + // All live variables we know about in the object/image file. + std::vector LiveVariables; + + // The columns we are currently drawing. + IndexedMap ActiveCols; + + const MCRegisterInfo &MRI; + const MCSubtargetInfo &STI; + + void addVariable(DWARFDie FuncDie, DWARFDie VarDie); + + void addFunction(DWARFDie D); + + // Get the column number (in characters) at which the first live variable + // line should be printed. + unsigned getIndentLevel() const; + + // 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 variable lines. Pick a less overloaded word. + unsigned moveToFirstVarColumn(formatted_raw_ostream &OS); + + unsigned findFreeColumn(); + +public: + LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) + : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} + + void dump() const; + + void addCompileUnit(DWARFDie D); + + /// 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 update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, bool IncludeDefinedVars); + + enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, + }; + const char *getLineChar(LineChar C) const; + + /// 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 printAfterOtherLine(formatted_raw_ostream &OS, bool AfterInst); + + /// Print any live variable range info needed to the right of a + /// non-instruction line of disassembly. This is where we print the variable + /// 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 printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint); + + /// Print the live variable ranges to the right of a disassembled instruction. + void printAfterInst(formatted_raw_ostream &OS); +}; + +class SourcePrinter { +protected: + DILineInfo OldLineInfo; + const object::ObjectFile *Obj = nullptr; + std::unique_ptr Symbolizer; + // File name to file contents of source. + std::unordered_map> SourceCache; + // Mark the line endings of the cached source. + std::unordered_map> LineCache; + // Keep track of missing sources. + StringSet<> MissingSources; + // Only emit 'invalid debug info' warning once. + bool WarnedInvalidDebugInfo = false; + +private: + bool cacheSource(const DILineInfo &LineInfoFile); + + void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef Delimiter, LiveVariablePrinter &LVP); + + void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP); + +public: + SourcePrinter() = default; + SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch); + virtual ~SourcePrinter() = default; + virtual void printSourceLine(formatted_raw_ostream &OS, + object::SectionedAddress Address, + StringRef ObjectFilename, + LiveVariablePrinter &LVP, + StringRef Delimiter = "; "); +}; + +} // namespace llvm + +#endif \ No newline at end of file Index: llvm/tools/llvm-objdump/SourcePrinter.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -0,0 +1,481 @@ +//===-- 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 LiveVariablePrinter and SourcePrinter classes to +// keep track of DWARF info as the current address is updated, and print out the +// source file line and variable liveness as needed. +// +//===----------------------------------------------------------------------===// + +#include "SourcePrinter.h" +#include "llvm-objdump.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormatVariadic.h" + +#define DEBUG_TYPE "objdump" + +namespace llvm { + +unsigned getInstStartColumn(const MCSubtargetInfo &STI) { + return objdump::NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; +} + +bool LiveVariable::liveAtAddress(object::SectionedAddress Addr) { + if (LocExpr.Range == None) + 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.data(), LocExpr.Expr.size()}, + Unit->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, Unit->getAddressByteSize()); + Expression.printCompact(OS, MRI); +} + +void LiveVariablePrinter::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 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) { + LiveVariables.emplace_back(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}; + LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); + } + } +} + +void LiveVariablePrinter::addFunction(DWARFDie D) { + for (const DWARFDie &Child : D.children()) { + if (Child.getTag() == dwarf::DW_TAG_variable || + Child.getTag() == dwarf::DW_TAG_formal_parameter) + addVariable(D, Child); + else + addFunction(Child); + } +} + +// Get the column number (in characters) at which the first live variable +// line should be printed. +unsigned LiveVariablePrinter::getIndentLevel() const { + return objdump::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 variable lines. Pick a less overloaded word. +unsigned LiveVariablePrinter::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 LiveVariablePrinter::findFreeColumn() { + for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) + if (!ActiveCols[ColIdx].isActive()) + return ColIdx; + + size_t OldSize = ActiveCols.size(); + ActiveCols.grow(std::max(OldSize * 2, 1)); + return OldSize; +} + +void LiveVariablePrinter::dump() const { + for (const LiveVariable &LV : LiveVariables) { + dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; + LV.print(dbgs(), MRI); + dbgs() << "\n"; + } +} + +void LiveVariablePrinter::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 LiveVariablePrinter::update(object::SectionedAddress ThisAddr, + object::SectionedAddress NextAddr, + bool IncludeDefinedVars) { + // First, check variables which have already been assigned a column, so + // that we don't change their order. + SmallSet CheckedVarIdxs; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + if (!ActiveCols[ColIdx].isActive()) + continue; + CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); + LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; + ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); + ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); + LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn + << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); + + if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) + ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; + } + + // Next, look for variables which don't already have a column, but which + // are now live. + if (IncludeDefinedVars) { + for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; + ++VarIdx) { + if (CheckedVarIdxs.count(VarIdx)) + continue; + LiveVariable &LV = LiveVariables[VarIdx]; + bool LiveIn = LV.liveAtAddress(ThisAddr); + bool LiveOut = LV.liveAtAddress(NextAddr); + if (!LiveIn && !LiveOut) + continue; + + unsigned ColIdx = findFreeColumn(); + LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" + << NextAddr.Address << ", " << LV.VarName << ", Col " + << ColIdx << ": LiveIn=" << LiveIn + << ", LiveOut=" << LiveOut << "\n"); + ActiveCols[ColIdx].VarIdx = VarIdx; + ActiveCols[ColIdx].LiveIn = LiveIn; + ActiveCols[ColIdx].LiveOut = LiveOut; + ActiveCols[ColIdx].MustDrawLabel = true; + } + } +} + +enum class LineChar { + RangeStart, + RangeMid, + RangeEnd, + LabelVert, + LabelCornerNew, + LabelCornerActive, + LabelHoriz, +}; +const char *LiveVariablePrinter::getLineChar(LineChar C) const { + bool IsASCII = objdump::DbgVariables == objdump::DVASCII; + 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 LiveVariablePrinter::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 variable range info needed to the right of a +/// non-instruction line of disassembly. This is where we print the variable +/// 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 LiveVariablePrinter::printBetweenInsts(formatted_raw_ostream &OS, + bool MustPrint) { + bool PrintedSomething = false; + for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { + 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 << " "; + } + + // 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) << " "; + WithColor(OS, raw_ostream::GREEN) + << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; + OS << " = "; + { + WithColor ExprColor(OS, raw_ostream::CYAN); + LiveVariables[ActiveCols[ColIdx].VarIdx].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 variable ranges to the right of a disassembled instruction. +void LiveVariablePrinter::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!"); + } +} + +bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { + std::unique_ptr Buffer; + if (LineInfo.Source) { + Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); + } else { + auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); + if (!BufferOrError) { + if (MissingSources.insert(LineInfo.FileName).second) + objdump::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 &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, + LiveVariablePrinter &LVP, + StringRef Delimiter) { + if (!Symbolizer) + return; + + DILineInfo LineInfo = DILineInfo(); + Expected ExpectedLineInfo = + Symbolizer->symbolizeCode(*Obj, Address); + std::string ErrorMessage; + if (ExpectedLineInfo) { + LineInfo = *ExpectedLineInfo; + } else if (!WarnedInvalidDebugInfo) { + WarnedInvalidDebugInfo = true; + // TODO Untested. + objdump::reportWarning("failed to parse debug information: " + + toString(ExpectedLineInfo.takeError()), + ObjectFilename); + } + + 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 (objdump::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 < objdump::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, objdump::Prefix, LineInfo.FileName); + + LineInfo.FileName = std::string(FilePath); + } + + if (objdump::PrintLines) + printLines(OS, LineInfo, Delimiter, LVP); + if (objdump::PrintSource) + printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); + OldLineInfo = LineInfo; +} + +void SourcePrinter::printLines(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, StringRef Delimiter, + LiveVariablePrinter &LVP) { + 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).endswith("()")) + 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; + LVP.printBetweenInsts(OS, true); + } +} + +void SourcePrinter::printSources(formatted_raw_ostream &OS, + const DILineInfo &LineInfo, + StringRef ObjectFilename, StringRef Delimiter, + LiveVariablePrinter &LVP) { + if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || + (OldLineInfo.Line == LineInfo.Line && + OldLineInfo.FileName == LineInfo.FileName)) + return; + + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return; + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer != LineCache.end()) { + if (LineInfo.Line > LineBuffer->second.size()) { + objdump::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 + OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; + LVP.printBetweenInsts(OS, true); + } +} + +SourcePrinter::SourcePrinter(const object::ObjectFile *Obj, + StringRef DefaultArch) + : Obj(Obj) { + symbolize::LLVMSymbolizer::Options SymbolizerOpts; + SymbolizerOpts.PrintFunctions = + DILineInfoSpecifier::FunctionNameKind::LinkageName; + SymbolizerOpts.Demangle = objdump::Demangle; + SymbolizerOpts.DefaultArch = std::string(DefaultArch); + Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts)); +} + +} // namespace llvm \ No newline at end of file Index: llvm/tools/llvm-objdump/llvm-objdump.h =================================================================== --- llvm/tools/llvm-objdump/llvm-objdump.h +++ llvm/tools/llvm-objdump/llvm-objdump.h @@ -31,7 +31,15 @@ namespace objdump { +enum DebugVarsFormat { + DVDisabled, + DVUnicode, + DVASCII, +}; + extern cl::opt ArchiveHeaders; +extern cl::opt DbgIndent; +extern cl::opt DbgVariables; extern cl::opt Demangle; extern cl::opt Disassemble; extern cl::opt DisassembleAll; @@ -44,6 +52,8 @@ extern cl::opt Prefix; extern cl::opt PrefixStrip; extern cl::opt PrintImmHex; +extern cl::opt PrintLines; +extern cl::opt PrintSource; extern cl::opt PrivateHeaders; extern cl::opt Relocations; extern cl::opt SectionHeaders; Index: llvm/tools/llvm-objdump/llvm-objdump.cpp =================================================================== --- llvm/tools/llvm-objdump/llvm-objdump.cpp +++ llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -19,6 +19,7 @@ #include "COFFDump.h" #include "ELFDump.h" #include "MachODump.h" +#include "SourcePrinter.h" #include "WasmDump.h" #include "XCOFFDump.h" #include "llvm/ADT/IndexedMap.h" @@ -211,11 +212,11 @@ cl::ZeroOrMore, cl::cat(ObjdumpCat)); -static cl::opt - PrintLines("line-numbers", - cl::desc("Display source line numbers with " - "disassembly. Implies disassemble object"), - cl::cat(ObjdumpCat)); +cl::opt + objdump::PrintLines("line-numbers", + cl::desc("Display source line numbers with " + "disassembly. Implies disassemble object"), + cl::cat(ObjdumpCat)); static cl::alias PrintLinesShort("l", cl::desc("Alias for --line-numbers"), cl::NotHidden, cl::Grouping, cl::aliasopt(PrintLines)); @@ -299,7 +300,7 @@ cl::desc("Display LMA column when dumping ELF section headers"), cl::cat(ObjdumpCat)); -static cl::opt PrintSource( +cl::opt objdump::PrintSource( "source", cl::desc( "Display source inlined with disassembly. Implies disassemble object"), @@ -364,13 +365,7 @@ "paths. No effect without --prefix"), cl::init(0), cl::cat(ObjdumpCat)); -enum DebugVarsFormat { - DVDisabled, - DVUnicode, - DVASCII, -}; - -static cl::opt DbgVariables( +cl::opt objdump::DbgVariables( "debug-vars", cl::init(DVDisabled), cl::desc("Print the locations (in registers or memory) of " "source-level variables alongside disassembly"), @@ -380,11 +375,11 @@ clEnumValN(DVASCII, "ascii", "unicode")), cl::cat(ObjdumpCat)); -static cl::opt - DbgIndent("debug-vars-indent", cl::init(40), - cl::desc("Distance to indent the source-level variable display, " - "relative to the start of the disassembly"), - cl::cat(ObjdumpCat)); +cl::opt objdump::DbgIndent( + "debug-vars-indent", cl::init(40), + cl::desc("Distance to indent the source-level variable display, " + "relative to the start of the disassembly"), + cl::cat(ObjdumpCat)); static cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); @@ -597,529 +592,6 @@ return NoShowRawInsn ? 16 : STI.getTargetTriple().isX86() ? 40 : 24; } -/// Stores a single expression representing the location of a source-level -/// variable, along with the PC range for which that expression is valid. -struct LiveVariable { - DWARFLocationExpression LocExpr; - const char *VarName; - DWARFUnit *Unit; - const DWARFDie FuncDie; - - LiveVariable(const DWARFLocationExpression &LocExpr, const char *VarName, - DWARFUnit *Unit, const DWARFDie FuncDie) - : LocExpr(LocExpr), VarName(VarName), Unit(Unit), FuncDie(FuncDie) {} - - bool liveAtAddress(object::SectionedAddress Addr) { - if (LocExpr.Range == None) - return false; - return LocExpr.Range->SectionIndex == Addr.SectionIndex && - LocExpr.Range->LowPC <= Addr.Address && - LocExpr.Range->HighPC > Addr.Address; - } - - void print(raw_ostream &OS, const MCRegisterInfo &MRI) const { - DataExtractor Data({LocExpr.Expr.data(), LocExpr.Expr.size()}, - Unit->getContext().isLittleEndian(), 0); - DWARFExpression Expression(Data, Unit->getAddressByteSize()); - Expression.printCompact(OS, MRI); - } -}; - -/// Helper class for printing source variable locations alongside disassembly. -class LiveVariablePrinter { - // Information we want to track about one column in which we are printing a - // variable live range. - struct Column { - unsigned VarIdx = NullVarIdx; - bool LiveIn = false; - bool LiveOut = false; - bool MustDrawLabel = false; - - bool isActive() const { return VarIdx != NullVarIdx; } - - static constexpr unsigned NullVarIdx = std::numeric_limits::max(); - }; - - // All live variables we know about in the object/image file. - std::vector LiveVariables; - - // The columns we are currently drawing. - IndexedMap ActiveCols; - - const MCRegisterInfo &MRI; - const MCSubtargetInfo &STI; - - void 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 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) { - LiveVariables.emplace_back(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}; - LiveVariables.emplace_back(WholeFuncExpr, VarName, U, FuncDie); - } - } - } - - void addFunction(DWARFDie D) { - for (const DWARFDie &Child : D.children()) { - if (Child.getTag() == dwarf::DW_TAG_variable || - Child.getTag() == dwarf::DW_TAG_formal_parameter) - addVariable(D, Child); - else - addFunction(Child); - } - } - - // Get the column number (in characters) at which the first live variable - // line should be printed. - unsigned 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 variable lines. Pick a less overloaded word. - unsigned 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 findFreeColumn() { - for (unsigned ColIdx = 0; ColIdx < ActiveCols.size(); ++ColIdx) - if (!ActiveCols[ColIdx].isActive()) - return ColIdx; - - size_t OldSize = ActiveCols.size(); - ActiveCols.grow(std::max(OldSize * 2, 1)); - return OldSize; - } - -public: - LiveVariablePrinter(const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) - : LiveVariables(), ActiveCols(Column()), MRI(MRI), STI(STI) {} - - void dump() const { - for (const LiveVariable &LV : LiveVariables) { - dbgs() << LV.VarName << " @ " << LV.LocExpr.Range << ": "; - LV.print(dbgs(), MRI); - dbgs() << "\n"; - } - } - - void 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 update(object::SectionedAddress ThisAddr, - object::SectionedAddress NextAddr, bool IncludeDefinedVars) { - // First, check variables which have already been assigned a column, so - // that we don't change their order. - SmallSet CheckedVarIdxs; - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { - if (!ActiveCols[ColIdx].isActive()) - continue; - CheckedVarIdxs.insert(ActiveCols[ColIdx].VarIdx); - LiveVariable &LV = LiveVariables[ActiveCols[ColIdx].VarIdx]; - ActiveCols[ColIdx].LiveIn = LV.liveAtAddress(ThisAddr); - ActiveCols[ColIdx].LiveOut = LV.liveAtAddress(NextAddr); - LLVM_DEBUG(dbgs() << "pass 1, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << ActiveCols[ColIdx].LiveIn - << ", LiveOut=" << ActiveCols[ColIdx].LiveOut << "\n"); - - if (!ActiveCols[ColIdx].LiveIn && !ActiveCols[ColIdx].LiveOut) - ActiveCols[ColIdx].VarIdx = Column::NullVarIdx; - } - - // Next, look for variables which don't already have a column, but which - // are now live. - if (IncludeDefinedVars) { - for (unsigned VarIdx = 0, End = LiveVariables.size(); VarIdx < End; - ++VarIdx) { - if (CheckedVarIdxs.count(VarIdx)) - continue; - LiveVariable &LV = LiveVariables[VarIdx]; - bool LiveIn = LV.liveAtAddress(ThisAddr); - bool LiveOut = LV.liveAtAddress(NextAddr); - if (!LiveIn && !LiveOut) - continue; - - unsigned ColIdx = findFreeColumn(); - LLVM_DEBUG(dbgs() << "pass 2, " << ThisAddr.Address << "-" - << NextAddr.Address << ", " << LV.VarName << ", Col " - << ColIdx << ": LiveIn=" << LiveIn - << ", LiveOut=" << LiveOut << "\n"); - ActiveCols[ColIdx].VarIdx = VarIdx; - ActiveCols[ColIdx].LiveIn = LiveIn; - ActiveCols[ColIdx].LiveOut = LiveOut; - ActiveCols[ColIdx].MustDrawLabel = true; - } - } - } - - enum class LineChar { - RangeStart, - RangeMid, - RangeEnd, - LabelVert, - LabelCornerNew, - LabelCornerActive, - LabelHoriz, - }; - const char *getLineChar(LineChar C) const { - bool IsASCII = DbgVariables == DVASCII; - 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 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 variable range info needed to the right of a - /// non-instruction line of disassembly. This is where we print the variable - /// 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 printBetweenInsts(formatted_raw_ostream &OS, bool MustPrint) { - bool PrintedSomething = false; - for (unsigned ColIdx = 0, End = ActiveCols.size(); ColIdx < End; ++ColIdx) { - 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 << " "; - } - - // 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) << " "; - WithColor(OS, raw_ostream::GREEN) - << LiveVariables[ActiveCols[ColIdx].VarIdx].VarName; - OS << " = "; - { - WithColor ExprColor(OS, raw_ostream::CYAN); - LiveVariables[ActiveCols[ColIdx].VarIdx].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 variable ranges to the right of a disassembled instruction. - void 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!"); - } - } -}; - -class SourcePrinter { -protected: - DILineInfo OldLineInfo; - const ObjectFile *Obj = nullptr; - std::unique_ptr Symbolizer; - // File name to file contents of source. - std::unordered_map> SourceCache; - // Mark the line endings of the cached source. - std::unordered_map> LineCache; - // Keep track of missing sources. - StringSet<> MissingSources; - // Only emit 'invalid debug info' warning once. - bool WarnedInvalidDebugInfo = false; - -private: - bool cacheSource(const DILineInfo& LineInfoFile); - - void printLines(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef Delimiter, LiveVariablePrinter &LVP); - - void printSources(formatted_raw_ostream &OS, const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP); - -public: - SourcePrinter() = default; - SourcePrinter(const 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)); - } - virtual ~SourcePrinter() = default; - virtual void printSourceLine(formatted_raw_ostream &OS, - object::SectionedAddress Address, - StringRef ObjectFilename, - LiveVariablePrinter &LVP, - StringRef Delimiter = "; "); -}; - -bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) { - std::unique_ptr Buffer; - if (LineInfo.Source) { - Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); - } else { - auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); - 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 &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, - LiveVariablePrinter &LVP, - StringRef Delimiter) { - if (!Symbolizer) - return; - - DILineInfo LineInfo = DILineInfo(); - Expected ExpectedLineInfo = - Symbolizer->symbolizeCode(*Obj, Address); - std::string ErrorMessage; - if (ExpectedLineInfo) { - LineInfo = *ExpectedLineInfo; - } else if (!WarnedInvalidDebugInfo) { - WarnedInvalidDebugInfo = true; - // TODO Untested. - reportWarning("failed to parse debug information: " + - toString(ExpectedLineInfo.takeError()), - ObjectFilename); - } - - if (!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, LineInfo, Delimiter, LVP); - if (PrintSource) - printSources(OS, LineInfo, ObjectFilename, Delimiter, LVP); - OldLineInfo = LineInfo; -} - -void SourcePrinter::printLines(formatted_raw_ostream &OS, - const DILineInfo &LineInfo, StringRef Delimiter, - LiveVariablePrinter &LVP) { - 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).endswith("()")) - 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; - LVP.printBetweenInsts(OS, true); - } -} - -void SourcePrinter::printSources(formatted_raw_ostream &OS, - const DILineInfo &LineInfo, - StringRef ObjectFilename, StringRef Delimiter, - LiveVariablePrinter &LVP) { - if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 || - (OldLineInfo.Line == LineInfo.Line && - OldLineInfo.FileName == LineInfo.FileName)) - return; - - if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) - if (!cacheSource(LineInfo)) - return; - auto LineBuffer = LineCache.find(LineInfo.FileName); - if (LineBuffer != LineCache.end()) { - 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 - OS << Delimiter << LineBuffer->second[LineInfo.Line - 1]; - LVP.printBetweenInsts(OS, true); - } -} - static bool isAArch64Elf(const ObjectFile *Obj) { const auto *Elf = dyn_cast(Obj); return Elf && Elf->getEMachine() == ELF::EM_AARCH64;