本文主要介绍如何使用gdb调试golang程序
介绍
- 扩展资料:
- 本地调试首选开发工具
vscode
或 Goland
如何生成 coredump
生成 coredump 文件主要是解决:当程序卡死时,保存卡死时的状态信息,方便程序重启后的环境重现和分析
generate-core-file 命令
# 查看
ulimit -c
# 临时配置不限制 core file size
ulimit -c unlimited
# 永久配置
$ vi /etc/profile
ulimit -c unlimited > /dev/null 2>&1
# kernel.core_uses_pid = 1
kernel.core_pattern=/tmp/core-%e-%p
$ sysctl -p /etc/sysctl.conf
$ gdb attach <pid>
$ generate-core-file
gcore 命令
gcore [-o filename] <pid>
安装 gdb
# ubuntu
apt install gdb -y
# mac
brew install gdb
如何判断文件是否可以 gdb 调试
readelf 信息判断
readelf -S helloWorld | grep debug
gdb 提示信息判断
$ gdb <xxx>
Reading symbols from <xxx>...(no debugging symbols found)...done.
file 命令判断
$ file <xxx>
<xxx>: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=Yh2hWP1Z5d_8dL4nCZz_/iv3tAH7QSf-UCldvBYHU/RC5nTuSGOsPTxMr8EolO/BlOkw58vu7dijXfFNiJ8, stripped
说明:
stripped
表示文件的符号表信息和调试信息已被去除,文件不能调试
not stripped
可能可以调试
调试的方法
二进制
$ gdb <bin>
# 运行
run
调试已运行程序
$ gdb
(gdb) attach <pid>
# 或
gdb <bin> <pid>
# 或
gdb <bin> --pid <pid>
- 若提示
Could not attach to process
,后文见 F&Q
- 当原进程文件没有调试信息时,使用
file
加载带 debug 的程序文件
$ gdb
(gdb) file <debug-bin>
Reading symbols from hello...done.
(gdb) attach <pid>
go 调试
代码&编译
package main
import "fmt"
func main(){
msg := "hello, world"
fmt.Println(msg)
}
# 初始化
go mod init main
# 编译:关闭内联优化,方便调试(本示例使用)
$ go build -gcflags "-N -l" main.go
# mac build 时,还有添加 `ldflags='-compressdwarf=false'`
# 发布版本,删除调试符号
go build -ldflags "-s -w"
echo "add-auto-load-safe-path /usr/share/go-1.13/src/runtime/runtime-gdb.py" > ~/.gdbinit
# 若未配置,第一次运行时会提示
$ gdb main
...
Reading symbols from main...
warning: File "/usr/share/go-1.13/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/share/go-1.13/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)
# 带界面的调试
$ gdb -tui main
# 命令行的调试
$ gdb main
...
Reading symbols from main...
Loading Go Runtime support.
(gdb) b main.main
Breakpoint 1 at 0x48cf20: file /data/abc/main.go, line 5.
(gdb) run
Starting program: /data/abc/main
[New LWP 2247]
[New LWP 2248]
[New LWP 2249]
[New LWP 2250]
[New LWP 2251]
Thread 1 "main" hit Breakpoint 1, main.main () at /data/abc/main.go:5
5 func main(){
(gdb) l
1 package main
2
3 import "fmt"
4
5 func main(){
6 msg := "hello, world"
7 fmt.Println(msg)
8 }
(gdb) n
6 msg := "hello, world"
(gdb) whatis msg
type = string
(gdb) p msg
$1 = 0x40509f "hello, world"
(gdb) info locals
msg = 0x40509f "\353\035H\307\200\240\000\000\000\000\000\000\000H\211\004$H\307D$\b\003\000\000\000\350\262d\002\000H\213D$\020H\205\300t\025\220\220H\205\300t\322H\213\210\240\000\000\000H\211L$\020\353\304H\213l$0H\203\304\070\303H\213D$(H\211\004$\350\033G\000\000H\215\005d\022\n\000H\211\004$H\215\005\tD\r\000H\211D$\b\350/@\002\000H\215\005H\022\n\000H\211\004$H\215\005\255C\r\000H\211D$\b\350\023@\002\000\220\350]\306\004\000\351\210\375\377\377\314\314\314\314\314\314\314\314H\203\354(H\211l$ H\215l$ H\213D$0H\211\004$H\213D$8H\211D$\b\306D$\020\001\350"...
(gdb) info args
No arguments.
(gdb) info frame
Stack level 0, frame at 0xc00008cf60:
rip = 0x48cf41 in main.main (/data/abc/main.go:6); saved rip = 0x42b14e
source language unknown.
Arglist at 0xc00008ced0, args:
Locals at 0xc00008ced0, Previous frame's sp is 0xc00008cf60
Saved registers:
rip at 0xc00008cf58
(gdb) n
7 fmt.Println(msg)
(gdb) n
hello, world
8 }
(gdb) n
runtime.main () at /usr/lib/go-1.13/src/runtime/proc.go:212
212 if atomic.Load(&runningPanicDefers) != 0 {
(gdb) n
225 exit(0)
调试命令
-
help
所有帮助信息
-
file <file>
加载要被 gdb 的文件
-
run(r)
执行程序
-
next(n)
下一步,不进入函数
-
step(s)
下一步,会进入函数
-
breakponit(b)
设置断点,使用 tab tab
键提示
b n
指定行打断点
b main.go:n
指定文件行号打断点
b main.main
指定函数打断点,要加包名,如 <project-name>/controller.GetXXX
-
delete(d)
删除断点
-
clear
清楚所有端点
-
list(l)
查看源码,使用 tab tab
键提示
l n
查看行数的下5行代码
l m:n
查看从 m~n 的代码
l main.go:n
查看指定文件行号的下5行代码
l main.main
查看函数,要加包名,如 <project-name>/controller.GetXXX
-
continue(c)
继续执行到下一断点
-
backtrace(bt)
查看当前调用栈
-
print(p)
打印查看变量
-
set variable
改变调试过程中的变量值
-
whatis
查看对象类型
-
info
显示信息
info breakpoints
查看所有的断点
info locals
查看局部变量
info args
查看函数的参数值及要返回的变量值
info frame
堆栈帧信息
info goroutines
查看 goroutines 信息
- 在使用前需配置
echo "source /usr/local/go/src/runtime/runtime-gdb.py"
-
goroutine 1 bt
查看指定序号的 goroutine 调用堆栈
-
quit(q)
退出 GDB
-
回车
执行命令
-
以下 参考
# 显示文件和代码行号、设置断点和反汇编
(gdb) list
(gdb) list line
(gdb) list file.go:line
(gdb) break line
(gdb) break file.go:line
(gdb) disas
# 显示回溯并解除堆栈帧
(gdb) bt
(gdb) frame n
# 显示堆栈帧中局部变量、参数和返回值的名称、类型和位置
(gdb) info locals
(gdb) info args
(gdb) p 变量
(gdb) whatis variable
# 显示全局变量的名称、类型和位置
(gdb) info variables regexp
F&Q
gdb: Could not attach to process
$ attach <pid>
Attaching to process <pid> Could not attach to process. If your uid matches the uid of the target process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf ptrace: Operation not permitted.
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# 修改
$ cat /etc/sysctl.d/10-ptrace.conf
# kernel.yama.ptrace_scope = 1
kernel.yama.ptrace_scope = 0
# 生效
$ sysctl -p
- docker 容器,添加启动参数添加
--cap-add=SYS_PTRACE
- k8s pod
apiVersion: v1
kind: Pod
metadata:
name: xxx
spec:
containers:
- name: xxx
image: "xxx"
securityContext:
capabilities:
add:
- SYS_NICE
drop:
- KILL
...
No debugging symbols found in xxx
编译产物没有调试信息,解决方式:
- 编译时要保留调试信息,如
gcc -c -g xxx.c