文章目录

  • 实验环境
  • 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调试启动过程相关推荐

  1. 【无标题】Vscode 配置golang dlv调试

    下载安装好dlv,配置好golang项目的mod文件,点击运行->添加配置->Go:launch package 生成launch文件后既可以启动调试了!

  2. golang源码分析-启动过程概述

    golang源码分析-启动过程概述 golang语言作为根据CSP模型实现的一种强类型的语言,本文主要就是通过简单的实例来分析一下golang语言的启动流程,为深入了解与学习做铺垫. golang代码 ...

  3. 使用dlv调试golang程序

    1.编译选项 go build -gcflags=all="-N -l" ## 必须这样编译,才能用gdb打印出变量,第二个是小写的L,不是大写的i 需要加编译选项,类似gcc中的 ...

  4. linux golang进程调试环境配置 dlv命令报错 总结详解

    1. golang服务编译避坑 现象:dlv 调试,p打印变量时无法正常打印,报错:Command failed: could not find symbol value for 变量 解决方法:一定 ...

  5. Bochs调试Linux内核6 - 启动过程调试 - 跳到bootsect引导程序执行

    接此,​​​​​​Bochs调试Linux内核5 - 启动过程调试 - 认识Bootsect.S_bcbobo21cn的专栏-CSDN博客 看一下,0x00007c11 这里是重复执行串传送:而后一条 ...

  6. 通过gdb调试分析Linux内核的启动过程

    作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验流程 1.打开环境 执 ...

  7. Cypress 本身启动过程的调试

    这个文件:node_modules\cypress\bin\cypress 里面的内容: #!/usr/bin/env noderequire('../lib/cli').init() 很多 requ ...

  8. dlv调试golang

    dlv调试golang 调试所用代码 dlv trace追踪调用轨迹 调试模式 调试模式基本命令 1.打断点: 2.带条件的断点 3.查看所有断点 4.重启当前进程 5.继续执行到断点处 6.逐行执行 ...

  9. Spring 容器的启动过程

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 本文来源:http://r6f.cn/b47K 一. 前言 ...

最新文章

  1. 011_Redis的快照持久化
  2. sscanf函数—(sprintf的反)
  3. dbforge schema compare for mysql_DbForge Schema Compare for MySQL入门教程:生成比较报告
  4. 2013\Province_Java_B\1.世纪末的星期
  5. Apache + Tomcat 配置多个应用
  6. php的SAPI,CLI SAPI,CGI SAPI
  7. JDBCUtils——C3P0
  8. urb传输的代码分析【转】
  9. 韩国企业百强排行榜!
  10. Windows基本磁盘结构简析(二)——MBR结构分析
  11. 【0】嵌入式TCP/IP协议——————Art-Net协议详解
  12. VMWare ESXi上传iso镜像文件
  13. mysql事务的隔离c_Mysql事务处理与隔离级别 -cyy
  14. 利用自定义函数实现批量爬取多家公司的新闻
  15. firefly rk3328学习笔记1-samba环境搭建
  16. 【pytorch EarlyStopping】深度学习之早停法入门·相信我,一篇就够。
  17. 文件被占用无法删除,解决办法
  18. c语言笔记(第一周)
  19. DELL 电脑 ubuntu20.04系统安装(最新最简版)
  20. echarts 柱状图如何不顶格_echarts图表的使用经验总结(避免踩坑)

热门文章

  1. mtk+android+之mt6577驱动笔记,MTK6577+Android之音频(audio)移植
  2. 微信小程序加密消息解密工具类
  3. MySQL常用操作之创建存储过程语法详解
  4. ASP新闻分页,将一篇过长的文章分页,生成静态页面
  5. 重新安装ODBC 驱动程序方法之一
  6. 单击选定单元格后输入新内容_Excel综合练习题
  7. 如何react中使用redux和react-redux
  8. 腾讯QQ登录服务器域名IP地址端口列表
  9. Python日常(5):元组的创建、增加、修改、删除
  10. 第三方存管业务基础知识