Golang gdb 调试介绍

发布时间: 更新时间: 总字数:1403 阅读时间:3m 作者: 分享 复制网址

本文主要介绍如何使用gdb调试golang程序

介绍

如何生成 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
  • 生成 coredump 文件命令
$ 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 调试

代码&编译

  • main.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"
  • gdb 初始化配置
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 调试
# 带界面的调试
$ 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

参考

  1. https://go.dev/doc/gdb
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数