(Presented in COSCUP 2022)
Debug information is a mapping between the original source code and low-level binary locations. It provides developers powerful insights to diagnose problems (via debuggers) in their code and acts as one of the most important foundations for modern software development. Furthermore, in recent years, we are seeing increasing demands of high quality debug information for highly optimized applications that are otherwise “un-debuggable”. For instance, debugging unoptimized games is generally not feasible since it’s likely to miss every single frame. In this talk, we are going to introduce how debug information works and how compilers generate proper debug info even with extensive levels of optimization enabled. At the end of this talk, you will gain insights into the structure of debug information and learn key compiler engineering knowledge on generating high quality debug info for critical, highly optimized software.
20. Example Input
8
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
*Host Architecture: x86_64
21. Example Input
8
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
$ cc -g -c demo.c -o demo.o
*Host Architecture: x86_64
22. Example Input
8
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
$ cc -g -c demo.c -o demo.o
$ llvm-dva demo.o …
*Host Architecture: x86_64
23. llvm-dva: Visualizing Debug Info
9
{CompileUnit} ‘demo.c'
....
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
....
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
3 {Line}
{Code} 'endbr64'
{Code} 'pushq %rbp'
....
4 {Line}
{Code} 'movl $0x0, -0x8(%rbp)'
{Code} 'movl $0x0, -0x4(%rbp)'
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
24. llvm-dva: Visualizing Debug Info
10
{CompileUnit} ‘demo.c'
....
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
....
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
3 {Line}
{Code} 'endbr64'
{Code} 'pushq %rbp'
....
4 {Line}
{Code} 'movl $0x0, -0x8(%rbp)'
{Code} 'movl $0x0, -0x4(%rbp)'
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
25. llvm-dva: Visualizing Debug Info
10
{CompileUnit} ‘demo.c'
....
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
....
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
3 {Line}
{Code} 'endbr64'
{Code} 'pushq %rbp'
....
4 {Line}
{Code} 'movl $0x0, -0x8(%rbp)'
{Code} 'movl $0x0, -0x4(%rbp)'
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
26. llvm-dva: Visualizing Debug Info
11
{CompileUnit} ‘demo.c'
....
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
....
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
3 {Line}
{Code} 'endbr64'
{Code} 'pushq %rbp'
....
4 {Line}
{Code} 'movl $0x0, -0x8(%rbp)'
{Code} 'movl $0x0, -0x4(%rbp)'
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
27. llvm-dva: Visualizing Debug Info
11
{CompileUnit} ‘demo.c'
....
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
....
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
3 {Line}
{Code} 'endbr64'
{Code} 'pushq %rbp'
....
4 {Line}
{Code} 'movl $0x0, -0x8(%rbp)'
{Code} 'movl $0x0, -0x4(%rbp)'
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
28. llvm-dva: Visualizing Debug Info
12
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
Line numbers & instructions
29. llvm-dva: Visualizing Debug Info
12
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
Line numbers & instructions
30. llvm-dva: Visualizing Debug Info
12
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
Where ‘c’ is stored
Line numbers & instructions
31. llvm-dva: Visualizing Debug Info
12
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
Where ‘c’ is stored
PC Address Line Number
Assembly
(NOT stored in debug info)
0x1C 5 cmpl $0x0,-0x18(%rbp)
0x20 5 je 0xc
Line numbers & instructions
32. llvm-dva: Visualizing Debug Info
13
{CompileUnit} ‘demo.c'
....
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
....
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
3 {Line}
{Code} 'endbr64'
{Code} 'pushq %rbp'
....
4 {Line}
{Code} 'movl $0x0, -0x8(%rbp)'
{Code} 'movl $0x0, -0x4(%rbp)'
5 {Line}
{Code} 'cmpl $0x0, -0x18(%rbp)'
{Code} 'je 0xc’
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
33. llvm-dva: Visualizing Debug Info
14
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
Variable locations
34. llvm-dva: Visualizing Debug Info
14
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
Variable locations
35. llvm-dva: Visualizing Debug Info
15
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Variable locations
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
Current Stack Frame
Lo Address
Hi Address
36. llvm-dva: Visualizing Debug Info
15
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Variable locations
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
Current Stack Frame
Lo Address
Hi Address
fbreg
37. llvm-dva: Visualizing Debug Info
15
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Variable locations
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
-24
Current Stack Frame
Lo Address
Hi Address
fbreg
38. llvm-dva: Visualizing Debug Info
15
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Variable locations
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
-24
-36
Current Stack Frame
Lo Address
Hi Address
fbreg
39. llvm-dva: Visualizing Debug Info
15
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Variable locations
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
-24
-36
-40
Current Stack Frame
Lo Address
Hi Address
fbreg
40. llvm-dva: Visualizing Debug Info
16
{CompileUnit} ‘demo.c'
1 {Struct} 'Point'
1 {Member} public 'x' -> 'int'
{Location}
{Entry} offset 0
1 {Member} public 'y' -> 'int'
{Location}
{Entry} offset 4
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Type Layout
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
Current Stack Frame
Lo Address
Hi Address
41. llvm-dva: Visualizing Debug Info
16
{CompileUnit} ‘demo.c'
1 {Struct} 'Point'
1 {Member} public 'x' -> 'int'
{Location}
{Entry} offset 0
1 {Member} public 'y' -> 'int'
{Location}
{Entry} offset 4
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Type Layout
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
Current Stack Frame
Lo Address
Hi Address
42. llvm-dva: Visualizing Debug Info
16
{CompileUnit} ‘demo.c'
1 {Struct} 'Point'
1 {Member} public 'x' -> 'int'
{Location}
{Entry} offset 0
1 {Member} public 'y' -> 'int'
{Location}
{Entry} offset 4
3 {Function} extern not_inlined 'foo' -> 'int'
3 {Parameter} 'k' -> 'int'
{Location}
{Entry} fbreg -36
3 {Parameter} 'c' -> 'int'
{Location}
{Entry} fbreg -40
4 {Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
Type Layout
Variable ‘point’
Variable ‘k’
Variable ‘c’
Previous Frame Ptr
Return Address
Current Stack Frame
Lo Address
Hi Address
Field ‘x’
Field ‘y’
+0
+4
49. Debug Info Standards
18
DWARF CodeView
• Default format in Linux, Apple platforms,
most of the Unix
• Supported by GNU & LLVM toolchain
50. Debug Info Standards
18
DWARF CodeView
• Default format in Linux, Apple platforms,
most of the Unix
• Supported by GNU & LLVM toolchain
• Container formats: DWO, DWP, dSYM
51. Debug Info Standards
18
DWARF CodeView
• Default format in Linux, Apple platforms,
most of the Unix
• Supported by GNU & LLVM toolchain
• Container formats: DWO, DWP, dSYM
• Default format in Windows (MSVC)
52. Debug Info Standards
18
DWARF CodeView
• Default format in Linux, Apple platforms,
most of the Unix
• Supported by GNU & LLVM toolchain
• Container formats: DWO, DWP, dSYM
• Default format in Windows (MSVC)
• Supported by MSVC & LLVM toolchain
53. Debug Info Standards
18
DWARF CodeView
• Default format in Linux, Apple platforms,
most of the Unix
• Supported by GNU & LLVM toolchain
• Container formats: DWO, DWP, dSYM
• Default format in Windows (MSVC)
• Supported by MSVC & LLVM toolchain
• Container format: PDB
58. Compilation Pipeline: A Crash Course
21
Source Code
AST
Parse
Intermediate
Representation
(IR)
Native Code
(e.g. *.o
fi
les)
59. Compilation Pipeline: A Crash Course
21
Source Code
AST
Parse
Intermediate
Representation
(IR)
Another
Intermediate
Representation
Native Code
(e.g. *.o
fi
les)
60. Debug Info in a Compilation Pipeline: Highlights
22
Source Code
AST
Parse
Intermediate
Representation
(IR)
Another
Intermediate
Representation
Native Code
(e.g. *.o
fi
les)
61. Debug Info in a Compilation Pipeline: Highlights
22
Source Code
AST
Parse
Intermediate
Representation
(IR)
Another
Intermediate
Representation
Native Code
(e.g. *.o
fi
les)
• How to “carry” debug info in IR ?
62. Debug Info in a Compilation Pipeline: Highlights
22
Source Code
AST
Parse
Intermediate
Representation
(IR)
Another
Intermediate
Representation
Native Code
(e.g. *.o
fi
les)
• How to “carry” debug info in IR ?
• Correctly translate from source to debug info in IR
63. Debug Info in a Compilation Pipeline: Highlights
22
Source Code
AST
Parse
Intermediate
Representation
(IR)
Another
Intermediate
Representation
Native Code
(e.g. *.o
fi
les)
• How to “carry” debug info in IR ?
• Correctly translate from source to debug info in IR
• Preserve debug info across transformations (e.g. optimizations)
64. Case Study: LLVM / Clang
23
void foo() {
int x = 9;
int y = 4;
}
C Source (foo.c)
65. Case Study: LLVM / Clang
23
define void @foo() {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1
store i32 4, i32* %2
ret void
}
void foo() {
int x = 9;
int y = 4;
}
C Source (foo.c)
LLVM IR (foo.ll)
$ clang -emit-llvm -S foo.c -o foo.ll
66. Case Study: LLVM / Clang
23
define void @foo() {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1
store i32 4, i32* %2
ret void
}
void foo() {
int x = 9;
int y = 4;
}
C Source (foo.c)
LLVM IR (foo.ll)
$ clang -emit-llvm -S foo.c -o foo.ll
Allocating stack space
67. Case Study: LLVM / Clang
23
define void @foo() {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1
store i32 4, i32* %2
ret void
}
void foo() {
int x = 9;
int y = 4;
}
C Source (foo.c)
LLVM IR (foo.ll)
$ clang -emit-llvm -S foo.c -o foo.ll
70. Debug Info in LLVM IR
25
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1, !dbg !13
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
void foo() {
int x = 9;
int y = 4;
}
C Source
1
2
3
4
LLVM IR
71. Debug Info in LLVM IR
25
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1, !dbg !13
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
void foo() {
int x = 9;
int y = 4;
}
C Source
1
2
3
4
LLVM IR
72. Debug Info in LLVM IR
25
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1, !dbg !13
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
void foo() {
int x = 9;
int y = 4;
}
C Source
1
2
3
4
LLVM IR
!13 = !DILocation(line: 2, column: 7, ...) At the bottom of IR
fi
le
73. Debug Info in LLVM IR
25
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
store i32 9, i32* %1, !dbg !13
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
void foo() {
int x = 9;
int y = 4;
}
C Source
1
2
3
4
LLVM IR
!13 = !DILocation(line: 2, column: 7, ...)
!15 = !DILocation(line: 3, column: 7, ...)
At the bottom of IR
fi
le
74. Debug Info in LLVM IR
26
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
call void @llvm.dbg.declare(metadata i32* %1, metadata !11, …), !dbg !13
store i32 9, i32* %1, !dbg !13
call void @llvm.dbg.declare(metadata i32* %2, metadata !14, …), !dbg !15
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
LLVM IR
75. Debug Info in LLVM IR
27
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
call void @llvm.dbg.declare(metadata i32* %1, metadata !11, …), !dbg !13
store i32 9, i32* %1, !dbg !13
call void @llvm.dbg.declare(metadata i32* %2, metadata !14, …), !dbg !15
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
LLVM IR
76. Debug Info in LLVM IR
27
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
call void @llvm.dbg.declare(metadata i32* %1, metadata !11, …), !dbg !13
store i32 9, i32* %1, !dbg !13
call void @llvm.dbg.declare(metadata i32* %2, metadata !14, …), !dbg !15
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
LLVM IR
!11 = !DILocalVariable(name: "x", ..., line: 2, ...)
77. Debug Info in LLVM IR
27
define void @foo() !dbg !7 {
%1 = alloca i32
%2 = alloca i32
call void @llvm.dbg.declare(metadata i32* %1, metadata !11, …), !dbg !13
store i32 9, i32* %1, !dbg !13
call void @llvm.dbg.declare(metadata i32* %2, metadata !14, …), !dbg !15
store i32 4, i32* %2, !dbg !15
ret void, !dbg !16
}
LLVM IR
!11 = !DILocalVariable(name: "x", ..., line: 2, ...)
96. Debugging Optimized Programs
• Games
• Di
ffi
cult (if not nearly impossible) to debug low-FPS games
• Embedded systems
• Size optimization is usually a hard requirement
33
97. Debugging Optimized Programs
• Games
• Di
ffi
cult (if not nearly impossible) to debug low-FPS games
• Embedded systems
• Size optimization is usually a hard requirement
• Easier debugging on release binaries
• E.g. Using core
fi
les directly from customers
33
98. Recall: Early Example Code
34
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
$ clang -g foo.c -emit-llvm -S
111. Preserving Debug Info in Optimized Code
• Most modern compilers preserve debug information as part of the code
transformations (e.g. optimizations)
43
112. Preserving Debug Info in Optimized Code
• Most modern compilers preserve debug information as part of the code
transformations (e.g. optimizations)
• Challenges
43
113. Preserving Debug Info in Optimized Code
• Most modern compilers preserve debug information as part of the code
transformations (e.g. optimizations)
• Challenges
• It’s easy for compiler developers to forget to handle debug info
43
114. Preserving Debug Info in Optimized Code
• Most modern compilers preserve debug information as part of the code
transformations (e.g. optimizations)
• Challenges
• It’s easy for compiler developers to forget to handle debug info
• It’s not possible to (faithfully) map optimized code back to source
locations in every cases
43
115. Preserving Debug Info in Optimized Code
• Most modern compilers preserve debug information as part of the code
transformations (e.g. optimizations)
• Challenges
• It’s easy for compiler developers to forget to handle debug info
• It’s not possible to (faithfully) map optimized code back to source
locations in every cases
• Debug info in optimized binaries is preserved on a best-e
ff
ort basis
43
144. Preserving Debug Locations in LLVM
57
Principles
• Don’t create misleading debug locations that are only correct in some cases
Actions
Keep Merge Delete
145. Preserving Debug Locations in LLVM
57
Principles
• Don’t create misleading debug locations that are only correct in some cases
• If you’re not sure, just drop the debug locations
Actions
Keep Merge Delete
146. Preserving Debug Locations in LLVM
57
Principles
• Don’t create misleading debug locations that are only correct in some cases
• If you’re not sure, just drop the debug locations
• Otherwise, preserve as much debug locations as possible
Actions
Keep Merge Delete
147. Debug Locations in our Example Code
58
define i32 @foo(i32 %0, i32 %1) {
%3 = icmp eq i32 %1, 0
%4 = select i1 %3, i32 0, i32 %0
%5 = add nsw i32 %4, %1
ret i32 %5
}
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
148. Debug Locations in our Example Code
59
define i32 @foo(i32 %0, i32 %1) {
%3 = icmp eq i32 %1, 0
%4 = select i1 %3, i32 0, i32 %0
%5 = add nsw i32 %4, %1
ret i32 %5
}
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
149. Debug Locations in our Example Code
59
define i32 @foo(i32 %0, i32 %1) {
%3 = icmp eq i32 %1, 0
%4 = select i1 %3, i32 0, i32 %0
%5 = add nsw i32 %4, %1
ret i32 %5
}
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
line 6 will only hit conditionally
150. Debug Locations in our Example Code
59
define i32 @foo(i32 %0, i32 %1) {
%3 = icmp eq i32 %1, 0
%4 = select i1 %3, i32 0, i32 %0
%5 = add nsw i32 %4, %1
ret i32 %5
}
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
line 6 will only hit conditionally
“Don’t create misleading debug locations
that are only correct in some cases”
151. Debug Locations in our Example Code
59
define i32 @foo(i32 %0, i32 %1) {
%3 = icmp eq i32 %1, 0
%4 = select i1 %3, i32 0, i32 %0
%5 = add nsw i32 %4, %1
ret i32 %5
}
struct Point { int x, y; };
int foo(int k, int c) {
struct Point point = {x: 0, y: 0};
if (c) {
point.x = k;
point.y = c;
}
return point.x + point.y;
}
1
2
3
4
5
6
7
8
9
10
line 6 will only hit conditionally
“Don’t create misleading debug locations
that are only correct in some cases”
Delete
152. Preserving Debug Locations in LLVM
60
Prantl and Kumar, US LLVM Dev Meeting 2020
Guidelines for updating debug locations in code transformations
https://tinyurl.com/llvmdebuginfo
Full write-up:
154. Preserving Debug Info in LLVM: Automatically
61
• Common transformation APIs will help you to keep / merge debug
location underlying
155. Preserving Debug Info in LLVM: Automatically
61
• Common transformation APIs will help you to keep / merge debug
location underlying
• e.g. Replace All Uses With (RAUW)
156. Preserving Debug Info in LLVM: Automatically
61
• Common transformation APIs will help you to keep / merge debug
location underlying
• e.g. Replace All Uses With (RAUW)
• Handy debug info utilities to make debug info manipulations easier
157. Preserving Debug Info in LLVM: Automatically
61
• Common transformation APIs will help you to keep / merge debug
location underlying
• e.g. Replace All Uses With (RAUW)
• Handy debug info utilities to make debug info manipulations easier
• salvageDebugInfo helps you to generate llvm.dbg.value intrinsics
158. Preserving Debug Info in LLVM: Automatically
61
• Common transformation APIs will help you to keep / merge debug
location underlying
• e.g. Replace All Uses With (RAUW)
• Handy debug info utilities to make debug info manipulations easier
• salvageDebugInfo helps you to generate llvm.dbg.value intrinsics
• Instruction::applyMergedLocation helps you to merge debug
locations
160. Summary
• We learned how line source locations (e.g. line number) and variable
locations are represented in debug info
62
161. Summary
• We learned how line source locations (e.g. line number) and variable
locations are represented in debug info
• We learned how debug info is stored in LLVM IR
62
162. Summary
• We learned how line source locations (e.g. line number) and variable
locations are represented in debug info
• We learned how debug info is stored in LLVM IR
• The challenges of debug info in optimized binaries, and how LLVM
mitigates those issues
62
166. llvm-dva
• LLVM DVA is still in the process of upstreaming to LLVM
• RFC: https://discourse.llvm.org/t/llvm-dev-rfc-llvm-dva-debug-
information-visual-analyzer/62570
• You can, however, build it with this patch: https://reviews.llvm.org/D88661
• The llvm-dva command I used in this slides:
• llvm-dva --attribute=location,format --output-sort=offset —
print=symbols,lines,instructions,scopes <object file>
66
173. Splitting Debug Info
• Saving disk spaces
• E.g. Able to de-duplicate type information
69
174. Splitting Debug Info
• Saving disk spaces
• E.g. Able to de-duplicate type information
• Attaching debug info
fi
les (e.g. *.dwo) on release binaries
• E.g. When debugging crashes reported by users
69
175. Splitting Debug Info
• Saving disk spaces
• E.g. Able to de-duplicate type information
• Attaching debug info
fi
les (e.g. *.dwo) on release binaries
• E.g. When debugging crashes reported by users
• Apple platforms are doing this by default (i.e. *.dSYM folders)
69
176. 70
{Struct} 'Point'
{Member} public 'x' -> 'int'
{Location}
{Entry} offset 0
{Member} public 'y' -> 'int'
{Location}
{Entry} offset 4
{Function} extern not_inlined 'foo' -> 'int'
{Variable} 'point' -> 'Point'
{Location}
{Entry} fbreg -24
llvm-dva output DWARF dump