gdb-debug-golang

背景

作为一个 cpper ,已经被 C++ 这种超大型的语言给搞怕了,听到 golang 是一门强类型的编译语言,先入为主的觉得不简单。万万没有想到 golang 是这么的实用主义,上手起来是真的快。

之前也一直是用 gdb 来调试程序,看了一下 go 也支持,所以这里就做一个 demo 记录下过程。


第一步 编写一个简单的 golang 程序

写一个简单的数数的程序。

package main

import "fmt"

func hello(i int) {
    if i <= 0 {
        fmt.Println("i must big then 0 .")
        return
    }

    for j := 0; j < i; j++ {
        fmt.Println(j)
    }
}

func main() {
    // 从 0 数到 4
    var i int = 5
    hello(i)
}

第二步 编译

默认情况下 go 编译器会帮我们做一系列的优化、内联 ... ,这个就会导致 gdb 的时候调试不了;所以我们要在编译的时候禁用这些东西。

go build -gcflags '-N -l' -o main main.go

编译后生成的文件为 main,下面准备用 gdb debug 。

gdb main
GNU gdb (GDB) Red Hat Enterprise Linux 10.2-10.el9
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
warning: File "/usr/local/go/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
    add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
    set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
    info "(gdb)Auto-loading safe path"
(gdb) list
2   
3   import "fmt"
4   
5   func hello(i int) {
6       if i <= 0 {
7           fmt.Println("i must big then 0 .")
8           return
9       }
10  
11      for j := 0; j < i; j++ {
(gdb) list
12          fmt.Println(j)
13      }
14  }
15  
16  func main() {
17      var i int = 5
18      hello(i)
19  }
(gdb) list
Line number 20 out of range; /tmp/main.go has 19 lines.
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b 18
Breakpoint 2 at 0x4806bd: file /tmp/main.go, line 18.
(gdb) run
Starting program: /tmp/main 
[New LWP 1101193]
[New LWP 1101194]
[New LWP 1101195]

Thread 1 "main" hit Breakpoint 2, main.main () at /tmp/main.go:18
18      hello(i)
(gdb) s
main.hello (i=5) at /tmp/main.go:5
5   func hello(i int) {
(gdb) n
6       if i <= 0 {
(gdb) n
11      for j := 0; j < i; j++ {
(gdb) n
12          fmt.Println(j)
(gdb) print j
$1 = 0

调试过程中的意外收获

  1. 发现 golang 的程序一起来就会启动 3 个线程,如果加上主线程那么一个 golang 程序会有四个线程。
ps -ef | grep main
root     1101021 1097017  0 21:11 pts/12   00:00:00 gdb main
root     1101189 1101021  0 21:12 pts/12   00:00:00 /tmp/main
root     1102234 1101278  0 21:16 pts/6    00:00:00 grep --color=auto main

top -Hp 1101189
Threads:   4 total,   0 running,   0 sleeping,   4 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   7432.3 total,   1211.3 free,   1261.5 used,   4959.5 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   5851.4 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND 
1101189 root      20   0  709984   2936    804 t   0.0   0.0   0:00.00 main    
1101193 root      20   0  709984   2936    804 t   0.0   0.0   0:00.00 main    
1101194 root      20   0  709984   2936    804 t   0.0   0.0   0:00.00 main    
1101195 root      20   0  709984   2936    804 t   0.0   0.0   0:00.00 main 

编译时的参数可以通过这个命令查看

go tool compile -help