go程序调试总结

首先需要注意的是:golang1.3之后的版本,对于支持gdb调试存在很大的问题。产生这个问题的原因是,golang的runtime没有完整的被gdb支持。

最新比较完整支持gdb调试的版本是golang 1.2.2,但是也有个别问题存在。

为什么会出现以上种种问题,golang官网给出的解释是:

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, even when the program is compiled with gccgo. As a consequence, although GDB can be useful in some situations, 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.

翻译一下:

GDB不能很好的理解GO程序。堆栈管理、线程和runtime包含了非常不一样的执行模式,这不是GDB期望的,他们会扰乱调试器,即使go程序是使用gccgo编译的。结果就是,虽然GDB在某些场合下是有用的,但是对go程序来说并不是一个可靠的调试器。尤其是在大量并发的时候。而且,这不是Golang项目优先考虑的事情,这很困难。总而言之,下面的操作手册,只是当GDB正常工作的时候,引导你如何使用GDB,不能保证总是成功。

并且从google group讨论组和stackoverflow中,可以看到golang的多个版本对于GDB的支持都有这样那样的问题。 不过既然官方的手册都这么说了,我们也只有在合适的场合使用GDB吧。

默认情况下,编译过的二进制文件已经包含了 DWARFv3 调试信息,只要 GDB7.1 以上版本都可以进行调试。 在OSX下,如无法执行调试指令,可尝试用sudo方式执行gdb。

在编译go程序的时候,需要关闭内联优化:** -gcflags “-N -l”**。可以在go get/build/test的时候指定这个参数。

有两种方式可以下断点:

  • gdb命令运行之后,使用break file:lineno
  • 使用runtime.BreakPoint()

演示环境:Ubuntu 14.04 LTS amd64 / Golang 1.2.2 / GDB 7.7

package main

import (
    "fmt"
    "runtime"
)

func test(s string, x int) (r string) {
    r = fmt.Sprintf("test: %s %d", s, x)
    runtime.Breakpoint()
    return r
}
func main() {
    s := "haha"
    i := 1234
    println(test(s, i))
}

启动GDB:

➜  ~  gdb ./test
GNU gdb (Ubuntu 7.7-0ubuntu3) 7.7
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
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:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...done.
**Loading Go Runtime support.**
(gdb)

以模块名的方式查看源码:

(gdb) l main.main
9       r = fmt.Sprintf("test: %s %d", s, x)
10      runtime.Breakpoint()
11      return r
12  }
13
14  func main() {
15      s := "haha"
16      i := 1234
17      println(test(s, i))

以文件名加行号的方式查看源码:

(gdb) l /home/roy/test.go:1
1   package main
2
3   import (
4       "fmt"
5       "runtime"
6   )
7
8   func test(s string, x int) (r string) {
9       r = fmt.Sprintf("test: %s %d", s, x)
10      runtime.Breakpoint()

设置断点:

(gdb) b main.main
Breakpoint 2 at 0x400d80: file /home/roy/test.go, line 14.
(gdb) b test.go:18
Breakpoint 3 at 0x400e0e: file /home/roy/test.go, line 18.

启动进程,触发断点: (n 单步执行,c执行到下一个断点)

(gdb) run
Starting program: /home/roy/test

Breakpoint 2, main.main () at /home/roy/test.go:14
14  func main() {

查看goroutines:

(gdb) info goroutines
* 1  running runtime.gosched
* 2  syscall runtime.notetsleepg

查看指定goroutine的调用堆栈:

(gdb) goroutine 1 bt
Python Exception  Cannot access memory at address 0x8:
Error occurred in Python command: Cannot access memory at address 0x8

当我们想查看goroutine调用堆栈的时候,gdb报告无法访问内存。 Cannot access memory at address 0x8,这句是说无法访问0x8位置的内存,所以接下来,gdb收到了一个SIGSEGV的信号,说明触发了段错误:

(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x000000000041529b in runtime.gosched () at /home/roy/go/src/pkg/runtime/proc.c:1368
1368        runtime·mcall(runtime·gosched0);

此时,我们需要重启gdb的调试,否则执行任何命令都会报告段错误。

打印栈帧信息:

(gdb) info frame
Stack level 0, frame at 0x7ffff7e2ef48:
 rip = 0x400d80 in main.main (/home/roy/test.go:14); saved rip = 0x412e1f
 source language go.
 Arglist at 0x7ffff7e2ef38, args:
 Locals at 0x7ffff7e2ef38, Previous frame's sp is 0x7ffff7e2ef48
 Saved registers:
 rip at 0x7ffff7e2ef40

查看局部变量:

(gdb) info locals
i = 1234
s = "haha"

以Pretty-Print的方式打印变量:

(gdb) p s
$1 = "haha"

获取对象的长度,即cap:

(gdb) p $len(s)
$3 = 4

查看对象类型:

(gdb) whatis i
type = int
(gdb) whatis s
type = struct string

输入n单步执行到进入test函数,查看传递给函数的参数:

(gdb) n
main.test (s="haha", x=1234, r="test: haha 1234") at /home/roy/test.go:11
11      return r
(gdb) info args
s = "haha"
x = 1234
r = "test: haha 1234"

输入q,或者Ctrl-D退出:

(gdb) q
A debugging session is active.

    Inferior 1 [process 6079] will be killed.

Quit anyway? (y or n) y

本文来自:天地孤影任我行

感谢作者:华子

查看原文:go程序调试总结

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。