成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一文搞懂如何在Go包中支持Hash-Based Bisect調(diào)試

開發(fā) 前端
在這篇文章中,我將帶領(lǐng)大家深入了解Hash-Based Bisect這一高級調(diào)試技術(shù),探索如何讓我們自己的Go包支持這一調(diào)試技術(shù),以及如何在日常開發(fā)中幫助我們快速定位一些難以排查的潛在問題。

bisect是一個英文動詞,意為“二分”或“分成兩部分”。在數(shù)學(xué)和計算機(jī)科學(xué)中,通常指將一個區(qū)間或一個集合分成兩個相等的部分。

對于程序員來說,最熟悉的bisect應(yīng)用莫過于下面兩個:

  • 算法中的二分查找(binary search)

二分查找是一個經(jīng)典且高效的查找算法,任何一本介紹數(shù)據(jù)結(jié)構(gòu)或計算機(jī)算法的書都會包含對二分查找的系統(tǒng)說明。所謂二分查找就是通過不斷將搜索區(qū)間一分為二來找到目標(biāo)值。一些排序算法也應(yīng)用了bisect的思想,比如快速排序(QuickSort)等。

  • git bisect

git bisect是一個非常實用的Git命令,它通過二分查找的方式有效縮小可能導(dǎo)致錯誤的提交范圍,幫助開發(fā)人員快速定位引入錯誤的提交。其工作原理是反復(fù)從版本控制系統(tǒng)中檢出不同的提交并運(yùn)行測試,將結(jié)果標(biāo)記為“good”或“bad”。這個過程持續(xù)進(jìn)行,直到找到引入bug的具體提交(bad commit):

圖片圖片

git bisect特別適用于當(dāng)你懷疑某個bug是由于代碼庫歷史中的特定更改引起時,這種情況在日常開發(fā)中非常常見。

然而,并非所有的bug都能通過git bisect查找出來。尤其在編譯器、運(yùn)行時庫以及大型復(fù)雜項目中,問題往往潛藏在難以排查的調(diào)用棧、數(shù)據(jù)流或代碼路徑中。在這些情況下,git bisect這種傳統(tǒng)的工具可能會顯得力不從心。

注:如果你還不熟悉git bisect的使用方法,可以參考本文后面附錄中的入門示例。

在今年7月份,Go團(tuán)隊前技術(shù)主管Russ Cox在他的博客上發(fā)表了一篇題為“Hash-Based Bisect Debugging in Compilers and Runtimes[1]”的文章,介紹了Go編譯器和運(yùn)行時團(tuán)隊內(nèi)部使用的高級調(diào)試技術(shù)——Hash-Based Bisect。這一技術(shù)為我們提供了一種全新的問題定位方式。

在這篇文章中,我將帶領(lǐng)大家深入了解Hash-Based Bisect這一高級調(diào)試技術(shù),探索如何讓我們自己的Go包支持這一調(diào)試技術(shù),以及如何在日常開發(fā)中幫助我們快速定位一些難以排查的潛在問題。

1. Hash-Based Bisect是什么

前面提到過,git bisect常用于代碼提交歷史的回歸問題排查。然而,當(dāng)問題不是由提交歷史引發(fā),而是涉及程序行為的動態(tài)變化時,git bisect便顯得無能為力。例如:

  • 某些代碼路徑或優(yōu)化規(guī)則在特定運(yùn)行時觸發(fā)錯誤。
  • 測試程序在調(diào)用棧中的某些路徑上表現(xiàn)異常。
  • 多線程或并行執(zhí)行中,因運(yùn)行時調(diào)度導(dǎo)致的問題。

Hash-Based Bisect正是為了解決這些問題而設(shè)計的。它突破了靜態(tài)版本的局限,將調(diào)試范圍擴(kuò)展到了動態(tài)行為層面。

那么Hash-Based Bisect究竟是什么技術(shù)呢?它是一種基于哈希值和二分搜索的調(diào)試技術(shù),旨在快速定位復(fù)雜程序中導(dǎo)致問題的最小變化點集合。通過為代碼中的變化點(如函數(shù)、行號或調(diào)用棧)生成唯一的哈希值,該技術(shù)將程序行為映射到這些標(biāo)識符上。接著,通過逐步啟用或禁用特定變化點,結(jié)合測試程序的運(yùn)行結(jié)果,遞歸縮小問題范圍,最終定位問題根源(某幾行代碼甚至是某一行代碼):

