1. 程式人生 > >Golang源碼探索(一) 編譯和調試源碼(轉)

Golang源碼探索(一) 編譯和調試源碼(轉)

install flags main \n cnblogs root rect code newobject

GO可以說是近幾年最熱門的新興語言之一了, 一般人看到分布式大數據就會想到GO,
這個系列的文章會通過研究golang的源代碼來分析內部的實現原理,
和CoreCLR不同的是, golang的源代碼已經被很多人研究過了, 我將會著重研究他們未提到過的部分.

另一點和CoreCLR不同的是, golang的源代碼非常易懂, 註釋也非常的豐富,
很明顯Google的工程師在寫代碼的時候有考慮其他人會去看這份代碼.

盡管代碼非常易懂, 研究它們還是需要實際運行和調試才能得到更好的理解,
這個系列分析的golang源代碼是Google官方的實現的1.9.2版本, 不適用於其他版本和gccgo等其他實現,

運行環境是Ubuntu 16.04 LTS 64bit.

編譯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編譯了:

技術分享圖片

編譯的結果在~/git_go/go_src/bin下:

技術分享圖片

調試golang源代碼

之前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自身的源代碼進行調試.

參考鏈接

https://golang.org
https://golang.org/doc/install/source
https://golang.org/doc/gdb
http://lldb.llvm.org/tutorial.html
http://legendtkl.com/archives

接下來我將分析golang的任務調度機制和三色GC的具體實現, 敬請期待.

分類: Golang源碼探索

Golang源碼探索(一) 編譯和調試源碼(轉)