27 Debugging Go Code with GDB 使用GDB调试go代码
Debugging Go Code with GDB 使用GDB调试go代码
The following instructions apply to the standard toolchain (the gc
Go compiler and tools). Gccgo has native gdb support.
Note that Delve is a better alternative to GDB when debugging Go programs built with the standard toolchain. It understands the Go runtime, data structures, and expressions better than GDB. Delve currently supports Linux, OSX, and Windows on amd64
. For the most up-to-date list of supported platforms, please see the Delve documentation.
GDB does not understand Go programs well. The stack management, threading, and runtime contain aspects that differ enough from the execution model GDB expects that they can confuse the debugger and cause incorrect results even when the program is compiled with gccgo. As a consequence, although GDB can be useful in some situations (e.g., debugging Cgo code, or debugging the runtime itself), it is not a reliable debugger for Go programs, particularly heavily concurrent ones. Moreover, it is not a priority for the Go project to address these issues, which are difficult.
In short, the instructions below should be taken only as a guide to how to use GDB when it works, not as a guarantee of success. Besides this overview you might want to consult the GDB manual.
Introduction
When you compile and link your Go programs with the gc
toolchain on Linux, macOS, FreeBSD or NetBSD, the resulting binaries contain DWARFv4 debugging information that recent versions (≥7.5) of the GDB debugger can use to inspect a live process or a core dump.
Pass the '-w'
flag to the linker to omit the debug information (for example, go
build
-ldflags=-w
prog.go
).
The code generated by the gc
compiler includes inlining of function invocations and registerization of variables. These optimizations can sometimes make debugging with gdb
harder. If you find that you need to disable these optimizations, build your program using go
build
-gcflags=all="-N -l"
.
If you want to use gdb to inspect a core dump, you can trigger a dump on a program crash, on systems that permit it, by setting GOTRACEBACK=crash
in the environment (see the runtime package documentation for more info).
Common Operations
- Show file and line number for code, set breakpoints and disassemble:
(gdb) list
(gdb) list line
(gdb) list file.go:line
(gdb) break line
(gdb) break file.go:line
(gdb) disas - Show backtraces and unwind stack frames:
(gdb) bt
(gdb) frame n - Show the name, type and location on the stack frame of local variables, arguments and return values:
(gdb) info locals
(gdb) info args
(gdb) p variable
(gdb) whatis variable - Show the name, type and location of global variables:
(gdb) info variables regexp
Go Extensions
A recent extension mechanism to GDB allows it to load extension scripts for a given binary. The toolchain uses this to extend GDB with a handful of commands to inspect internals of the runtime code (such as goroutines) and to pretty print the built-in map, slice and channel types.
- Pretty printing a string, slice, map, channel or interface:
(gdb) p var
- A $len() and $cap() function for strings, slices and maps:
(gdb) p $len(var)
- A function to cast interfaces to their dynamic types:
(gdb) p $dtype(var)
(gdb) iface varKnown issue: GDB can’t automatically find the dynamic type of an interface value if its long name differs from its short name (annoying when printing stacktraces, the pretty printer falls back to printing the short type name and a pointer).
- Inspecting goroutines:
(gdb) info goroutines
(gdb) goroutine n cmd
(gdb) help goroutineFor example:
(gdb) goroutine 12 bt
If you'd like to see how this works, or want to extend it, take a look at src/runtime/runtime-gdb.py in the Go source distribution. It depends on some special magic types (hash<T,U>
) and variables (runtime.m
andruntime.g
) that the linker (src/cmd/link/internal/ld/dwarf.go) ensures are described in the DWARF code.
If you're interested in what the debugging information looks like, run objdump
-W
a.out
and browse through the .debug_*
sections.
Known Issues
- String pretty printing only triggers for type string, not for types derived from it.
- Type information is missing for the C parts of the runtime library.
- GDB does not understand Go’s name qualifications and treats
"fmt.Print"
as an unstructured literal with a"."
that needs to be quoted. It objects even more strongly to method names of the formpkg.(*MyType).Meth
. - All global variables are lumped into package
"main"
.
Tutorial
In this tutorial we will inspect the binary of the regexp package's unit tests. To build the binary, change to $GOROOT/src/regexp
and run go
test
-c
. This should produce an executable file named regexp.test
.
Getting Started
Launch GDB, debugging regexp.test
:
$ gdb regexp.test
GNU gdb (GDB) 7.2-gg8
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv 3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
Type "show copying" and "show warranty" for licensing/warranty details.
This GDB was configured as "x86_64-linux". Reading symbols from /home/user/go/src/regexp/regexp.test...
done.
Loading Go Runtime support.
(gdb)
The message "Loading Go Runtime support" means that GDB loaded the extension from $GOROOT/src/runtime/runtime-gdb.py
.
To help GDB find the Go runtime sources and the accompanying support script, pass your $GOROOT
with the '-d'
flag:
$ gdb regexp.test -d $GOROOT
If for some reason GDB still can't find that directory or that script, you can load it by hand by telling gdb (assuming you have the go sources in ~/go/
):
(gdb) source ~/go/src/runtime/runtime-gdb.py
Loading Go Runtime support.
Inspecting the source
Use the "l"
or "list"
command to inspect source code.
(gdb) l
List a specific part of the source parametrizing "list"
with a function name (it must be qualified with its package name).
(gdb) l main.main
List a specific file and line number:
(gdb) l regexp.go:1
(gdb) # Hit enter to repeat last command. Here, this lists next 10 lines.
Naming
Variable and function names must be qualified with the name of the packages they belong to. The Compile
function from the regexp
package is known to GDB as 'regexp.Compile'
.
Methods must be qualified with the name of their receiver types. For example, the *Regexp
type’s String
method is known as 'regexp.(*Regexp).String'
.
Variables that shadow other variables are magically suffixed with a number in the debug info. Variables referenced by closures will appear as pointers magically prefixed with '&'.
Setting breakpoints
Set a breakpoint at the TestFind
function:
(gdb) b 'regexp.TestFind'
Breakpoint 1 at 0x424908: file /home/user/go/src/regexp/find_test.go, line 148.
Run the program:
(gdb) run
Starting program: /home/user/go/src/regexp/regexp.test Breakpoint 1, regexp.TestFind (t=0xf8404a89c0) at /home/user/go/src/regexp/find_test.go:148
148 func TestFind(t *testing.T) {
Execution has paused at the breakpoint. See which goroutines are running, and what they're doing:
(gdb) info goroutines
1 waiting runtime.gosched
* 13 running runtime.goexit
the one marked with the *
is the current goroutine.
Inspecting the stack
Look at the stack trace for where we’ve paused the program:
(gdb) bt # backtrace
#0 regexp.TestFind (t=0xf8404a89c0) at /home/user/go/src/regexp/find_test.go:148
#1 0x000000000042f60b in testing.tRunner (t=0xf8404a89c0, test=0x573720) at /home/user/go/src/testing/testing.go:156
#2 0x000000000040df64 in runtime.initdone () at /home/user/go/src/runtime/proc.c:242
#3 0x000000f8404a89c0 in ?? ()
#4 0x0000000000573720 in ?? ()
#5 0x0000000000000000 in ?? ()
The other goroutine, number 1, is stuck in runtime.gosched
, blocked on a channel receive:
(gdb) goroutine 1 bt
#0 0x000000000040facb in runtime.gosched () at /home/user/go/src/runtime/proc.c:873
#1 0x00000000004031c9 in runtime.chanrecv (c=void, ep=void, selected=void, received=void)
at /home/user/go/src/runtime/chan.c:342
#2 0x0000000000403299 in runtime.chanrecv1 (t=void, c=void) at/home/user/go/src/runtime/chan.c:423
#3 0x000000000043075b in testing.RunTests (matchString={void (struct string, struct string, bool *, error *)}
0x7ffff7f9ef60, tests= []testing.InternalTest = {...}) at /home/user/go/src/testing/testing.go:201
#4 0x00000000004302b1 in testing.Main (matchString={void (struct string, struct string, bool *, error *)}
0x7ffff7f9ef80, tests= []testing.InternalTest = {...}, benchmarks= []testing.InternalBenchmark = {...})
at /home/user/go/src/testing/testing.go:168
#5 0x0000000000400dc1 in main.main () at /home/user/go/src/regexp/_testmain.go:98
#6 0x00000000004022e7 in runtime.mainstart () at /home/user/go/src/runtime/amd64/asm.s:78
#7 0x000000000040ea6f in runtime.initdone () at /home/user/go/src/runtime/proc.c:243
#8 0x0000000000000000 in ?? ()
The stack frame shows we’re currently executing the regexp.TestFind
function, as expected.
(gdb) info frame
Stack level 0, frame at 0x7ffff7f9ff88:
rip = 0x425530 in regexp.TestFind (/home/user/go/src/regexp/find_test.go:148);
saved rip 0x430233
called by frame at 0x7ffff7f9ffa8
source language minimal.
Arglist at 0x7ffff7f9ff78, args: t=0xf840688b60
Locals at 0x7ffff7f9ff78, Previous frame's sp is 0x7ffff7f9ff88
Saved registers:
rip at 0x7ffff7f9ff80
The command info
locals
lists all variables local to the function and their values, but is a bit dangerous to use, since it will also try to print uninitialized variables. Uninitialized slices may cause gdb to try to print arbitrary large arrays.
The function’s arguments:
(gdb) info args
t = 0xf840688b60
When printing the argument, notice that it’s a pointer to a Regexp
value. Note that GDB has incorrectly put the *
on the right-hand side of the type name and made up a 'struct' keyword, in traditional C style.
(gdb) p re
(gdb) p t
$1 = (struct testing.T *) 0xf840688b60
(gdb) p t
$1 = (struct testing.T *) 0xf840688b60
(gdb) p *t
$2 = {errors = "", failed = false, ch = 0xf8406f5690}
(gdb) p *t->ch
$3 = struct hchan<*testing.T>
That struct
hchan<*testing.T>
is the runtime-internal representation of a channel. It is currently empty, or gdb would have pretty-printed its contents.
Stepping forward:
(gdb) n # execute next line
149 for _, test := range findTests {
(gdb) # enter is repeat
150 re := MustCompile(test.pat)
(gdb) p test.pat
$4 = ""
(gdb) p re
$5 = (struct regexp.Regexp *) 0xf84068d070
(gdb) p *re
$6 = {expr = "", prog = 0xf840688b80, prefix = "", prefixBytes = []uint8, prefixComplete = true,
prefixRune = 0, cond = 0 '\000', numSubexp = 0, longest = false, mu = {state = 0, sema = 0},
machine = []*regexp.machine}
(gdb) p *re->prog
$7 = {Inst = []regexp/syntax.Inst = {{Op = 5 '\005', Out = 0, Arg = 0, Rune = []int}, {Op =
6 '\006', Out = 2, Arg = 0, Rune = []int}, {Op = 4 '\004', Out = 0, Arg = 0, Rune = []int}},
Start = 1, NumCap = 2}
We can step into the String
function call with "s"
:
(gdb) s
regexp.(*Regexp).String (re=0xf84068d070, noname=void) at /home/user/go/src/regexp/regexp.go:97
97 func (re *Regexp) String() string {
Get a stack trace to see where we are:
(gdb) bt
#0 regexp.(*Regexp).String (re=0xf84068d070, noname=void)
at /home/user/go/src/regexp/regexp.go:97
#1 0x0000000000425615 in regexp.TestFind (t=0xf840688b60)
at /home/user/go/src/regexp/find_test.go:151
#2 0x0000000000430233 in testing.tRunner (t=0xf840688b60, test=0x5747b8)
at /home/user/go/src/testing/testing.go:156
#3 0x000000000040ea6f in runtime.initdone () at /home/user/go/src/runtime/proc.c:243
....
Look at the source code:
(gdb) l
92 mu sync.Mutex
93 machine []*machine
94 }
95
96 // String returns the source text used to compile the regular expression.
97 func (re *Regexp) String() string {
98 return re.expr
99 }
100
101 // Compile parses a regular expression and returns, if successful,
Pretty Printing
GDB's pretty printing mechanism is triggered by regexp matches on type names. An example for slices:
(gdb) p utf
$22 = []uint8 = {0 '\000', 0 '\000', 0 '\000', 0 '\000'}
Since slices, arrays and strings are not C pointers, GDB can't interpret the subscripting operation for you, but you can look inside the runtime representation to do that (tab completion helps here):
(gdb) p slc
$11 = []int = {0, 0}
(gdb) p slc-><TAB>
array slc len
(gdb) p slc->array
$12 = (int *) 0xf84057af00
(gdb) p slc->array[1]
$13 = 0
The extension functions $len and $cap work on strings, arrays and slices:
(gdb) p $len(utf)
$23 = 4
(gdb) p $cap(utf)
$24 = 4
Channels and maps are 'reference' types, which gdb shows as pointers to C++-like types hash<int,string>*
. Dereferencing will trigger prettyprinting
Interfaces are represented in the runtime as a pointer to a type descriptor and a pointer to a value. The Go GDB runtime extension decodes this and automatically triggers pretty printing for the runtime type. The extension function $dtype
decodes the dynamic type for you (examples are taken from a breakpoint at regexp.go
line 293.)
(gdb) p i
$4 = {str = "cbb"}
(gdb) whatis i
type = regexp.input
(gdb) p $dtype(i)
$26 = (struct regexp.inputBytes *) 0xf8400b4930
(gdb) iface i
regexp.input: struct regexp.inputBytes *
27 Debugging Go Code with GDB 使用GDB调试go代码的更多相关文章
- GDB + gdbserver 远程调试android native code
原文地址:GDB + gdbserver 远程调试android native code 作者:tq08g2z 以调试模拟器中的native library code为例. Host: ubuntuT ...
- https://sourceware.org/gdb/onlinedocs/gdb/Forks.html
https://sourceware.org/gdb/onlinedocs/gdb/Forks.html Next: Checkpoint/Restart, Previous: Threads, Up ...
- GDB和GDB Server
gdb是linux c编程标配的调试工具,平时接触比较多的可能是本机随gcc一起安装的调试工具.但是,即使是本机的gdb,也经常被printf代替,所以接触也仅限于知道. 简单程序固然可以用print ...
- 使用GDB调试PHP代码,解决PHP代码死循环
最近在帮同事解决Swoole Server问题时,发现有1个worker进程一直处于R的状态,而且CPU耗时非常高.初步断定是PHP代码中发生死循环. 下面通过一段代码展示如何解决PHP死循环问题. ...
- GDB + gdbserver 远程调试mediaserver进程
远程调试步骤 在Android设备上启动gdbserver并attach你想调试的进程,并指定监听调试命令的端口(此端口是TV上的端口) $ adb shell # ps |grep media # ...
- GDB中汇编调试
GDB中汇编调试 1.输入代码 2.使用gcc - g example.c -o example -m32指令在64位的机器上产生32位汇编,时遇到问题使用-m32指令报错,参考卢肖明同学博客知道这是 ...
- 20145311利用gdb调试汇编代码
利用GDB调试汇编代码 首先编写c语言原代码,我使用的是同学分析过的代码 #include<stdio.h>short addend1 = 1;static int addend2 = 2 ...
- linux下的gdb调试工具--内存调试
接着上一节的代码,在while(1)的循环里面增加代码:sum=0 #include <stdio.h> int main(void) { int sum = 0, i = 0; char ...
- linux下的gdb调试工具--断点调试
到目前为止我们的调试手段只有一种: 根据程序执行时的出错现象假设错误原因,然后在代码中适当的位置插入printf,执行程序并分析打印结果,如果结果和预期的一样,就基本上证明了自己假设的错误原因,就可以 ...
随机推荐
- 洛谷 P1939 【模板】矩阵加速(数列) 解题报告
P1939 [模板]矩阵加速(数列) 题目描述 a[1]=a[2]=a[3]=1 a[x]=a[x-3]+a[x-1] (x>3) 求a数列的第n项对1000000007(10^9+7)取余的值 ...
- 前端学习 -- Css -- 伪元素
:first-letter 表示第一个字符 :first-line 表示文字的第一行 :before 选中元素的最前边,一般该伪类都会结合content一起使用,通过content可以向指定位置添加内 ...
- Android Log详解(Log.v,Log.d,Log.i,Log.w,Log.e)
1.Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v("",""); 2.Log.d的输出颜色是蓝 ...
- 线性判别分析 LDA
点到判决面的距离 点\(x_0\)到决策面\(g(x)= w^Tx+w_0\)的距离:\(r={g(x)\over \|w\|}\) 广义线性判别函数 因任何非线性函数都可以通过级数展开转化为多项式函 ...
- Java泛型底层源码解析--ConcurrentHashMap(JDK1.7)
1. Concurrent相关历史 JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全 ...
- P4310 绝世好题
P4310 绝世好题 题目描述 给定一个长度为n的数列ai,求ai的子序列bi的最长长度,满足bi&bi-1!=0(2<=i<=len). 说明 对于100%的数据,1<=n ...
- P2627 修剪草坪
P2627 修剪草坪 题目描述 在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪.现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠. 然而,Fa ...
- 区间DP的思路(摘自NewErA)及自己的心得
以下为摘要 区间dp能解决的问题就是通过小区间更新大区间,最后得出指定区间的最优解 个人认为,想要用区间dp解决问题,首先要确定一个大问题能够剖分成几个相同较小问题,且小问题很容易组合成大问题,从而从 ...
- vue+element 切换正式和测试环境
1.package.json { "name": "element-starter", "description": "A Vue ...
- linux ln链接详解
1.序 Linux具有为一个文件起多个名字的功能,称为链接.被链接的文件可以存放在相同的目录下,但是必须有不同的文件名,而不用在硬盘上为同样的数据重复备份.另外,被链接的文件也可以有相同的文件名,但是 ...