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

使用 gdb 工具調試 Go

開發 開發工具
排除應用程序故障是比較復雜的,特別是處理像 Go 這樣的高并發語言。它更容易在具體位置使用 print 打印語句來確定程序狀態,但是這個方法很難根據條件發展去動態響應你的代碼。

排除應用程序故障是比較復雜的,特別是處理像 Go 這樣的高并發語言。它更容易在具體位置使用 print 打印語句來確定程序狀態,但是這個方法很難根據條件發展去動態響應你的代碼

調試器提供了一個強大得令人難以置信的故障排除機制。添加排除故障的代碼可以巧妙地影響到應用程序該如何運行。調試器可以給正在迷茫的你更精確的看法。

[[145285]]

已經有許多 Go 的調試器存在了,其中一些調試器的不好之處是通過在編譯時注入代碼來提供一個交互終端。gdb 調試器則允許你調試已經編譯好的二進制文件,只要他們已經與 debug 信息連接,并不用修改源代碼。這是個相當不錯的特性,因此你可以從你的部署環境中取一個產品然后靈活地調試它。你可以從Golang 官方文檔中閱讀更多關于 gdb 的信息,那么這篇指南將簡單講解使用 gdb 調試器來調試 Go 應用程序的基本用法。

這兒會宣布一些 gdb 的***更新,最特別的是替換 -> 操作為 . 符號來訪問對象屬性。記住這兒可能在gdb 和 Go 版本中有細微改變。本篇指南基于 gdb 7.7.1和go 1.5beta2。

開始 gdb 調試