圖片圖片

與git bisect專注于找到引入錯誤的提交不同,基于散列的bisect不會去遍歷版本歷史,而是直接對代碼的結(jié)構(gòu)和執(zhí)行流進(jìn)行操作,其調(diào)試的結(jié)果也不會與特定提交相關(guān),而是與代碼與特定執(zhí)行路徑或功能的交互相關(guān),即精確定位特定的代碼行,函數(shù)調(diào)用,甚至是觸發(fā)失敗的調(diào)用堆棧

下面我們再來仔細(xì)說明一下該技術(shù)的工作原理。

2. Hash-Based Bisect的工作原理

Hash-Based Bisect的核心在于利用哈希值為程序的變化點(如函數(shù)、代碼行、調(diào)用棧等)分配唯一標(biāo)識,并通過二分搜索算法,逐步縮小問題范圍。它通過動態(tài)啟用或禁用這些變化點,結(jié)合測試結(jié)果判斷問題是否被觸發(fā),從而定位導(dǎo)致問題的最小變化集。

這個方法有兩個關(guān)鍵要素:

  • 變化點的唯一標(biāo)識

在Russ Cox的文章中,他提及了一些傳統(tǒng)的二分方法,比如List-Based Bisect-Reduce、Counter-Based Bisect-Reduce等,但這些方法存在編號順序不穩(wěn)定、多變化點調(diào)試?yán)щy、擴(kuò)展性有限以及不適合并發(fā)或動態(tài)場景等問題。

而通過哈希函數(shù)生成變化點的標(biāo)識,確保無論代碼執(zhí)行順序、環(huán)境或并發(fā)情況如何,變化點的標(biāo)識始終唯一且穩(wěn)定的。同時輸入更為簡潔,通過簡短的哈希模式(如001+110),避免長列表或復(fù)雜編號,并且可適配多種問題類型(優(yōu)化規(guī)則、運(yùn)行時行為、動態(tài)調(diào)用棧等)。

  • 二分搜索

利用二分搜索算法在運(yùn)行時動態(tài)啟用和禁用變化點,高效縮小問題范圍,減少需要手動排查的復(fù)雜度。

下面我們再通過Hash-Based Bisect的典型工作流程來進(jìn)一步理解它的原理。

首先是定義變化點

將程序中可能導(dǎo)致問題的變化點抽象出來,比如:

  • 函數(shù)(函數(shù)名、文件路徑)
  • 代碼行(文件路徑和行號)
  • 調(diào)用棧(運(yùn)行時捕獲)

接下來,生成變化點的唯一哈希值

以Go當(dāng)前的hash-based bisect工具[2]以及支持該工具調(diào)試的Go包為例,對于每個變化點,Go包需要通過bisect.Hash方法生成哈希值,用于唯一標(biāo)識。例如:

id := bisect.Hash("foo.go", 10) // 生成foo.go文件第10行的唯一標(biāo)識。

第三步,利用二分搜索進(jìn)行自動的遞歸測試。具體來說,就是通過二分搜索逐步啟用或禁用變化點:

  • 啟用一個變化點集合,運(yùn)行測試程序,觀察是否觸發(fā)問題。
  • 根據(jù)測試結(jié)果縮小范圍,繼續(xù)遞歸,直到找到最小變化點集合。

最后,報告變化點,即最終輸出導(dǎo)致問題的最小變化集,幫助開發(fā)者快速定位問題。

Russ Cox文章中給了一個“某個函數(shù)的編譯優(yōu)化規(guī)則導(dǎo)致測試失敗”的例子,例子中包含一組數(shù)學(xué)函數(shù):

add, cos, div, exp, mod, mul, sin, sqr, sub, tan

要針對這個問題場景使用hash-based bisect進(jìn)行調(diào)試,第一步就是要定義函數(shù)變化點,并為每個變化點生成唯一哈希值標(biāo)識:

add: 00110010
cos: 00010000
sin: 11000111
...

然后啟用二分搜索,利用Hash-Based Bisect工具依次禁用某些函數(shù)的優(yōu)化,逐步縮小范圍。例如:

