go compile debug - yaokun123/php-wiki GitHub Wiki

编译和调试源代码

一、编译golang源代码

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编译了

> cd ~/git_go/go_src/src
> ./all.bash

// 编译的结果在~/git_go/go_src/bin下:

二、调试golang源代码

go build -gcflags "-Nl" xx.go

go build 可以用-gcflags给go编译器传入参数,也就是传给go tool compile的参数,因此可以用go tool compile --help查看所有可用的参数。
go build 可以用-ldflags给go链接器传入参数,实际是给go tool link的参数,可以用go tool link --help查看可用的参数。

其中-m可以检查代码的编译优化情况,包括逃逸情况和函数是否内联。

以源代码(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) b main.main

查看函数的汇编代码:
(lld) di --frame