1. 摘要

本文讲解gdb调试GOLANG程序的入门配置,以及gdb命令详解备忘。

2. gdb调试go程序入门

gdb是linux系统自带的调试器,功能十分强大,它不仅支持C/C++调试,也支持GO程序调试。
GDB是FSF(自由软件基金会)发布的一个强大的类UNIX系统下的程序调试工具。使用GDB可以做如下事情:
(1)启动程序,可以按照开发者的自定义要求运行程序。
(2)可让被调试的程序在开发者设定的调置的断点处停住。(断点可以是条件表达式)
(3)当程序被停住时,可以检查此时程序中所发生的事。
(4)动态的改变当前程序的执行环境。
目前支持调试Go程序的GDB版本必须大于7.1。

编译Go程序的时候需要注意以下几点:
(1)传递参数-ldflags "-s",忽略debug的打印信息
(2)传递-gcflags "-N -l" 参数,这样可以忽略Go内部做的一些优化,聚合变量和函数等优化,这样对于GDB调试来说非常困难,所以在编译的时候加入这两个参数避免这些优化。

2.1 配置gdb

(1) 打开gdb初始化配置文件

vim ~/.gdbinit
(2) 增加一行,:wq!保存后退出
add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py

2.2 编译golang

假设源码文件为main.go,查看循环进行了几次。

package main

import ( 
    "fmt" 
)

func main() { 
    for i := 0; i < 5; i++ {
        fmt.Println("looping %d times", i) 
    } 
    fmt.Println("Done") 
}

正常情况下编译运行的结果如下:

root@iZ2zeaij0rxu0p2muizoirZ:~/go/src/test# go run main.go
looping %d times 0
looping %d times 1
looping %d times 2
looping %d times 3
looping %d times 4
Done

虽然gdb也支持golang了,但是在编译golang仍然需要加一些特殊的参数,否则出现如下的错误:

No symbol  in current context

就是程序内的变量,你都无法打印,gdb说找不到。所以编译时,需要加下列的参数:

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

2.3 gdb调试

使用gdb启动一个go程序:

root@iZ2zeaij0rxu0p2muizoirZ:~/go/src/test# gdb main
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 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-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://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...done.
Loading Go Runtime support.
(gdb) 

其中,出现“Loading Go Runtime support "这句话,就表示gdb可以支持golang。”

2.3.1 打断点

在第9行打断点:

(gdb) b main.go:9
Breakpoint 1 at 0x486a97: file /root/go/src/test/main.go, line 9.

2.3.2 运行到断点

(gdb) run
Starting program: /root/go/src/test/main 
[New LWP 7260]
[New LWP 7261]
[New LWP 7262]
[New LWP 7263]

Thread 1 "main" hit Breakpoint 1, main.main () at /root/go/src/test/main.go:9
9           fmt.Println("looping %d times", i) 

2.3.3 查看当前的局部变量,继续运行到下一个断点

(gdb) p i
$1 = 0
(gdb) info locals
i = 0
(gdb) c
Continuing.
looping %d times 0

Thread 1 "main" hit Breakpoint 1, main.main () at /root/go/src/test/main.go:9
9           fmt.Println("looping %d times", i) 
(gdb) p i
$3 = 1

2.3.4 查看当前所有断点,取消断点

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000486a97 in main.main at /root/go/src/test/main.go:9
    breakpoint already hit 2 times
(gdb) disable breakpoint 1

2.3.5 退出

(gdb) quit
root@iZ2zeaij0rxu0p2muizoirZ:~/go/src/test# 
3,GDB调试命令列表

(gdb) list 15 //显示十行代码,其中第15行在显示的十行里面的中间,如下所示

(gdb) b 10 //break,在第十行设置断点;

(gdb) d 2 //delete,后面跟上断点设置的序号,这个序号可以通过info breakpoints获取相应的设置的断点序号

(gdb) info breakpoints // 查看所有断点。

(gdb) r //run, 启动进程,触发第一个断点。

(gdb) next //简写命令 n,用来单步调试,跳到下一步,当有断点之后,可以输入n跳转到下一步继续执行

(gdb) c //continue,继续执行,触发下一个断点。

(gdb) set variable //该命令用来改变运行过程中的变量值,格式如:set variable <var>=<value>;

(gdb) info goroutines // 查看 goroutines 信息。

(gdb) goroutine 1 bt // 查看指定序号的 goroutine 调用堆栈

(gdb) bt // backtrace,查看当前调⽤堆栈,可以与当前 goroutine 调用堆栈对比。

(gdb) info frame // 堆栈帧信息。

(gdb) info locals // 查看局部变量。

(gdb) info goroutines //显示当前执行的goroutine列表,如下代码所示,带*的表示当前执行的

(gdb) p s // 以 Pretty-Print 方式查看变量。

(gdb) clear //清除所有设置在函数上的断点。

(gdb) help all //可以看到所有的命令

4. 参考 5 面试题目