第一步:禁用add, cos, div, exp, mod,測試通過。
第二步:禁用mul, sin, sqr, sub, tan,測試失敗。
第三步:進(jìn)一步細(xì)分,最終定位sin為導(dǎo)致問題的函數(shù)。開發(fā)者只需檢查該函數(shù)的優(yōu)化規(guī)則即可解決問題。

原文章中,Russ Cox利用函數(shù)變化點哈希值的位后綴構(gòu)建了一顆二叉樹(如下圖),并利用后綴模式的不同進(jìn)行問題定位:

圖片

圖來自Russ Cox博客

了解了大致的工作原理后,我們再來看看Hash-Based Bisect在Go項目中的使用現(xiàn)狀。

3. Hash-Based Bisect在Go項目中的使用現(xiàn)狀

目前Hash-Based Bisect已經(jīng)成為Go項目編譯器和運(yùn)行時的重要調(diào)試工具之一,其工具鏈(golang.org/x/tools/cmd/bisect)和庫(golang.org/x/tools/internal/bisect)提供了強(qiáng)大的功能支持,幫助Go團(tuán)隊在編譯器開發(fā)、運(yùn)行時庫升級和語言特性修改等場景下快速定位問題。

Go實現(xiàn)的hash-based bisect調(diào)試技術(shù)包含兩部分:

  • bisect命令行工具[3]

bisect命令行工具可用于驅(qū)動測試運(yùn)行(如go test)并自動化調(diào)試過程,支持靈活的模式定義(如-godebug、-compile選項),結(jié)合用戶輸入定位問題點。

  • golang.org/x/tools/internal/bisect包

該包為庫和工具開發(fā)者提供一個接口,輕松實現(xiàn)與bisect工具的集成。并且提供了哈希生成、啟用判斷和變化點報告等功能,適配復(fù)雜調(diào)試需求。

上述工具目前在Go編譯器的SSA(靜態(tài)單賦值)后端開發(fā)、Go運(yùn)行時庫升級(比如Go 1.23的Timer Stop/Reset的新實現(xiàn)[4])以及語言特性的修改(比如loopvar語義變更[5])等方面都有重要的應(yīng)用,大大提高了Go團(tuán)隊在定位復(fù)雜問題時的調(diào)試效率。

以上工具和包在Go項目中已經(jīng)演化多年,頗為成熟。Russ Cox已經(jīng)發(fā)起提案#67140[6],旨在將golang.org/x/tools/internal/bisect包發(fā)布為標(biāo)準(zhǔn)庫debug/bisect包,這樣編譯器、運(yùn)行時、標(biāo)準(zhǔn)庫甚至標(biāo)準(zhǔn)庫之外的包都可以基于它提供的功能實現(xiàn)與bisect工具的兼容,并利用bisect工具實現(xiàn)基于變更點hash值的高級調(diào)試。

講到這里,屏幕前的你是否已經(jīng)感到“迫不及待”了呢?這樣優(yōu)秀的工具!我們現(xiàn)在能否使用它?是否可以將其應(yīng)用于我們自己的Go包的調(diào)試過程中呢?接下來,我就來用一個示例演示一下如何讓我們自己的包支持Go bisect工具,以幫助我們提升調(diào)試效率。

4. 讓你的庫支持Hash-Based Bisect調(diào)試

要利用bisect調(diào)試技術(shù),我們首先要解決的是bisect包位于internal中的問題,好在Russ Cox在實現(xiàn)bisect包時考慮了這個問題,bisect包沒有任何外部依賴,連Go標(biāo)準(zhǔn)庫都不依賴,這樣避免了后續(xù)變?yōu)閐ebug/bisect后導(dǎo)致標(biāo)準(zhǔn)庫循環(huán)依賴的問題。現(xiàn)在,我們可以將它直接copy出來,放到我們自己的工程中使用。

下面是我準(zhǔn)備的示例的目錄結(jié)構(gòu):

$tree -F hash-based-bisect/bisect-demo
hash-based-bisect/bisect-demo
├── bisect/
│   └── bisect.go
├── foo/
│   ├── foo.go
│   └── foo_test.go
└── go.mod

其中bisect目錄下的bisect.go來自github.com/golang/tools/blob/master/internal/bisect/bisect.go,foo包是我們這次要調(diào)試的目標(biāo)包,我們先來看看foo.go的代碼:

