使用gdb调试运行时的程序小技巧

标签: 未分类 gdb pstack | 发表时间:2012-10-15 04:32 | 作者:士豪
分享到:

出处:http://rdc.taobao.com/blog/cs

原创文章,欢迎转载。转载请注明:转载自淘宝核心系统团队博客,谢谢! 
原文链接地址: 使用gdb调试运行时的程序小技巧

下面介绍我调试时经常遇到的三种问题,如果大家也有类似的问题交流一下解决方法: 
情景1:在不中止程序服务的情况下,怎么调试正在运行时的程序 
情景2:需要同时看几个变量的值或者批量查看多个core文件的堆栈信息怎么办 
情景3:遇到需要查看、队列、链表、树、堆等数据结构里的变量怎么办

1. 情景1:在不中止程序服务的情况下,怎么调试正在运行时的程序 
我们在生产环境或者测试环境,会遇到一些异常,我们需要知道程序中的变量或者内存的值来确定程序运行状态 
之前听过@淘宝褚霸讲过用systemstap可以实现这种功能,但systamstap写起来复杂一些,
还有时候在低内核版本的操作系统上用stap之后,程序或者操作系统都有可能死掉。

看过多隆调试程序时用pstack(修改了pstack代码,用gdb实现的,详见http://blog.yufeng.info/archives/873)查看和修改一个正在 
执行程序的全局变量,感觉很神奇,尝试用gdb实现这种功能:

保存下面代码到文件runstack.sh

    #!/bin/sh
if test $# -ne 2; then
echo "Usage: `basename $0 .sh` <process-id> cmd" 1>&2
echo "For exampl: `basename $0 .sh` 1000 bt" 1>&2
exit 1
fi
if test ! -r /proc/$1; then
echo "Process $1 not found." 1>&2
exit 1
fi
result=""
GDB=${GDB:-/usr/bin/gdb}
# Run GDB, strip out unwanted noise.
result=`$GDB --quiet -nx /proc/$1/exe $1 <<EOF 2>&1
$2
EOF`
echo "$result" | egrep -A 1000 -e "^\(gdb\)" | egrep -B 1000 -e "^\(gdb\)"

用于测试runstack.sh调试的c代码

    #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> typedef struct slist {
struct slist *next;
char data[4096];
} slist; slist input_list = {NULL, {'\0'}};
int count = 0; static void stdin_read (int fd)
{
char buf[4096];
int ret; memset(buf, 0, 4096); fprintf(stderr, "please input string:"); while (ret = read(fd, buf, 4096)) { slist *node = calloc(1, sizeof(slist));
memcpy(node->data, buf, ret);
node->next = input_list.next;
input_list.next = node;
count ++; if (memcmp(buf, "quit", 4) == 0) {
fprintf(stdout, "input quit:\n");
return;
}
fprintf(stderr, "ret: %d, there is %d strings, current is %s\nplease input string:", ret, count, buf);
}
} int main()
{
fprintf(stderr, "main run!\n"); stdin_read(STDIN_FILENO); slist *nlist;
slist *list = input_list.next;
while (list) {
fprintf(stderr, "%s\n", list->data);
nlist = list->next;
free(list);
list = nlist;
} return 0;
}

编译c代码:gcc -g -o read_input read_input.c 
执行./read_input 我们开始使用runstack.sh来调试 
使用方法:sh ./runstack.sh pid “command”

来试验一下: 
[shihao@xxx]$ ps aux |grep read_input|grep -v grep 
shihao 10933 0.0 0.0 3668 332 pts/4 S+ 09:41 0:00 ./read_input 
10933是一个read_input程序的进程号

