<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一個(gè) Demo 學(xué)會(huì)使用 Go Delve 調(diào)試

          共 9969字,需瀏覽 20分鐘

           ·

          2021-08-01 16:04

          在 Go 語(yǔ)言中,除了 go tool 工具鏈中的 pprof、trace 等剖析工具的大利器外。常常還會(huì)有小伙伴問(wèn),有沒(méi)有更好用,更精細(xì)的,

          大家總嫌棄 pprof、trace 等工具,不夠細(xì),沒(méi)法一口氣看到根因,或者具體變量...希望能夠最好能追到代碼級(jí)別調(diào)試的,看到具體變量的值是怎么樣的,隨意想怎么看怎么看的那種。

          為此今天給大家介紹 Go 語(yǔ)言強(qiáng)大的 Delve (dlv)調(diào)試工具,來(lái)更深入問(wèn)題剖析。

          安裝

          我們需要先安裝 Go delve,若是 Go1.16 及以后的版本,可以直接執(zhí)行下述命令安裝:

          $ go install github.com/go-delve/delve/cmd/dlv@latest

          也可以通過(guò) git clone 的方式安裝:

          $ git clone https://github.com/go-delve/delve
          cd delve
          $ go install github.com/go-delve/delve/cmd/dlv

          在安裝完畢后,我們執(zhí)行 dlv version 命令,查看安裝情況:

          $ dlv version
          Delve Debugger
          Version: 1.7.0
          Build: $Id: e353a65161e6ed74952b96bbb62ebfc56090832b $

          可以明確看到我們所安裝的版本是 v1.7.0。

          演示程序

          我們計(jì)劃用一個(gè)反轉(zhuǎn)字符串的演示程序來(lái)進(jìn)行 Go 程序的調(diào)試。第一部分先是完成 stringer 包的 Reverse 方法。

          代碼如下:

          package stringer

          func Reverse(s string) string {
           r := []rune(s)
           for i, j := 0len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
            r[i], r[j] = r[j], r[i]
           }
           return string(r)
          }

          再在具體的 main 啟動(dòng)函數(shù)中進(jìn)行調(diào)用。代碼如下:

          import (
           "fmt"

           "github.com/eddycjy/awesome-project/stringer"
          )

          func main() {
           fmt.Println(stringer.Reverse("腦子進(jìn)煎魚(yú)了!"))
          }

          輸出結(jié)果:

          !了魚(yú)煎進(jìn)子腦

          進(jìn)行調(diào)試

          Delve 是 Go 程序的源代碼級(jí)調(diào)試器。Delve 使您能夠通過(guò)控制流程的執(zhí)行與您的程序進(jìn)行交互,查看變量,提供線(xiàn)程、goroutine、CPU 狀態(tài)等信息。

          其一共支持如下 11 個(gè)子命令:

          Available Commands:
            attach      Attach to running process and begin debugging.
            connect     Connect to a headless debug server.
            core        Examine a core dump.
            dap         [EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
            debug       Compile and begin debugging main package in current directory, or the package specified.
            exec        Execute a precompiled binary, and begin a debug session.
            help        Help about any command
            run         Deprecated command. Use 'debug' instead.
            test        Compile test binary and begin debugging program.
            trace       Compile and begin tracing program.
            version     Prints version.

          我們今天主要用到的是 debug 命令,他能夠編譯并開(kāi)始調(diào)試當(dāng)前目錄下的主包,或指定的包,是最常用的功能之一。

          接下來(lái)我們利用這個(gè)演示程序來(lái)進(jìn)行 dlv 的深入調(diào)試和應(yīng)用。

          執(zhí)行如下命令:

          ?  awesomeProject dlv debug .
          Type 'help' for list of commands.
          (dlv) 

          我們先在演示程序根目錄下執(zhí)行了 debug,進(jìn)入了 dlv 的交互模式。

          再使用關(guān)鍵字 b(break 的縮寫(xiě))對(duì) main.main 方法設(shè)置斷點(diǎn):

          (dlv) b main.main
          Breakpoint 1 (enabled) set at 0x10cbab3 for main.main() ./main.go:9
          (dlv) 

          設(shè)置完畢后,我們可以看到方法對(duì)應(yīng)的文件名、行數(shù)。接著我們可以執(zhí)行關(guān)鍵字 c(continue 的縮寫(xiě))跳轉(zhuǎn)到下一個(gè)斷點(diǎn)處:

          (dlv) c
          > main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x10cbab3)
               4:  "fmt"
               5: 
               6:  "github.com/eddycjy/awesome-project/stringer"
               7: )
               8: 
          =>   9: func main() {
              10:  fmt.Println(stringer.Reverse("腦子進(jìn)煎魚(yú)了!"))
              11: }
          (dlv) 

          在斷點(diǎn)處,我看可以看到具體的代碼塊、goroutine、CPU 寄存器地址等運(yùn)行時(shí)信息。

          緊接著執(zhí)行關(guān)鍵字 n(next 的縮寫(xiě))單步執(zhí)行程序的下一步:

          (dlv) n
          > main.main() ./main.go:10 (PC: 0x10cbac1)
               5: 
               6:  "github.com/eddycjy/awesome-project/stringer"
               7: )
               8: 
               9: func main() {
          =>  10:  fmt.Println(stringer.Reverse("腦子進(jìn)煎魚(yú)了!"))
              11: }

          我們可以看到程序走到了 main.go 文件中的第 10 行中,并且調(diào)用了 stringer.Reverse 方法去處理。

          此時(shí)我們可以執(zhí)行關(guān)鍵字 s(step 的關(guān)鍵字)進(jìn)入到這個(gè)函數(shù)中去繼續(xù)調(diào)試:

          (dlv) s
          > github.com/eddycjy/awesome-project/stringer.Reverse() ./stringer/string.go:3 (PC: 0x10cb87b)
               1: package stringer
               2: 
          =>   3: func Reverse(s string) string {
               4:  r := []rune(s)
               5:  for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
               6:   r[i], r[j] = r[j], r[i]
               7:  }
               8:  return string(r)

          輸入后,調(diào)試的光標(biāo)會(huì)到 Reverse 方法上,此時(shí)我們可以調(diào)用關(guān)鍵字 p(print 的縮寫(xiě))傳出所傳入的變量的值:

          (dlv) p s
          "腦子進(jìn)煎魚(yú)了!"

          此處函數(shù)的形參變量是 s,輸出了 “腦子進(jìn)煎魚(yú)了!”,與我們所傳入的是一致的。

          但故事一般沒(méi)有這么的簡(jiǎn)單,會(huì)用到 Delve 來(lái)調(diào)試,說(shuō)明是比較細(xì)致、隱患的 BUG。為此我們大多需要更進(jìn)一步的深入。

          我們繼續(xù)圍觀(guān) Reverse 方法:

               5:  for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
               6:   r[i], r[j] = r[j], r[i]
               7:  }

          從表現(xiàn)來(lái)看,我們常常會(huì)懷疑是第 6 行可能是問(wèn)題的所在。這時(shí)可以針對(duì)性的對(duì)第 6 行進(jìn)行斷點(diǎn)查看:

          (dlv) b 6
          Breakpoint 2 (enabled) set at 0x10cb92c for github.com/eddycjy/awesome-project/stringer.Reverse() ./stringer/string.go:6

          設(shè)置完斷點(diǎn)后,我們只需要執(zhí)行關(guān)鍵字 c,繼續(xù)下一步:

          (dlv) c
          > github.com/eddycjy/awesome-project/stringer.Reverse() ./stringer/string.go:6 (hits goroutine(1):1 total:1) (PC: 0x10cb92c)
               1: package stringer
               2: 
               3: func Reverse(s string) string {
               4:  r := []rune(s)
               5:  for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
          =>   6:   r[i], r[j] = r[j], r[i]
               7:  }
               8:  return string(r)
               9: }

          走到對(duì)應(yīng)的代碼片段后,執(zhí)行關(guān)鍵字 locals

          (dlv) locals
          r = []int32 len: 7, cap: 32, [...]
          j = 6
          i = 0

          我們就可以看到對(duì)應(yīng)的變量 r, i, j 的值是多少,可以根據(jù)此來(lái)分析程序流轉(zhuǎn)是否與我們預(yù)想的一致。

          另外也可以調(diào)用關(guān)鍵字 set 去針對(duì)特定變量設(shè)置期望的值:

          (dlv) set i = 1
          (dlv) locals
          r = []int32 len: 7, cap: 32, [...]
          j = 6
          i = 1

          設(shè)置后,若還需要繼續(xù)排查,可以繼續(xù)調(diào)用關(guān)鍵字 c 去定位,這種常用于特定變量的特定值的異常,這樣一設(shè)置一調(diào)試基本就能排查出來(lái)了。

          在排查完畢后,我們可以執(zhí)行關(guān)鍵字 r(reset 的縮寫(xiě)):

          (dlv)  r
          Process restarted with PID 56614

          執(zhí)行完畢后,整個(gè)調(diào)試就會(huì)重置,像是前面在打斷點(diǎn)時(shí)所設(shè)置的變量值就會(huì)恢復(fù)。

          若要查看設(shè)置的斷點(diǎn)情況,也可以執(zhí)行關(guān)鍵字 bp 查看:

          (dlv) bp
          Breakpoint runtime-fatal-throw (enabled) at 0x1038fc0 for runtime.fatalthrow() /usr/local/Cellar/go/1.16.2/libexec/src/runtime/panic.go:1163 (0)
          Breakpoint unrecovered-panic (enabled) at 0x1039040 for runtime.fatalpanic() /usr/local/Cellar/go/1.16.2/libexec/src/runtime/panic.go:1190 (0)
           print runtime.curg._panic.arg
          Breakpoint 1 (enabled) at 0x10cbab3 for main.main() ./main.go:9 (0)
          Breakpoint 2 (enabled) at 0x10cb92c for github.com/eddycjy/awesome-project/stringer.Reverse() ./stringer/string.go:6 (0)

          查看斷點(diǎn)情況后,若有部分已經(jīng)排除了,可以調(diào)用關(guān)鍵字 clearall 對(duì)一些斷點(diǎn)清除:

          (dlv) clearall main.main
          Breakpoint 1 (enabled) cleared at 0x10cbab3 for main.main() ./main.go:9

          若不指點(diǎn)斷點(diǎn),則會(huì)默認(rèn)清除全部斷點(diǎn)。

          在日常的 Go 工程中,若都從 main 方法進(jìn)入就太繁瑣了。我們可以直接借助函數(shù)名進(jìn)行調(diào)式定位:

          (dlv) funcs Reverse
          github.com/eddycjy/awesome-project/stringer.Reverse
          (dlv) b stringer.Reverse
          Breakpoint 3 (enabled) set at 0x10cb87b for github.com/eddycjy/awesome-project/stringer.Reverse() ./stringer/string.go:3
          (dlv) c
          > github.com/eddycjy/awesome-project/stringer.Reverse() ./stringer/string.go:3 (hits goroutine(1):1 total:1) (PC: 0x10cb87b)
               1: package stringer
               2: 
          =>   3: func Reverse(s string) string {
               4:  r := []rune(s)
               5:  for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
               6:   r[i], r[j] = r[j], r[i]
               7:  }
               8:  return string(r)

          緊接著其他步驟都與先前的一樣,進(jìn)行具體的調(diào)試就好了。我們也可以借助 Go 語(yǔ)言的公共函數(shù)進(jìn)行計(jì)算:

          (dlv) p len(r)-1
          6

          也可以借助關(guān)鍵字 vars 查看某個(gè)包下的所有全局變量的值,例如:vars main。這種方式對(duì)于查看全局變量的情況非常有幫助。

          排查完畢后,執(zhí)行關(guān)鍵字 exit 就可以愉快的退出了:

          (dlv) exit

          解決完問(wèn)題,可以下班了 :)

          總結(jié)

          在 Go 語(yǔ)言中,Delve 調(diào)試工具是與 Go 語(yǔ)言親和度最高的,因?yàn)?Delve 是 Go 語(yǔ)言實(shí)現(xiàn)的。其在我們?nèi)粘9ぷ髦校浅3S谩?/p>

          像是假設(shè)程序的 for 循環(huán)運(yùn)行到第 N 次才出現(xiàn) BUG 時(shí),我們就可以通過(guò)斷點(diǎn)對(duì)應(yīng)的方法和代碼塊,再設(shè)置變量的值,進(jìn)行具體的查看,就可以解決。


             


          喜歡明哥文章的同學(xué)
          歡迎長(zhǎng)按下圖訂閱!

          ???

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费在线观看网站性情淫乱做爱 | 国产视频你懂的 | 特级西西444www精品视频 | 人人爱人人看人人搞 | 色婷网站|