Golang gdb 调试介绍

发布时间: 更新时间: 总字数:1545 阅读时间:4m 作者: IP上海 分享 网址

本文主要介绍如何使用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)
  • 如下通过查看 Entry point 得知入口在 /usr/local/go/src/runtime/rt0_darwin_amd64.s:8
$ GOFLAGS="-ldflags=-compressdwarf=false" go build -o main
$ gdb main
GNU gdb (GDB) 14.2
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
Loading Go Runtime support.
(gdb) info files
Symbols from "/Users/xiexianbin/Downloads/abc/main".
Local exec file:
	`/Users/xiexianbin/Downloads/abc/main', file type mach-o-x86-64.
	Entry point: 0x105dd80
	0x0000000001001000 - 0x00000000010870c8 is .text
	0x00000000010870e0 - 0x0000000001087206 is __TEXT.__symbol_stub1
	0x0000000001087220 - 0x00000000010c2e9d is __TEXT.__rodata
	0x00000000010c2ea0 - 0x00000000010c3444 is __TEXT.__typelink
	0x00000000010c3460 - 0x00000000010c34d0 is __TEXT.__itablink
	0x00000000010c34d0 - 0x00000000010c34d0 is __TEXT.__gosymtab
	0x00000000010c34e0 - 0x0000000001129940 is __TEXT.__gopclntab
	0x000000000112a000 - 0x000000000112a150 is __DATA.__go_buildinfo
	0x000000000112a150 - 0x000000000112a2d8 is __DATA.__nl_symbol_ptr
	0x000000000112a2e0 - 0x000000000112f7c0 is __DATA.__noptrdata
	0x000000000112f7c0 - 0x0000000001133490 is .data
	0x00000000011334a0 - 0x00000000011613f8 is .bss
	0x0000000001161400 - 0x0000000001164c50 is __DATA.__noptrbss
(gdb) b *0x105dd80
Breakpoint 1 at 0x105dd80: file /usr/local/go/src/runtime/rt0_darwin_amd64.s, line 8.
(gdb)

调试命令

  • 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
本文总阅读量 次 本站总访问量 次 本站总访客数