Go语言编译器的正确打开方式(二)- 通过Debug理解Go的编译过程

# Go语言编译器的正确打开方式(一)- 从源码编译 go 中,我们做好了debug go源码的前期准备。

学会debug跟踪Go编译的源码,对于理解Go编译过程至关重要。

通过在关键位置暂停,我们可以更深入地了解编译的不同阶段,以及AST和IR的结构

观察 go build 过程

使用go build -x命令,可以详细查看Go编译的全过程,包括 编译(compile)链接(link) 两大步骤。

以gorm代码为例,跳转码utils 目录下,并执行go build -x命令。

可以看到编译utils.go文件的具体步骤,如创建目录、写入importcfg文件等。

完整输出如下

bash 复制代码
WORK=/var/folders/nv/f11l1jld7ql771rfz_b21yhc0000gr/T/go-build3026780938
mkdir -p $WORK/b001/
cat >/var/folders/nv/f11l1jld7ql771rfz_b21yhc0000gr/T/go-build3026780938/b001/importcfg << 'EOF' # internal
# import config
packagefile database/sql/driver=/Users/hangwu/Library/Caches/go-build/2e/2e6a3a4ec553e101f2e36fd8b0049a72f9ba9e349a0f5a3c22b3b8b3ad090229-d
packagefile fmt=/Users/hangwu/Library/Caches/go-build/04/04764c52faa1d3e6e698518e1dc3b3ffae4c25ac7875fe814b3fbf9ca8963b8c-d
packagefile path/filepath=/Users/hangwu/Library/Caches/go-build/8f/8fa7f7e56ec93875e3135dd3e55a91129cfee1a57957e0ca012b09d8f12822a2-d
packagefile reflect=/Users/hangwu/Library/Caches/go-build/f7/f79d69d68777453429cb7edbe0d5be687a91314798b7943367822cefba2aff48-d
packagefile runtime=/Users/hangwu/Library/Caches/go-build/a5/a5f3be7ffcd435cd4c0f4a05cbc6191cd7f9b3820831da31d77836525ca793d0-d
packagefile strconv=/Users/hangwu/Library/Caches/go-build/5f/5f04c11b590cb2a403b65d7cc2338fb5f42b711c927b98a3b423ba42a8b8d742-d
packagefile strings=/Users/hangwu/Library/Caches/go-build/db/db137e54b24d29ba4dc77debbdee4b4a69497480b580d888ea6b2cc072fccddd-d
packagefile unicode=/Users/hangwu/Library/Caches/go-build/92/92c0c5fa656d96d5befb9e4fffc51083c1a5170e54b6876a8993ce9415d24de8-d
EOF
cd /Users/hangwu/0_studio/github.com/gorm/utils
/Users/hangwu/0_studio/github.com/golang/go/pkg/tool/darwin_arm64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p gorm.io/gorm/utils -lang=go1.18 -complete -buildid UOPFRWgEHothTi9USrEK/UOPFRWgEHothTi9USrEK -goversion go1.21.11 -c=4 -shared -nolocalimports -importcfg $WORK/b001/importcfg -pack ./utils.go
/Users/hangwu/0_studio/github.com/golang/go/pkg/tool/darwin_arm64/buildid -w $WORK/b001/_pkg_.a # internal
cp $WORK/b001/_pkg_.a /Users/hangwu/Library/Caches/go-build/0b/0bd61e4e037c153dc08d70e9cdf18fcd2b5e194695984ae0f2875fe6bdbc03f5-d # internal

启动 Debug

从上面的输出,我们注意到 compile 命令正是go编译的过程,我们需要模仿 compile 的命令,然后启动debug。

第一步,构造importcfg文件

utils目录下创建importcfg文件,将上面# import cfgEOF中间的内容拷贝到文件中。

编译过程中,依赖的模块会先被编译,并以靜态库的方式,缓存在Caches目录下,当前代码的编译会查找importcfg中指定的文件并引用。

第二步,配置GOROOT(以Goland为例)

# Go语言编译器的正确打开方式(一)- 从源码编译 go 中的go源码目录配置为GOROOT

第三步,依样画葫芦,配好go compile的运行参数

这里只需要摘抄第一步中go build -x命令过程中编译参数

bash 复制代码
/Users/hangwu/0_studio/github.com/golang/go/pkg/tool/darwin_arm64/compile -o 
$WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p gorm.io/gorm/utils -lang=go1.18 -complete -buildid UOPFRWgEHothTi9USrEK/UOPFRWgEHothTi9USrEK -goversion go1.21.11 -c=4 -shared -nolocalimports -importcfg $WORK/b001/importcfg -pack ./utils.go

如果想知道这些参数控制什么,请用命令go tool compile --help查看帮助文档

点击Run运行,配置正确的话,在gorm/utils目录下生成一个编译后的_pkg_.a文件

第四步,debug一个示例

通过阅读go源码中src/cmd/compile目录下的README.md文件,了解go编译器的结构。

要注意不同go版本的这个文件内容是不一样的,注意尽量阅读当前版本的文件。

通过这个文档,我们知道了cmd/compile/internal/syntax目录下是编译的前端,也是编译的第一阶段,包括词法分析,语法分析,构造 AST(抽像语法树)

我们找到noder.goLoadPackage函数, 在最后的unified调用处打上断点。 unify之前是 ASTunifiy之后是 IR

IR 是 Internal Representation 的缩写,将代码转换为底层操作指令的中间产物 每个编译器都会定义自己的IR,后续的阶段都是基于IR进行优化并生成最终底层代码

一个AST的例子

接下来将第三步的配置以Debug方式启动,程序会停到断点这里。

观察这里的内存变量。要知道go编译器是把每个文件都扫描生成一个AST,一对一非常直观。

看到noders数组,每个noder代表一个文件的AST,当前包下只有utils.go一个文件,所以数组长度为1。

具体函数的例子

再展开,能看到代码被扫描成不同的syntax.Decl,包括了前面是一些import语句,var全局变量定义语句,以及后面是一些函数定义。

这里的Decl还是按代码顺序整理的,所以可以比较方便的看到我们想要的。

打开最后一个Decl结构,可以看到对应代码文件中的RTrimSlice函数。它包含了两个if语句(Stmt指Statment),和一个return语句。

后续

如果上面的过程顺利,那么你就具备了跟踪编译所有阶段的能力,

通过在不同的地方打断点跟踪,甚至修改代码,可以更好地理解Go编译过程。

相关推荐
胡乱儿起个名1 天前
Relay算子注册(在pytorch.py端调用)
c++·人工智能·tvm·编译器·ai编译器
白泽来了1 天前
字节大模型应用开发框架 Eino 全解(一)|结合 RAG 知识库案例分析框架生态
开源·go·大模型应用开发
致于数据科学家的小陈2 天前
Go 层级菜单树转 json 处理
python·go·json·菜单树·菜单权限·children
白总Server3 天前
Golang领域Beego框架的中间件开发实战
服务器·网络·websocket·网络协议·udp·go·ssl
ん贤4 天前
GoWeb开发
开发语言·后端·tcp/ip·http·https·go·goweb
纪元A梦4 天前
华为OD机试真题——荒岛求生(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
java·c语言·javascript·c++·python·华为od·go
胡乱儿起个名6 天前
PrimExpr 与 RelayExpr 的区别
人工智能·tvm·编译器·ai编译器
chxii7 天前
3.2goweb框架GORM
go
42fourtytoo8 天前
从0开始建立Github个人博客(hugo&PaperMod)
运维·服务器·python·go·github
xuhe28 天前
[tldr] GO语言异常处理
go·error