go的源代码是用go写的, 编译也需要一个可运行的go.
首先我们从官网下载源代码和二进制文件.
go1.9.2.src.tar.gz
go1.9.2.linux-amd64.tar.gz
注意两个压缩包解压出来文件夹名称都是go, 我们解压到以下目录:
源代码: ~/git_go/go_src
二进制: ~/git_go/go_bin
编译go之前需要设置环境变量,GOROOT_BOOTSTRAP
是go二进制文件夹的所在目录,GO_GCFLAGS
是编译go时使用的参数.
export GOROOT_BOOTSTRAP=~/git_go/go_bin
export GO_GCFLAGS="-N -l"
这里的-N
参数代表禁止优化, -l
参数代表禁止内联, go在编译目标程序的时候会嵌入运行时(runtime)的二进制,
禁止优化和内联可以让运行时(runtime)中的函数变得更容易调试.
都准备好以后就可以进入go的源代码文件夹执行all.bash
编译了:
编译的结果在~/git_go/go_src/bin
下:
之前CoreCLR的系列中我使用了lldb, 在这个系列中我继续沿用这个调试器.
这个系列中使用的是lldb 4.0.
以以下源代码(hello.go)为例:
package main
import (
"fmt"
"time"
)
func printNumber(from, to int, c chan int) {
for x := from; x <= to; x++ {
fmt.Printf("%d\n", x)
time.Sleep(1 * time.Millisecond)
}
c <- 0
}
func main() {
c := make(chan int, 3)
go printNumber(1, 3, c)
go printNumber(4, 6, c)
_, _ = <- c, <- c
}
编译源代码使用以下命令, 这里的-l
参数的意思和上面一样, 如果有需要还可以加-N
参数:
~/git_go/go_src/bin/go build -gcflags "-l" hello.go
编译后使用lldb运行:
lldb ./hello
go里面的函数符号名称的命名规则是包名称.函数名称
, 例如主函数的符号名称是main.main
, 运行时中的newobject
的符号名称是runtime.newobject
.
首先给主函数下一个断点然后运行:
可以看到成功的进入了主函数, 并且有源代码提示.
接下来给按文件名和行数来下断点:
然后查看函数的汇编代码:
关于lldb的命令可以查看这篇文档.
在我使用的环境中lldb可以正常的下断点, 步进和步过go代码或者汇编指令,
但打印变量输出的值有可能是错的, 即使不开启优化.
虽然打印变量这个功能不好用, 我们仍然可以直接让go输出我们想要的值,
例如修改runtime/malloc.go
输出当前环境下arena|spans|bitmap区的大小:
修改后进入src
并执行./make.bash
, 然后重新编译目标程序, 运行:
可以看到当前环境下arena是512G, spans是512M, bitmap是16G.
这个方法虽然比较笨, 但是可以在任何情况下输出我们想要的值.
此外, go运行时(runtime)的源代码会包括在目标文件中,
例如你对runtime.newobject
下断点可以对go自身的源代码进行调试.
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
添加我为好友,拉您入交流群!
请使用微信扫一扫!