使用GDB調試Go語言

用Go語言已經有一段時間了,總結一下如何用GDB來調試它!html

ps:網上有不少文章都有描述,可是都不是很全面,這裏將那些方法彙總一下 java

GDB簡介 git

GDB是GNU開源組織發佈的⼀一個強⼤大的UNIX下的程序調試⼯工具。或許,各位⽐比較喜歡那種圖形界⾯面⽅方式的,像VC、BCB等IDE的調試,但若是你是在UNIX平臺下作軟件,你會 發現GDB這個調試⼯工具備⽐比VC、BCB的圖形化調試器更強⼤大的功能。所謂「⼨寸有所⻓長,尺有所短」就是這個道理。 github

目前支持的語言 (GNU gdb (GDB) 7.8)
進入 gdb以後輸入 set language 能夠查看支持的語言列表 golang

$ gdb
(gdb) set language
Requires an argument. Valid arguments are auto, local, unknown, ada, c, c++, asm, minimal, d, fortran, objective-c, go, java, modula-2, opencl, pascal.
(gdb)

準備工做objective-c

首先看下已經編寫好的一個簡單的go語言程序 安全

➜  go-debug-example  tree
.
├── lib
│   └── calc.go
└── main.go

1 directory, 2 files

main.go函數

package main

import (
        "fmt"
        "github.com/beyondblog/go-debug-example/lib"
        "os"
        "runtime"
        //"runtime/debug"
)

func main() {
        var modify string
        argsLen := len(os.Args)
        if argsLen < 2 {
                fmt.Printf("Usage go-debug-example [username] \r\n")
                os.Exit(-1)
        }
        username := os.Args[1]
        var password string
        fmt.Printf("%s welcome!\r\nplease input password:", username)
        fmt.Scanf("%s", &password)
        fmt.Printf("%s  password: %s\r\n", username, password)

        sum := 0
        for i := 0; i < 10; i++ {
                sum += i
                if i == 5 {
                        modify = "modify!"
                }
        }

        fmt.Println(lib.Add(sum, 10))

        runtime.Breakpoint()
        //debug.PrintStack()
        fmt.Println(sum)
        fmt.Println(modify)
}

calc.go工具

package lib

func Add(a int, b int) int {
        c := 10
        a = c + b
        return a + b
}

程序很簡單,就是從命令行獲取一個值而後作了寫簡單的計算,最後輸出一下優化

那麼咱們編譯一下 ^.^

go build
而後生成了咱們要的文件,而後咱們執行 gdb go-debug-example

輸入 run(簡寫r)命令運行程序,這個時候可能會有一個提示

(gdb) run
Starting program: /Users/****/gopath/src/github.com/beyondblog/go-debug-example/go-debug-example
Unable to find Mach task port for process-id 40056: (os/kern) failure (0x5).
 (please check gdb is codesigned - see taskgated(8))

