GoLang dlv调试启动过程
文章目录
- 实验环境
- dlv 调试指令简介
- 调试入口点
- 开始调试探索
- 启动流程放大图
实验环境
- 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 := 0sum = a + breturn sum
}
//go:noinline
func main() {a := 3b := 5c := 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
程序入口符号:_rt0_amd64_linux
开始调试探索
先看图片有个印象
~/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) c6: 7: TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
=> 8: JMP _rt0_amd64(SB)
(dlv) si15: TEXT _rt0_amd64(SB),NOSPLIT,$-8
=> 16: MOVQ 0(SP), DI // argc17: LEAQ 8(SP), SI // argv18: JMP runtime·rt0_go(SB)
(dlv) n
(dlv) n15: TEXT _rt0_amd64(SB),NOSPLIT,$-816: MOVQ 0(SP), DI // argc17: LEAQ 8(SP), SI // argv
=> 18: JMP runtime·rt0_go(SB)
(dlv) si81: TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$082: // copy arguments forward on an even stack
=> 83: MOVQ DI, AX // argc84: MOVQ SI, BX // argv85: SUBQ $(4*8+7), SP // 2args 2auto86: ANDQ $~15, SP87: MOVQ AX, 16(SP)88: MOVQ BX, 24(SP)
(dlv) s 多次
(dlv) s206: MOVL 16(SP), AX // copy argc207: MOVL AX, 0(SP)208: MOVQ 24(SP), AX // copy argv209: 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 = c63: argv = v64: 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 alginit693: alginit() // maps must not be used before this call
=> 694: modulesinit() // provides activeModules695: typelinksinit() // uses maps, activeModules696: 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 { //创建P718: 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) //创建多处理器P5024: // Copy everything up to allp's cap so we5025: // never lose old allocated Ps.5026: copy(nallp, allp[:cap(allp)])5027: allp = nallp5028: }
(dlv) n 多次 5046: // initialize new P's5047: 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 program215: MOVQ $runtime·mainPC(SB), AX // entry216: PUSHQ AX217: 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 AX220: POPQ AX221: 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 + _StackGuard1362: // This is the g0, so we can also call go:systemstack1363: // 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 = 01413: }
=>1414: schedule()
(dlv) si 多次
=>3291: func schedule() {3292: _g_ := getg()3310: pp := _g_.m.p.ptr()3311: pp.preempt = false3312: .....
=>3313: if sched.gcwaiting != 0 {3314: gcstopm()3315: goto top3316: }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本地队列取g3363: // 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 an2674: // M.2675: _g_.m.curg = gp2676: gp.m = _g_.m
(dlv) n 多次2677: casgstatus(gp, _Grunnable, _Grunning)2678: gp.waitsince = 02679: 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 // gobuf259: MOVQ gobuf_g(BX), DX260: MOVQ 0(DX), CX // make sure g != nil261: JMP gogo<>(SB)
(dlv) si 多次257: TEXT runtime·gogo(SB), NOSPLIT, $0-8258: MOVQ buf+0(FP), BX // gobuf259: MOVQ gobuf_g(BX), DX260: MOVQ 0(DX), CX // make sure g != nil
=> 261: JMP gogo<>(SB)
(dlv) regsRip = 0x00000000004566acRsp = 0x00007ffe3e239650Rax = 0x000000c000000378Rbx = 0x000000c000000378Rcx = 0x000000c00002e000Rdx = 0x000000c000000340Rsi = 0x0000000000000001Rdi = 0x0000000000000000Rbp = 0x00007ffe3e239670R8 = 0x0000000000430901R9 = 0x0000000000430c80R10 = 0x0000000000000008R11 = 0x0000000000000206R12 = 0x00007ffe3e2392e8R13 = 0x0000000000000000R14 = 0x00000000004b6800R15 = 0x0000000000002030Rflags = 0x0000000000000246 [PF ZF IF IOPL=0]Es = 0x0000000000000000Cs = 0x0000000000000033Ss = 0x000000000000002bDs = 0x0000000000000000Fs = 0x0000000000000000Gs = 0x0000000000000000
Fs_base = 0x00000000004b6a30
Gs_base = 0x0000000000000000(dlv) si 多次263: TEXT gogo<>(SB), NOSPLIT, $0264: get_tls(CX)
=> 265: MOVQ DX, g(CX)266: MOVQ DX, R14 // set the g register267: MOVQ gobuf_sp(BX), SP // restore SP268: MOVQ gobuf_ret(BX), AX269: MOVQ gobuf_ctxt(BX), DX270: MOVQ gobuf_bp(BX), BP
(dlv) si 多次271: MOVQ $0, gobuf_sp(BX) // clear to help garbage collector272: 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) regsRip = 0x000000000045595eRsp = 0x000000c00002e7d8Rax = 0x0000000000000000Rbx = 0x0000000000430960Rcx = 0x000000c00002e000Rdx = 0x000000000046d288Rsi = 0x0000000000000001Rdi = 0x0000000000000000Rbp = 0x0000000000000000R8 = 0x0000000000430901R9 = 0x0000000000430c80R10 = 0x0000000000000008R11 = 0x0000000000000206R12 = 0x00007ffe3e2392e8R13 = 0x0000000000000000R14 = 0x000000c000000340R15 = 0x0000000000002030Rflags = 0x0000000000000246 [PF ZF IF IOPL=0]Es = 0x0000000000000000Cs = 0x0000000000000033Ss = 0x000000000000002bDs = 0x0000000000000000Fs = 0x0000000000000000Gs = 0x0000000000000000
Fs_base = 0x00000000004b6a30
Gs_base = 0x0000000000000000(dlv) ls271: MOVQ $0, gobuf_sp(BX) // clear to help garbage collector272: 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 = 1000000000157: } else {158: maxstacksize = 250000000159: }
(dlv) p sys.PtrSize
8(dlv) si 多次169: if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon170: // For runtime_syscall_doAllThreadsSyscall, we171: // register sysmon is not ready for the world to be172: // stopped.173: atomic.Store(&sched.sysmonStarting, 1)
=> 174: systemstack(func() { //创建sysmon175: 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 := 313: b := 514: c := sum(a, b)15: a = c + b16: }(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: break269: }(dlv) p runningPanicDefers
0(dlv) n 多次
=> 273: if atomic.Load(&panicking) != 0 { //执行panic274: gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1)275: }(dlv) p panicking
0(dlv) n 多次
=> 277: exit(0) //程序结束278: for {279: var x *int32280: *x = 0281: }
启动流程放大图
GoLang dlv调试启动过程相关推荐
- 【无标题】Vscode 配置golang dlv调试
下载安装好dlv,配置好golang项目的mod文件,点击运行->添加配置->Go:launch package 生成launch文件后既可以启动调试了!
- golang源码分析-启动过程概述
golang源码分析-启动过程概述 golang语言作为根据CSP模型实现的一种强类型的语言,本文主要就是通过简单的实例来分析一下golang语言的启动流程,为深入了解与学习做铺垫. golang代码 ...
- 使用dlv调试golang程序
1.编译选项 go build -gcflags=all="-N -l" ## 必须这样编译,才能用gdb打印出变量,第二个是小写的L,不是大写的i 需要加编译选项,类似gcc中的 ...
- linux golang进程调试环境配置 dlv命令报错 总结详解
1. golang服务编译避坑 现象:dlv 调试,p打印变量时无法正常打印,报错:Command failed: could not find symbol value for 变量 解决方法:一定 ...
- Bochs调试Linux内核6 - 启动过程调试 - 跳到bootsect引导程序执行
接此,Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S_bcbobo21cn的专栏-CSDN博客 看一下,0x00007c11 这里是重复执行串传送:而后一条 ...
- 通过gdb调试分析Linux内核的启动过程
作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验流程 1.打开环境 执 ...
- Cypress 本身启动过程的调试
这个文件:node_modules\cypress\bin\cypress 里面的内容: #!/usr/bin/env noderequire('../lib/cli').init() 很多 requ ...
- dlv调试golang
dlv调试golang 调试所用代码 dlv trace追踪调用轨迹 调试模式 调试模式基本命令 1.打断点: 2.带条件的断点 3.查看所有断点 4.重启当前进程 5.继续执行到断点处 6.逐行执行 ...
- Spring 容器的启动过程
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 本文来源:http://r6f.cn/b47K 一. 前言 ...
最新文章
- 011_Redis的快照持久化
- sscanf函数—(sprintf的反)
- dbforge schema compare for mysql_DbForge Schema Compare for MySQL入门教程:生成比较报告
- 2013\Province_Java_B\1.世纪末的星期
- Apache + Tomcat 配置多个应用
- php的SAPI,CLI SAPI,CGI SAPI
- JDBCUtils——C3P0
- urb传输的代码分析【转】
- 韩国企业百强排行榜!
- Windows基本磁盘结构简析(二)——MBR结构分析
- 【0】嵌入式TCP/IP协议——————Art-Net协议详解
- VMWare ESXi上传iso镜像文件
- mysql事务的隔离c_Mysql事务处理与隔离级别 -cyy
- 利用自定义函数实现批量爬取多家公司的新闻
- firefly rk3328学习笔记1-samba环境搭建
- 【pytorch EarlyStopping】深度学习之早停法入门·相信我,一篇就够。
- 文件被占用无法删除,解决办法
- c语言笔记(第一周)
- DELL 电脑 ubuntu20.04系统安装(最新最简版)
- echarts 柱状图如何不顶格_echarts图表的使用经验总结(避免踩坑)