如何用gdb调试erlang运行期

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用gdb调试erlang运行期相关的知识,希望对你有一定的参考价值。

1: 对于在应用程序中加入参数进行调试的方法:
直接用 gdb app -p1 -p2 这样进行调试是不行的。
需要像以下这样使用:
#gdb app
(gdb) r -p1 -p2
或者在运行run命令前使用set args命令:
(gdb) set args p1 p2
可以用show args 命令来查看

2. 加入断点:
break
break
break +offset
break -offset
(在当前行号的前面或后面的offset行停住。)

break filename:linenum
在源文件filename的linenum行处停住。

break filename:function
在源文件filename的function函数的入口处停住。

break ... if
...可以是上述的参数,condition表示条件,在条件成立时停住。比如在循环境体中,可以设置 break if i=100,表示当i为100时停住程序。

3. 查看运行时的堆栈:
使用bt命令

4. 打印某个变量的值:
print val

5. 单步: n
继续运行: c
step
单步跟踪,如果有函数调用,他会进入该函数。
next
同样单步跟踪,如果有函数调用,他不会进入该函数。很像VC等工具中的step over。后面可以加count也可以不加,不加表示一条条地执行,加表示执行后面的count条指令,然后再停住。
set step-mode
set step-mode on
打开step-mode模式,于是,在进行单步跟踪时,程序不会因为没有debug信息而不停住。这个参数有很利于查看机器码。
set step-mod off
关闭step-mode模式。
finish
运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返回值及参数值等信息。
until 或 u
当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。

6.在GDB中执行shell命令:
在gdb环境中,你可以执行UNIX的shell的命令,使用gdb的shell命令来完成:
eg. shell make

7. 运行环境
可设定程序的运行路径。
show paths 查看程序的运行路径。
set environment varname [=value] 设置环境变量。如:set env USER=hchen
show environment [varname] 查看环境变量。

8.观察点(WatchPoint)
观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程 序。我们有下面的几种方法来设置观察点:
watch
为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序。
rwatch
当表达式(变量)expr被读时,停住程序。
awatch
当表达式(变量)的值被读或被写时,停住程序。
info watchpoints
列出当前所设置了的所有观察点。

9. 维护breakpoint
clear
清除所有的已定义的停止点。
clear func
清除所有设置在函数上的停止点。
delete [breakpoints] [range...]
删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点。range 表示断点号的范围(如:3-7)。其简写命令为d。
比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好像回收站一样。
disable [breakpoints] [range...]
disable所指定的停止点,breakpoints为停止点号。如果什么都不指定,表示disable所有的停止 点。简写命令是dis.
enable [breakpoints] [range...]
enable所指定的停止点,breakpoints为停止点号。

10、程序变量
查看文件中某变量的值:
file::variable
function::variable
可以通过这种形式指定你所想查看的变量,是哪个文件中的或是哪个函数中的。例如,查看文件f2.c中的全局变量x的值:
gdb) p 'f2.c'::x

查看数组的值
有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。例如,你的程序中有这样的语句:
int *array = (int *) malloc (len * sizeof (int));
于是,在GDB调试过程中,你可以以如下命令显示出这个动态数组的取值:
p *array@len
如果是静态数组的话,可以直接用print数组名,就可以显示数组中所有数据的内容了。

11.输出格式
一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:

x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
(gdb) p i
$21 = 101
(gdb) p/a i
$22 = 0x65
(gdb) p/c i
$23 = 101 'e'
(gdb) p/f i
$24 = 1.41531145e-43
(gdb) p/x i
$25 = 0x65
(gdb) p/t i
$26 = 1100101

11.查看内存
使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:
x/
n、f、u是可选的参数。
n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。

n/f/u三个参数可以一起使用。例如:
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。

12.自动显示

你可以设置一些自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量会自动显示。相关的GDB命令是display。
display
display/
display/ expr
expr是一个表达式,fmt表示显示的格式,addr表示内存地址,当你用display设定好了一个或多个表达式后,只要你的程序被停下来,GDB会自动显示你所设置的这些表达式的值。

格式i和s同样被display支持,一个非常有用的命令是:
display/i $pc

undisplay
delete display
删除自动显示,dnums意为所设置好了的自动显式的编号。

disable display
enable display
disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。

info display
查看display设置的自动显示的信息。GDB会打出一张表格,向你报告当然调试中设置了多少个自动显示设置,其中包括,设置的编号,表达式,是否enable。

13. 设置显示选项
set print address
set print address on
打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。系统默认为打开的,
show print address
查看当前地址显示选项是否打开。

