调试go代码
go version: 1.16
system: Macos
package main
func sum(a, b int) int {
c := a + b
return c
}
func main() {
sum(1, 2)
}
gdb
gdb必须要build出可执行文件
go build -gcflags "-N -l" -ldflags=-compressdwarf=false main.go
- -N 去除编译器优化
- -l 禁止编译器内联
- -compressdwarf=false,自从go1.11,默认压缩调试信息,而Macos无法识别这个信息,所以使用这个参数来禁止压缩,否则在对go代码下断点的时候,会报错
No symbol table is loaded. Use the "file" command,具体详情可看这里
1. 启动gdb 进行debug sudo gdb main 这里不知道为啥,不加sudo会卡死
$ sudo gdb main
GNU gdb (GDB) 11.1
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin20.4.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
Loading Go Runtime support.
(gdb)
2. 显示代码 l
(gdb) l
1 package main
2
3 func sum(a, b int) int {
4 c := a + b
5
6 return c
7 }
8
9 func main() {
10 sum(1, 2)
(gdb)
3.下断点 b 行号或 b 函数名
(gdb) b 10
Breakpoint 1 at 0x105e2bd: file /main.go, line 10.
(gdb) b 6
Breakpoint 2 at 0x105e285: file /main.go, line 6.
(gdb) b main.main
Breakpoint 3 at 0x105e2a0: file /main.go, line 9
4.显示断点信息 info b
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000105e2bd in main.main at /main.go:10
breakpoint already hit 1 time
2 breakpoint keep y 0x000000000105e285 in main.sum at /main.go:6
breakpoint already hit 1 time
3 breakpoint keep y 0x000000000105e2a0 in main.main at /main.go:9
(gdb)
5. 开始运行 r
(gdb) r
Starting program: /main
[New Thread 0x220b of process 31901]
[New Thread 0x1e07 of process 31901]
warning: unhandled dyld version (17)
[New Thread 0x1d17 of process 31901]
[New Thread 0x200b of process 31901]
[New Thread 0x5303 of process 31901]
[New Thread 0x5413 of process 31901]
Thread 2 hit Breakpoint 3, main.main () at /main.go:9
9 func main() {
(gdb)
6.继续运行直到下个断点 c
(gdb) c
Continuing.
Thread 2 hit Breakpoint 1, main.main () at /main.go:10
10 sum(1, 2)
(gdb)
7.打印当前函数frame的信息 f
栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句
(gdb) f
#0 main.main () at /main.go:10
10 sum(1, 2)
8.打印变量值 p
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, main.sum (a=1, b=2, ~r2=0) at /main.go:6
6 return c
(gdb) l
1 package main
2
3 func sum(a, b int) int {
4 c := a + b
5
6 return c
7 }
8
9 func main() {
10 sum(1, 2)
(gdb) p c
$1 = 3
(gdb)
9.单行执行n以及进入函数s
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /main
[New Thread 0x5307 of process 32022]
[New Thread 0x1d1b of process 32022]
warning: unhandled dyld version (17)
[New Thread 0x2013 of process 32022]
[New Thread 0x2213 of process 32022]
[New Thread 0x5103 of process 32022]
[New Thread 0x5207 of process 32022]
Thread 2 hit Breakpoint 3, main.main () at /main.go:9
9 func main() {
(gdb) l
4 c := a + b
5
6 return c
7 }
8
9 func main() {
10 sum(1, 2)
11 }
(gdb) n
Thread 2 hit Breakpoint 1, main.main () at /main.go:10
10 sum(1, 2)
(gdb) s
main.sum (a=1, b=2, ~r2=824634196056) at /main.go:3
3 func sum(a, b int) int {
(gdb) n
4 c := a + b
(gdb) n
Thread 2 hit Breakpoint 2, main.sum (a=1, b=2, ~r2=0) at /main.go:6
6 return c
dlv
~/$ dlv debug
Type 'help' for list of commands.
(dlv) b main.main //下断点
Breakpoint 1 set at 0x1067d2f for main.main() ./main.go:9
(dlv) r //开始运行
Process restarted with PID 32020
(dlv) c //continue
> main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x1067d2f)
4: c := a + b
5:
6: return c
7: }
8:
=> 9: func main() {
10: sum(1, 2)
11: }
(dlv) n //单行调试
> main.main() ./main.go:10 (PC: 0x1067d3d)
5:
6: return c
7: }
8:
9: func main() {
=> 10: sum(1, 2)
11: }
(dlv) s //进入函数
> main.sum() ./main.go:3 (PC: 0x1067ce0)
1: package main
2:
=> 3: func sum(a, b int) int {
4: c := a + b
5:
6: return c
7: }
8:
(dlv) args //打印函数的参数
a = 1
b = 2
~r2 = 824634294360
(dlv) n
> main.sum() ./main.go:4 (PC: 0x1067cf7)
1: package main
2:
3: func sum(a, b int) int {
=> 4: c := a + b
5:
6: return c
7: }
8:
9: func main() {
(dlv) n
> main.sum() ./main.go:6 (PC: 0x1067d05)
1: package main
2:
3: func sum(a, b int) int {
4: c := a + b
5:
=> 6: return c
7: }
8:
9: func main() {
10: sum(1, 2)
11: }
(dlv) p c //打印c的值
3
(dlv)
调试go汇编代码
gdb
1.启动gdb
sudo gdb main
GNU gdb (GDB) 11.1
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin20.4.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
Loading Go Runtime support.
(gdb)
2. 查看main函数的反汇编代码
(gdb) disas 'main.main'
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb)
3.下断点到main函数汇编代码第一行,并开始运行程序,停到刚下的断点上
End of assembler dump.
(gdb) b *0x000000000105e2a0
Breakpoint 1 at 0x105e2a0: file /main.go, line 9.
(gdb) r
Starting program: /main
[New Thread 0x1f03 of process 32258]
[New Thread 0x1d03 of process 32258]
warning: unhandled dyld version (17)
[New Thread 0x1a07 of process 32258]
[New Thread 0x1b03 of process 32258]
[New Thread 0x1c03 of process 32258]
[New Thread 0x2a03 of process 32258]
Thread 2 hit Breakpoint 1, main.main () at /main.go:9
9 func main() {
(gdb
4. 查看当前汇编代码 =>表示CPU即将执行的指令
(gdb) disas
Dump of assembler code for function main.main:
=> 0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
5.汇编指令的单步调试 ni
(gdb) ni
0x000000000105e2a9 9 func main() {
(gdb) ni
0x000000000105e2ad 9 func main() {
(gdb) disas
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
=> 0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
6. 汇编指令级别的si(类似于普通go代码的的s)
(gdb) b *0x000000000105e2ce //这里先下断点到call执行上
Breakpoint 2 at 0x105e2ce: file /main.go, line 10.
(gdb) c // 停在call指令上
Continuing.
Thread 2 hit Breakpoint 2, 0x000000000105e2ce in main.main () at /main.go:10
10 sum(1, 2)
(gdb) disas //查看当前汇编代码
Dump of assembler code for function main.main:
0x000000000105e2a0 <+0>: mov %gs:0x30,%rcx
0x000000000105e2a9 <+9>: cmp 0x10(%rcx),%rsp
0x000000000105e2ad <+13>: jbe 0x105e2e0 <main.main+64>
0x000000000105e2af <+15>: sub $0x20,%rsp
0x000000000105e2b3 <+19>: mov %rbp,0x18(%rsp)
0x000000000105e2b8 <+24>: lea 0x18(%rsp),%rbp
0x000000000105e2bd <+29>: movq $0x1,(%rsp)
0x000000000105e2c5 <+37>: movq $0x2,0x8(%rsp)
=> 0x000000000105e2ce <+46>: call 0x105e260 <main.sum>
0x000000000105e2d3 <+51>: mov 0x18(%rsp),%rbp
0x000000000105e2d8 <+56>: add $0x20,%rsp
0x000000000105e2dc <+60>: ret
0x000000000105e2dd <+61>: nopl (%rax)
0x000000000105e2e0 <+64>: call 0x1059560 <runtime.morestack_noctxt>
0x000000000105e2e5 <+69>: jmp 0x105e2a0 <main.main>
End of assembler dump.
(gdb) si // 进入call指令调用的函数
main.sum (a=1, b=2, ~r2=824634196056) at /main.go:3
3 func sum(a, b int) int {
(gdb) disas //可以看到,进入了sum函数
Dump of assembler code for function main.sum:
=> 0x000000000105e260 <+0>: sub $0x10,%rsp
0x000000000105e264 <+4>: mov %rbp,0x8(%rsp)
0x000000000105e269 <+9>: lea 0x8(%rsp),%rbp
0x000000000105e26e <+14>: movq $0x0,0x28(%rsp)
0x000000000105e277 <+23>: mov 0x18(%rsp),%rax
0x000000000105e27c <+28>: add 0x20(%rsp),%rax
0x000000000105e281 <+33>: mov %rax,(%rsp)
0x000000000105e285 <+37>: mov %rax,0x28(%rsp)
0x000000000105e28a <+42>: mov 0x8(%rsp),%rbp
0x000000000105e28f <+47>: add $0x10,%rsp
0x000000000105e293 <+51>: ret
End of assembler dump.
(gdb)
7. 打印寄存器的值 i r 寄存器名称
End of assembler dump.
(gdb) i r rbp rsp rip
rbp 0xc00004e778 0xc00004e778
rsp 0xc00004e758 0xc00004e758
rip 0x105e260 0x105e260 <main.sum>
(gdb)
dlv
1.启动dlv dlv debug(需要先build成可执行文件) dlv exec
$ dlv exec ./main
Type 'help' for list of commands.
(dlv) b main.main
2.下断点 b main.main
``go (dlv) b main.main Breakpoint 1 set at 0x105e2af for main.main() ./main.go:9
### 3.执行到断点 `c`
```go
(dlv) c
> main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x105e2af)
4: c := a + b
5:
6: return c
7: }
8:
=> 9: func main() {
10: sum(1, 2)
11: }
(dlv) disass
4.查看汇编代码disass
(dlv) disass
TEXT main.main(SB) /main.go
main.go:9 0x105e2a0 65488b0c2530000000 mov rcx, qword ptr gs:[0x30]
main.go:9 0x105e2a9 483b6110 cmp rsp, qword ptr [rcx+0x10]
main.go:9 0x105e2ad 7631 jbe 0x105e2e0
=> main.go:9 0x105e2af* 4883ec20 sub rsp, 0x20
main.go:9 0x105e2b3 48896c2418 mov qword ptr [rsp+0x18], rbp
main.go:9 0x105e2b8 488d6c2418 lea rbp, ptr [rsp+0x18]
main.go:10 0x105e2bd 48c7042401000000 mov qword ptr [rsp], 0x1
main.go:10 0x105e2c5 48c744240802000000 mov qword ptr [rsp+0x8], 0x2
main.go:10 0x105e2ce e88dffffff call $main.sum
main.go:11 0x105e2d3 488b6c2418 mov rbp, qword ptr [rsp+0x18]
main.go:11 0x105e2d8 4883c420 add rsp, 0x20
main.go:11 0x105e2dc c3 ret
main.go:9 0x105e2dd 0f1f00 nop dword ptr [rax], eax
main.go:9 0x105e2e0 e87bb2ffff call $runtime.morestack_noctxt
.:0 0x105e2e5 ebb9 jmp $main.main
(dlv)
5.单步运行汇编指令si
(dlv) si
> main.main() ./main.go:9 (PC: 0x105e2b3)
4: c := a + b
5:
6: return c
7: }
8:
=> 9: func main() {
10: sum(1, 2)
11: }
(dlv) disass
TEXT main.main(SB) /main.go
main.go:9 0x105e2a0 65488b0c2530000000 mov rcx, qword ptr gs:[0x30]
main.go:9 0x105e2a9 483b6110 cmp rsp, qword ptr [rcx+0x10]
main.go:9 0x105e2ad 7631 jbe 0x105e2e0
main.go:9 0x105e2af* 4883ec20 sub rsp, 0x20
=> main.go:9 0x105e2b3 48896c2418 mov qword ptr [rsp+0x18], rbp
main.go:9 0x105e2b8 488d6c2418 lea rbp, ptr [rsp+0x18]
main.go:10 0x105e2bd 48c7042401000000 mov qword ptr [rsp], 0x1
main.go:10 0x105e2c5 48c744240802000000 mov qword ptr [rsp+0x8], 0x2
main.go:10 0x105e2ce e88dffffff call $main.sum
main.go:11 0x105e2d3 488b6c2418 mov rbp, qword ptr [rsp+0x18]
main.go:11 0x105e2d8 4883c420 add rsp, 0x20
main.go:11 0x105e2dc c3 ret
main.go:9 0x105e2dd 0f1f00 nop dword ptr [rax], eax
main.go:9 0x105e2e0 e87bb2ffff call $runtime.morestack_noctxt
.:0 0x105e2e5 ebb9 jmp $main.main
(dlv)
6.下断点到call sum的地方
(dlv) b *0x105e2ce
Breakpoint 2 set at 0x105e2ce for main.main() ./main.go:10
(dlv) c
> main.main() ./main.go:10 (hits goroutine(1):1 total:1) (PC: 0x105e2ce)
5:
6: return c
7: }
8:
9: func main() {
=> 10: sum(1, 2)
11: }
(dlv) disass
TEXT main.main(SB) /main.go
main.go:9 0x105e2a0 65488b0c2530000000 mov rcx, qword ptr gs:[0x30]
main.go:9 0x105e2a9 483b6110 cmp rsp, qword ptr [rcx+0x10]
main.go:9 0x105e2ad 7631 jbe 0x105e2e0
main.go:9 0x105e2af* 4883ec20 sub rsp, 0x20
main.go:9 0x105e2b3 48896c2418 mov qword ptr [rsp+0x18], rbp
main.go:9 0x105e2b8 488d6c2418 lea rbp, ptr [rsp+0x18]
main.go:10 0x105e2bd 48c7042401000000 mov qword ptr [rsp], 0x1
main.go:10 0x105e2c5 48c744240802000000 mov qword ptr [rsp+0x8], 0x2
=> main.go:10 0x105e2ce* e88dffffff call $main.sum
main.go:11 0x105e2d3 488b6c2418 mov rbp, qword ptr [rsp+0x18]
main.go:11 0x105e2d8 4883c420 add rsp, 0x20
main.go:11 0x105e2dc c3 ret
main.go:9 0x105e2dd 0f1f00 nop dword ptr [rax], eax
main.go:9 0x105e2e0 e87bb2ffff call $runtime.morestack_noctxt
.:0 0x105e2e5 ebb9 jmp $main.main
(dlv)
7.si进入sum函数
(dlv) si
> main.sum() ./main.go:3 (PC: 0x105e260)
1: package main
2:
=> 3: func sum(a, b int) int {
4: c := a + b
5:
6: return c
7: }
8:
(dlv) disass
TEXT main.sum(SB) /main.go
=> main.go:3 0x105e260 4883ec10 sub rsp, 0x10
main.go:3 0x105e264 48896c2408 mov qword ptr [rsp+0x8], rbp
main.go:3 0x105e269 488d6c2408 lea rbp, ptr [rsp+0x8]
main.go:3 0x105e26e 48c744242800000000 mov qword ptr [rsp+0x28], 0x0
main.go:4 0x105e277 488b442418 mov rax, qword ptr [rsp+0x18]
main.go:4 0x105e27c 4803442420 add rax, qword ptr [rsp+0x20]
main.go:4 0x105e281 48890424 mov qword ptr [rsp], rax
main.go:6 0x105e285 4889442428 mov qword ptr [rsp+0x28], rax
main.go:6 0x105e28a 488b6c2408 mov rbp, qword ptr [rsp+0x8]
.:0 0x105e28f 4883c410 add rsp, 0x10
.:0 0x105e293 c3 ret
(dlv)
8.打印寄存器的值
(dlv) regs
Rip = 0x000000000105e260
Rsp = 0x000000c000050758
Rax = 0x000000000105e2a0
Rbx = 0x0000000000000000
Rcx = 0x000000c000000180
Rdx = 0x0000000001078b68
Rsi = 0x0000000000000001
Rdi = 0x000000c000076010
Rbp = 0x000000c000050778
R8 = 0x0000000000000000
R9 = 0x0000000000000000
R10 = 0x0000000000000001
R11 = 0x000000c000076000
R12 = 0x0000000000000001
R13 = 0x0000000000000040
R14 = 0x0000000000000040
R15 = 0x0000000000000000
Rflags = 0x0000000000000206 [PF IF IOPL=0]
Cs = 0x000000000000002b
Fs = 0x0000000000000000
Gs = 0x0000000000000000
(dlv)
调试runtime
gdb
1.找到entrypoint
GNU gdb (GDB) 11.1
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin20.4.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
Loading Go Runtime support.
(gdb) info files
Symbols from "/main".
Local exec file:
`/main', file type mach-o-x86-64.
Entry point: 0x105c660 //这里就是entrypoint
0x0000000001001000 - 0x000000000105e2e7 is .text
0x000000000105e300 - 0x000000000105e3de is __TEXT.__symbol_stub1
0x000000000105e3e0 - 0x000000000108b16b is __TEXT.__rodata
0x000000000108b180 - 0x000000000108b61c is __TEXT.__typelink
0x000000000108b620 - 0x000000000108b628 is __TEXT.__itablink
0x000000000108b628 - 0x000000000108b628 is __TEXT.__gosymtab
0x000000000108b640 - 0x00000000010c7770 is __TEXT.__gopclntab
0x00000000010c8000 - 0x00000000010c8020 is __DATA.__go_buildinfo
0x00000000010c8020 - 0x00000000010c8148 is __DATA.__nl_symbol_ptr
0x00000000010c8160 - 0x00000000010c9360 is __DATA.__noptrdata
0x00000000010c9360 - 0x00000000010cb150 is .data
0x00000000010cb160 - 0x00000000010f8470 is .bss
0x00000000010f8480 - 0x00000000010fd570 is __DATA.__noptrbss
(gdb)
3.停到entrypoint断点上
(gdb) b *0x105c660
Breakpoint 1 at 0x105c660: file /usr/local/go/src/runtime/rt0_darwin_amd64.s, line 8.
(gdb) r
Starting program: /main
[New Thread 0x2b03 of process 33171]
[New Thread 0x2c03 of process 33171]
warning: unhandled dyld version (17)
Thread 2 hit Breakpoint 1, _rt0_amd64_darwin () at /usr/local/go/src/runtime/rt0_darwin_amd64.s:8
8 JMP _rt0_amd64(SB)
(gdb)
4. 进入rt0_go函数
gdb) si
_rt0_amd64 () at /usr/local/go/src/runtime/asm_amd64.s:15
15 MOVQ 0(SP), DI // argc
(gdb) n
16 LEAQ 8(SP), SI // argv
(gdb) n
17 JMP runtime·rt0_go(SB)
(gdb) si
runtime.rt0_go () at /usr/local/go/src/runtime/asm_amd64.s:91
91 MOVQ DI, AX // argc
(gdb) l
86
87 // Defined as ABIInternal since it does not use the stack-based Go ABI (and
88 // in addition there are no calls to this entry point from Go code).
89 TEXT runtime·rt0_go<ABIInternal>(SB),NOSPLIT,$0
90 // copy arguments forward on an even stack
91 MOVQ DI, AX // argc
92 MOVQ SI, BX // argv
93 SUBQ $(4*8+7), SP // 2args 2auto
94 ANDQ $~15, SP
95 MOVQ AX, 16(SP)
(gdb)
5.继续往下走,停到调用schedinit的地方
(gdb)
220 CALL runtime·schedinit(SB)
(gdb) si
runtime.schedinit () at /usr/local/go/src/runtime/proc.go:600
600 func schedinit() {
(gdb) l
595 // call schedinit
596 // make & queue new G
597 // call runtime·mstart
598 //
599 // The new G calls runtime·main.
600 func schedinit() {
601 lockInit(&sched.lock, lockRankSched)
602 lockInit(&sched.sysmonlock, lockRankSysmon)
603 lockInit(&sched.deferlock, lockRankDefer)
604 lockInit(&sched.sudoglock, lockRankSudog)
(gdb)
这样就可以debug runtime了
gdb也可以直接下断点到要debug的runtime函数
b file:line或者b package.func
(gdb) b /usr/local/go/src/runtime/proc.go:600
Breakpoint 1 at 0x1030c20: file /usr/local/go/src/runtime/proc.go, line 600.
(gdb) r
Starting program: /main
[New Thread 0x5403 of process 33241]
[New Thread 0x5203 of process 33241]
warning: unhandled dyld version (17)
Thread 2 hit Breakpoint 1, runtime.schedinit () at /usr/local/go/src/runtime/proc.go:600
600 func schedinit() {
(gdb)
gdb) b runtime.schedinit
Breakpoint 1 at 0x1030c20: file /usr/local/go/src/runtime/proc.go, line 600.
dlv
1.先通过gdb找到entrypoint
Loading Go Runtime support.
(gdb) info files
Symbols from "/main".
Local exec file:
`/main', file type mach-o-x86-64.
Entry point: 0x105c660
0x0000000001001000 - 0x000000000105e2e7 is .text
0x000000000105e300 - 0x000000000105e3de is __TEXT.__symbol_stub1
0x000000000105e3e0 - 0x000000000108b16b is __TEXT.__rodata
0x000000000108b180 - 0x000000000108b61c is __TEXT.__typelink
0x000000000108b620 - 0x000000000108b628 is __TEXT.__itablink
0x000000000108b628 - 0x000000000108b628 is __TEXT.__gosymtab
0x000000000108b640 - 0x00000000010c7770 is __TEXT.__gopclntab
0x00000000010c8000 - 0x00000000010c8020 is __DATA.__go_buildinfo
0x00000000010c8020 - 0x00000000010c8148 is __DATA.__nl_symbol_ptr
0x00000000010c8160 - 0x00000000010c9360 is __DATA.__noptrdata
0x00000000010c9360 - 0x00000000010cb150 is .data
0x00000000010cb160 - 0x00000000010f8470 is .bss
0x00000000010f8480 - 0x00000000010fd570 is __DATA.__noptrbss
(gdb)
2.下断点
Type 'help' for list of commands.
(dlv) b *0x105c660
Breakpoint 1 set at 0x105c660 for _rt0_amd64_darwin() /usr/local/go/src/runtime/rt0_darwin_amd64.s:8
(dlv) c
> _rt0_amd64_darwin() /usr/local/go/src/runtime/rt0_darwin_amd64.s:8 (hits total:1) (PC: 0x105c660)
Warning: debugging optimized function
3: // license that can be found in the LICENSE file.
4:
5: #include "textflag.h"
6:
7: TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
=> 8: JMP _rt0_amd64(SB)
9:
10: // When linking with -shared, this symbol is called when the shared library
11: // is loaded.
12: TEXT _rt0_amd64_darwin_lib(SB),NOSPLIT,$0
13: JMP _rt0_amd64_lib(SB)
(dlv)
其实上面的输出能看出来entry point的具体文件,我们可以记下来,直接对这个文件以及行数下断点也行
$ dlv exec ./main
Type 'help' for list of commands.
(dlv) b /usr/local/go/src/runtime/rt0_darwin_amd64.s:8
Breakpoint 1 set at 0x105c660 for _rt0_amd64_darwin() /usr/local/go/src/runtime/rt0_darwin_amd64.s:8
3.开始执行到断点位置 c
(dlv) c
> _rt0_amd64_darwin() /usr/local/go/src/runtime/rt0_darwin_amd64.s:8 (hits total:1) (PC: 0x105c660)
Warning: debugging optimized function
3: // license that can be found in the LICENSE file.
4:
5: #include "textflag.h"
6:
7: TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
=> 8: JMP _rt0_amd64(SB)
9:
10: // When linking with -shared, this symbol is called when the shared library
11: // is loaded.
12: TEXT _rt0_amd64_darwin_lib(SB),NOSPLIT,$0
13: JMP _rt0_amd64_lib(SB)
(dlv)
4.单步执行n以及si
(dlv) si
> _rt0_amd64() /usr/local/go/src/runtime/asm_amd64.s:15 (PC: 0x1059160)
Warning: debugging optimized function
10: // _rt0_amd64 is common startup code for most amd64 systems when using
11: // internal linking. This is the entry point for the program from the
12: // kernel for an ordinary -buildmode=exe program. The stack holds the
13: // number of arguments and the C-style argv.
14: TEXT _rt0_amd64(SB),NOSPLIT,$-8
=> 15: MOVQ 0(SP), DI // argc
16: LEAQ 8(SP), SI // argv
17: JMP runtime·rt0_go(SB)
18:
19: // main is common startup code for most amd64 systems when using
20: // external linking. The C startup code will call the symbol "main"
(dlv) n
> _rt0_amd64() /usr/local/go/src/runtime/asm_amd64.s:16 (PC: 0x1059164)
Warning: debugging optimized function
11: // internal linking. This is the entry point for the program from the
12: // kernel for an ordinary -buildmode=exe program. The stack holds the
13: // number of arguments and the C-style argv.
14: TEXT _rt0_amd64(SB),NOSPLIT,$-8
15: MOVQ 0(SP), DI // argc
=> 16: LEAQ 8(SP), SI // argv
17: JMP runtime·rt0_go(SB)
18:
19: // main is common startup code for most amd64 systems when using
20: // external linking. The C startup code will call the symbol "main"
21: // passing argc and argv in the usual C ABI registers DI and SI.
(dlv) n
> _rt0_amd64() /usr/local/go/src/runtime/asm_amd64.s:17 (PC: 0x1059169)
Warning: debugging optimized function
12: // kernel for an ordinary -buildmode=exe program. The stack holds the
13: // number of arguments and the C-style argv.
14: TEXT _rt0_amd64(SB),NOSPLIT,$-8
15: MOVQ 0(SP), DI // argc
16: LEAQ 8(SP), SI // argv
=> 17: JMP runtime·rt0_go(SB)
18:
19: // main is common startup code for most amd64 systems when using
20: // external linking. The C startup code will call the symbol "main"
21: // passing argc and argv in the usual C ABI registers DI and SI.
22: TEXT main(SB),NOSPLIT,$-8
(dlv) si
> runtime.rt0_go() /usr/local/go/src/runtime/asm_amd64.s:91 (PC: 0x1059180)
Warning: debugging optimized function
86:
87: // Defined as ABIInternal since it does not use the stack-based Go ABI (and
88: // in addition there are no calls to this entry point from Go code).
89: TEXT runtime·rt0_go<ABIInternal>(SB),NOSPLIT,$0
90: // copy arguments forward on an even stack
=> 91: MOVQ DI, AX // argc
92: MOVQ SI, BX // argv
93: SUBQ $(4*8+7), SP // 2args 2auto
94: ANDQ $~15, SP
95: MOVQ AX, 16(SP)
96: MOVQ BX, 24(SP)
(dlv)
5.进入runtime函数
我们继续执行n,让他停留在CALL runtime·schedinit上,执行si
> runtime.rt0_go() /usr/local/go/src/runtime/asm_amd64.s:220 (PC: 0x10592a2)
Warning: debugging optimized function
215: MOVL AX, 0(SP)
216: MOVQ 24(SP), AX // copy argv
217: MOVQ AX, 8(SP)
218: CALL runtime·args(SB)
219: CALL runtime·osinit(SB)
=> 220: CALL runtime·schedinit(SB)
221:
222: // create a new goroutine to start program
223: MOVQ $runtime·mainPC(SB), AX // entry
224: PUSHQ AX
225: PUSHQ $0 // arg size
(dlv) si
> runtime.schedinit() /usr/local/go/src/runtime/proc.go:600 (PC: 0x1030c20)
Warning: debugging optimized function
595: // call schedinit
596: // make & queue new G
597: // call runtime·mstart
598: //
599: // The new G calls runtime·main.
=> 600: func schedinit() {
601: lockInit(&sched.lock, lockRankSched)
602: lockInit(&sched.sysmonlock, lockRankSysmon)
603: lockInit(&sched.deferlock, lockRankDefer)
604: lockInit(&sched.sudoglock, lockRankSudog)
605: lockInit(&deadlock, lockRankDeadlock)
(dlv)
这样,就进入了runtime函数
其实上面那么多步骤,如果是想debug下go的启动流程是比较有用的,如果只是想debug runtime函数,那么直接使用b file:line或者b funcname的方式就可以,而且更快速
$ dlv exec ./main ✔ 8m 2s 15:52:20
Type 'help' for list of commands.
(dlv) b schedinit
Breakpoint 1 set at 0x1030c33 for runtime.schedinit() /usr/local/go/src/runtime/proc.go:600
(dlv) c
> runtime.schedinit() /usr/local/go/src/runtime/proc.go:600 (hits total:1) (PC: 0x1030c33)
Warning: debugging optimized function
595: // call schedinit
596: // make & queue new G
597: // call runtime·mstart
598: //
599: // The new G calls runtime·main.
=> 600: func schedinit() {
601: lockInit(&sched.lock, lockRankSched)
602: lockInit(&sched.sysmonlock, lockRankSysmon)
603: lockInit(&sched.deferlock, lockRankDefer)
604: lockInit(&sched.sudoglock, lockRankSudog)
605: lockInit(&deadlock, lockRankDeadlock)
(dlv)
gdb和dlv对比
| 标题 | 源代码 | 汇编代码 | runtime代码 |
|---|---|---|---|
| gdb | 方便 | 需要先编译成可执行文件 | 方便 |
| dlv | 方便 | dlv exec需要可执行文件,dlv debug不需要,打印寄存器只能一次性全打出来,看起来会有些乱 | 第一次需要借助gdb来获取entry poing |
以上对比未涉及其他的场景,如goroutine等(可能dlv在debug goroutine上会更方便)