Index: llvm/trunk/include/llvm/DebugInfo/DIContext.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/DIContext.h +++ llvm/trunk/include/llvm/DebugInfo/DIContext.h @@ -28,6 +28,10 @@ /// A format-neutral container for source line information. struct DILineInfo { + // DILineInfo contains "" for function/filename it cannot fetch. + static constexpr const char *const BadString = ""; + // Use "??" instead of "" to make our output closer to addr2line. + static constexpr const char *const Addr2LineBadString = "??"; std::string FileName; std::string FunctionName; Optional Source; @@ -38,7 +42,7 @@ // DWARF-specific. uint32_t Discriminator = 0; - DILineInfo() : FileName(""), FunctionName("") {} + DILineInfo() : FileName(BadString), FunctionName(BadString) {} bool operator==(const DILineInfo &RHS) const { return Line == RHS.Line && Column == RHS.Column && @@ -61,9 +65,9 @@ void dump(raw_ostream &OS) { OS << "Line info: "; - if (FileName != "") + if (FileName != BadString) OS << "file '" << FileName << "', "; - if (FunctionName != "") + if (FunctionName != BadString) OS << "function '" << FunctionName << "', "; OS << "line " << Line << ", "; OS << "column " << Column << ", "; @@ -109,7 +113,7 @@ uint64_t Start = 0; uint64_t Size = 0; - DIGlobal() : Name("") {} + DIGlobal() : Name(DILineInfo::BadString) {} }; struct DILocal { Index: llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp +++ llvm/trunk/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -1111,8 +1111,8 @@ if (!CU) return Lines; - std::string FunctionName = ""; uint32_t StartLine = 0; + std::string FunctionName(DILineInfo::BadString); getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, FunctionName, StartLine); Index: llvm/trunk/lib/DebugInfo/Symbolize/DIPrinter.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ llvm/trunk/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -30,11 +30,6 @@ namespace llvm { namespace symbolize { -// By default, DILineInfo contains "" for function/filename it -// cannot fetch. We replace it to "??" to make our output closer to addr2line. -static const char kDILineInfoBadString[] = ""; -static const char kBadString[] = "??"; - // Prints source code around in the FileName the Line. void DIPrinter::printContext(const std::string &FileName, int64_t Line) { if (PrintSourceContext <= 0) @@ -68,16 +63,16 @@ void DIPrinter::print(const DILineInfo &Info, bool Inlined) { if (PrintFunctionNames) { std::string FunctionName = Info.FunctionName; - if (FunctionName == kDILineInfoBadString) - FunctionName = kBadString; + if (FunctionName == DILineInfo::BadString) + FunctionName = DILineInfo::Addr2LineBadString; StringRef Delimiter = PrintPretty ? " at " : "\n"; StringRef Prefix = (PrintPretty && Inlined) ? " (inlined by) " : ""; OS << Prefix << FunctionName << Delimiter; } std::string Filename = Info.FileName; - if (Filename == kDILineInfoBadString) - Filename = kBadString; + if (Filename == DILineInfo::BadString) + Filename = DILineInfo::Addr2LineBadString; else if (Basenames) Filename = llvm::sys::path::filename(Filename); if (!Verbose) { @@ -115,8 +110,8 @@ DIPrinter &DIPrinter::operator<<(const DIGlobal &Global) { std::string Name = Global.Name; - if (Name == kDILineInfoBadString) - Name = kBadString; + if (Name == DILineInfo::BadString) + Name = DILineInfo::Addr2LineBadString; OS << Name << "\n"; OS << Global.Start << " " << Global.Size << "\n"; return *this; Index: llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-invalid-source.test =================================================================== --- llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-invalid-source.test +++ llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-invalid-source.test @@ -1,5 +1,5 @@ ## Test llvm-objdump's --source behaviour when a line number is greater than the -## file length. +## file length and ensure that we emit a warning. # RUN: sed -e "s,SRC_COMPDIR,%/p/Inputs,g" %p/Inputs/source-interleave.ll > %t.ll # RUN: sed -e "s,line: 7,line: 9999,g" %t.ll > %t2.ll @@ -8,9 +8,11 @@ # RUN: llc -o %t2.o -filetype=obj -mtriple=x86_64-pc-linux %t2.ll # RUN: llvm-objdump --source %t.o | FileCheck %s --check-prefixes=CHECK,GOOD -# RUN: llvm-objdump --source %t2.o | FileCheck %s --implicit-check-not="int *b = &a;" +# RUN: llvm-objdump --source %t2.o 2> %t2.e | FileCheck %s --check-prefixes=CHECK --implicit-check-not="int *b = &a;" +# RUN: FileCheck %s --input-file %t2.e --check-prefixes=WARN # CHECK: main: # CHECK-NEXT: ; int main() { +# WARN: warning: debug info line number 9999 exceeds the number of lines in {{.*}}source-interleave-x86_64.c # GOOD: ; int *b = &a; # CHECK: ; return *b + foo(); Index: llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-missing-source.test =================================================================== --- llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-missing-source.test +++ llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-missing-source.test @@ -1,5 +1,5 @@ ## Test that if the source cannot be found that disassembly is still printed, -## and that no source is printed. +## that no source is printed, and that we emit a warning. # RUN: sed -e "s,SRC_COMPDIR,%/t,g" %p/Inputs/source-interleave.ll > %t.ll # RUN: sed -e "s,SRC_COMPDIR,%/p/Inputs,g" %p/Inputs/source-interleave.ll > %t2.ll @@ -7,9 +7,11 @@ # RUN: llc -o %t.o -filetype=obj -mtriple=x86_64-pc-linux %t.ll # RUN: llc -o %t2.o -filetype=obj -mtriple=x86_64-pc-linux %t2.ll -# RUN: llvm-objdump --source %t.o | FileCheck %s --implicit-check-not='main()' +# RUN: llvm-objdump --source %t.o 2> %t.e | FileCheck %s --check-prefixes=CHECK --implicit-check-not='main()' # RUN: llvm-objdump --source %t2.o | FileCheck %s --check-prefixes=CHECK,SOURCE +# RUN: FileCheck %s --input-file %t.e --check-prefixes=WARN +# WARN: warning: failed to find source {{.*}}source-interleave-x86_64.c # CHECK: 0000000000000010 main: # SOURCE-NEXT: ; int main() { # CHECK-NEXT: 10: 55 pushq %rbp Index: llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-no-debug-info.test =================================================================== --- llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-no-debug-info.test +++ llvm/trunk/test/tools/llvm-objdump/X86/source-interleave-no-debug-info.test @@ -1,13 +1,15 @@ ## Test that if an object has no debug information, only the disassembly is -## printed when --source is specified. +## printed when --source is specified, and that we emit a warning. # RUN: sed -e "s,SRC_COMPDIR,%/p/Inputs,g" %p/Inputs/source-interleave.ll > %t.ll # RUN: llc -o %t.o -filetype=obj -mtriple=x86_64-pc-linux %t.ll # RUN: llvm-objcopy --strip-debug %t.o %t2.o # RUN: llvm-objdump --source %t.o | FileCheck %s --check-prefixes=CHECK,SOURCE -# RUN: llvm-objdump --source %t2.o | FileCheck %s --implicit-check-not='main()' +# RUN: llvm-objdump --source %t2.o 2> %t2.e | FileCheck %s --check-prefixes=CHECK --implicit-check-not='main()' +# RUN: FileCheck %s --input-file %t2.e --check-prefixes=WARN +# WARN: warning: failed to parse debug information for {{.*}}2.o # CHECK: 0000000000000010 main: # SOURCE-NEXT: ; int main() { # CHECK-NEXT: 10: 55 pushq %rbp Index: llvm/trunk/tools/llvm-cfi-verify/lib/FileAnalysis.cpp =================================================================== --- llvm/trunk/tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ llvm/trunk/tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -521,7 +521,7 @@ continue; } - if (LineInfo->FileName == "") + if (LineInfo->FileName == DILineInfo::BadString) continue; } Index: llvm/trunk/tools/llvm-objdump/llvm-objdump.h =================================================================== --- llvm/trunk/tools/llvm-objdump/llvm-objdump.h +++ llvm/trunk/tools/llvm-objdump/llvm-objdump.h @@ -129,7 +129,7 @@ void printSectionContents(const object::ObjectFile *O); void printSymbolTable(const object::ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName = StringRef()); -void warn(StringRef Message); +void warn(Twine Message); LLVM_ATTRIBUTE_NORETURN void error(Twine Message); LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, Twine Message); LLVM_ATTRIBUTE_NORETURN void report_error(Error E, StringRef File); Index: llvm/trunk/tools/llvm-objdump/llvm-objdump.cpp =================================================================== --- llvm/trunk/tools/llvm-objdump/llvm-objdump.cpp +++ llvm/trunk/tools/llvm-objdump/llvm-objdump.cpp @@ -51,6 +51,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/GraphWriter.h" #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" @@ -385,12 +386,7 @@ exit(1); } -void warn(StringRef Message) { - WithColor::warning(errs(), ToolName) << Message << ".\n"; - errs().flush(); -} - -static void warn(Twine Message) { +void warn(Twine Message) { // Output order between errs() and outs() matters especially for archive // files where the output is per member object. outs().flush(); @@ -552,17 +548,22 @@ DILineInfo OldLineInfo; const ObjectFile *Obj = nullptr; std::unique_ptr Symbolizer; - // File name to file contents of source + // File name to file contents of source. std::unordered_map> SourceCache; - // Mark the line endings of the cached source + // Mark the line endings of the cached source. std::unordered_map> LineCache; + // Keep track of missing sources. + StringSet<> MissingSources; + // Only emit 'no debug info' warning once. + bool WarnedNoDebugInfo; private: bool cacheSource(const DILineInfo& LineInfoFile); public: SourcePrinter() = default; - SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) { + SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) + : Obj(Obj), WarnedNoDebugInfo(false) { symbolize::LLVMSymbolizer::Options SymbolizerOpts; SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None; SymbolizerOpts.Demangle = false; @@ -572,6 +573,7 @@ virtual ~SourcePrinter() = default; virtual void printSourceLine(raw_ostream &OS, object::SectionedAddress Address, + StringRef ObjectFilename, StringRef Delimiter = "; "); }; @@ -581,8 +583,12 @@ Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source); } else { auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName); - if (!BufferOrError) + if (!BufferOrError) { + if (MissingSources.insert(LineInfo.FileName).second) + warn("failed to find source " + LineInfo.FileName); + return false; + } Buffer = std::move(*BufferOrError); } // Chomp the file to get lines @@ -603,20 +609,33 @@ void SourcePrinter::printSourceLine(raw_ostream &OS, object::SectionedAddress Address, + StringRef ObjectFilename, StringRef Delimiter) { if (!Symbolizer) return; DILineInfo LineInfo = DILineInfo(); auto ExpectedLineInfo = Symbolizer->symbolizeCode(*Obj, Address); + std::string ErrorMessage; if (!ExpectedLineInfo) - consumeError(ExpectedLineInfo.takeError()); + ErrorMessage = toString(ExpectedLineInfo.takeError()); else LineInfo = *ExpectedLineInfo; - if ((LineInfo.FileName == "") || LineInfo.Line == 0 || - ((OldLineInfo.Line == LineInfo.Line) && - (OldLineInfo.FileName == LineInfo.FileName))) + if (LineInfo.FileName == DILineInfo::BadString) { + if (!WarnedNoDebugInfo) { + std::string Warning = + "failed to parse debug information for " + ObjectFilename.str(); + if (!ErrorMessage.empty()) + Warning += ": " + ErrorMessage; + warn(Warning); + WarnedNoDebugInfo = true; + } + return; + } + + if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) && + (OldLineInfo.FileName == LineInfo.FileName))) return; if (PrintLines) @@ -627,8 +646,12 @@ return; auto LineBuffer = LineCache.find(LineInfo.FileName); if (LineBuffer != LineCache.end()) { - if (LineInfo.Line > LineBuffer->second.size()) + if (LineInfo.Line > LineBuffer->second.size()) { + warn(formatv( + "debug info line number {0} exceeds the number of lines in {1}", + LineInfo.Line, LineInfo.FileName)); return; + } // Vector begins at 0, line numbers are non-zero OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n'; } @@ -667,10 +690,10 @@ ArrayRef Bytes, object::SectionedAddress Address, raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, - SourcePrinter *SP, + SourcePrinter *SP, StringRef ObjectFilename, std::vector *Rels = nullptr) { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address); + SP->printSourceLine(OS, Address, ObjectFilename); size_t Start = OS.tell(); if (!NoLeadingAddr) @@ -711,9 +734,10 @@ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, object::SectionedAddress Address, raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, + StringRef ObjectFilename, std::vector *Rels) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ""); + SP->printSourceLine(OS, Address, ObjectFilename, ""); if (!MI) { printLead(Bytes, Address.Address, OS); OS << " "; @@ -750,7 +774,7 @@ OS << Separator; Separator = "\n"; if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address, ""); + SP->printSourceLine(OS, Address, ObjectFilename, ""); printLead(Bytes, Address.Address, OS); OS << Preamble; Preamble = " "; @@ -780,9 +804,10 @@ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, object::SectionedAddress Address, raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, + StringRef ObjectFilename, std::vector *Rels) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address); + SP->printSourceLine(OS, Address, ObjectFilename); if (MI) { SmallString<40> InstStr; @@ -831,9 +856,10 @@ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef Bytes, object::SectionedAddress Address, raw_ostream &OS, StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP, + StringRef ObjectFilename, std::vector *Rels) override { if (SP && (PrintSource || PrintLines)) - SP->printSourceLine(OS, Address); + SP->printSourceLine(OS, Address, ObjectFilename); if (!NoLeadingAddr) OS << format("%8" PRId64 ":", Address.Address / 8); if (!NoShowRawInsn) { @@ -1382,10 +1408,10 @@ if (Size == 0) Size = 1; - PIP.printInst( - *IP, Disassembled ? &Inst : nullptr, Bytes.slice(Index, Size), - {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, outs(), - "", *STI, &SP, &Rels); + PIP.printInst(*IP, Disassembled ? &Inst : nullptr, + Bytes.slice(Index, Size), + {SectionAddr + Index + VMAAdjustment, Section.getIndex()}, + outs(), "", *STI, &SP, Obj->getFileName(), &Rels); outs() << CommentStream.str(); Comments.clear(); Index: llvm/trunk/tools/llvm-xray/func-id-helper.cpp =================================================================== --- llvm/trunk/tools/llvm-xray/func-id-helper.cpp +++ llvm/trunk/tools/llvm-xray/func-id-helper.cpp @@ -36,7 +36,7 @@ ModuleAddress.SectionIndex = object::SectionedAddress::UndefSection; if (auto ResOrErr = Symbolizer.symbolizeCode(BinaryInstrMap, ModuleAddress)) { auto &DI = *ResOrErr; - if (DI.FunctionName == "") + if (DI.FunctionName == DILineInfo::BadString) F << "@(" << std::hex << It->second << ")"; else F << DI.FunctionName;