為了實驗 gdb 我使用了一個測試程序,完整的源代碼可以在gdb_sandbox_on_Github上查看。讓我們從一個非常簡單的程序開始吧:

 

  1. package main 
  2.  
  3. import (  
  4.     "fmt"  
  5.  
  6. func main() {  
  7.     for i := 0; i < 5; i++ { 
  8.         fmt.Println("looping")  
  9.     }  
  10.     fmt.Println("Done")  

我們可以運行這段代碼并看到它輸出內容的和我們想象的一樣:

  1. $ go run main.go 
  2. looping 
  3. looping 
  4. looping 
  5. looping 
  6. looping 
  7. Done 

我們來調試這個程序吧。首先,使用 go build 編譯成二進制文件,接著使用這個二進制文件的路徑做為參數運行 gdb。根據你的設定,你也可以使用 source 命令來獲取 Go 運行時(Go runtime)的支持。現在我們已經在 gdb 的命令行中了,我們可以在運行我們的二進制文件前為它設置斷點。

  1. $ go build -gcflags "-N -l" -o gdb_sandbox main.go  
  2. $ ls 
  3. gdb_sandbox  main.go  README.md 
  4. $ gdb gdb_sandbox 
  5. .... 
  6. (gdb) source /usr/local/src/go/src/runtime/runtime-gdb.py 
  7. Loading Go Runtime support. 

***關,我們在 for 循環里面設置一個斷點(b)來查看執行每次循環時我們的代碼會各有什么狀態。我們可以使用print(p)命令來檢查當前內容的一個變量,還有 list(l)和 backtrace(bt)命令查看當前步驟周圍的代碼。程序運行時可以使用 next(n)執行下一步或者使用 breakpoint(c)執行到下一個斷點。

  1. (gdb) b main.go:9  
  2. Breakpoint 1 at 0x400d35: file /home/bfosberry/workspace/gdb_sandbox/main.go, line 9.  
  3. (gdb) run  
  4. Starting program: /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/gdb_sandbox Breakpoint 1, main.main () at  
  5. /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:9  
  6. 9         fmt.Println("looping")  
  7. (gdb) l  
  8. 4         "fmt"  
  9. 5         )  
  10. 6   
  11. 7 func main() { 
  12. 8         for i := 0; i < 5; i++ {  
  13. 9         fmt.Println("looping")  
  14. 10        }`  
  15. 11        fmt.Println("Done")  
  16. 12 }  
  17. (gdb) p i  
  18. $1 = 0  
  19. (gdb) n  
  20. looping  
  21. Breakpoint 1, main.main () at  
  22. /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:9  
  23. 9        fmt.Println("looping")  
  24. (gdb) p i  
  25. $2 = 1  
  26. (gdb) bt 
  27. 0 main.main () at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:9 

我們的斷點可以設置在關聯文件的行號中、GOPATH里的文件的行號或一個包里的函數。如下也是一個有效的斷點:

  1. (gdb) b github.com/bfosberry/gdb_sandbox/main.go:9 
  2. (gdb) b 'main.main' 

Structs

我們可以用稍微復雜一點的代碼來實例演示如何調試。我們將使用f函數生成一個簡單的pair,x和y,當x相等時y=f(x),否則=x。

 

  1. type pair struct {  
  2.     x int  
  3.     y int  
  4.  
  5. func handleNumber(i int) *pair {  
  6.     val := i  
  7.     if i%2 == 0 {  
  8.         val = f(i)  
  9.     }  
  10.     return &pair{  
  11.        x: i,  
  12.        y: val,  
  13.     }  
  14.  
  15. func f(int x) int {  
  16.     return x*x + x  

也可以在循環中改變代碼來訪問這些新函數。

  1. p := handleNumber(i) 
  2. fmt.Printf("%+v/n", p) 
  3. fmt.Println("looping"

因為我們需要調試的是變量 y。我們可以在y被設置的地方放置斷點然后單步執行。可以使用 info args 查看函數的參數,在 bt 之前可以返回當前回溯。

 

  1. (gdb) b 'main.f'  
  2. (gdb) run  
  3. Starting program: /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/gdb_sandbox 
  4.  
  5. Breakpoint 1, main.f (x=0, ~anon1=833492132160)  
  6.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:33  
  7. 33       return x*x + x  
  8. (gdb) info args  
  9. x = 0  
  10. (gdb) continue  
  11. Breakpoint 1, main.f (x=0, ~anon1=833492132160)  
  12.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:33  
  13. 33       return x*x + x  
  14. (gdb) info args  
  15. x = 2  
  16. (gdb) bt 
  17. #0 main.f (x=2, ~anon1=1)  
  18.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:33 
  19. #1 0x0000000000400f0e in main.handleNumber (i=2, ~anon1=0x1
  20.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:24 
  21. #2 0x0000000000400c47 in main.main () 
  22.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:14 

因為我們在變量 y 是在函數 f 中被設定的這樣一個條件下,我們可以跳到這個函數的上下文并檢查堆區的代碼。應用運行時我們可以在一個更高的層次上設置斷點并檢查其狀態。

  1. (gdb) b main.go:26  
  2. Breakpoint 2 at 0x400f22: file  
  3. /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go, line 26.  
  4. (gdb) continue  
  5. Continuing. 
  6. Breakpoint 2, main.handleNumber (i=2, ~anon1=0x1)  
  7.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:28  
  8. 28             y: val,  
  9. (gdb) l  
  10. 23         if i%2 == 0 {  
  11. 24             val = f(i)  
  12. 25         }  
  13. 26         return &pair{  
  14. 27             x: i,  
  15. 28             y: val,  
  16. 29         }  
  17. 30     }  
  18. 31   
  19. 32 func f(x intint {  
  20. (gdb) p val  
  21. $1 = 6  
  22. (gdb) p i  
  23. $2 = 2 

如果我們在這個斷點處繼續住下走我們將越過在這個函數中的斷點1,而且將立即觸發在 HandleNumer 函數中的斷點,因為函數 f 只是對變量 i 每隔一次才執行。我們可以通過暫時使斷點 2不工作來避免這種情況的發生。

  1. (gdb) disable breakpoint 2  
  2. (gdb) continue  
  3. Continuing.  
  4. &{x:2 y:6}  
  5. looping  
  6. &{x:3 y:3}  
  7. looping  
  8. [New LWP 15200]  
  9. [Switching to LWP 15200
  10. Breakpoint 1, main.f (x=4, ~anon1=1)  
  11.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:33  
  12. 33         return x*x + x  
  13. (gdb) 

我們也可以分別使用 clear 和 delete breakpoint NUMBER 來清除和刪除斷點。動態產生和系住斷點,我們可以有效地在應用流中來回移動。

Slices and Pointers

上例程序太簡單了,只用到了整數型和字符串,所以我們將寫一個稍微復雜一點的。首先添加一個slice(切片類型)的指針到 main 函數,并保存生成的 pair,我們后面將用到它。

  1. var pairs []*pair 
  2. for i := 0; i < 10; i++ { 
  3.     p := handleNumber(i) 
  4.     fmt.Printf("%+v/n", p) 
  5.     pairs = append(pairs, p) 
  6.     fmt.Println("looping"
  7.     } 

現在我們來檢查生成出來的 slice 或 pairs,首先我們用轉換成數組來看一下這個 slice。因為 handleNumber 返回的是一個 *pair 類型,我們需要引用這個指針來訪問 struct(結構)的屬性。

 

  1. (gdb) b main.go:18  
  2. Breakpoint 1 at 0x400e14: file /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go, line 18.  
  3. (gdb) run  
  4. Starting program: /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/gdb_sandbox &{x:0 y:0
  5.  
  6. Breakpoint 1, main.main () at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:18  
  7. 18         fmt.Println("looping")  
  8. (gdb) p pairs  
  9. $1 = []*main.pair = {0xc82000a3a0}  
  10. (gdb) p pairs[0]  
  11. Structure has no component named operator[].  
  12. (gdb) p pairs.array  
  13. $2 = (struct main.pair **) 0xc820030028  
  14. (gdb) p pairs.array[0]  
  15. $3 = (struct main.pair *) 0xc82000a3a0  
  16. (gdb) p *pairs.array[0]  
  17. $4 = {x = 0, y = 0}  
  18. (gdb) p (*pairs.array[0]).x  
  19. $5 = 0  
  20. (gdb) p (*pairs.array[0]).y  
  21. $6 = 0  
  22. (gdb) continue  
  23. Continuing.  
  24. looping  
  25. &{x:1 y:1
  26.  
  27. Breakpoint 1, main.main () at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:18  
  28. 18         fmt.Println("looping")  
  29. (gdb) p (pairs.array[1][5]).y  
  30. $7 = 1  
  31. (gdb) continue  
  32. Continuing.  
  33. looping  
  34. &{x:2 y:6
  35.  
  36. Breakpoint 1, main.main () at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:18  
  37. 18         fmt.Println("looping")  
  38. (gdb) p (pairs.array[2][6]).y  
  39. $8 = 6  
  40. (gdb) 

你會發現這里 gdb 并不確定 pairs 是一個 slice 類型,我們不能直接訪問它的屬性,為了訪問它的成員我們需要使用 pairs.array 來轉換成數組,然后我們就可以檢查 slice 的 length(長度)和 capacity(容量):

  1. (gdb) p $len(pairs) 
  2. $12 = 3 
  3. (gdb) p $cap(pairs) 
  4. $13 = 4 

這時我們可以讓它循環幾次,并透過這個 slice 不用的成員方法監聽增加的 xy 的值,要注意的是,這里的 struct 屬性可以通過指針訪問,所以 p pairs.array[2].y 一樣可行。

Goroutines

現在我們已經可以訪問 struct 和 slice 了,下面再來更加復雜一點的程序吧。讓我們添加一些goroutines 到 mian 函數,并行處理每一個數字,返回的結果存入信道(chan)中:

 

  1.     pairs := []*pair{} 
  2.     pairChan := make(chan *pair) 
  3.     wg := sync.WaitGroup{} 
  4.         for i := 0; i < 10; i++ { 
  5.           wg.Add(1
  6.           go func(val int) { 
  7.             p := handleNumber(val) 
  8.             fmt.Printf("%+v/n", p) 
  9.             pairChan <- p 
  10.             wg.Done() 
  11.             }(i) 
  12.     } 
  13.     go func() { 
  14.             for p := range pairChan { 
  15.               pairs = append(pairs, p) 
  16.             } 
  17.     }() 
  18.     wg.Wait() 
  19.     close(pairChan) 
  20.  
  21. 如果我等待 WaitGroup 執行完畢再檢查 pairs slice 的結果,我們可以預期到內容是完全相同的,雖然它的排序可能有些出入。gdb 真正的威力來自于它可以在 goroutines 正在運行時進行檢查: 
  22.  
  23. (gdb) b main.go:43  
  24. Breakpoint 1 at 0x400f7f: file /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go, line 43.  
  25. (gdb) run  
  26. Starting program: /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/gdb_sandbox 
  27.  
  28. Breakpoint 1, main.handleNumber (i=0, ~r1=0x0)  
  29.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:43  
  30. 43         y: val,  
  31. (gdb) l  
  32. 38     if i%2 == 0 {  
  33. 39         val = f(i)  
  34. 40     }  
  35. 41     return &pair{  
  36. 42         x: i,  
  37. 43         y: val,  
  38. 44     }  
  39. 45 }  
  40. 46   
  41. 47 func f(x intint {  
  42. (gdb) info args  
  43. i = 0  
  44. ~r1 = 0x0  
  45. (gdb) p val  
  46. $1 = 0 

你會發現我們在 goroutine 要執行的代碼段中放置了一個斷點,從這里我們可以檢查到局部變量,和進程中的其它 goroutines:

  1. (gdb) info goroutines  
  2.   1 waiting runtime.gopark  
  3.   2 waiting runtime.gopark  
  4.   3 waiting runtime.gopark  
  5.   4 waiting runtime.gopark  
  6. 5 running main.main.func1  
  7.   6 runnable main.main.func1  
  8.   7 runnable main.main.func1  
  9.   8 runnable main.main.func1  
  10.   9 runnable main.main.func1  
  11. 10 running main.main.func1  
  12.   11 runnable main.main.func1  
  13.   12 runnable main.main.func1  
  14.   13 runnable main.main.func1  
  15.   14 runnable main.main.func1  
  16.   15 waiting runtime.gopark  
  17. (gdb) goroutine 11 bt 
  18. #0 main.main.func1 (val=6, pairChan=0xc82001a180, &wg=0xc82000a3a0
  19.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:19 
  20. #1 0x0000000000454991 in runtime.goexit () at /usr/local/go/src/runtime/asm_amd64.s:1696 
  21. #2 0x0000000000000006 in ?? () 
  22. #3 0x000000c82001a180 in ?? () 
  23. #4 0x000000c82000a3a0 in ?? () 
  24. #5 0x0000000000000000 in ?? () 
  25. (gdb) goroutine 11 l  
  26. 48         return x*x + x  
  27. 49     }  
  28. (gdb) goroutine 11 info args  
  29. val = 6  
  30. pairChan = 0xc82001a180  
  31. &wg = 0xc82000a3a0  
  32. (gdb) goroutine 11 p val  
  33. $2 = 6 

在這里我們做的***件事就是列出所有正在運行的 goroutine,并確定我們正在處理的那一個。然后我們可以看到一些回溯,并發送任何調試命令到 goroutine。這個回溯和列表清單并不太準確,如何讓回溯更準確,goroutine 上的 info args 顯示了我們的局部變量,以及主函數中的可用變量,goroutine 函數之外的使用前綴&

結論

當調試應用時,gdb 的強大令人難以置信。但它仍然是一個相當新的事物,并不是所有的地方工作地都很***。使用***的穩定版 gdb,go 1.5 beta2,有不少地方有突破:

Interfaces

根據 go 博客上的文章, go 的 interfaces 應該已經支持了,這允許在 gdb 中動態的投影其基類型。這應該算一個突破。

Interface{} 類型

目前沒有辦法轉換 interface{} 為它的類型。

列出 goroutine 的不同點

在其他 goroutine 中列出周邊代碼會導致一些行數的漂移,最終導致 gdb 認為當前的行數超出文件范圍并拋出一個錯誤:

  1. (gdb) info goroutines  
  2.   1 waiting runtime.gopark  
  3.   2 waiting runtime.gopark  
  4.   3 waiting runtime.gopark  
  5.   4 waiting runtime.gopark  
  6. 5 running main.main.func1  
  7.   6 runnable main.main.func1  
  8.   7 runnable main.main.func1  
  9.   8 runnable main.main.func1  
  10.   9 runnable main.main.func1  
  11. 10 running main.main.func1  
  12.   11 runnable main.main.func1  
  13.   12 runnable main.main.func1  
  14.   13 runnable main.main.func1  
  15.   14 runnable main.main.func1  
  16.   15 waiting runtime.gopark  
  17. (gdb) goroutine 11 bt 
  18. #0 main.main.func1 (val=6, pairChan=0xc82001a180, &wg=0xc82000a3a0
  19.     at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:19 
  20. #1 0x0000000000454991 in runtime.goexit () at /usr/local/go/src/runtime/asm_amd64.s:1696 
  21. #2 0x0000000000000006 in ?? () 
  22. #3 0x000000c82001a180 in ?? () 
  23. #4 0x000000c82000a3a0 in ?? () 
  24. #5 0x0000000000000000 in ?? () 
  25. (gdb) goroutine 11 l  
  26. 48         return x*x + x  
  27. 49     }  
  28. (gdb) goroutine 11 l  
  29. Python Exception <class 'gdb.error'> Line number 50 out of range; /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go has 49 lines.:  
  30. Error occurred in Python command: Line number 50 out of range; /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go has 49 lines. 

Goroutine 調試還不穩定

處理 goroutines 往往不穩定;我遇到過執行簡單命令產生錯誤的情況。現階段你應該做好處理類似問題的準備。

gdb 支持 Go 的配置非常麻煩

運行 gdb 支持 Go 調試的配置非常麻煩,獲取正確的路徑結合與構建 flags,還有 gdb 自動加載功能好像都不能正常的工作。首先,通過一個 gdb 初始化文件加載 Go 運行時支持就會產生初始化錯誤。這就需要手動通過一個源命令去加載,調試 shell 需要像指南里面描述的那樣去進行初始化。

我什么時候該使用一個調試器?

所以什么情況下使用 gdb 更有用?使用 print 語言和調試代碼是更有針對性的方法。

  • 當不適合修改代碼的時候

  • 當調試一個問題,但是不知道源頭,動態斷點或許更有效

  • 當包含許多 goroutines 時,暫停然后審查程序狀態會更好

“Debugging #golang with gdb” – via @codeship —— from Tweet

 

責任編輯:王雪燕 來源: oschina
相關推薦

2021-07-28 08:53:53

GoGDB調試

2021-03-15 06:23:40

GDB調試代碼編程語言

2025-03-31 03:25:00

2010-06-04 17:48:20

Linux編程工具

2022-07-25 07:57:19

工具代碼調試

2017-02-06 18:42:37

Linuxgdb程序

2023-03-29 08:18:16

Go調試工具

2024-02-26 00:02:00

開發Go

2021-06-04 05:18:29

ARM程序Gdbserver

2020-07-10 16:52:43

DelveGo程序開源

2016-03-29 10:32:34

2022-09-15 14:56:12

GDB調試鴻蒙

2023-11-22 13:13:54

多線程死鎖

2022-12-19 10:10:07

GDB命令

2025-06-26 05:00:00

2010-03-26 15:41:39

Python腳本

2021-07-26 10:14:38

Go語言工具

2016-12-02 20:23:51

AndroidADB

2018-11-27 11:35:32

systemtapMySQL調試工具

2019-12-06 14:30:41

GNU調試器GDB修復代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品国产a级 | 欧美日韩综合精品 | 成人黄页在线观看 | 黄色骚片 | 国产精品91视频 | 97精品国产97久久久久久免费 | 亚洲国产一区视频 | 精品欧美久久 | 九色 在线 | 精品一区二区在线观看 | 中文字幕成人 | 精品久草| 69热视频在线观看 | 国产亚洲www | 99久久久国产精品 | 日韩中文字幕久久 | 国产高清在线 | 久久久噜噜噜久久中文字幕色伊伊 | 99视频久 | 成人午夜精品 | 噜噜噜噜狠狠狠7777视频 | 国内精品久久久久久 | 亚洲精品18| 91成人午夜性a一级毛片 | 二区在线视频 | 日韩av在线中文字幕 | 中文字幕av网 | 国产精品1区2区 | 精品少妇一区二区三区在线播放 | 欧美精品一区免费 | 亚洲一区电影 | 成人性视频免费网站 | 久久精品欧美一区二区三区不卡 | 欧美日韩精品中文字幕 | 鸳鸯谱在线观看高清 | 91欧美精品成人综合在线观看 | 国产精品18久久久久久白浆动漫 | 国产一区二区三区色淫影院 | 国产激情视频在线观看 | 免费电影av | 国产精品欧美一区喷水 |