Start a new project, the debugger on linux platform for go.
This project is inspired by dlv github地址
背景
没想到这个项目最终还是沦为为一个玩具。初次有这个想法是因为发现 gdb调试golang代码是可以进入runtime的指令,而dlv却不可以。例如像map这种的初始化
s := make(map[int]string, 20)
主要是dlv将runtime的代码都skip掉了,表示对golanger无需关心底层的逻辑,只需要关心自己编写的代码即可。
先看一下效果

实现
代码本身分为5个步骤,代码是在main.go
- 获取生成含.debug可执行文件的路径
- 编译创建可执行文件
- 分析可执行文件里面的_zdebug的字段,以便后续调试使用
- 执行可执行文件,并且ptrace
- 处理用户所有的输入
注意两点:
1.本身
linux里面并没有多线程的概念,都是task;没有做上下文切换,所以暂时不支持多协程,全局唯一的pid;2.为了
ptrace生效,需要将协程和task一一绑定,runtime.LockOSThread();
前两个步骤没什么好讲的,主要从分析二进制文件开始
分析
分为两个部分,第一个部分是line和info两个节区的结合,记录出行信息的表,对应每一行汇编是源码中哪一行生成的,主要是为后续list和breakpoint服务的 ;第二部分是FrameSection,主要是为打印变量时候,算调用栈帧用的 ;
line和info识别dwarf里面compileunit的tag,每个编译单元都是记录当前单元的所有行信息,一个编译单元可以理解为一个packageline和info识别dwarf里面SubProgram的tag,这个可以理解是每一个函数包含哪些指令范围,这样在程序运行时候拿到pc寄存器值进行反查就能知道当前运行到哪一个函数了line和info识别dwarf里面的Variable的tag,是为了找到局部变量的定义、名称、结构以及数据内存地址等等frame识别里面的CIE(全称:CommonInfomationEntry)和FDE(全称FrameDescriptionEntry,FDE对应一定指向一个公用的CIE),在程序运行时可以按照CIE和FDE的规则拿到CFA的计算规则
处理
断点:
设置断点其实没有什么很深的东西,主要是利用行信息表查出对应的内存块,ptrace将需要下断点的地方内存指令换成软断点0xcc;当进程执行到0xcc会触发一个trap停止执行后续指令;
这个会出现一个问题,就是当期望恢复断点继续执行的时候,由于破坏了原来的指令则无法继续continue,需要将指令暂时(注意是暂时,如果不是暂时那么断点就失效了)恢复,并且pc寄存器数值减少一个指令重新执行;
还有就是golang是具有gc高级特效的语言,许多地方增加了checkpoint,例如每次函数调用的时候,都会去check当前栈是否需要扩容,如果你的断点下在了checkpoint的地方,那么很有可能会在同一行代码(checkpoint也对应用户代码的行表)多次触发,看起来就像是continue一直在同一行卡住,解决这个问题需要主要一下PrologueEnd(编译器生成debug信息提供的)
断点列表、显示源码的原理都需要记载在内存里面对应关系就可以做到了;
继续:
continue上面讲过了,需要将断点暂时恢复一下;而next和stepin,正常来讲应该是内部断点的实现方式,这里只用到了一个简单逻辑(暴露玩具的本质);next就是在当前pc值(如果当前是断点,应该是pc-1)过后不停地PtraceSingleStep直到发现 1.文件名或者行数变了 2.碰到别的断点 3.如果当前是函数调用记录一个calling状态继续PtraceSingleStep直到stepout;
列举:
list原理跟breakpoint类似
重启:
原理就是将程序原来的kill掉,重新执行一个
反汇编:
就是上面根据SubProgram讲到的pc找到对应的指令范围,然后反汇编就可以了,注意断点的部分需要特殊处理(因为已经被替换掉了,否则反汇编会报错)
变量:
核心是要计算出来cfa(callframeaddress),http://dwarfstd.org/doc/Dwarf3.pdf大概在122页左右;
计算过程相当繁琐,例如这样能发现delve某些地方解析也是不对的,https://github.com/go-delve/delve/pull/1679;
参考
https://www.cnblogs.com/tsecer/p/10485670.html 其实没有完全看懂
http://ucla.jamesyxu.com/?p=231 也是完全没有看懂
https://www.corsix.org/content/cfa-rsp-x86-64 又是完全看不懂
http://www.cs.uwm.edu/classes/cs315/Bacon/Lecture/HTML/ch10s07.html 好像看懂了
http://dwarfstd.org/doc/Dwarf3.pdf 这本书很重要,结合dlv才能看得懂