Go deadcode:查詢沒意義的死程式碼,對於維護專案挺有用!

發表於2024-02-25

大家好,我是煎魚。

還記得我前兩年在深圳參加了個技術大會,其中一個議題是攜程的一個大佬分享他在日常工作中,發現一大堆過時的無意義程式碼和邏輯,導致大家工作較為繁瑣且較為辛苦的情況。

攜程應該是 Java 應用為主,他基於 Java 各種研究,透過 JVM 內引數結合各種手段找到了無意義的死程式碼,並透過灰度機制等完成了逐步上線和替換。

最近 Go 官方也終於有了類似的工具,今天分享給大家,可以持續關注!

用 deadcode 檢測程式碼

普遍來講,作為 Go 專案原始碼一部分,但在任何執行過程中都無法觸及的函式被稱為 "死程式碼",它們會拖累程式碼庫的維護工作。

也會造成程式設計師在閱讀程式碼時的認知負擔,看了半天發現這程式碼根本沒用。或是莫名其妙就被引入模組依賴裡裡。尷尬得很。

現在我們可以用 deadcode 來識別他。安裝方式如下:

$ go install golang.org/x/tools/cmd/deadcode@latest
$ deadcode -help
The deadcode command reports unreachable functions in Go programs.

Usage: deadcode [flags] package...

以下是一個簡單 Demo:

func main() {
    var g Greeter
    g = Helloer{}
    g.Greet()
}

type Greeter interface{ Greet() }

type Helloer struct{}
type Goodbyer struct{}

var _ Greeter = Helloer{}
var _ Greeter = Goodbyer{}

func (Helloer) Greet()  { hello() }
func (Goodbyer) Greet() { goodbye() }

func hello()   { fmt.Println("你好,煎魚!") }
func goodbye() { fmt.Println("再見,煎魚!") }

執行結果:

$ go run main.go
你好,煎魚!

咋一眼一看,可能沒法知道是哪塊程式碼沒用到。還需要多看兩眼。

這時候我們只需要藉助 deadcode 工具去掃描,一下子就能得到結果了。

執行如下命令並檢視輸出結果:

$ deadcode .
main.go:20:17: unreachable func: Goodbyer.Greet
main.go:23:6: unreachable func: goodbye

檢測結果告訴我們 goodbye 函式和 Goodbyer.Greet 方法都無法訪問。也就是這個程式碼本身的存在是沒有執行意義的。

如果你希望清除這些騷擾程式碼,就可以依據這個結果去做刪除程式碼了。

同時也可以藉助命令的子選項 -whylive,讓檢測工具給我們解釋為什麼 greet.hello 函式是有效的。

如下解釋結果:

$ deadcode -whylive=example.com/greet.hello .
                  example.com/greet.main
dynamic@L0008 --> example.com/greet.Helloer.Greet
 static@L0019 --> example.com/greet.hello
                   example.com/greet.main
  ...

該命令會把 main 開始到函式呼叫的過程列印出來,作為一種解釋。證明這個函式確實是存在使用的。

注意點和發現機制

需要留意的是:deadcode 工具,必須要包含 main 函式。言外之意就是其檢測鏈路是從 main 函式開始的。

否則會產生如下的報錯資訊:

$ deadcode .
deadcode: no main packages

deadcode 工具本身會載入、解析和型別檢查指定的包,然後將其轉換為類似編譯器的中間表示形式。

然後會使用 Rapid Type Analysis(RTA)的演算法來建立可達函式集,最初只包括每個主要包的入口點:main 函式和包初始化函式(分配全域性變數並呼叫名為 init 的函式)。

RTA 分析每個可達函式的語句體以收集三種型別的資訊:直接呼叫的函式集合、透過介面方法進行的動態呼叫集合以及轉換為介面的型別集合。

因此他必須依賴 main 函式作為主入口來做呼叫鏈路分析。當然了,這也是相對正常的。總得有個 “客戶端” 來做開始邏輯。

我們可以定期在 Go 專案上執行 deadcode 命令(特別是在重構工作之後),以幫助識別程式中不再需要的部分。

但需要留意的是,deadcode 工具還是在發展階段。複雜場景下,可能無法保證 100% 的準確率,我們最好還是要自己做一遍 double check 和灰度上線。

總結

今天基於官方的《Finding unreachable functions with deadcode》給大家分享了 deadcode 工具的使用和機制。

整體上來講,還是非常樂見這個工具的誕生和發展的。歷史專案維護舊了,很多地方刪刪改改,堆積久了後,確實會給大家開發造成不少的認知負擔和維護成本。

文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。

推薦閱讀

相關文章