Skip to content

Commit 266877a

Browse files
committed
[llvm-objdump] Print method name from debug info in disassembly output.
Summary: GNU objdump prints the method name in disassembly output, and upon further investigation this seems to come from debug info, not the symbol table. Some additional refactoring is necessary to make this work even when the line number is 0/the filename is unknown. The added test case includes a note for this scenario. See http://llvm.org/PR41341 for more info. Reviewers: dblaikie, MaskRay, jhenderson Reviewed By: MaskRay Subscribers: ormris, jvesely, aprantl, kerbowa, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D74507
1 parent 0050e8f commit 266877a

File tree

6 files changed

+166
-24
lines changed

6 files changed

+166
-24
lines changed

llvm/test/tools/llvm-objdump/AMDGPU/source-lines.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
; Prologue.
77
; LINE: source_lines_test:
8+
; LINE-NEXT: ; source_lines_test():
89
; LINE-NEXT: ; {{.*}}source-lines.cl:1
910
; Kernel.
1011
; LINE: v_mov_b32_e32 v{{[0-9]+}}, 0x777

llvm/test/tools/llvm-objdump/Hexagon/source-interleave-hexagon.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ attributes #1 = { nounwind readnone }
6666
!22 = !DILocation(line: 8, column: 13, scope: !14)
6767
!23 = !DILocation(line: 8, column: 3, scope: !14)
6868
; LINES: main:
69+
; LINES-NEXT: main():
6970
; LINES-NEXT: SRC_COMPDIR/source-interleave-hexagon.c:6
7071