1)打印代码 
sudo sh ./runstack.sh 10933 “list main” 
结果 
(gdb) 35 fprintf(stderr, “ret: %d, there is %d strings, current is %s\nplease input string:”, ret, count, buf); 
36 } 
37 } 
38 
39 int main() 
40 { 
41 fprintf(stderr, “main run!\n”); 
42 
43 stdin_read(STDIN_FILENO); 
44 
(gdb) quit

2)显示程序全局变量值 
./runstack.sh 10933 “p count” 
(gdb) $1 = 1 
(gdb) quit

3)修改变量值 
执行下面命令前 
[shihao@tfs036097 gdb]$ runstack.sh 11190 “set count=100″ 
结果: (gdb) (gdb) quit

我们可以用上面命令看我们修改成功没有 
[shihao@tfs036097 gdb]$ runstack.sh 11190 “p count” 
(gdb) $1 = 100 
(gdb) quit 
全局变量count变成100了。

注:1)有一些程序经过操作系统优化过,直接用上面的方法可能有找不到符号表的情况

            result=`$GDB --quiet -nx /proc/$1/exe $1 <<EOF 2>&1
$2
EOF`

可以把上面的代码改成下面的试试,如果不行可能是其他原因

            BIN=`readlink -f /proc/$1/exe`
result=`$GDB --quiet -nx $BIN $1 <<EOF 2>&1
$2
EOF`

2)需要有查看和修改运行的进程的权限 
2. 情景2:需要同时看几个变量的值或者批量查看多个core文件的信息怎么办 
1)多个变量的情景 
我们同时看一下count和input_list里面的值和堆栈信息,我们可以写一个script.gdb 
$ cat script.gdb

    p input_list
p count
bt
f 1
p buf

执行 runstack.sh 10933 “source script.gdb” 
(gdb) $1 = {next = 0x597c020, data = ” } 
$2 = 2 
#0 0x0000003fa4ec5f00 in __read_nocancel () from /lib64/libc.so.6 
#1 0x00000000004007c7 in stdin_read (fd=0) at read_input.c:23 
#2 0×0000000000400803 in main () at read_input.c:43 
#1 0x00000000004007c7 in stdin_read (fd=0) at read_input.c:23 
23 while (ret = read(fd, buf, 4096)) { 
$3 = “12345\n”, ” 
(gdb) quit 
这样就可以同时做多个操作 
2)批处理查看core的情况 
有的时候会出现很多core文件,我们想知道哪些core文件是因为相同的原因,哪些是不相同的,看一个两个的时候还比较轻松 
$ ls core.* 
core.12281 core.12282 core.12283 core.12284 core.12286 core.12287 core.12288 core.12311 core.12313 core.12314 
像上面有很多core文件,一个一个用gdb去执行bt去看core在哪里有点麻烦,我们想有把所有的core文件的堆栈和变量信息打印出来 
我对runstack稍作修改就可以实现我们的需求,我们起名叫corestack.sh

    #!/bin/sh

    if test $# -ne 3; then
echo "Usage: `basename $0 .sh` program core cmd" 1>&2
echo "For example: `basename $0 .sh` ./main core.1111 bt" 1>&2
exit 1
fi if test ! -r $1; then
echo "Process $1 not found." 1>&2
exit 1
fi result=""
GDB=${GDB:-/usr/bin/gdb}
# Run GDB, strip out unwanted noise.
result=`$GDB --quiet -nx $1 $2 <<EOF 2>&1
$3
EOF`
echo "$result" | egrep -A 1000 -e "^\(gdb\)" | egrep -B 1000 -e "^\(gdb\)"

我们可以这样执行: 
./corestack.sh ./read_input core.12281 “bt” 
执行结果: 
(gdb) #0 0x0000003fa4e30265 in raise (sig=) 
at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x0000003fa4e31d10 in abort () at abort.c:88 
#2 0x0000003fa4e296e6 in __assert_fail (assertion=, 
file=, line=, 
function=) at assert.c:78 
#3 0x00000000004008ba in main () at read_input.c:55 
(gdb) quit 
查看多个core文件堆栈信息的准备工作差不多了,我们写个脚本就可以把所有的core文件堆栈打印出来了

