dlv和gdb调试go的区别

1,742 阅读12分钟

调试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上会更方便)