set print array
set print array on
打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。这个选项默认是关闭的。与之相关的两个命令如下,我就不再多说了。

set print array off
show print array

set print elements
这个选项主要是设置数组的,如果你的数组太大了,那么就可以指定一个来指定数据显示的最大长度,当到达这个长度时,GDB就不再往下显示了。如果设置为0,则表示不限制。

show print elements
查看print elements的选项信息。

set print null-stop
如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。这个选项默认为off。

set print pretty on
如果打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮。

14.关于显示源码list
参考技术A

    注意要点

    有时候bug很难复现、或者环境搭建起来比较困难,所以在出现bug的时候可以使用gdb的attach功能调试正在运行的GCC编译出来的程序,注意编译选项要加-g,否则没有符号表。

    调试步骤

    编译时候带-g选项。

    运行程序。

    ps找到进程号。

    启动gdb,使用attach选项,这时gdb会停止在程序的某处。

    按照GDB调试方法调试。当程序退出之后,依然可以使用run命令重启程序。

    第一步  编译一个死循环程序。 

    第二步  让gdb连接到正在执行的进程上去。

    第三步 在gdb中重启程序。

参考技术B 有时候bug很难复现、或者环境搭建起来比较困难,所以在出现bug的时候可以使用gdb的attach功能调试正在运行的GCC编译出来的程序,注意编译选项要加-g,否则没有符号表。
调试步骤
编译时候带-g选项。
运行程序。
ps找到进程号。
启动gdb,使用attach选项,这时gdb会停止在程序的某处。
按照GDB调试方法调试。当程序退出之后,依然可以使用run命令重启程序。
第一步 编译一个死循环程序。
第二步 让gdb连接到正在执行的进程上去。
第三步 在gdb中重启程序。
参考技术C # This is a script to start Erlang/OTP for debugging. PATH is set to
# include this script so if slave nodes are started they will use this
# script as well.
#
# usage: cerl [ OPTIONS ] [ ARGS ]
#
# The OPTIONS are
#
# -rootdir $MYROOTDIR
# Run an installed emulator built from this source
# -debug Run debug compiled emulator
# -gdb Run the debug compiled emulator in emacs and gdb.
# You have to start beam in gdb using “run”.
# -break F Run the debug compiled emulator in emacs and gdb and set break.
# The session is started, i.e. “run” is already don for you.
# -xxgdb FIXME currently disabled
# -purify Run emulator compiled for purify
# -quantify Run emulator compiled for quantify
# -purecov Run emulator compiled for purecov
# -gcov Run emulator compiled for gcov
# -valgrind Run emulator compiled for valgrind
# -lcnt Run emulator compiled for lock counting
# -nox Unset the DISPLAY variable to disable us of X Windows
#

Gdb调试多进程程序

Gdb调试多进程程序

程序经常使用fork/exec创建多进程程序。多进程程序有自己独立的地址空间,这是多进程调试首要注意的地方。Gdb功能强大,对调试多线程提供很多支持。

方法1:调试多进程最土的办法:attach pid

Attach是调试进程的常用办法,只要有可执行程序以及相应PID,即可工作。当然,为方便调试,可以在进程启动后,设定sleep一段时间,如30s,这样即可有充足的时间来attach。

查找进程id:
ps -fu YOUR_USER_NAME
运行gdb:
gdb
(gdb) attach xxxxx --- xxxxx为利用ps命令获得的子进程process id
(gdb) stop --- 这点很重要,你需要先暂停那个子进程,然后设置一些断点和一些Watch
(gdb) break 37 -- 在result = wib(value, div);这行设置一个断点,可以使用list命令察看源代码
Breakpoint 1 at 0x10808: file eg1.c, line 37.
(gdb) continue
Continuing.

方法2: set follow-fork-mode child + main断点

当设置set follow-fork-mode child,gdb将在fork之后直接执行子进程,知道碰到断点后停止。如何设置子进程的断点呢?在父进程中是无法知道子进程的地址空间的(只有等程序载入后方可知)。Gdb提供一个很方便的机制:main函数的断点将被子进程继承(毕竟main是任何程序的入口)。

注意:程序在main停下后,可尝试设置断点。断点是否有效,取决于gdb是否已经载入目标程序的地址空间。

方法3: set follow-fork-mode child + catch exec

Cache点是一种特殊的breakpoint。Gdb能够catch的事件很多,如throw/catch/exception/syscall/exec/fork/vfork等。其中和多进程关系最大的就是exec/fork事件。

举例:

    

GNU gdb Fedora (6.8-27.el5)

 

