gdb调试运行时的程序小技巧
使用gdb调试运行时的程序小技巧
出处: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调试运行时的程序小技巧的更多相关文章
- gdb 调试c/c++的一些小技巧
ptype obj/class/struct 查看obj/class/struct的成员,但是会把基类指针指向的派生类识别为基类 set print object on 这个选项可以看到派生对象的 ...
- 献身说法---修复bug时的一些小技巧
最近,修复了项目当中的一些bug,觉着有些思路可以分享出来供大家借鉴. 场景一 开发环境中系统正常运行,测试环境中,部分机器未能正常运行. 解决过程:远程连接了测试环境中的机器,观察了系统的运行情况, ...
- gdb调试带参数的程序 (转载)
转自:http://www.cnblogs.com/rosesmall/archive/2012/04/10/2440514.html 一般来说GDB主要调试的是C/C++的程序.要调试C/C++的程 ...
- 使用GDB调试产生多进程的程序
如果一个进程fork了多个进程,这时使用GBD工具对程序进行调试会如何呢? 实际上,GDB 没有对多进程程序调试提供直接支持.例如,使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试 ...
- 使用HBuilder编辑器进行真机调试运行时提示Waiting for debugger!
在使用HBuilder编辑器创建mui项目进行真机调试的时候,手机总是提示Waiting for debugger! 现在终于找到了解决办法: 手机 设置 -> 开发人员选项 -> USB ...
- linux系统维护时的一些小技巧,包括系统挂载新磁盘的方法!可收藏!
这里发布一些平时所用到的小技巧,不多,不过会持续更新.... 1.需要将history创建硬链接ln 全盘需要备份硬链接 ln /etc/xxx /home/xxx 2.root用户不可以远程 /et ...
- gdb调试运行程序带参数(调用动态链接库),debug过程记录
library多线程file1.gdb (运行程序名称) 例如 gdb cbenchmark 2.设置运行参数 set args -c 1 -n 1 -F ./libaliww.so -l 1 3.如 ...
- 硬盘上的一些算法小题目||and今天看了下林锐的书以及gdb调试 及一些变成算法小题目
gdb调试:观察点,断点,事件捕捉点.step 进入函数,next 跳过函数,until 跳出循环,finish 结束函数 林锐:书后试题 & c++的对象模型图 看了二叉树的非递归遍历, 链 ...
- gdb 调试,当发现程序退出,需要定位程序退出位置时。
在进入gdb后设置,执行下面语句 handle SIGSEGV nopass handle SIGSEGV nostop 执行程序,触发问题,gdb侧执行c 故障出现时,执行bt,显示堆栈调用.
随机推荐
- hdu 4722
比赛的时候这道题一直都没出来,承启提醒我之后还是一直WA: 其实规律早就找到了```` 其实这题还可以用数位dp来做,不过从来没写过,以后再贴: 代码: #include<iostream> ...
- Codeforces Round #197 (Div. 2) : E
看了codeforces上的大神写的题解之后,才知道这道题水的根本! 不过相对前面两题来说,这道题的思维要难一点: 不过想到了水的根本,这题也真心不难: 方法嘛,就像剥洋葱一样,从外面往里面剥: 所以 ...
- 【BZOJ 1594】 [Usaco2008 Jan]猜数游戏 (二分+并查集)
1594: [Usaco2008 Jan]猜数游戏 Description 为了提高自己低得可怜的智商,奶牛们设计了一个新的猜数游戏,来锻炼她们的逻辑推理能力. 游戏开始前,一头指定的奶牛会在牛棚后面 ...
- OA学习笔记-003-Hibernate3.6配置
一.jar包:核心包, 必须包, jpa, c3p0, jdbc antlr-2.7.6.jarc3p0-0.9.1.jarcommons-collections-3.1.jardom4j-1.6.1 ...
- JDBC事务控制管理
1.事务 (1)事务的概念 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功. 例如:A——B转帐,对应于如下两条sql语句 update account set mone ...
- bzoj1415
比较简单的数学期望,先预处理出当聪聪在i,可可在j时聪聪往哪个点走然后做dp即可,我用了记忆化搜索实现 type node=record po,next:longint; end; ..,..] of ...
- asp.net 前后台交互
转自:http://www.cnblogs.com/softpower/archive/2010/11/16/1878769.html 1.如何在JavaScript访问C#函数? 2.如何在Java ...
- Lua运算符
+ 加- 减* 乘/ 除^ 幂% 求余# 求长度= 赋值< 小于> 大于== 等于<= 不大于=> 不小于~= 不等于and 逻辑与or 逻辑或not 逻辑非.. 连接字符串
- VS发布,TFS邮件设置截图
vs2012发布截图: TFS邮件设置截图:
- Android学习笔记(九)一个例子弄清Service与Activity通信
上一篇博文主要整理了Service的创建.绑定过程,本篇主要整理一下Service与Activity的通信方式.包括在启动一个Service时向它传递数据.怎样改变运行中的Service中得数据和侦听 ...