若是你用的是OSX應該就能看到這個,這個提示簽名錯誤,
Darwin kernel出於安全考慮,在沒有特殊受權的狀況下不容許gdb調試任何程序,由於能夠調試就掌握了進程的控制權。不過若是是root用戶就沒有這個問題,不過誰願意用root來調試程序呢
解決辦法能夠經過[這篇文章](http://blog.csdn.net/powerlly/article/details/30323015)來查看
或者啓動gdb的時候 加個sudo唄,那麼從新來過

sudo gdb go-debug-example
r
而後提示了

(gdb) r
Starting program: /Users/****/gopath/src/github.com/beyondblog/go-debug-example/go-debug-example
Usage go-debug-example [username]
[Inferior 1 (process 40111) exited with code 0377]


程序成功執行,可是咱們那個Go程序提示須要加一個參數纔可以繼續下去
能夠直接 r [參數] 獲取在 使用 set args [參數]

ps:能夠用 r > file 或者 >> file 支持結果重定向到文件

set args 這個命令是設置參數信息
show args 查看啓動的參數信息

既然調試代碼就能看到源碼啊,使用list(簡寫l) 命令查看代碼執行位置附近10行

(gdb) list
1    package main
2
3    import (
4        "fmt"
5        "github.com/beyondblog/go-debug-example/lib"
6        "os"
7        "runtime"
8        //"runtime/debug"
9    )
10
(gdb)

默認顯示了10行 查看更多的話能夠在輸入list 或者敲回車(gdb 會默認記住上一個指令而後回車就能繼續執行了 -,- )
查看list幫助

(gdb) help list
List specified function or line.
With no argument, lists ten more lines after or around previous listing.
"list -" lists the ten lines before a previous ten-line listing.
One argument specifies a line, and ten lines are listed around that line.
Two arguments with comma between specify starting and ending lines to list.
Lines can be specified in these ways:
  LINENUM, to list around that line in current file,
  FILE:LINENUM, to list around that line in that file,
  FUNCTION, to list around beginning of that function,
  FILE:FUNCTION, to distinguish among like-named static functions.
  *ADDRESS, to list around the line containing that address.
With two args if one is empty it stands for ten lines away from the other arg.
(gdb)

大概的意思就是

    list 20     //查看第20行周圍的10行

    list -         //查看上一個list代碼以前的10行
    
    list 1,100    //查看1到100行 若是不足100行就顯示末尾
    
    list main     //查看main函數
    執行這個命令的時候會發現顯示的不是main函數的信息而是一段彙編代碼
    
    (gdb) list main
    9        MOVQ    0(SP), DI // argc
    10        MOVQ    $main(SB), AX
    11        JMP    AX
    12
    13    TEXT main(SB),NOSPLIT,$-8
    14        MOVQ    $runtime·rt0_go(SB), AX
    15        JMP    AX
    (gdb)
    
    大概的意思是go程序的真正入口點應該是這玩意 (ps: $main(SB) SB  = ,=)
    這個時候用 list main.main 便可 意思是查看main.go文件裏面main函數附近的10行
    
    list main.main,20   //查看從main文件中main函數中從函數開始到第20行
    
    l main.go:0         //以 :方式查看指定文件源碼
    l calc.go:0         //查看calc.go的源碼
    l github.com/beyondblog/go-debug-example/lib/calc.go:0 //絕對文件路徑查看
    
    還能夠搜索代碼用 search text   //可顯示在當前文件中包含text串的下一行
    reverse-search text         //顯示包含text 的前一行
    forward-search text         //不解釋

如今設置一個斷點看看,命令是 break (簡寫b) 後面的參數和list命令後面的參數大體同樣,例如

    b main.main //在main下設置斷點
    b 11         //在11行設置斷點
    
    //查看斷點
    info breakpoints
    
    (gdb) info breakpoints
    Num     Type           Disp Enb Address            What
    5       breakpoint     keep y   0x0000000000002000 in main.main     at /Users/****/github.com/    beyondblog/go-debug-example/main.go:11
    6       breakpoint     keep y   0x000000000000203b in main.main     at /Users/****/github.com/    beyondblog/go-debug-example/main.go:14
    
    可以顯示詳細的信息,注意這上面有個End 是是否啓用的意思可使用
    disable Num或者 enable Num 來設置斷點是否有效
    
    //刪除斷點
    
    delete(簡寫d) //不帶參數清空全部斷點
    d    1          //刪除編號爲1的斷點
    
    //條件斷點 很是實用!
    b Num if [表達式] //在第Num設置斷點當知足表達式的條件是觸發
    例如
    b 26 if i=5

下面讓程序運行起來

Argument list to give program being debugged when it is started is "".
(gdb) set args beyond
(gdb) show args
Argument list to give program being debugged when it is started is "beyond".
(gdb) b main.main
Breakpoint 1 at 0x2000: file /Users/****/gopath/src/github.com/beyondblog/go-debug-example/main.go, line 11.
(gdb) r
Starting program: /Users/****/gopath/src/github.com/beyondblog/go-debug-example/go-debug-example beyond
[New Thread 0x1617 of process 41584]
[New Thread 0x1803 of process 41584]

Breakpoint 1, main.main () at /Users/****/github.com/beyondblog/go-debug-example/main.go:11
11    func main() {
(gdb) n
12        var modify string
(gdb)
    next(簡寫n)         //下一步
    step(簡寫s)         //單步執行,例如跳進函數內部
    finish            //退出該函數返回到它的調用函數中
    until(簡寫u)        //直接執行到下一行,若是遇到循環語句,會執行完當前循環
    u Num             //指哪打哪,繼續執行直到Num行時觸發斷點
    continue(簡寫c) //從斷點開始繼續執行
    
    例如
    (gdb) u 22
    beyond welcome!
    please input password:123456
    main.main () at /Users/****/gopath/src/github.com/beyondblog/go-debug-example/main.go:22
    22        fmt.Printf("%s  password: %s\r\n", username, password)
    (gdb)
    
    frame(簡寫f)    //查看當前命令幀,也就是看當前程序執行到那一行了
    
    info locals //查看當前變量信息
    (gdb) info locals
    sum = 1005232
    &password = 0x2081b4210
    username = 0x7fff5fbffc58 "beyond"
    modify = 0x0 ""
    這個時候會發現,怎麼有些變量沒顯示出來,官方說默認的編譯會給調試帶來一些不變的優化,可使用
    go build -gcflags "-N -l" 來關閉這個優化從而方便調試
    那麼從新編譯後運行在 info local 下
    (gdb) info locals
    sum = 180144
    i = 53967
    argsLen = 2
    &password = 0x2081b4210
    username = 0x7fff5fbffc58 "beyond"
    modify = 0x0 ""
    
    會發現 i 和 sum的值不是一個預期的0,這個是咱們程序還沒執行到初始化那一塊,默認取的一個隨機數吧
    ps: 雖然從代碼上看好像程序在這個時候尚未聲明 i 和 sum,可是最終生成的go語言程序應該是有本身的優化自動聲明瞭
    
    相似的還有不少種命令就不一一介紹了,下面來個逼格高的
    layout src 或者 ctrl x + ctrl a
    
    啓動 tui 界面
    或者啓動gdb 的時候加上 tui參數
    例如
    gdb -tui

gdb tui

逼格滿滿有木有哇!

tui有4中窗口模式分別是

    command 命令窗口. 能夠鍵入調試命令
    source 源代碼窗口. 顯示當前行,斷點等信息
    assembly 彙編代碼窗口
    register 寄存器窗口

詳細的說明:https://sourceware.org/gdb/current/onlinedocs/gdb/TUI.html#TUI

這個時候想查看源代碼能夠用list 也能夠用方向鍵

首先先將窗口的焦點設置到源代碼窗口上

    focus src

而後就能夠用方向鍵來查看源代碼了,這個時候若是想回到command窗口同理只須要

    focus cmd

這個時候你執行命令n 會有一個文本的圖形界面還顯示

gdb 還有一個特別有用的jump命令,它容許強制的跳轉,不會改變棧的結構.
意思就是 若是個人程序運行到第10行的時候我如今想回到第5行調試看看又不想從新運行一邊
舉個栗子
如今咱們執行到第32行,直接輸入命令

u 32

    B+>│32              fmt.Println(lib.Add(sum, 10))
    
    s //單步進入
    
    3    func Add(a int, b int) int {
    (gdb) list
    1    package lib
    2
    3    func Add(a int, b int) int {
    4        c := 10
    5        a = c + b
    6        return a + b
    7    }
    (gdb) n
    4        c := 10
    (gdb) n
    5        a = c + b
    (gdb) f
    #0  github.com/beyondblog/go-debug-example/lib.Add (a=45, b=10, ~r2=0) at /Users/****/github.com/beyondblog/go-debug-example/lib/calc.go:5
    5        a = c + b

這個時候已經到了第5行 我如今要回到第4行,jump 4 發現沒有用由於jump是跳轉到第4行開始執行,但不觸發斷點因此通常先在要跳轉的行號哪兒設置個斷點

    github.com/beyondblog/go-debug-example/lib.Add (a=45, b=10, ~r2=8725978992) at /Users/****/github.com/beyondblog/go-debug-example/lib/calc.go:3
    3    func Add(a int, b int) int {
    (gdb) b 4
    Breakpoint 2 at 0x50fed: file /Users/****/github.com/beyondblog/go-debug-example/lib/calc.go, line 4.
    (gdb) n

    Breakpoint 2, github.com/beyondblog/go-debug-example/lib.Add (a=45, b=10, ~r2=0) at /Users/****/github.com/beyondblog/go-debug-example/lib/calc.go:4
    4        c := 10
    (gdb) n
    5        a = c + b
    (gdb) n
    6        return a + b
    (gdb) jump 4
    Continuing at 0x50fed.

    Breakpoint 2, github.com/beyondblog/go-debug-example/lib.Add (a=20, b=10, ~r2=0) at /Users/****/github.com/beyondblog/go-debug-example/lib/calc.go:4
    4        c := 10
    (gdb) n
    5        a = c + b
    (gdb)
    6        return a + b
    (gdb) jump 4
    Continuing at 0x50fed.

    Breakpoint 2, github.com/beyondblog/go-debug-example/lib.Add (a=20, b=10, ~r2=0) at /Users/****/github.com/beyondblog/go-debug-example/lib/calc.go:4
    4        c := 10
    (gdb)
    
    //還可使用set 命令來設置變量的值 例如
    set b = 10

最後附上gdb的官方文檔
https://sourceware.org/gdb/current/onlinedocs/gdb/

這個example的連接 

https://github.com/beyondblog/go-debug-example.git

----
參考文章:

[0] http://blog.csdn.net/haoel/article/details/2879

[1] http://blog.studygolang.com/2012/12/gdb%E8%B0%83%E8%AF%95go%E7%A8%8B%E5%BA%8F/

[2] http://laokaddk.blog.51cto.com/368606/945057/

[3] http://blog.csdn.net/lwbeyond/article/details/7839225

相關文章
相關標籤/搜索