Copyright (C) 2008 Free Software Foundation, Inc.

 

(gdb) catch exec

 

Catchpoint 1 (exec)

 

(gdb) set follow-fork-mode child

 

(gdb) r  -d ***

 

Catchpoint 1 (exec‘d /****/binary), 0x0000003c68800a70  in _start ()

 

   from /lib64/ld-linux-x86-64.so.2

 

(gdb) bt

 

#0  0x0000003c68800a70 in _start () from  /lib64/ld-linux-x86-64.so.2

 

#1  0x0000000000000003 in ?? ()

 

#2  0x00007fff65c6e85a in ?? ()

 

#3  0x00007fff65c6e85d in ?? ()

 

#4  0x00007fff65c6e860 in ?? ()

 

(gdb) b lib.cc:8720

 

No symbol table is loaded.  Use the  "file" command.

 

(gdb) c

 

Continuing

 

(gdb) bt

 

#0  0x0000003c68800a70 in _start () from  /lib64/ld-linux-x86-64.so.2

 

#1  0x0000000000000002 in ?? ()

 

#2  0x00007fff1af7682a in ?? ()

 

#3  0x0000000000000000 in ?? ()

 

(gdb)  b lib.cc:8720

 

Breakpoint 2 at 0x15f9694: file lib.cc, line 8720.

 

(gdb) c

 

Continuing.

 

[Thread debugging using libthread_db enabled]

 

[Thread 0x40861940 (LWP 12602) exited]

 

[Switching to process 12630]

 

0x0000003c6980d81c in vfork () from  /lib64/libpthread.so.0

 

Warning:

 

Cannot insert breakpoint 2.

 

Error accessing memory address 0x15f9694: Input/output  error.

 

(gdb) bt

 

#0  0x0000003c6980d81c in vfork () from  /lib64/libpthread.so.0

 

#1  0x000000000040c3fb in ?? ()

 

#2  0x00002adeab604000 in ?? ()

 

#3  0x01000000004051ef in ?? ()

 

#4  0x00007fffff4a42f0 in ?? ()

 

#5  0x686365746e6f6972 in ?? ()

 

#6  0x0000000d0000000c in ?? ()

 

#7  0x0000000b0000000a in ?? ()

 

#8  0x0000000000000000 in ?? ()

 

(gdb) delete 2  --此处当breakpoint无效时,必须删除,否则程序无法继续

 

(gdb) c

 

Continuing.

 

[New process 12630]

 

Executing new program: /****/binary

 

warning: Cannot initialize thread debugging  library: generic error

 

[Switching to process 12630]

 

 

 

Catchpoint 1 (exec‘d /****/binary), 0x0000003c68800a70  in _start ()

 

   from /lib64/ld-linux-x86-64.so.2

 

(gdb) bt

 

#0  0x0000003c68800a70 in _start () from  /lib64/ld-linux-x86-64.so.2

 

#1  0x0000000000000009 in ?? ()

 

Backtrace stopped: previous frame inner to this frame  (corrupt stack?)

 

(gdb) b lib.cc:8720

 

Breakpoint 4 at 0x15f9694: file lib.cc, line 8720.

 

(gdb) b type.cc:32

 

Breakpoint 5 at 0x1693050: file type.cc, line 32.

 

(gdb) c

 

Continuing.

 

(gdb)  -- 和正常程序调试一样

 

说明:catch exec后,程序将在fork/vfork/exec处停下。并非每次停下后,设置断点都是有效的。如提供断点无效,需要删除,否则程序无法继续。要能够在新进程中设置断点,一定要等到新进程的地址空间被载入后,设置断点是才有效(exec将改变原程序的地址空间)。上述例子,主要想展示如何对新进程设置断点!

注意:

1)程序地址非常重要(代码和数据地址一样重要)。使用gdb时,多多注意和利用地址信息。

2)On some systems, when a child process isspawned by vfork, you cannot debug the child or parent until an exec callcompletes.

方法4:info inferiors/inferiors inferiors

设置set detach-on-fork off/set follow-exec-mode new。

If you choose toset `detach-on-fork‘ mode off, then gdb will retain control of all forkedprocesses (including nested forks). You can list the forked processes under thecontrol of gdb by using the info inferiors command, and switch from one fork to another by using the inferior command.

以上是关于如何用gdb调试erlang运行期的主要内容,如果未能解决你的问题,请参考以下文章

GDB调试技巧:常用命令

如何用vs code调试运行c语言程序

如何更有效的调试运行MATLAB程序

GDB调试——启动调试程序

Gdb调试多进程程序

使用gdb+gdbserver调试应用程序