自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一文搞懂如何在Go包中支持Hash-Based Bisect調試

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

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

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

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

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

  • git bisect

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

圖片圖片

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

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

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

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

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

1. Hash-Based Bisect是什么

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

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

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

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

圖片圖片

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

下面我們再來仔細說明一下該技術的工作原理。

2. Hash-Based Bisect的工作原理

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

這個方法有兩個關鍵要素:

  • 變化點的唯一標識

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

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

  • 二分搜索

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

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

首先是定義變化點

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

圖片

圖來自Russ Cox博客

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

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

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

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

  • bisect命令行工具[3]

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

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

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

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

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

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

4. 讓你的庫支持Hash-Based Bisect調試

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

下面是我準備的示例的目錄結構:

$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包是我們這次要調試的目標包,我們先來看看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
}

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

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

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

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

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

// 2. 檢查是否應該啟用該特性
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)化:當不在bisect調試模式時,matcher為nil。此時我們直接啟用所有特性,不需要計算hash和調用其他方法。

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

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

這里還有一個接收bisect pattern的設置,我們是通過命令行參數(shù)來接收bisect每次傳給foo包的Pattern的,這里我們在Init函數(shù),而不是init函數(shù)中調用Parse,是因為如果在init函數(shù)中調用Parse,會干擾go test測試框架,導致出現(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,在其中調用了foo.Init函數(shù)。

接下來,我們就來執(zhí)行一下bisect工具,對foo包進行一下調試,你可以通過go install golang.org/x/tools/cmd/bisect@latest安裝bisect。此外下面bisect命令行中的PATTERN是一個“占位符”,bisect命令會識別該“占位符”,并將其替換為相應的字符串,這個在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調試過程的輸出。

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

  • 初始檢查階段

首先用-bisect=n禁用所有變更進行測試 → 測試通過(ok) 然后用-bisect=y啟用所有變更進行測試 → 測試失?。‵AIL)

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

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

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

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

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

  • 確認階段

使用v+x3f 模式再次確認 → 測試失?。‵AIL) 明確找到了導致失敗的變更集合:

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

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

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

5. 小結

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

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

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

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

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

本文涉及的源碼可以在這里[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使用示例

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

你的項目目錄設計如下:

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(-)

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

$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

標記當前最新提交為bad:

$git bisect bad

標記首次提交為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

測試失敗,我們將該提交標記為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

測試通過,我們將這個中間提交標記為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”。

結束git bisect模式:

$git bisect reset

上面的過程可以使用git bisect run進行自動化,而無需中間手動多次的執(zhí)行go test和標記,下面是一個等價的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可以更快速地定位問題,而無需中間的手工操作,這是我們日常開發(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

責任編輯:武曉燕 來源: TonyBai
相關推薦

2022-10-12 07:24:18

大文件哈希算法Hash

2023-03-14 09:03:20

Go語法腳本

2019-10-18 10:43:11

JPASpring Boot Flyway

2024-04-12 12:19:08

語言模型AI

2022-03-24 08:51:48

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

2021-03-22 10:05:59

netstat命令Linux

2023-09-15 12:00:01

API應用程序接口

2023-09-08 08:20:46

ThreadLoca多線程工具

2023-11-25 09:41:34

GogRPCHandler

2021-02-22 09:44:03

KubernetesDNSLinux

2021-01-13 05:21:59

參數(shù)

2021-06-30 08:45:02

內存管理面試

2022-08-15 15:39:23

JavaScript面向對象數(shù)據(jù)

2023-04-03 15:04:00

RPCPHP語言

2023-10-16 08:16:31

Bean接口類型

2024-06-05 11:43:10

2020-03-18 14:00:47

MySQL分區(qū)數(shù)據(jù)庫

2019-11-19 08:00:00

神經(jīng)網(wǎng)絡AI人工智能

2023-08-24 16:50:45

2022-06-07 10:13:22

前端沙箱對象
點贊
收藏

51CTO技術棧公眾號