7172
; SOURCE: main:
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
;; Verify that llvm-objdump -l also prints the function name in disassembly
2+
;; output, getting it from the debug info.
3+
4+
; RUN: llc < %s -o %t.o -filetype=obj -mtriple=x86_64-unknown-linux-gnu
5+
; RUN: llvm-objdump -dl %t.o | FileCheck %s --check-prefixes=CHECK,CHECK-NO-DEMANGLE
6+
7+
; RUN: llc < %s -o %t.o -filetype=obj -mtriple=x86_64-unknown-linux-gnu
8+
; RUN: llvm-objdump -dlC %t.o | FileCheck %s --check-prefixes=CHECK,CHECK-DEMANGLE
9+
10+
; CHECK: 0000000000000000 foo:
11+
; CHECK-NEXT: ; foo():
12+
; CHECK-NEXT: ; /tmp/src.cc:1
13+
; CHECK-NEXT: 0: b8 05 00 00 00 movl $5, %eax
14+
; CHECK-NEXT: 5: c3 retq
15+
16+
; CHECK-NO-DEMANGLE: 0000000000000010 _ZN3xyz3barEv:
17+
; CHECK-NO-DEMANGLE-NEXT: ; _ZN3xyz3barEv():
18+
; CHECK-DEMANGLE: 0000000000000010 xyz::bar():
19+
; CHECK-DEMANGLE-NEXT: ; xyz::bar():
20+
21+
; CHECK-NEXT: ; /tmp/src.cc:3
22+
; CHECK-NEXT: 10: b8 0a 00 00 00 movl $10, %eax
23+
; CHECK-NEXT: 15: c3 retq
24+
25+
; CHECK-NO-DEMANGLE: 0000000000000020 _ZN3xyz3bazEv:
26+
; CHECK-NO-DEMANGLE-NEXT: ; _ZN3xyz3bazEv():
27+
; CHECK-DEMANGLE: 0000000000000020 xyz::baz():
28+
; CHECK-DEMANGLE-NEXT: ; xyz::baz():
29+
30+
; CHECK-NEXT: ; /tmp/src.cc:3
31+
; CHECK-NEXT: 20: b8 14 00 00 00 movl $20, %eax
32+
; CHECK-NEXT: 25: c3 retq
33+
34+
;; When symbol information is missing, we can get function names from debug
35+
;; info. The IR is intentionally doctored to have different names in debug info
36+
;; for the test case here.
37+
; RUN: llvm-strip %t.o -N foo -N _ZN3xyz3barEv -N _ZN3xyz3bazEv -o %t-stripped.o
38+
; RUN: llvm-objdump -dlC %t-stripped.o | FileCheck %s --check-prefix=STRIPPED
39+
40+
; STRIPPED: 0000000000000000 .text:
41+
; STRIPPED-NEXT: ; Function1():
42+
; STRIPPED-NEXT: ; /tmp/src.cc:1
43+
; STRIPPED-NEXT: 0: b8 05 00 00 00 movl $5, %eax
44+
; STRIPPED-NEXT: 5: c3 retq
45+
46+
; STRIPPED: ; xyz::bar():
47+
; STRIPPED-NEXT: ; /tmp/src.cc:3
48+
; STRIPPED-NEXT: 10: b8 0a 00 00 00 movl $10, %eax
49+
; STRIPPED-NEXT: 15: c3 retq
50+
51+
; STRIPPED: ; xyz::baz():
52+
; STRIPPED-NEXT: ; /tmp/src.cc:3
53+
; STRIPPED-NEXT: 20: b8 14 00 00 00 movl $20, %eax
54+
; STRIPPED-NEXT: 25: c3 retq
55+
56+
;; IR adapted from:
57+
;; $ cat /tmp/src.cc
58+
;; extern "C" int foo() { return 5; };
59+
;; namespace xyz {
60+
;; int bar() { return 10; } int baz() { return 20; }
61+
;; } // namespace xyz
62+
;; $ clang++ -O -g -c /tmp/src.cc -S -emit-llvm
63+
;; Note: bar() and baz() intentionally written on the same line.
64+
65+
; ModuleID = '/tmp/src.cc'
66+
source_filename = "/tmp/src.cc"
67+
target triple = "x86_64-unknown-linux-gnu"
68+
69+
define dso_local i32 @foo() #0 !dbg !7 {
70+
entry:
71+
ret i32 5, !dbg !12
72+
}
73+
74+
define dso_local i32 @_ZN3xyz3barEv() #0 !dbg !13 {
75+
entry:
76+
ret i32 10, !dbg !15
77+
}
78+
79+
define dso_local i32 @_ZN3xyz3bazEv() #0 !dbg !16 {
80+
entry:
81+
ret i32 20, !dbg !17
82+
}
83+
84+
attributes #0 = { "frame-pointer"="none" }
85+
86+
!llvm.dbg.cu = !{!0}
87+
!llvm.module.flags = !{!3, !4, !5}
88+
!llvm.ident = !{!6}
89+
90+
!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)
91+
; Note: <invalid> triggers a bad DILineInfo. We still print "Function1()".
92+
!1 = !DIFile(filename: "<invalid>", directory: "")
93+
!2 = !{}
94+
!3 = !{i32 7, !"Dwarf Version", i32 4}
95+
!4 = !{i32 2, !"Debug Info Version", i32 3}
96+
!5 = !{i32 1, !"wchar_size", i32 4}
97+
!6 = !{!"clang trunk)"}
98+
!7 = distinct !DISubprogram(name: "Function1", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
99+
!8 = !DIFile(filename: "/tmp/src.cc", directory: "")
100+
!9 = !DISubroutineType(types: !10)
101+
!10 = !{!11}
102+
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
103+
!12 = !DILocation(line: 1, column: 24, scope: !7)
104+
!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)
105+
!14 = !DINamespace(name: "xyz", scope: null)
106+
!15 = !DILocation(line: 3, column: 13, scope: !13)
107+
!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)
108+
!17 = !DILocation(line: 3, column: 38, scope: !16)

llvm/test/tools/llvm-objdump/X86/source-interleave-x86_64.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# RUN: FileCheck --check-prefix=SOURCE --strict-whitespace %s < %t2
1111

1212
# LINES: main:
13+
# LINES-NEXT: ; main():
1314
# LINES-NEXT: ; {{[ -\(\)_A-Za-z0-9.\\/:]+}}source-interleave-x86_64.c:6
1415

1516
# SOURCE: main:

llvm/test/tools/llvm-objdump/embedded-source.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
; }
1414

1515
; LINE: main:
16+
; LINE-NEXT: ; main():
1617
; LINE-NEXT: ; {{.*}}embedded-source.c:1
1718
; LINE-NEXT: pushq %rbp
1819
; LINE: ; {{.*}}embedded-source.c:2

