实验环境
- Ubuntu 20.04 x86_64 5.4.0 2CPU 4GB
- go1.17.12 linux/amd64
- Delve Debugger 1.9.0
- 待调试代码示例
package main
//go:noinline
func sum(a, b int) int {
sum := 0
sum = a + b
return sum
}
//go:noinline
func main() {
a := 3
b := 5
c := sum(a, b)
a = c + b
}
dlv 调试指令简介
指令 | 含义 |
---|---|
n | 一行一行执行 |
s | 单步 多指令, 调试汇编代码 |
si | 单步 单指令 ,调试汇编代码 |
ls | 显示源代码 |
p | 打印变量 |
b | 设置断点 |
bp | 打印断点 |
regs | 打印寄存器值 |
调试入口点
二进制可执行文件在Linux系统是一种ELF文件格式,与进城虚拟地址空间分布一一对应。ELF文件可以显示入口点;
go build -o cmd cmd.go
readelf -h cmd | less
![](https://i-blog.csdnimg.cn/blog_migrate/2468f8f6f531bf39bb567d2f2d8dc811.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3e7a8f2c220e7aee1bc4912107613dfa.png)
开始调试探索
![](https://i-blog.csdnimg.cn/blog_migrate/b03a0872356ccc877c50ab72e2d07c29.png)
![](https://i-blog.csdnimg.cn/blog_migrate/43127f68de8be117358b20570ae2892c.png)
~/go/bin/dlv debug cmd.go
(dlv) b _rt0_amd64_linux
(dlv) bp
...
Breakpoint 1 (enabled) at 0x458180 for _rt0_amd64_linux() /usr/local/go/src/runtime/rt0_linux_amd64.s:8 (0)
(dlv)
(dlv) c
6:
7: TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
=> 8: JMP _rt0_amd64(SB)
(dlv) si
15: TEXT _rt0_amd64(SB),NOSPLIT,$-8
=> 16: MOVQ 0(SP), DI // argc
17: LEAQ 8(SP), SI // argv
18: JMP runtime·rt0_go(SB)
(dlv) n
(dlv) n
15: TEXT _rt0_amd64(SB),NOSPLIT,$-8
16: MOVQ 0(SP), DI // argc
17: LEAQ 8(SP), SI // argv
=> 18: JMP runtime·rt0_go(SB)
(dlv) si
81: TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0
82: // copy arguments forward on an even stack
=> 83: MOVQ DI, AX // argc
84: MOVQ SI, BX // argv
85: SUBQ $(4*8+7), SP // 2args 2auto
86: ANDQ $~15, SP
87: MOVQ AX, 16(SP)
88: MOVQ BX, 24(SP)
(dlv) s 多次
(dlv) s
206: MOVL 16(SP), AX // copy argc
207: MOVL AX, 0(SP)
208: MOVQ 24(SP), AX // copy argv
209: MOVQ AX, 8(SP)
=> 210: CALL runtime·args(SB)
211: CALL runtime·osinit(SB)
212: CALL runtime·schedinit(SB)
(dlv) si 多次
61: func args(c int32, v **byte) {
=> 62: argc = c
63: argv = v
64: sysargs(c, v)
65: }
(dlv) n 多次
210: CALL runtime·args(SB)
=> 211: CALL runtime·osinit(SB)
212: CALL runtime·schedinit(SB)
(dlv) si 多次
=> 301: func osinit() {
302: ncpu = getproccount()
(dlv) n 多次
210: CALL runtime·args(SB)
211: CALL runtime·osinit(SB)
=> 212: CALL runtime·schedinit(SB)
(dlv) si 多次
=> 654: func schedinit() {
655: lockInit(&sched.lock, lockRankSched)
656: lockInit(&sched.sysmonlock, lockRankSysmon)
657: lockInit(&sched.deferlock, lockRankDefer)
658: lockInit(&sched.sudoglock, lockRankSudog)
659: lockInit(&deadlock, lockRankDeadlock)
(dlv) n 多次
681:
682: sched.maxmcount = 10000 //设置调度g最大值
683:
684: // The world starts stopped.
=> 685: worldStopped()
686:
687: moduledataverify()
688: stackinit() //栈初始化
689: mallocinit() //内存分配初始化
690: fastrandinit()
691: mcommoninit(_g_.m, -1)
692: cpuinit() // must run before alginit
693: alginit() // maps must not be used before this call
=> 694: modulesinit() // provides activeModules
695: typelinksinit() // uses maps, activeModules
696: itabsinit() // uses activeModules
(dlv) n 多次
706: goargs() //go 请求参数
707: goenvs() //go 环境变量
708: parsedebugvars()
709: gcinit() //垃圾回收初始化
710:
=> 711: lock(&sched.lock)
712: sched.lastpoll = uint64(nanotime())
713: procs := ncpu //系统核数
(dlv) p procs
2
(dlv) n 多次
=> 717: if procresize(procs) != nil { //创建P
718: throw("unknown runnable goroutine during bootstrap")
719: }
(dlv) si
=>4994: func procresize(nprocs int32) *p {
4995: assertLockHeld(&sched.lock)
4996: assertWorldStopped()
(dlv) n 多次
5020: if nprocs <= int32(cap(allp)) {
5021: allp = allp[:nprocs]
5022: } else {
=>5023: nallp := make([]*p, nprocs) //创建多处理器P
5024: // Copy everything up to allp's cap so we
5025: // never lose old allocated Ps.
5026: copy(nallp, allp[:cap(allp)])
5027: allp = nallp
5028: }
(dlv) n 多次
5046: // initialize new P's
5047: for i := old; i < nprocs; i++ {
5048: pp := allp[i]
=>5049: if pp == nil {
5050: pp = new(p)
5051: }
5052: pp.init(i)
5053: atomicstorep(unsafe.Pointer(&allp[i]), unsafe.Pointer(pp))
5054: }
(dlv) n 多次
212: CALL runtime·schedinit(SB)
213:
214: // create a new goroutine to start program
215: MOVQ $runtime·mainPC(SB), AX // entry
216: PUSHQ AX
217: PUSHQ $0 // arg size
=> 218: CALL runtime·newproc(SB)
(dlv) si 多次
=>4250: func newproc(siz int32, fn *funcval) {
4251: argp := add(unsafe.Pointer(&fn), sys.PtrSize)
4252: gp := getg()
4253: pc := getcallerpc()
(dlv) n 多次
218: CALL runtime·newproc(SB)
219: POPQ AX
220: POPQ AX
221:
222: // start this M
=> 223: CALL runtime·mstart(SB)
224:
225: CALL runtime·abort(SB) // mstart should never return
(dlv) si 多次
247: TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0
=> 248: CALL runtime·mstart0(SB)
249: RET // not reached
(dlv) si 多次
=>1339: func mstart0() {
1340: _g_ := getg()
(dlv) n 多次
1361: _g_.stackguard0 = _g_.stack.lo + _StackGuard
1362: // This is the g0, so we can also call go:systemstack
1363: // functions, which check stackguard1.
1364: _g_.stackguard1 = _g_.stackguard0
=>1365: mstart1()
(dlv) p _StackGuard
928
(dlv) si 多次
=>1380: func mstart1() {
1381: _g_ := getg()
(dlv) n 多次
1393: _g_.sched.g = guintptr(unsafe.Pointer(_g_))
1394: _g_.sched.pc = getcallerpc()
=>1395: _g_.sched.sp = getcallersp()
1396:
1397: asminit()
1398: minit()
(dlv) n 多次
1410: if _g_.m != &m0 {
1411: acquirep(_g_.m.nextp.ptr())
1412: _g_.m.nextp = 0
1413: }
=>1414: schedule()
(dlv) si 多次
=>3291: func schedule() {
3292: _g_ := getg()
3310: pp := _g_.m.p.ptr()
3311: pp.preempt = false
3312: .....
=>3313: if sched.gcwaiting != 0 {
3314: gcstopm()
3315: goto top
3316: }
3327: .....
=>3328: checkTimers(pp, 0)
(dlv) n 多次
3351: if gp == nil { //防止全局g饥饿,每61次取全局
=>3355: if _g_.m.p.ptr().schedtick%61 == 0 && sched.runqsize > 0 {
3356: lock(&sched.lock)
3357: gp = globrunqget(_g_.m.p.ptr(), 1)
3358: unlock(&sched.lock)
3359: }
3360: }
(dlv) n 多次
=>3361: if gp == nil {
3362: gp, inheritTime = runqget(_g_.m.p.ptr()) //从P本地队列取g
3363: // We can see gp != nil here even if the M is spinning,
3364: // if checkTimers added a local goroutine via goready.
3365: }
(dlv) n 多次
=>3366: if gp == nil {
3367: gp, inheritTime = findrunnable() //本地队列空时,寻找可用g,复杂
3368: }
(dlv) n 多次
=>3406: execute(gp, inheritTime)
(dlv) p inheritTime
true
(dlv) si 多次
2670: func execute(gp *g, inheritTime bool) {
=>2671: _g_ := getg()
2672:
2673: // Assign gp.m before entering _Grunning so running Gs have an
2674: // M.
2675: _g_.m.curg = gp
2676: gp.m = _g_.m
(dlv) n 多次
2677: casgstatus(gp, _Grunnable, _Grunning)
2678: gp.waitsince = 0
2679: gp.preempt = false
=>2680: gp.stackguard0 = gp.stack.lo + _StackGuard //栈边界,用于伸缩栈
2681: if !inheritTime {
2682: _g_.m.p.ptr().schedtick++
2683: }
(dlv) n 多次
=>2700: gogo(&gp.sched)
(dlv) p gp.sched
runtime.gobuf {sp: 824633911256, pc: 4393312, g: 824633721664, ctxt: unsafe.Pointer(0x46d288), ret: 0, lr: 0, bp: 0}
(dlv) si 多次
257: TEXT runtime·gogo(SB), NOSPLIT, $0-8
=> 258: MOVQ buf+0(FP), BX // gobuf
259: MOVQ gobuf_g(BX), DX
260: MOVQ 0(DX), CX // make sure g != nil
261: JMP gogo<>(SB)
(dlv) si 多次
257: TEXT runtime·gogo(SB), NOSPLIT, $0-8
258: MOVQ buf+0(FP), BX // gobuf
259: MOVQ gobuf_g(BX), DX
260: MOVQ 0(DX), CX // make sure g != nil
=> 261: JMP gogo<>(SB)
(dlv) regs
Rip = 0x00000000004566ac
Rsp = 0x00007ffe3e239650
Rax = 0x000000c000000378
Rbx = 0x000000c000000378
Rcx = 0x000000c00002e000
Rdx = 0x000000c000000340
Rsi = 0x0000000000000001
Rdi = 0x0000000000000000
Rbp = 0x00007ffe3e239670
R8 = 0x0000000000430901
R9 = 0x0000000000430c80
R10 = 0x0000000000000008
R11 = 0x0000000000000206
R12 = 0x00007ffe3e2392e8
R13 = 0x0000000000000000
R14 = 0x00000000004b6800
R15 = 0x0000000000002030
Rflags = 0x0000000000000246 [PF ZF IF IOPL=0]
Es = 0x0000000000000000
Cs = 0x0000000000000033
Ss = 0x000000000000002b
Ds = 0x0000000000000000
Fs = 0x0000000000000000
Gs = 0x0000000000000000
Fs_base = 0x00000000004b6a30
Gs_base = 0x0000000000000000
(dlv) si 多次
263: TEXT gogo<>(SB), NOSPLIT, $0
264: get_tls(CX)
=> 265: MOVQ DX, g(CX)
266: MOVQ DX, R14 // set the g register
267: MOVQ gobuf_sp(BX), SP // restore SP
268: MOVQ gobuf_ret(BX), AX
269: MOVQ gobuf_ctxt(BX), DX
270: MOVQ gobuf_bp(BX), BP
(dlv) si 多次
271: MOVQ $0, gobuf_sp(BX) // clear to help garbage collector
272: MOVQ $0, gobuf_ret(BX)
273: MOVQ $0, gobuf_ctxt(BX)
274: MOVQ $0, gobuf_bp(BX)
275: MOVQ gobuf_pc(BX), BX
=> 276: JMP BX
(dlv) regs
Rip = 0x000000000045595e
Rsp = 0x000000c00002e7d8
Rax = 0x0000000000000000
Rbx = 0x0000000000430960
Rcx = 0x000000c00002e000
Rdx = 0x000000000046d288
Rsi = 0x0000000000000001
Rdi = 0x0000000000000000
Rbp = 0x0000000000000000
R8 = 0x0000000000430901
R9 = 0x0000000000430c80
R10 = 0x0000000000000008
R11 = 0x0000000000000206
R12 = 0x00007ffe3e2392e8
R13 = 0x0000000000000000
R14 = 0x000000c000000340
R15 = 0x0000000000002030
Rflags = 0x0000000000000246 [PF ZF IF IOPL=0]
Es = 0x0000000000000000
Cs = 0x0000000000000033
Ss = 0x000000000000002b
Ds = 0x0000000000000000
Fs = 0x0000000000000000
Gs = 0x0000000000000000
Fs_base = 0x00000000004b6a30
Gs_base = 0x0000000000000000
(dlv) ls
271: MOVQ $0, gobuf_sp(BX) // clear to help garbage collector
272: MOVQ $0, gobuf_ret(BX)
273: MOVQ $0, gobuf_ctxt(BX)
274: MOVQ $0, gobuf_bp(BX)
275: MOVQ gobuf_pc(BX), BX
=> 276: JMP BX
(dlv) si 多次
=> 145: func main() {
146: g := getg()
(dlv) si 多次
155: if sys.PtrSize == 8 { //最大栈
=> 156: maxstacksize = 1000000000
157: } else {
158: maxstacksize = 250000000
159: }
(dlv) p sys.PtrSize
8
(dlv) si 多次
169: if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon
170: // For runtime_syscall_doAllThreadsSyscall, we
171: // register sysmon is not ready for the world to be
172: // stopped.
173: atomic.Store(&sched.sysmonStarting, 1)
=> 174: systemstack(func() { //创建sysmon
175: newm(sysmon, nil, -1)
176: })
177: }
(dlv) si 多次
=> 204: doInit(&runtime_inittask) // 先执行 init 函数
(dlv) si 多次
=> 214: gcenable() // 开启gc回收逻辑
(dlv) si 多次
254: fn := main_main //编写代码的main函数
=> 255: fn()
(dlv) si 多次
10: //go:noinline
=> 11: func main() {
12: a := 3
13: b := 5
14: c := sum(a, b)
15: a = c + b
16: }
(dlv) n 多次
=> 264: if atomic.Load(&runningPanicDefers) != 0{//执行main函数执行defer 函数
265: // Running deferred functions should not take long.
266: for c := 0; c < 1000; c++ {
267: if atomic.Load(&runningPanicDefers) == 0 {
268: break
269: }
(dlv) p runningPanicDefers
0
(dlv) n 多次
=> 273: if atomic.Load(&panicking) != 0 { //执行panic
274: gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1)
275: }
(dlv) p panicking
0
(dlv) n 多次
=> 277: exit(0) //程序结束
278: for {
279: var x *int32
280: *x = 0
281: }
启动流程放大图