// bisect-demo/foo/foo.go

package foo

import (
 "bisect-demo/bisect"
 "flag"
)

var (
 bisectFlag = flag.String("bisect", "", "bisect pattern")
 matcher    *bisect.Matcher
)

// Features represents different features that might cause issues
const (
 FeatureRangeIteration  = "range-iteration"  // Using range vs classic for loop
 FeatureConcurrentLogic = "concurrent-logic" // Adding concurrent modifications
)

func Init() {
 flag.Parse()
 if *bisectFlag != "" {
  matcher, _ = bisect.New(*bisectFlag)
 }
}

func ProcessItems(items []int) []int {
 result := make([]int, 0, len(items))

 // First potential problematic change: different iteration approach
 id1 := bisect.Hash(FeatureRangeIteration)
 if matcher == nil || matcher.ShouldEnable(id1) {
  if matcher != nil && matcher.ShouldReport(id1) {
   println(bisect.Marker(id1), "enabled feature:", FeatureRangeIteration)
  }
  // Potentially problematic implementation using range
  for i := range items {
   result = append(result, items[i]*2)
  }
 } else {
  // Correct implementation using value iteration
  for _, v := range items {
   result = append(result, v*2)
  }
 }

 // Second potential problematic change: concurrent modifications
 id2 := bisect.Hash(FeatureConcurrentLogic)
 if matcher == nil || matcher.ShouldEnable(id2) {
  if matcher != nil && matcher.ShouldReport(id2) {
   println(bisect.Marker(id2), "enabled feature:", FeatureConcurrentLogic)
  }
  // Potentially problematic implementation with concurrency
  for i := 0; i < len(result); i++ {
   go func(idx int) {
    result[idx] += 1 // Race condition
   }(i)
  }
 }

 return result
}

大家可以結(jié)合前面提及的Hash-Based Bisect的典型工作流程來理解上面的代碼。

首先,我們模擬可能導(dǎo)致問題的兩個功能特性并定義了變化點,變化點由特性標(biāo)識符的hash值標(biāo)識,這里我們定義的特性標(biāo)識符為:

const (
    // 使用有意義的特性名稱作為 hash 的輸入
    FeatureRangeIteration  = "range-iteration"  // 使用 range vs 經(jīng)典 for 循環(huán)
    FeatureConcurrentLogic = "concurrent-logic" // 添加并發(fā)修改邏輯
)

接下來,對于每個可能有問題的變化點,都遵循相同的模式:

// 1. 計算特性的唯一Hash值
id1 := bisect.Hash(FeatureRangeIteration)

// 2. 檢查是否應(yīng)該啟用該特性
if matcher == nil || matcher.ShouldEnable(id1) {
    // 3. 如果需要,報告該特性被啟用
    if matcher != nil && matcher.ShouldReport(id1) {
        println(bisect.Marker(id1), "enabled feature:", FeatureRangeIteration)
    }
    
    // 4. 執(zhí)行可能有問題的實現(xiàn)
    for i := range items {
        result = append(result, items[i]*2)
    }
} else {
    // 5. 執(zhí)行正確的實現(xiàn)
    for _, v := range items {
        result = append(result, v*2)
    }
}

這里對matcher == nil的檢查算是一個小優(yōu)化:當(dāng)不在bisect調(diào)試模式時,matcher為nil。此時我們直接啟用所有特性,不需要計算hash和調(diào)用其他方法。

代碼中的ShouldEnable()決定是否啟用該特性的代碼,ShouldReport() 決定是否需要報告該特性被啟用。這兩個可能返回不同的值,尤其是在bisect搜索最小失敗集合時。

Marker()用于生成標(biāo)準(zhǔn)格式的匹配標(biāo)記,這些標(biāo)記會被bisect工具用來識別和追蹤啟用了哪些特性,標(biāo)記會在最終輸出中被移除,只顯示實際的描述文本。

這里還有一個接收bisect pattern的設(shè)置,我們是通過命令行參數(shù)來接收bisect每次傳給foo包的Pattern的,這里我們在Init函數(shù),而不是init函數(shù)中調(diào)用Parse,是因為如果在init函數(shù)中調(diào)用Parse,會干擾go test測試框架,導(dǎo)致出現(xiàn)類似“flag provided but not defined: -test.paniconexit0”的測試執(zhí)行錯誤。