llvm/tools/llvm-objdump/llvm-objdump.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -553,13 +553,20 @@ class SourcePrinter {
553553
private:
554554
bool cacheSource(const DILineInfo& LineInfoFile);
555555

556+
void printLines(raw_ostream &OS, const DILineInfo &LineInfo,
557+
StringRef Delimiter);
558+
559+
void printSources(raw_ostream &OS, const DILineInfo &LineInfo,
560+
StringRef ObjectFilename, StringRef Delimiter);
561+
556562
public:
557563
SourcePrinter() = default;
558564
SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch)
559565
: Obj(Obj), WarnedNoDebugInfo(false) {
560566
symbolize::LLVMSymbolizer::Options SymbolizerOpts;
561-
SymbolizerOpts.PrintFunctions = DILineInfoSpecifier::FunctionNameKind::None;
562-
SymbolizerOpts.Demangle = false;
567+
SymbolizerOpts.PrintFunctions =
568+
DILineInfoSpecifier::FunctionNameKind::LinkageName;
569+
SymbolizerOpts.Demangle = Demangle;
563570
SymbolizerOpts.DefaultArch = std::string(DefaultArch);
564571
Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
565572
}
@@ -624,34 +631,57 @@ void SourcePrinter::printSourceLine(raw_ostream &OS,
624631
reportWarning(Warning, ObjectFilename);
625632
WarnedNoDebugInfo = true;
626633
}
627-
return;
628634
}
629635

630-
if (LineInfo.Line == 0 || ((OldLineInfo.Line == LineInfo.Line) &&
631-
(OldLineInfo.FileName == LineInfo.FileName)))
632-
return;
633-
634636
if (PrintLines)
637+
printLines(OS, LineInfo, Delimiter);
638+
if (PrintSource)
639+
printSources(OS, LineInfo, ObjectFilename, Delimiter);
640+
OldLineInfo = LineInfo;
641+
}
642+
643+
void SourcePrinter::printLines(raw_ostream &OS, const DILineInfo &LineInfo,
644+
StringRef Delimiter) {
645+
bool PrintFunctionName = LineInfo.FunctionName != DILineInfo::BadString &&
646+
LineInfo.FunctionName != OldLineInfo.FunctionName;
647+
if (PrintFunctionName) {
648+
OS << Delimiter << LineInfo.FunctionName;
649+
// If demangling is successful, FunctionName will end with "()". Print it
650+
// only if demangling did not run or was unsuccessful.
651+
if (!StringRef(LineInfo.FunctionName).endswith("()"))
652+
OS << "()";
653+
OS << ":\n";
654+
}
655+
if (LineInfo.FileName != DILineInfo::BadString && LineInfo.Line != 0 &&
656+
(OldLineInfo.Line != LineInfo.Line ||
657+
OldLineInfo.FileName != LineInfo.FileName || PrintFunctionName))
635658
OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n";
636-
if (PrintSource) {
637-
if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
638-
if (!cacheSource(LineInfo))
639-
return;
640-
auto LineBuffer = LineCache.find(LineInfo.FileName);
641-
if (LineBuffer != LineCache.end()) {
642-
if (LineInfo.Line > LineBuffer->second.size()) {
643-
reportWarning(
644-
formatv(
645-
"debug info line number {0} exceeds the number of lines in {1}",
646-
LineInfo.Line, LineInfo.FileName),
647-
ObjectFilename);
648-
return;
649-
}
650-
// Vector begins at 0, line numbers are non-zero
651-
OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n';
659+
}
660+
661+
void SourcePrinter::printSources(raw_ostream &OS, const DILineInfo &LineInfo,
662+
StringRef ObjectFilename,
663+
StringRef Delimiter) {
664+
if (LineInfo.FileName == DILineInfo::BadString || LineInfo.Line == 0 ||
665+
(OldLineInfo.Line == LineInfo.Line &&
666+
OldLineInfo.FileName == LineInfo.FileName))
667+
return;
668+
669+
if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
670+
if (!cacheSource(LineInfo))
671+
return;
672+
auto LineBuffer = LineCache.find(LineInfo.FileName);
673+
if (LineBuffer != LineCache.end()) {
674+
if (LineInfo.Line > LineBuffer->second.size()) {
675+
reportWarning(
676+
formatv(
677+
"debug info line number {0} exceeds the number of lines in {1}",
678+
LineInfo.Line, LineInfo.FileName),
679+
ObjectFilename);
680+
return;
652681
}
682+
// Vector begins at 0, line numbers are non-zero
683+
OS << Delimiter << LineBuffer->second[LineInfo.Line - 1] << '\n';
653684
}
654-
OldLineInfo = LineInfo;
655685
}
656686

657687
static bool isAArch64Elf(const ObjectFile *Obj) {

0 commit comments

Comments
 (0)