diff --git a/llvm/include/llvm/DebugInfo/BTF/BTFContext.h b/llvm/include/llvm/DebugInfo/BTF/BTFContext.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/BTF/BTFContext.h @@ -0,0 +1,45 @@ +#ifndef LLVM_DEBUGINFO_BPF_BPFCONTEXT_H +#define LLVM_DEBUGINFO_BPF_BPFCONTEXT_H + +#include "llvm/DebugInfo/DIContext.h" +#include + +namespace llvm { + +class BTFContext : public DIContext { + struct Impl; + std::unique_ptr Impl; + +public: + BTFContext(struct Impl *Impl); + ~BTFContext(); + + void dump(raw_ostream &OS, DIDumpOptions DumpOpts) override; + + bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) override; + + DILineInfo getLineInfoForAddress( + object::SectionedAddress Address, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + DILineInfo + getLineInfoForDataAddress(object::SectionedAddress Address) override; + + DILineInfoTable getLineInfoForAddressRange( + object::SectionedAddress Address, uint64_t Size, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + DIInliningInfo getInliningInfoForAddress( + object::SectionedAddress Address, + DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override; + + std::vector + getLocalsForAddress(object::SectionedAddress Address) override; + + static std::unique_ptr create(const object::ObjectFile &Obj); + static bool hasBTFSections(const object::ObjectFile &Obj); +}; + +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_BPF_BPFCONTEXT_H diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h --- a/llvm/include/llvm/DebugInfo/DIContext.h +++ b/llvm/include/llvm/DebugInfo/DIContext.h @@ -38,6 +38,7 @@ std::string FunctionName; std::string StartFileName; std::optional Source; + std::optional LineSource; uint32_t Line = 0; uint32_t Column = 0; uint32_t StartLine = 0; @@ -228,7 +229,7 @@ class DIContext { public: - enum DIContextKind { CK_DWARF, CK_PDB }; + enum DIContextKind { CK_DWARF, CK_PDB, CK_BTF }; DIContext(DIContextKind K) : Kind(K) {} virtual ~DIContext() = default; diff --git a/llvm/lib/DebugInfo/BTF/BTFContext.cpp b/llvm/lib/DebugInfo/BTF/BTFContext.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/BTF/BTFContext.cpp @@ -0,0 +1,335 @@ +#include "llvm/DebugInfo/BTF/BTFContext.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DIContext.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include +#include +#include +#include +#include + +using namespace llvm; +using object::ObjectFile; +using object::SectionedAddress; +using object::SectionRef; + +#define DEBUG_TYPE "debug-info-bpf" + +#define EBPF_MAGIC 0xEB9F +#define BTF_SECTION_NAME ".BTF" +#define BTF_EXT_SECTION_NAME ".BTF.ext" + +class Err { + std::string Buffer; + raw_string_ostream Stream; + +public: + Err(const char *InitialMsg) : Buffer(InitialMsg), Stream(Buffer){}; + + template Err &operator<<(T Val) { + Stream << Val; + return *this; + } + + Err &write_hex(unsigned long long Val) { + Stream.write_hex(Val); + return *this; + } + + Err &operator<<(Error Val) { + handleAllErrors(std::move(Val), + [=](ErrorInfoBase &Info) { Stream << Info.message(); }); + return *this; + } + + operator Error() const { + return make_error(Buffer, errc::invalid_argument); + } + + template operator Expected() const { return (Error) * this; } +}; + +struct BTFLineInfo { + // Offset of instruction from beginning of a section (in bytes) + uint32_t InsnAddress; + // Offsets in strings table (in bytes) + uint32_t FileNameOff; + uint32_t LineOff; + // Combined line and column number information + uint32_t LineCol; + + uint32_t getLine() { return LineCol >> 10; } + uint32_t getCol() { return LineCol & 0x3ff; } +}; + +// The vector is sorted by InsnOff for fast lookups +using BTFLinesVector = std::vector; + +struct BTFInfo { + StringRef StringsTable; + std::map> SectionLines; + + StringRef findString(uint32_t Offset) { + return StringsTable.slice(Offset, StringsTable.find(0, Offset)); + } +}; + +class ParseContext { + const ObjectFile &Obj; + std::map Sections; + +public: + ParseContext(const ObjectFile &Obj) : Obj(Obj) {} + + Error init() { + for (SectionRef Sec : Obj.sections()) { + Expected MaybeName = Sec.getName(); + if (!MaybeName) + return MaybeName.takeError(); + + Sections[*MaybeName] = Sec; + } + + return Error::success(); + } + + Expected makeExtractor(SectionRef Sec) { + auto Contents = Sec.getContents(); + if (!Contents) + return Contents.takeError(); + + return DataExtractor(Contents.get(), Obj.isLittleEndian(), + Obj.getBytesInAddress()); + } + + std::optional findSection(StringRef Name) { + auto It = Sections.find(Name); + if (It != Sections.end()) + return It->second; + return std::nullopt; + } +}; + +static Error parseBTF(ParseContext &Ctx, SectionRef BTF, BTFInfo &Info) { + auto MaybeExtractor = Ctx.makeExtractor(BTF); + if (!MaybeExtractor) + return MaybeExtractor.takeError(); + + auto Extractor = MaybeExtractor.get(); + DataExtractor::Cursor C = DataExtractor::Cursor(0); + + auto Magic = Extractor.getU16(C); + if (Magic != EBPF_MAGIC) + return Err("Invalid .BTF.ext magic: ").write_hex(Magic); + + Extractor.getU8(C); // version + Extractor.getU8(C); // flags + auto HdrLen = Extractor.getU32(C); + if (HdrLen < 8) + return Err("Unexpected .BTF header length: ") << HdrLen; + + Extractor.getU32(C); // type_off + Extractor.getU32(C); // type_len + auto StrOff = Extractor.getU32(C); + auto StrLen = Extractor.getU32(C); + auto StrStart = HdrLen + StrOff; + + if (!C) + return C.takeError(); + + Info.StringsTable = Extractor.getData().slice(StrStart, StrStart + StrLen); + + return Error::success(); +} + +static Error parseLineInfo(ParseContext &Ctx, DataExtractor &Extractor, + uint64_t LineInfoStart, uint64_t LineInfoEnd, + BTFInfo &Info) { + DataExtractor::Cursor C = DataExtractor::Cursor(LineInfoStart); + auto RecSize = Extractor.getU32(C); + if (RecSize < 16) + return Err("Unexpected .BTF.ext line info record length: ") << RecSize; + + while (C && C.tell() < LineInfoEnd) { + std::unique_ptr Lines(new BTFLinesVector()); + + auto SecNameOff = Extractor.getU32(C); + auto NumInfo = Extractor.getU32(C); + + auto SecName = Info.findString(SecNameOff); + auto Sec = Ctx.findSection(SecName); + + for (uint32_t I = 0; C && I < NumInfo; ++I) { + auto RecStart = C.tell(); + + auto InsnOff = Extractor.getU32(C); + auto FileNameOff = Extractor.getU32(C); + auto LineOff = Extractor.getU32(C); + auto LineCol = Extractor.getU32(C); + + Lines->push_back({InsnOff, FileNameOff, LineOff, LineCol}); + + C.seek(RecStart + RecSize); + } + + std::sort(Lines->begin(), Lines->end(), + [](const BTFLineInfo &L, const BTFLineInfo &R) { + return L.InsnAddress < R.InsnAddress; + }); + + if (Sec) + Info.SectionLines[Sec->getIndex()] = std::move(Lines); + } + + if (!C) + return Err("Error while reading .BTF.ext line info ") << C.takeError(); + + return Error::success(); +} + +static Error parseBTFExt(ParseContext &Ctx, SectionRef BTFExt, BTFInfo &Info) { + auto MaybeExtractor = Ctx.makeExtractor(BTFExt); + if (!MaybeExtractor) + return MaybeExtractor.takeError(); + + auto Extractor = MaybeExtractor.get(); + DataExtractor::Cursor C = DataExtractor::Cursor(0); + + auto Magic = Extractor.getU16(C); + if (Magic != EBPF_MAGIC) + return Err("Invalid .BTF.ext magic: ").write_hex(Magic); + + Extractor.getU8(C); // version + Extractor.getU8(C); // flags + auto HdrLen = Extractor.getU32(C); + if (HdrLen < 8) + return Err("Unexpected .BTF.ext header length: ") << HdrLen; + + Extractor.getU32(C); // func_info_off + Extractor.getU32(C); // func_info_len + auto LineInfoOff = Extractor.getU32(C); + auto LineInfoLen = Extractor.getU32(C); + + if (!C) + return Err("Error while reading .BTF.ext header ") << C.takeError(); + + auto LineInfoStart = HdrLen + LineInfoOff; + auto LineInfoEnd = LineInfoStart + LineInfoLen; + + if (auto E = parseLineInfo(Ctx, Extractor, LineInfoStart, LineInfoEnd, Info)) + return E; + + return Error::success(); +} + +static Error parseBTFSections(const ObjectFile &Obj, BTFInfo &Info) { + ParseContext Ctx(Obj); + + if (auto E = Ctx.init()) + return E; + + auto BTF = Ctx.findSection(BTF_SECTION_NAME); + auto BTFExt = Ctx.findSection(BTF_EXT_SECTION_NAME); + + if (!BTF) + return Err("Can't find .BTF section"); + + if (!BTFExt) + return Err("Can't find .BTF.ext section"); + + if (auto E = parseBTF(Ctx, *BTF, Info)) + return E; + + if (auto E = parseBTFExt(Ctx, *BTFExt, Info)) + return E; + + return Error::success(); +} + +struct llvm::BTFContext::Impl { + BTFInfo BTF; +}; + +BTFContext::BTFContext(struct Impl *Impl) : DIContext(CK_BTF), Impl(Impl) {} + +BTFContext::~BTFContext() {} + +void BTFContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + // TODO +} + +bool BTFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { + // TODO + return true; +} + +DILineInfo BTFContext::getLineInfoForAddress(SectionedAddress Address, + DILineInfoSpecifier Specifier) { + BTFInfo &BTF = Impl->BTF; + auto MaybeSecInfo = BTF.SectionLines.find(Address.SectionIndex); + if (MaybeSecInfo == BTF.SectionLines.end()) { + LLVM_DEBUG(llvm::dbgs() << "No sec info for " << Address << "\n"); + return {}; + } + + auto &SecInfo = MaybeSecInfo->second; + auto LineInfo = std::lower_bound( + SecInfo->begin(), SecInfo->end(), Address.Address, + [](BTFLineInfo &Line, uint64_t Addr) { return Line.InsnAddress < Addr; }); + if (LineInfo == SecInfo->end() || LineInfo->InsnAddress != Address.Address) { + LLVM_DEBUG(llvm::dbgs() << "No line info for " << Address << "\n"); + return {}; + } + + DILineInfo Result; + Result.LineSource = BTF.findString(LineInfo->LineOff); + Result.FileName = BTF.findString(LineInfo->FileNameOff); + Result.Line = LineInfo->getLine(); + Result.Column = LineInfo->getCol(); + return Result; +} + +DILineInfo BTFContext::getLineInfoForDataAddress(SectionedAddress Address) { + return DILineInfo(); +} + +DILineInfoTable +BTFContext::getLineInfoForAddressRange(SectionedAddress Address, uint64_t Size, + DILineInfoSpecifier Specifier) { + return DILineInfoTable(); +} + +DIInliningInfo +BTFContext::getInliningInfoForAddress(SectionedAddress Address, + DILineInfoSpecifier Specifier) { + return DIInliningInfo(); +} + +std::vector BTFContext::getLocalsForAddress(SectionedAddress Address) { + return std::vector(); +} + +std::unique_ptr BTFContext::create(const ObjectFile &Obj) { + auto Result = std::make_unique(new struct BTFContext::Impl()); + if (auto E = parseBTFSections(Obj, Result->Impl->BTF)) + WithColor::defaultErrorHandler(std::move(E)); + return Result; +} + +bool BTFContext::hasBTFSections(const ObjectFile &Obj) { + bool HasBTF = false; + bool HasBTFExt = false; + for (auto Sec : Obj.sections()) { + if (auto Name = Sec.getName()) { + HasBTF |= *Name == BTF_SECTION_NAME; + HasBTFExt |= *Name == BTF_EXT_SECTION_NAME; + if (HasBTF && HasBTFExt) + return true; + } + } + return false; +} diff --git a/llvm/lib/DebugInfo/BTF/CMakeLists.txt b/llvm/lib/DebugInfo/BTF/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/BTF/CMakeLists.txt @@ -0,0 +1,8 @@ +add_llvm_component_library(LLVMDebugInfoBTF + BTFContext.cpp + ADDITIONAL_HEADER_DIRS + "${LLVM_MAIN_INCLUDE_DIR}/llvm/DebugInfo/BTF" + + LINK_COMPONENTS + Support + ) diff --git a/llvm/lib/DebugInfo/CMakeLists.txt b/llvm/lib/DebugInfo/CMakeLists.txt --- a/llvm/lib/DebugInfo/CMakeLists.txt +++ b/llvm/lib/DebugInfo/CMakeLists.txt @@ -5,3 +5,4 @@ add_subdirectory(CodeView) add_subdirectory(PDB) add_subdirectory(Symbolize) +add_subdirectory(BTF) diff --git a/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt b/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt --- a/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt +++ b/llvm/lib/DebugInfo/Symbolize/CMakeLists.txt @@ -11,6 +11,7 @@ LINK_COMPONENTS DebugInfoDWARF DebugInfoPDB + DebugInfoBTF Object Support Demangle diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -13,6 +13,7 @@ #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/BTF/BTFContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/PDB/PDB.h" #include "llvm/DebugInfo/PDB/PDBContext.h" @@ -615,6 +616,19 @@ return ModuleOrErr; } +// For BPF programs .BTF.ext section contains line numbers information, +// use it if regular DWARF is not available (e.g. for stripped binary). +static bool useBTFContext(const ObjectFile &Obj) { + auto Arch = Obj.getArch(); + if (Arch != Triple::ArchType::bpfel && Arch != Triple::ArchType::bpfeb) + return false; + + if (Obj.hasDebugInfo()) + return false; + + return BTFContext::hasBTFSections(Obj); +} + Expected LLVMSymbolizer::getOrCreateModuleInfo(const ObjectFile &Obj) { StringRef ObjName = Obj.getFileName(); @@ -622,7 +636,11 @@ if (I != Modules.end()) return I->second.get(); - std::unique_ptr Context = DWARFContext::create(Obj); + std::unique_ptr Context; + if (useBTFContext(Obj)) + Context = BTFContext::create(Obj); + else + Context = DWARFContext::create(Obj); // FIXME: handle COFF object with PDB info to use PDBContext return createModuleInfo(&Obj, std::move(Context), ObjName); } diff --git a/llvm/test/tools/llvm-objdump/BPF/Inputs/test.c b/llvm/test/tools/llvm-objdump/BPF/Inputs/test.c new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/BPF/Inputs/test.c @@ -0,0 +1,16 @@ +extern int consume(int); + +void foo(void) { + consume(1); + consume(2); +} + +__attribute__((section("a"))) +void bar(void) { + consume(3); +} + +__attribute__((section("b"))) +void buz(void) { + consume(4); +} diff --git a/llvm/test/tools/llvm-objdump/BPF/interleaved-source-test.ll b/llvm/test/tools/llvm-objdump/BPF/interleaved-source-test.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/BPF/interleaved-source-test.ll @@ -0,0 +1,119 @@ +; REQUIRES: bpf-registered-target +; +; Verify that llvm-objdump can use .BTF.ext to extract line number +; information in disassembly when DWARF is not available. +; +; The 'sed' part is needed because llc would look for source file in +; order to embed line info when BPF is compiled. +; +; RUN: sed -e "s,SRC_COMPDIR,%p/Inputs,g" %s > %t.ll +; +; First, check bpfel: +; +; RUN: llc --mtriple bpfel %t.ll --filetype=obj -o %t +; RUN: llvm-strip --strip-debug %t +; RUN: llvm-objdump --section-headers %t | FileCheck --check-prefix=SECTIONS %s +; RUN: llvm-objdump --no-addresses --no-show-raw-insn -Sd %t | FileCheck %s +; +; Next, check bpfeb: +; +; RUN: llc --mtriple bpfeb %t.ll --filetype=obj -o %t +; RUN: llvm-strip --strip-debug %t +; RUN: llvm-objdump --section-headers %t | FileCheck --check-prefix=SECTIONS %s +; RUN: llvm-objdump -Sd %t | FileCheck %s +; +; Test case adapted from output of the following command: +; +; clang -g -target bpf -emit-llvm -S ./Inputs/test.c +; +; DIFile::directory is changed to SRC_COMPDIR. +; + +; Make sure that BTF is present and DWARF is stripped: +; +; SECTIONS: .BTF +; SECTIONS: .BTF.ext +; SECTIONS-NOT: .debug_line +; SECTIONS-NOT: .debug_line_str + +; Check source code in disassembly: +; +; CHECK: Disassembly of section .text: +; CHECK-EMPTY: +; CHECK-NEXT: : +; CHECK-NEXT: ; consume(1); +; CHECK-NEXT: r1 = 0x1 +; CHECK-NEXT: call -0x1 +; CHECK-NEXT: ; consume(2); +; CHECK-NEXT: r1 = 0x2 +; CHECK-NEXT: call -0x1 +; CHECK-NEXT: ; } +; CHECK-NEXT: exit +; CHECK-EMPTY: +; CHECK-NEXT: Disassembly of section a: +; CHECK-EMPTY: +; CHECK-NEXT: : +; CHECK-NEXT: ; consume(3); +; CHECK-NEXT: r1 = 0x3 +; CHECK-NEXT: call -0x1 +; CHECK-NEXT: ; } +; CHECK-NEXT: exit +; CHECK-EMPTY: +; CHECK-NEXT: Disassembly of section b: +; CHECK-EMPTY: +; CHECK-NEXT: : +; CHECK-NEXT: ; consume(4); +; CHECK-NEXT: r1 = 0x4 +; CHECK-NEXT: call -0x1 +; CHECK-NEXT: ; } +; CHECK-NEXT: exit + +; Function Attrs: noinline nounwind optnone +define dso_local void @foo() #0 !dbg !7 { + %1 = call i32 @consume(i32 noundef 1), !dbg !12 + %2 = call i32 @consume(i32 noundef 2), !dbg !13 + ret void, !dbg !14 +} + +declare dso_local i32 @consume(i32 noundef) #1 + +; Function Attrs: noinline nounwind optnone +define dso_local void @bar() #0 section "a" !dbg !15 { + %1 = call i32 @consume(i32 noundef 3), !dbg !16 + ret void, !dbg !17 +} + +; Function Attrs: noinline nounwind optnone +define dso_local void @buz() #0 section "b" !dbg !18 { + %1 = call i32 @consume(i32 noundef 4), !dbg !19 + ret void, !dbg !20 +} + +attributes #0 = { noinline nounwind optnone "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "Ubuntu clang version 14.0.0-1ubuntu1", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "/home/eddy/work/llvm-project/llvm/test/tools/llvm-objdump/BPF/Inputs/test.c", directory: "/home/eddy", checksumkind: CSK_MD5, checksum: "73d9dbf2a4ebbbb25f026b623953473b") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"frame-pointer", i32 2} +!6 = !{!"Ubuntu clang version 14.0.0-1ubuntu1"} +!7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !11) +!8 = !DIFile(filename: "test.c", directory: "SRC_COMPDIR", checksumkind: CSK_MD5, checksum: "73d9dbf2a4ebbbb25f026b623953473b") +!9 = !DISubroutineType(types: !10) +!10 = !{null} +!11 = !{} +!12 = !DILocation(line: 4, column: 3, scope: !7) +!13 = !DILocation(line: 5, column: 3, scope: !7) +!14 = !DILocation(line: 6, column: 1, scope: !7) +!15 = distinct !DISubprogram(name: "bar", scope: !8, file: !8, line: 9, type: !9, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !11) +!16 = !DILocation(line: 10, column: 3, scope: !15) +!17 = !DILocation(line: 11, column: 1, scope: !15) +!18 = distinct !DISubprogram(name: "buz", scope: !8, file: !8, line: 14, type: !9, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !11) +!19 = !DILocation(line: 15, column: 3, scope: !18) +!20 = !DILocation(line: 16, column: 1, scope: !18) diff --git a/llvm/tools/llvm-objdump/SourcePrinter.h b/llvm/tools/llvm-objdump/SourcePrinter.h --- a/llvm/tools/llvm-objdump/SourcePrinter.h +++ b/llvm/tools/llvm-objdump/SourcePrinter.h @@ -151,6 +151,9 @@ StringRef ObjectFilename, StringRef Delimiter, LiveVariablePrinter &LVP); + std::optional getLine(const DILineInfo &LineInfo, + StringRef ObjectFilename); + public: SourcePrinter() = default; SourcePrinter(const object::ObjectFile *Obj, StringRef DefaultArch); diff --git a/llvm/tools/llvm-objdump/SourcePrinter.cpp b/llvm/tools/llvm-objdump/SourcePrinter.cpp --- a/llvm/tools/llvm-objdump/SourcePrinter.cpp +++ b/llvm/tools/llvm-objdump/SourcePrinter.cpp @@ -452,6 +452,31 @@ } } +std::optional SourcePrinter::getLine(const DILineInfo &LineInfo, + StringRef ObjectFilename) { + if (LineInfo.LineSource) + return LineInfo.LineSource; + + if (SourceCache.find(LineInfo.FileName) == SourceCache.end()) + if (!cacheSource(LineInfo)) + return std::nullopt; + + auto LineBuffer = LineCache.find(LineInfo.FileName); + if (LineBuffer == LineCache.end()) + return std::nullopt; + + 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 std::nullopt; + } + + // 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, @@ -461,21 +486,8 @@ 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]; + if (auto Line = getLine(LineInfo, ObjectFilename)) { + OS << Delimiter << Line; LVP.printBetweenInsts(OS, true); } }