下面是foo_test.go的代碼:

// bisect-demo/foo/foo_test.go

package foo

import (
 "flag"
 "testing"
 "time"
)

func TestMain(m *testing.M) {
 flag.Parse()
 Init()
 m.Run()
}

func TestProcessItems(t *testing.T) {
 input := []int{1, 2, 3, 4, 5}
 result := ProcessItems(input)

 // Wait for all goroutines to complete
 time.Sleep(1000 * time.Millisecond)

 // Verify results
 if len(result) != len(input) {
  t.Fatalf("got len=%d, want len=%d", len(result), len(input))
 }

 // Check if results are correct
 for i, v := range input {
  expected := v * 2
  if result[i] != expected {
   t.Errorf("result[%d] = %d, want %d", i, result[i], expected)
  }
 }
}

顯然為了foo包能成功獲取命令行參數(shù),我們重寫了TestMain,在其中調(diào)用了foo.Init函數(shù)。

接下來,我們就來執(zhí)行一下bisect工具,對foo包進(jìn)行一下調(diào)試,你可以通過go install golang.org/x/tools/cmd/bisect@latest安裝bisect。此外下面bisect命令行中的PATTERN是一個“占位符”,bisect命令會識別該“占位符”,并將其替換為相應(yīng)的字符串,這個在bisect的執(zhí)行過程中你也會看到:

// 在hash-based-bisect/bisect-demo/foo目錄下執(zhí)行

