博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
gdb调试段错误及使用
阅读量:6853 次
发布时间:2019-06-26

本文共 4545 字,大约阅读时间需要 15 分钟。

在编程调试中,经常出现段错误,此时可用gdb调试。具体方法为注册段错误信号处理函数,在处理函数中启动gdb。

具体代码如下:

void segv_handler(int no) {char buf[512];char cmd[512];FILE *file;snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());if(!(file = fopen(buf, "r"))){ exit(EXIT_FAILURE);} if(!fgets(buf, sizeof(buf), file)){ eixt(EXIT_FAILURE);} if(buf[strlen(buf) - 1 ] == '\n'){ buf[strlen(buf) -1] = '\0';} snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());system(cmd);}

注册函数:

signal(SIGSEGV, segv_handler);

下面转自一些总结:

作为一名程序猿,日常开发中解决各种bug是不可避免的。对于简单的bug通过日志分析,或者增加打印信息就能很快定位到原因并解决。但是对于某些比较复杂的情况,想要定位到bug往往十分困难。查阅了很多资料,经过不断尝试,我发现gdb调试能够起到很大的帮助。下面我将对使用gdb的一些常用技巧和实例做下总结。
下面总结下比较关键的几个用法:
一、启动GDB调试
使用gdb调试首先在编译程序时加上-g参数:$ gcc –g –o foo foo.c
启动gdb调试有多种方法,可以根据不同的场景选择合适的方式,这也是gdb比较好用的地方。
1. 程序没有运行时,gdb +<program> 直接用gdb运行程序;
2. 程序运行中的gdb调试有两种方式:
a.ps查看程序的PID,gdb + <program> + PID ,自动挂接到已运行的程序;
b.ps产看程序的PID,gdb + <program>运行gdb后,用attach + PID指令挂接到程序, 并用detach来取消挂接的进程。
3. 程序已经死掉后,gdb +<program> + core文件进行调试,core文件是程序非法执行后产生的“核心转储”文件。有些情况下core不能生成,需要用ulimit -c unlimited指令先设置系统环境。
二、针对第二种gdb启动方式,可以有如下实现方式。
程序发生段错误,但是该异常发生有一定的随机性,为了捕获异常并进行gdb调试,在程序中捕获SIGSEGV信号并进行如下处理,这样当程序运行出现段错误时直接进入gdb调试环境。

三、gdb调试常用指令

关于gdb调试的常用指令及介绍可以参考这里,http://blog.csdn.net/liwf616/article/details/46833107

Gdb环境下直接按下回车表示执行上一条命令
1. break 设置断点,
break10 设置断点,在源程序第10行
breakfunc 设置断点,在func函数入口处
infobreak 查看断点信息
2. run 运行程序,可简写为r
3. next 单步跟踪,函数调用当作一条简单语句执行,可简写为n
step 单步跟踪,函数调进入被调用函数体内,可简写为s
stepi 或si单步跟踪一条机器指令
nexti 或ni单步跟踪一条机器指令
4. continue 继续运行程序,可简写为c
5. print 打印变量、字符串、表达式等的值,可简写为p
p count 打印count的值
p cou1+cou2+cou3 打印表达式值
6. bt 查看函数堆栈
7. finish 退出函数
8. quit 退出GDB
9. shell 不退出GDB就使用shell命令
10. make <make-args>不退出GDB就重新编译程序
11. set args指定运行时参数。(如:set args10 20 30 40 50)
showargs查看设置好的运行参数。
12. path <dir>设定程序的运行路径。
showpaths 查看程序的运行路径。
13. set environment varname [=value] 设置环境变量。如:setenv USER=hchen
showenvironment [varname] 查看环境变量。
14. 其他
cd<dir> 相当于shell的cd命令。
pwd 显示当前的所在目录。
infoterminal 显示程序用到的终端模式。
tty指写输入输出的终端设备。如:tty /dev/ttyb
until在一个循环体内单步跟踪时,该命令运行程序到退出循环体。简写u
四、多线程调试
用gdb调试的好处是可以查看程序运行时的堆栈等信息,可以很直观的发现问题。
info threads //显示当前可调试的所有线程,每个线程前面有一个gdb为其分配的ID;
thread ID //切换到该ID对应的线程;
bt //显示该线程的堆栈信息
thread apply all bt //显示所有线程堆栈信息
break fun.c:123 thread ID //设置指定线程的断点
set scheduler-locking off|on|step //off表示不锁定任何线程,所有线程都执行;on只有当前线程执行;step在单步时除了next过一个函数外,只有当前线程执行