执行以下:for i in `ls core.*`;do ./corestack.sh ./read_input $i “bt”; done 
(gdb) #0 0x0000003fa4e30265 in raise (sig=) 
at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x0000003fa4e31d10 in abort () at abort.c:88 
#2 0x0000003fa4e296e6 in __assert_fail (assertion=, 
file=, line=, 
function=) at assert.c:78 
#3 0x00000000004008ba in main () at read_input.c:55 
(gdb) quit 
…… 
(gdb) #0 0x0000003fa4e30265 in raise (sig=) 
at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x0000003fa4e31d10 in abort () at abort.c:88 
#2 0x0000003fa4e296e6 in __assert_fail (assertion=, 
file=, line=, 
function=) at assert.c:78 
#3 0x00000000004008ba in main () at read_input.c:55 
(gdb) quit 
ok, 我们看到了所有core文件的堆栈。

3. 情景3:遇到需要查看、队列、链表、树、堆等数据结构里的变量怎么办? 
下面介绍链表怎么处理,对其他数据结构感兴趣的同学可以自己尝试编写一些gdb脚本(麻烦@周哲士豪一下我,我也学习学习), 
希望我们可以实现一个gdb调试工具箱

gdb是支持编写的脚本的 http://sourceware.org/gdb/onlinedocs/gdb/Command-Files.html 
我们写个plist.gdb,用while循环来遍历链表 
$ cat plist.gdb

    set $list=&input_list

    while($list)
p *$list
set $list=$list->next
end

我们执行一下:runstack.sh 13434 “source plist.gdb” 
(gdb) $1 = {next = 0x3d61040, data = ” } 
$2 = {next = 0x3d60030, data = “123456\n”, ” } 
$3 = {next = 0x3d5f020, data = “12345\n”, ” } 
$4 = {next = 0x3d5e010, data = “1234\n”, ” } 
$5 = {next = 0×0, data = “123\n”, ” } 
(gdb) quit

实际上我们可以把plist写成自定义函数,执行gdb的时候会在当前目下查找.gdbinit文件加载到gdb: 
$ cat .gdbinit

    define plist

        set $list=$arg0

        while($list)
p *$list
set $list=$list->next
end
end

这样就可以用plist命令遍历list的值 
$ runstack.sh 13434 “plist &input_list” 
(gdb) $1 = {next = 0x3d61040, data = ” } 
$2 = {next = 0x3d60030, data = “123456\n”, ” } 
$3 = {next = 0x3d5f020, data = “12345\n”, ” } 
$4 = {next = 0x3d5e010, data = “1234\n”, ” } 
$5 = {next = 0×0, data = “123\n”, ” } 
(gdb) quit

参考资料: 
霸爷的博客:http://blog.yufeng.info/archives/873 
gdb从脚本加载命令:http://blog.lifeibo.com/?p=380 
gdb官方文档:http://sourceware.org/gdb/onlinedocs/gdb/Command-Files.html