$bisect -v go test -v -args -bisect=PATTERN
bisect: checking target with all changes disabled
bisect: run: go test -v -args -bisect=n... ok (0 matches)
bisect: matches:
bisect: run: go test -v -args -bisect=n... ok (0 matches)
bisect: matches:
bisect: checking target with all changes enabled
bisect: run: go test -v -args -bisect=y... FAIL (2 matches)
bisect: matches:
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
bisect: run: go test -v -args -bisect=y... FAIL (2 matches)
bisect: matches:
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
bisect: target succeeds with no changes, fails with all changes
bisect: searching for minimal set of enabled changes causing failure
bisect: run: go test -v -args -bisect=+0... ok (1 matches)
bisect: matches:
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
bisect: run: go test -v -args -bisect=+0... ok (1 matches)
bisect: matches:
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
bisect: run: go test -v -args -bisect=+1... FAIL (1 matches)
bisect: matches:
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
bisect: run: go test -v -args -bisect=+1... FAIL (1 matches)
bisect: matches:
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
bisect: confirming failing change set
bisect: run: go test -v -args -bisect=v+x3f... FAIL (1 matches)
bisect: matches:
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
bisect: run: go test -v -args -bisect=v+x3f... FAIL (1 matches)
bisect: matches:
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
bisect: FOUND failing change set
--- change set #1 (enabling changes causes failure)
enabled feature: concurrent-logic
---
bisect: checking for more failures
bisect: run: go test -v -args -bisect=-x3f... ok (1 matches)
bisect: matches:
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
bisect: run: go test -v -args -bisect=-x3f... ok (1 matches)
bisect: matches:
[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
bisect: target succeeds with all remaining changes enabled

簡單解讀一下這個bisect調(diào)試過程的輸出。

bisect執(zhí)行分為幾個階段:

  • 初始檢查階段

首先用-bisect=n禁用所有變更進(jìn)行測試 → 測試通過(ok) 然后用-bisect=y啟用所有變更進(jìn)行測試 → 測試失敗(FAIL)

這表明程序在沒有任何變更時是正常的,但啟用所有變更后會失敗。

啟用所有變更時觀察到兩個特性:

[bisect-match 0xcf0b8943315a7804] enabled feature: range-iteration
[bisect-match 0x4d642a7960e4693f] enabled feature: concurrent-logic
  • 二分查找階段

測試+0(啟用第一個變更:range-iteration)→ 測試通過(ok) 測試+1(啟用第二個變更:concurrent-logic)→ 測試失敗(FAIL)

這個過程幫助定位到具體是哪個變更導(dǎo)致了失敗。

  • 確認(rèn)階段

使用v+x3f 模式再次確認(rèn) → 測試失敗(FAIL) 明確找到了導(dǎo)致失敗的變更集合:

--- change set #1 (enabling changes causes failure)
enabled feature: concurrent-logic
---
  • 最終驗證

使用-x3f 模式(禁用確認(rèn)的問題變更)進(jìn)行測試 → 測試通過(ok) 確認(rèn)啟用其他所有變更(除了concurrent-logic)時程序都能正常運(yùn)行。

從中得出調(diào)試結(jié)論:bisect工具成功定位到問題出在concurrent-logic特性上,range-iteration特性是安全的,不會導(dǎo)致測試失敗。問題明確是在并發(fā)邏輯中的“故意”邏輯導(dǎo)致的,這符合我們的代碼實現(xiàn)中的預(yù)期問題(在 concurrent-logic 特性中,我們確實故意修改了數(shù)據(jù))。

5. 小結(jié)

在本文中,我們深入探討了Hash-Based Bisect這一先進(jìn)的調(diào)試技術(shù),特別是在Go語言項目中的應(yīng)用。Hash-Based Bisect通過為代碼的變化點生成唯一的哈希值,結(jié)合二分搜索算法,幫助開發(fā)者快速定位復(fù)雜程序中的問題,超越傳統(tǒng)的git bisect方法。我們還詳細(xì)介紹了其工作原理、在Go項目中的現(xiàn)狀,以及如何將這一技術(shù)集成到自己的Go庫中,以提升調(diào)試效率。也許這里的示例也許并不恰當(dāng),但已經(jīng)達(dá)成了我向你展示如何使用bisect工具和bisect包的目的。

盡管Hash-Based Bisect在定位復(fù)雜問題上表現(xiàn)出色,但感覺其當(dāng)前設(shè)計仍存在一些不足,這些不足可能會影響開發(fā)者的使用體驗,尤其是在將其集成到Go包或項目時,這個不足主要體現(xiàn)在對代碼的侵入性上。為了支持Hash-Based Bisect,Go包需要顯式實現(xiàn)與bisect工具交互的協(xié)議,包括支持從命令行或環(huán)境變量接收bisect傳入的模式(pattern);需要在代碼中創(chuàng)建bisect.Matcher對象,并調(diào)用ShouldEnable和ShouldReport接口來管理變化點;代碼中必須為潛在變化點顯式生成唯一的哈希值,并根據(jù)需要啟用或禁用。

這種顯式集成導(dǎo)致代碼邏輯被調(diào)試相關(guān)代碼“污染”,增加了代碼復(fù)雜度和維護(hù)成本。對于一些簡單的庫或項目,開發(fā)者可能不愿為調(diào)試需求增加這種負(fù)擔(dān)。

在$GOROOT/src/cmd/compile/internal/base中,編譯器相關(guān)代碼就將bisect封裝到了一個HashDebug結(jié)構(gòu)中,一定程度上減少了代碼的侵入深度以及手動集成的工作量。

此外,golang.org/x/tools/internal/bisect包尚未正式變?yōu)閐ebug/bisect,后續(xù)其API是否會發(fā)生變化,尚不得而知,本文中的示例代碼不保證在后續(xù)的Go版本調(diào)整后依然能夠正確運(yùn)行。

本文涉及的源碼可以在這里[7]下載。

6. 參考資料

  • Hash-Based Bisect Debugging in Compilers and Runtimes[8] - https://research.swtch.com/bisect
  • proposal: debug/bisect: publish x/tools/internal/bisect[9] - https://github.com/golang/go/issues/67140
  • golang.org/x/tools/internal/bisect package[10] - https://pkg.go.dev/golang.org/x/tools/internal/bisect
  • Hacker News- Hash-based bisect debugging in compilers and runtimes[11] - https://news.ycombinator.com/item?id=40995982

7. 附錄:git bisect使用示例

假設(shè)你有一個Go語言項目,并且發(fā)現(xiàn)最近的某次提交引入了一個問題(例如,某個測試用例失敗了)。你希望使用git bisect找到引入該問題的具體提交。

你的項目目錄設(shè)計如下:

my-go-project/
├── main.go
└── main_test.go

我們來建立這個示例項目:

// 在hash-based-bisect/git-bisect下面執(zhí)行
$mkdir my-go-project
$cd my-go-project
$git init