五、示例

当本目录文件wang大小超过2*1024时可能出现段错误(超过很大时,小时短时间不会出现),会进入segv_handler(),但不会触发gdb。

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SYSLOG_LINE_BUFFER_SIZE 2*1024#define BUFFER_SIZE 4*1024#define FILEN "wang"char bbuf[BUFFER_SIZE]={ 0};void m_syslog_dbg(char *format, ...){ va_list ptr; char buf[SYSLOG_LINE_BUFFER_SIZE] = { 0}; openlog("log", 0, LOG_DAEMON); // put log va_start(ptr, format); vsprintf(buf, format, ptr); va_end(ptr); syslog(LOG_DEBUG, "%s", buf); closelog(); return;}void segv_handler(int no) { printf("seg....\n");#if 1 void * array[10]; /* 25 层,太够了 : ),你也可以自己设定个其他值 */ char **strings; int nSize = backtrace(array, sizeof(array)/sizeof(array[0]));for (int i=nSize-1; i>=0; i--){ /* 头尾几个地址不必输出,看官要是好奇,输出来看看就知道了 */ /* 修正array使其指向正在执行的代码 */ // printf("SIGSEGV catched when running code at %x\n", (char*)array[i] - 1); printf("SIGSEGV catched when running code at %x\n", array[i]); }#if 1 strings = backtrace_symbols(array, nSize); printf("backtrace...%d\n", nSize); if(strings == NULL){ printf("strings NULL\n"); exit(EXIT_FAILURE); } for(int i=nSize-1; i>=0; i--){ printf("%s\n", strings[i]); } free(strings);#endif#endif#if 0 char buf[128]; char cmd[128]; FILE *file; pid_t pid = getpid(); snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); if((file = fopen(buf, "r")) == NULL){ exit(EXIT_FAILURE); } if(fgets(buf, sizeof(buf), file) == NULL ){ exit(EXIT_FAILURE); } if(buf[strlen(buf)-1] == '\n'){ buf[strlen(buf)-1] = '\0'; } snprintf(cmd, sizeof(cmd), "/usr/bin/gdb %s %d", buf, pid); printf("receive signo %d-->%s\n", no, cmd);// system(cmd);// while(1);#endif}int main(void ){ signal(SIGSEGV, segv_handler); struct stat st; int ret = 0; ret = stat(FILEN, &st); if(ret != 0){ perror("stat"); return -1; } int fd = open(FILEN, O_RDONLY); if(fd < 0){ printf("open error\n"); return -1; } //read(fd, bbuf, st.st_size); ret = read(fd, bbuf, sizeof(bbuf)); printf("file size = %d, %d\n", ret, getpid()); m_syslog_dbg("recevice [%s]\n", bbuf); printf("the end\n"); while(1){ sleep(1); printf("running...\n"); } close(fd); return 0;}

 

参考:

1. 

2. 

3. 

4. 

你可能感兴趣的文章
laravel package 推荐,数据备份
查看>>
Synchronized锁在Spring事务管理下,为啥还线程不安全?
查看>>
环境变量PATH cp命令 mv命令 文档查看cat/more/less/head/tail
查看>>
阿里云亮相2019联通合作伙伴大会,边缘计算等3款云产品助力5G时代产业数字化转型...
查看>>
dubbo源码分析-服务端发布流程-笔记
查看>>
阿里云发布Apsara SA系列混合云存储阵列
查看>>
GoJS教程:链接模版
查看>>
QListWidget方式显示缩略图
查看>>
金三银四:蚂蚁金服JAVA后端面试题及答案之二面
查看>>
Ubuntu 外网不通解决方案
查看>>
OSChina 周六乱弹 —— 历史总是惊人的相似
查看>>
MySQL 大小写
查看>>
Lync 2013部署图片赏析-证书服务安装配置
查看>>
HTML5 本地缓存 (web存储)
查看>>
tomcat redis session共享(包含redis安全设置)
查看>>
iptables中DNAT、SNAT和MASQUERADE的作用
查看>>
kvm命令学习记录
查看>>
小菜鸡进阶之路-First week
查看>>
ORACLE 10g SYSAUX表空间快速增长之WRH$_ACTIVE_SESSION_HISTORY篇
查看>>
我的友情链接
查看>>