介紹 GDB 除錯 Go

yexiaobai發表於2019-02-16

注:本文作者是 YANN,原文是 Introduction to Go Debugging with GDB

在過去的 4 年中,我花了我絕大部分的時間用來寫,讀以及除錯 Python 或 JavaScript 程式碼。在學習 Go 的過程中,像穿著一雙有小石子的鞋子在美麗的山中遠行。很多事情給我留下了深刻的印象,但是使用 println 除錯我的程式碼在過去走的太遠了。在 Python 中,當程式碼在執行的時候,我們使用 pdb/ipdb 除錯它,JavaScript 提供了類似的工具。在這些年中,這個模式已經變成了我工作流中非常重要的一部分了。

今天,我認識到 Go 已經內建支援 Gnu debugger (aka GDB)

為了這篇文章,我們使用以下這個簡單的程式:

package main

import (
    "fmt"
    "time"
)

func counting(c chan<- int) {
    for i := 0; i < 10; i++ {
        time.Sleep(2 * time.Second)
        c <- i
    }
    close(c)
}

func main() {
    msg := "Starting main"
    fmt.Println(msg)
    bus := make(chan int)
    msg = "starting a gofunc"
    go counting(bus)
    for count := range bus {
        fmt.Println("count:", count)
    }
}

為了使用 GDB,你需要使用 -gcflags ”-N -l” 選項編譯你的程式,這些選項阻止編譯器使用行內函數和變數。

go build -gcflags "-N -l" gdbsandbox.go

這是一個互動式的除錯 GDB 的會話示例:

yml@simba$  gdb gdbsandbox 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

首先,我們執行我們的程式:

(gdb) run
Starting program: /home/yml/Developments/go/src/gdbsandbox/gdbsandbox 
Starting main
count: 0
count: 1
count: 2
[...]
count: 9
[Inferior 1 (process 13507) exited normally]

現在我們知道怎樣執行我們的程式,我們可能想設定一個斷點。

(gdb) help break 
Set breakpoint at specified line or function.
break [LOCATION] [thread THREADNUM] [if CONDITION]
LOCATION may be a line number, function name, or "*" and an address.
[...]
(gdb) break 22
Breakpoint 1 at 0x400d7a: file /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go, line 22.
(gdb) run
Starting program: /home/yml/Developments/go/src/gdbsandbox/gdbsandbox 
Starting main
[New LWP 13672]
[Switching to LWP 13672]
Breakpoint 1, main.main () at /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go:22
22              for count := range bus {
(gdb) 

一旦 GDB 在你的斷點處停止,你將看到上下文:

(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.
[...]
(gdb) list
17              msg := "Starting main"
18              fmt.Println(msg)
19              bus := make(chan int)
20              msg = "starting a gofunc"
21              go counting(bus)
22              for count := range bus {
23                      fmt.Println("count:", count)
24              }
25      }

你也可以檢查變數:

(gdb) help print
Print value of expression EXP.
Variables accessible are those of the lexical environment of the selected
stack frame, plus all those whose scope is global or an entire file.
[...]
(gdb) print msg
$1 = "starting a gofunc"

在程式碼的早期,我們啟動了一個 goroutine,當我執行到第 10 行的時候,我想檢視我程式中的這部分。

(gdb) break 10
Breakpoint 3 at 0x400c28: file /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go, line 10.
(gdb) help continue
Continue program being debugged, after signal or breakpoint.
If proceeding from breakpoint, a number N may be used as an argument,
which means to set the ignore count of that breakpoint to N - 1 (so that
the breakpoint won`t break until the Nth time it is reached).
[...]
(gdb) continue
Continuing.

在今天我們要做的最後一件事情就是在執行期改變一個變數的值:

Breakpoint 3, main.counting (c=0xf840001a50) at /home/yml/Developments/go/src/gdbsandbox/gdbsandbox.go:10
10                      time.Sleep(2 * time.Second)
(gdb) help whatis
Print data type of expression EXP.
Only one level of typedefs is unrolled.  See also "ptype".
(gdb) whatis count
type = int
(gdb) print count
$3 = 1
(gdb) set variable count=3
(gdb) print count
$4 = 3
(gdb) c
Continuing.
count: 3

我們僅僅覆蓋了以下命令:

  • list
  • next
  • print
  • continue
  • break
  • whatis
  • set variable =

這幾乎只是涉及到了使用 GDB 的表面知識,如果你想學習關於 GDB 更多的知識,這裡有更多的連結:

相關文章