創(chuàng)建main.go:

// main.go
package main

func main() {
    println("Hello, world!")
}

func Add(a, b int) int {
    return a + b
}

提交變更:

$git add main.go
git commit -m "Initial commit with Add function"
[master (root-commit) 16f8736] Initial commit with Add function
 1 file changed, 9 insertions(+)
 create mode 100644 main.go

創(chuàng)建main_test.go:

// main_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
    if Add(2, 3) != 5 {
        t.Error("Expected 5, got something else")
    }
}

提交變更:

$git add main_test.go
git commit -m "Add test for Add function"
[master b7b3c44] Add test for Add function
 1 file changed, 9 insertions(+)
 create mode 100644 main_test.go

故意引入一個bug并提交變更:

$sed -i 's/return a + b/return a - b/' main.go
$git commit -am "Introduce a bug in Add function"
[master 977e647] Introduce a bug in Add function
 1 file changed, 1 insertion(+), 1 deletion(-)

添加一些其他提交(無關(guān)的變更):

$echo "http:// Just a comment" >> main.go
$git commit -am "Add a comment"
[master 25f88b0] Add a comment
 1 file changed, 2 insertions(+)

這里列出上面所有commit的list,便于后續(xù)對照:

$git log --oneline
25f88b0 (HEAD -> master) Add a comment
977e647 Introduce a bug in Add function
b7b3c44 Add test for Add function
16f8736 Initial commit with Add function

接下來,我們就可以演示git bisect了,先來演示一下手工bisect。

啟動git bisect模式:

$git bisect start

標(biāo)記當(dāng)前最新提交為bad:

$git bisect bad

標(biāo)記首次提交為good:

$git bisect good 16f8736
Bisecting: 0 revisions left to test after this (roughly 1 step)
[977e647e7461c4c03ee25e53728dd743af925f17] Introduce a bug in Add function

我們看到git bisect自動切換到一個中間的提交,我們需要驗證這次中間提交是否能通過測試:

$go test  
--- FAIL: TestAdd (0.00s)
    main_test.go:7: Expected 5, got something else
FAIL
exit status 1
FAIL github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project 0.006s

測試失敗,我們將該提交標(biāo)記為bad:

$git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[b7b3c444f0fd55086e6ce36fb543a136a1611b61] Add test for Add function

git bisect又切換到了另外一個中間提交,我們用go test驗證是否能通過:

$go test 
PASS
ok   github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project 0.005s

測試通過,我們將這個中間提交標(biāo)記為good:

$git bisect good
977e647e7461c4c03ee25e53728dd743af925f17 is the first bad commit
commit 977e647e7461c4c03ee25e53728dd743af925f17
Author: Tony Bai <bigwhite.cn@aliyun.com>
Date:   Fri Nov 24 13:27:08 2024 +0800

    Introduce a bug in Add function

:100644 100644 e357c05d933724eb8b7c1aafee34b8f95913355e e65baa0414a2a1f983379c23ac549b7d8b056db3 M main.go

我們看到:git bisect找到了一個bad commit,并顯示“977e647e7461c4c03ee25e53728dd743af925f17 is the first bad commit”。

結(jié)束git bisect模式:

$git bisect reset

上面的過程可以使用git bisect run進(jìn)行自動化,而無需中間手動多次的執(zhí)行g(shù)o test和標(biāo)記,下面是一個等價的git bisect過程:

$git bisect start

$git bisect bad

$git bisect good 16f8736
Bisecting: 0 revisions left to test after this (roughly 1 step)
[977e647e7461c4c03ee25e53728dd743af925f17] Introduce a bug in Add function

$git bisect run go test
running go test
--- FAIL: TestAdd (0.00s)
    main_test.go:7: Expected 5, got something else
FAIL
exit status 1
FAIL github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project 0.006s
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[b7b3c444f0fd55086e6ce36fb543a136a1611b61] Add test for Add function
running go test
PASS
ok   github.com/bigwhite/experiments/hash-based-bisect/git-bisect/my-go-project 0.006s
977e647e7461c4c03ee25e53728dd743af925f17 is the first bad commit
commit 977e647e7461c4c03ee25e53728dd743af925f17
Author: Tony Bai <bigwhite.cn@aliyun.com>
Date:   Fri Nov 24 13:27:08 2024 +0800

    Introduce a bug in Add function

