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编译过程。

相关推荐
lekami_兰7 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘10 小时前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤11 小时前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt111 天前
AI DDD重构实践
go
Grassto2 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto4 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室5 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题5 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo
啊汉7 天前
古文观芷App搜索方案深度解析:打造极致性能的古文搜索引擎
go·软件随想
asaotomo8 天前
一款 AI 驱动的新一代安全运维代理 —— DeepSentry(深哨)
运维·人工智能·安全·ai·go