gdb调试运行时的程序小技巧的更多相关文章

  1. gdb 调试c/c++的一些小技巧

    ptype obj/class/struct 查看obj/class/struct的成员,但是会把基类指针指向的派生类识别为基类   set print object on 这个选项可以看到派生对象的 ...

  2. 献身说法---修复bug时的一些小技巧

    最近,修复了项目当中的一些bug,觉着有些思路可以分享出来供大家借鉴. 场景一 开发环境中系统正常运行,测试环境中,部分机器未能正常运行. 解决过程:远程连接了测试环境中的机器,观察了系统的运行情况, ...

  3. gdb调试带参数的程序 (转载)

    转自:http://www.cnblogs.com/rosesmall/archive/2012/04/10/2440514.html 一般来说GDB主要调试的是C/C++的程序.要调试C/C++的程 ...

  4. 使用GDB调试产生多进程的程序

    如果一个进程fork了多个进程,这时使用GBD工具对程序进行调试会如何呢? 实际上,GDB 没有对多进程程序调试提供直接支持.例如,使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试 ...

  5. 使用HBuilder编辑器进行真机调试运行时提示Waiting for debugger!

    在使用HBuilder编辑器创建mui项目进行真机调试的时候,手机总是提示Waiting for debugger! 现在终于找到了解决办法: 手机 设置 -> 开发人员选项 -> USB ...

  6. linux系统维护时的一些小技巧,包括系统挂载新磁盘的方法!可收藏!

    这里发布一些平时所用到的小技巧,不多,不过会持续更新.... 1.需要将history创建硬链接ln 全盘需要备份硬链接 ln /etc/xxx /home/xxx 2.root用户不可以远程 /et ...

  7. gdb调试运行程序带参数(调用动态链接库),debug过程记录

    library多线程file1.gdb (运行程序名称) 例如 gdb cbenchmark 2.设置运行参数 set args -c 1 -n 1 -F ./libaliww.so -l 1 3.如 ...

  8. 硬盘上的一些算法小题目||and今天看了下林锐的书以及gdb调试 及一些变成算法小题目

    gdb调试:观察点,断点,事件捕捉点.step 进入函数,next 跳过函数,until 跳出循环,finish 结束函数 林锐:书后试题 & c++的对象模型图 看了二叉树的非递归遍历, 链 ...

  9. gdb 调试,当发现程序退出,需要定位程序退出位置时。

    在进入gdb后设置,执行下面语句 handle SIGSEGV nopass handle SIGSEGV nostop 执行程序,触发问题,gdb侧执行c 故障出现时,执行bt,显示堆栈调用.

随机推荐

  1. [转]HTTP Error 400. The request hostname is invalid.

    一般看到网站提示Bad Request(Invalid Hostname)错误我们都会说是iis,apache出问题了,iis出现这种问题解决办法大概是:IIS> 默认网站> > 属 ...

  2. go与rpc

    Go语言的RPC介绍(含Protobuf-RPC) http://www.open-open.com/lib/view/open1389251727289.html

  3. android java 堆栈的实现

    android和java不提供堆栈的实现,只提供了list,vector,deque得存储结构,对于以前做面向过程语言的程序员来说,总觉得缺少了些什么: Stack.java文件: public cl ...

  4. 2.6.1 使用toast显示提示信息框

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout ...

  5. Learning WCF Chapter2 WCF Contracts and Serialization

    So far I’ve talked about the standards behind it all,but in fact WCF hides most of this from the dev ...

  6. USACO3.41Closed Fences(几何)

    这题水的真不易..300多行 累死了 对着数据查错啊 枚举每个边上的点到源点 是否中间隔着别的边  每条边划分500份就够了  注意一下与源点在一条直线上的边不算 几何 啊,,好繁琐 参考各种模版.. ...

  7. oracle object_id和data_object_id的区别

    Oracle的数据字典表dba_objects包含了两个字段,object_id, data_object_id,官方文档上的解释是: object_id: Dictionary object num ...

  8. BZOJ2802: [Poi2012]Warehouse Store

    2802: [Poi2012]Warehouse Store Time Limit: 10 Sec  Memory Limit: 64 MBSec  Special JudgeSubmit: 121  ...

  9. Makefile第一讲:一个简单的Makefile

    摘要 假定你对linux已经比较的熟悉,假定你编程已经稍有经验,本文不会对文章作出太多基础性解释,看不懂莫怪,只当作给学习的朋友一个引导思路,我也是一个初学者,边学边写,将学会的教给大家,文章有错误之 ...

  10. 初学acm感想

    初学acm,觉得大部分题对我来说都是陌生的,好多类型没见过,好多题没思路,打击确实不小,或许这个阶段正是比较能考验人的时候吧,因为只有坚持下来才有收获,没有人生下来就是大神,所以不能气馁更不能放弃,有 ...