:100644 100644 e357c05d933724eb8b7c1aafee34b8f95913355e e65baa0414a2a1f983379c23ac549b7d8b056db3 M main.go
bisect run success

$git bisect reset
Previous HEAD position was b7b3c44 Add test for Add function
Switched to branch 'master'

我們看到通過git bisect run可以更快速地定位問題,而無需中間的手工操作,這是我們?nèi)粘i_發(fā)中主要使用的bisect手段!

參考資料

[1] Hash-Based Bisect Debugging in Compilers and Runtimes: https://research.swtch.com/bisect

[2] hash-based bisect工具: https://github.com/golang/tools/tree/master/cmd/bisect

[3] bisect命令行工具: https://github.com/golang/tools/tree/master/cmd/bisect

[4] Go 1.23的Timer Stop/Reset的新實現(xiàn): https://github.com/golang/go/issues/37196

[5] loopvar語義變更: https://tonybai.com/2023/08/20/some-changes-in-go-1-21

[6] Russ Cox已經(jīng)發(fā)起提案#67140: https://github.com/golang/go/issues/67140

[7] 這里: https://github.com/bigwhite/experiments/tree/master/hash-based-bisect

[8] Hash-Based Bisect Debugging in Compilers and Runtimes: https://research.swtch.com/bisect

[9] proposal: debug/bisect: publish x/tools/internal/bisect: https://github.com/golang/go/issues/67140

[10] golang.org/x/tools/internal/bisect package: https://pkg.go.dev/golang.org/x/tools/internal/bisect

[11] Hacker News- Hash-based bisect debugging in compilers and runtimes: https://news.ycombinator.com/item?id=40995982

責(zé)任編輯:武曉燕 來源: TonyBai
相關(guān)推薦

2023-03-14 09:03:20

Go語法腳本

2022-10-12 07:24:18

大文件哈希算法Hash

2022-03-24 08:51:48

Redis互聯(lián)網(wǎng)NoSQL

2024-04-12 12:19:08

語言模型AI

2019-10-18 10:43:11

JPASpring Boot Flyway

2021-03-22 10:05:59

netstat命令Linux

2023-09-08 08:20:46

ThreadLoca多線程工具

2023-09-15 12:00:01

API應(yīng)用程序接口

2023-11-25 09:41:34

GogRPCHandler

2021-02-22 09:44:03

KubernetesDNSLinux

2023-09-02 21:27:09

2021-03-04 00:09:31

MySQL體系架構(gòu)

2021-02-28 20:53:37

Cookie存儲瀏覽器

2023-03-06 21:29:41

mmap技術(shù)操作系統(tǒng)

2020-09-03 06:35:44

Linux權(quán)限文件

2022-07-15 08:16:56

Stream函數(shù)式編程

2024-07-12 14:46:20

2020-12-07 06:19:50

監(jiān)控前端用戶

2021-07-08 10:08:03

DvaJS前端Dva

2023-05-22 13:27:17

點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 91在线精品视频 | www国产成人 | 日韩一区二区三区视频在线播放 | 亚洲 自拍 另类 欧美 丝袜 | 日本精品视频一区二区 | 国产农村妇女精品一二区 | 自拍偷拍视频网 | 国产精品日日摸夜夜添夜夜av | 日韩在线观看中文字幕 | 日本免费在线 | 国产中文视频 | 鲁大师一区影视 | 91社影院在线观看 | 欧美三区视频 | 亚洲a视频 | 欧美日韩亚洲一区 | 久久爆操 | 一级毛片中国 | 天天操天天干天天透 | 国产日韩av一区二区 | 国产免费xxx | 日本久久一区二区三区 | 国产天堂 | 免费看a| 国产欧美精品一区 | 九九精品在线 | 香蕉视频在线播放 | 欧美在线a| 国产一级片免费看 | 夜夜久久| 色狠狠一区 | 韩日av片 | 午夜影院在线观看视频 | 成人av免费网站 | a级毛片基地 | 久在草 | 仙人掌旅馆在线观看 | 爱爱视频在线观看 | 国产福利视频网站 | 一区二区三区四区在线视频 | 亚洲成av人影片在线观看 |