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

原创文章,欢迎转载。转载请注明:转载自淘宝核心系统团队博客,谢谢!
原文链接地址:使用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”

来试验一下:
[[email protected]]$ 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)修改变量值
执行下面命令前
[[email protected] gdb]$ runstack.sh
11190 “set count=100″
结果: (gdb) (gdb) quit

我们可以用上面命令看我们修改成功没有
[[email protected] 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回退:
http://sourceware.org/gdb/news/reversible.html
gdb
stl调试脚本:http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt
gdb
高级调试方法:http://blog.csdn.net/wwwsq/article/details/7086151

时间: 2024-10-20 03:18:56

[转]使用gdb调试运行时的程序小技巧的相关文章

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

使用gdb调试运行时的程序小技巧 标签: 未分类 gdb pstack | 发表时间:2012-10-15 04:32 | 作者:士豪 分享到: 出处:http://rdc.taobao.com/blog/cs 原创文章,欢迎转载.转载请注明:转载自淘宝核心系统团队博客,谢谢! 原文链接地址: 使用gdb调试运行时的程序小技巧 下面介绍我调试时经常遇到的三种问题,如果大家也有类似的问题交流一下解决方法: 情景1:在不中止程序服务的情况下,怎么调试正在运行时的程序 情景2:需要同时看几个变量的值或

使用 gdb 调试运行中的 Python 进程

本文和大家分享的是使用 gdb 调试运行中的 Python 进程相关内容,一起来看看吧,希望对大家学习python有所帮助. 准备工作 安装 gdb 和 python2.7-dbg: $ sudo apt-get install gdb python2.7-dbg 设置 /proc/sys/kernel/yama/ptrace_scope: $ sudo su# echo 0 > /proc/sys/kernel/yama/ptrace_scope 运行 test.py: $ python te

gdb调试带参数的程序

gdb调试带参数的程序 解决方案: gdb --args ./app arg1 arg2 

对于shell脚本参数获取时的一点小技巧

问题如下: 根据脚本参数的个数$#进行一个循环,在依次输出每个参数$1 $2 $3...... 我有一个循环变量i $i 取到这时的i为1,我想使用这个1再去调用$1,也是就是打印出第一个参数 就是$($i)的意思来取到第几个参数,当然$($i)是不好用的 当时纠结了好久,最后上百度提问,两位高手给出了答案: 1) #!/bin/sh NUMBER=$# echo $NUMBER i=1 while [ $i -le $NUMBER ] do a[$i]=$1 #将数组a[i]赋值为$1,即取得

当磁盘被大文件填满时的一些小技巧

当磁盘被大文件填满时的一些小技巧 生产环境中会出现磁盘被一些大文件填满,但是大文件却因为被打开而无法马上删除释放空间的情况,以下技巧是解决此类问题的一种方法 模拟环境 先用/dev/zero 将/boot分区填满 [root@centos7 boot]# cp /dev/zero /boot/bigfile cp: error writing '/boot/bigfile': No space left on device cp: failed to extend '/boot/bigfile'

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

这里发布一些平时所用到的小技巧,不多,不过会持续更新.... 1.需要将history创建硬链接ln 全盘需要备份硬链接 ln /etc/xxx /home/xxx 2.root用户不可以远程 /etc/ssh/sshd_config PermitRootLogin no AllowUsers XXX 3.同步软件时钟 date MMDDhhmmCCYY.ss hwclock --set --date="2011-08-14 16:45:05" 4.SSH服务重启及开关 ubuntu

gdb 调试带参数的程序

转载:http://blog.sina.com.cn/s/blog_4b6f784001011yvh.html 例子: #include <stdio.h> int main(int argc,char *argv[]) { int i = argc; printf("argc:%d\n",i); for(i = 0;i < argc;i++) printf("argv[%d]:%s\n",i,argv[i]); return 1; } 输出: [

Windows下使用Sublime Text 3调试/运行 C/C++ 程序

系统:Windows 8.1 64bit 软件:Sublime Text 3 Build3065 x86 编译软件:MinGW 1.    下载Sublime Text 3 2.    我用的是流风清音修改的32位版本,64位貌似不稳定. 3.    下载地址:http://haojian138.blog.163.com/blog/static/212643110201393010438357/ 4.    下载:MinGW.地址:http://sourceforge.net/projects/

django forms定义时的一些小技巧

我们在使用django定义好一个model后,可能需要添加或者修改model数据,此时可以借助于django提供的forms表单组件. forms表单和model结合使用时,必须在Meta类中指定关联的model和需要展示的字段(field) django会自动根据每个字段的类型来生成适合的forms字段 如有一个外键字段,django会自动把他转化成其关联的对象而不是简单的展示其关联的id. 一个简单的model关联的forms表单定义,如FlowStageForm1. class FlowS