--- /dev/null
+;; Verify that llvm-objdump -l also prints the function name in disassembly
+;; output, getting it from the debug info.
+
+; RUN: llc < %s -o %t.o -filetype=obj -mtriple=x86_64-unknown-linux-gnu
+; RUN: llvm-objdump -dl %t.o | FileCheck %s --check-prefixes=CHECK,CHECK-NO-DEMANGLE
+
+; RUN: llc < %s -o %t.o -filetype=obj -mtriple=x86_64-unknown-linux-gnu
+; RUN: llvm-objdump -dlC %t.o | FileCheck %s --check-prefixes=CHECK,CHECK-DEMANGLE
+
+; CHECK: 0000000000000000 foo:
+; CHECK-NEXT: ; foo():
+; CHECK-NEXT: ; /tmp/src.cc:1
+; CHECK-NEXT: 0: b8 05 00 00 00 movl $5, %eax
+; CHECK-NEXT: 5: c3 retq
+
+; CHECK-NO-DEMANGLE: 0000000000000010 _ZN3xyz3barEv:
+; CHECK-NO-DEMANGLE-NEXT: ; _ZN3xyz3barEv():
+; CHECK-DEMANGLE: 0000000000000010 xyz::bar():
+; CHECK-DEMANGLE-NEXT: ; xyz::bar():
+
+; CHECK-NEXT: ; /tmp/src.cc:3
+; CHECK-NEXT: 10: b8 0a 00 00 00 movl $10, %eax
+; CHECK-NEXT: 15: c3 retq
+
+; CHECK-NO-DEMANGLE: 0000000000000020 _ZN3xyz3bazEv:
+; CHECK-NO-DEMANGLE-NEXT: ; _ZN3xyz3bazEv():
+; CHECK-DEMANGLE: 0000000000000020 xyz::baz():
+; CHECK-DEMANGLE-NEXT: ; xyz::baz():
+
+; CHECK-NEXT: ; /tmp/src.cc:3
+; CHECK-NEXT: 20: b8 14 00 00 00 movl $20, %eax
+; CHECK-NEXT: 25: c3 retq
+
+;; When symbol information is missing, we can get function names from debug
+;; info. The IR is intentionally doctored to have different names in debug info
+;; for the test case here.
+; RUN: llvm-strip %t.o -N foo -N _ZN3xyz3barEv -N _ZN3xyz3bazEv -o %t-stripped.o
+; RUN: llvm-objdump -dlC %t-stripped.o | FileCheck %s --check-prefix=STRIPPED
+
+; STRIPPED: 0000000000000000 .text:
+; STRIPPED-NEXT: ; Function1():
+; STRIPPED-NEXT: ; /tmp/src.cc:1
+; STRIPPED-NEXT: 0: b8 05 00 00 00 movl $5, %eax
+; STRIPPED-NEXT: 5: c3 retq
+
+; STRIPPED: ; xyz::bar():
+; STRIPPED-NEXT: ; /tmp/src.cc:3
+; STRIPPED-NEXT: 10: b8 0a 00 00 00 movl $10, %eax
+; STRIPPED-NEXT: 15: c3 retq
+
+; STRIPPED: ; xyz::baz():
+; STRIPPED-NEXT: ; /tmp/src.cc:3
+; STRIPPED-NEXT: 20: b8 14 00 00 00 movl $20, %eax
+; STRIPPED-NEXT: 25: c3 retq
+
+;; IR adapted from:
+;; $ cat /tmp/src.cc
+;; extern "C" int foo() { return 5; };
+;; namespace xyz {
+;; int bar() { return 10; } int baz() { return 20; }
+;; } // namespace xyz
+;; $ clang++ -O -g -c /tmp/src.cc -S -emit-llvm
+;; Note: bar() and baz() intentionally written on the same line.
+
+; ModuleID = '/tmp/src.cc'
+source_filename = "/tmp/src.cc"
+target triple = "x86_64-unknown-linux-gnu"
+
+define dso_local i32 @foo() #0 !dbg !7 {
+entry:
+ ret i32 5, !dbg !12
+}
+
+define dso_local i32 @_ZN3xyz3barEv() #0 !dbg !13 {
+entry:
+ ret i32 10, !dbg !15
+}
+
+define dso_local i32 @_ZN3xyz3bazEv() #0 !dbg !16 {
+entry:
+ ret i32 20, !dbg !17
+}
+
+attributes #0 = { "frame-pointer"="none" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang trunk", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+; Note: <invalid> triggers a bad DILineInfo. We still print "Function1()".
+!1 = !DIFile(filename: "<invalid>", directory: "")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang trunk)"}
+!7 = distinct !DISubprogram(name: "Function1", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!8 = !DIFile(filename: "/tmp/src.cc", directory: "")
+!9 = !DISubroutineType(types: !10)
+!10 = !{!11}
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !DILocation(line: 1, column: 24, scope: !7)
+!13 = distinct !DISubprogram(name: "bar", linkageName: "_ZN3xyz3barEv", scope: !14, file: !8, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!14 = !DINamespace(name: "xyz", scope: null)
+!15 = !DILocation(line: 3, column: 13, scope: !13)
+!16 = distinct !DISubprogram(name: "baz", linkageName: "_ZN3xyz3bazEv", scope: !14, file: !8, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!17 = !DILocation(line: 3, column: 38, scope: !16)
private:
bool cacheSource(const DILineInfo& LineInfoFile);
+ void printLines(raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef Delimiter);
+
+ void printSources(raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef ObjectFilename, StringRef Delimiter);
+
public:
SourcePrinter() = default;
SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch)
: Obj(Obj), WarnedNoDebugInfo(false) {
symbolize::LLVMSymbolizer::Options SymbolizerOpts;
- SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None;
- SymbolizerOpts.Demangle = false;
+ SymbolizerOpts.PrintFunctions =
+ DILineInfoSpecifier::FunctionNameKind::LinkageName;
+ SymbolizerOpts.Demangle = Demangle;
SymbolizerOpts.DefaultArch = std::string(DefaultArch);
Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
}
reportWarning(Warning, ObjectFilename);
WarnedNoDebugInfo = true;
}
- return;
}
- if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) &&
- (OldLineInfo.FileName == LineInfo.FileName)))
- return;
-
if (PrintLines)
+ printLines(OS, LineInfo, Delimiter);
+ if (PrintSource)
+ printSources(OS, LineInfo, ObjectFilename, Delimiter);
+ OldLineInfo = LineInfo;
+}
+
+void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef Delimiter) {
+ 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 << "\n";
- if (PrintSource) {
- 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] << '\n';
+}
+
+void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo,
+ StringRef ObjectFilename,
+ StringRef Delimiter) {
+ 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] << '\n';
}
- OldLineInfo = LineInfo;
}
static bool isAArch64Elf(const ObjectFile *Obj) {