• gdb调试段错误及使用


    在编程调试中,经常出现段错误,此时可用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 ] == '
    ')
    { 
    buf[strlen(buf) -1] = '';
    } 
    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 <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <syslog.h>
    #include <fcntl.h>
    #include <stdarg.h>
    #include <sys/stat.h>
    #include <signal.h>
    #include <string.h>
    #include <execinfo.h>
    
    #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....
    ");
    #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
    ", (char*)array[i] - 1);
            printf("SIGSEGV catched when running code at %x
    ", array[i]);
    
        }
    #if 1
        strings = backtrace_symbols(array, nSize);
        printf("backtrace...%d
    ", nSize);
        if(strings == NULL){
            printf("strings NULL
    ");
            exit(EXIT_FAILURE);
        }
    
        for(int i=nSize-1; i>=0; i--){
            printf("%s
    ", 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] == '
    '){
            buf[strlen(buf)-1] = '';
        } 
    
        snprintf(cmd, sizeof(cmd), "/usr/bin/gdb %s %d", buf, pid);
        printf("receive signo %d-->%s
    ", 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
    ");
            return -1;
        }
    
    
        //read(fd, bbuf, st.st_size);
        ret = read(fd, bbuf, sizeof(bbuf));
        printf("file size = %d, %d
    ", ret, getpid());
        m_syslog_dbg("recevice [%s]
    ", bbuf);
    
        printf("the end
    ");
        while(1){
            sleep(1);
            printf("running...
    ");
        }
    
        close(fd);
        return 0;
    }

    参考:

    1. backtrace、backtrace_symbols、backtrace_symbols_fd-support for application self-debugging

    2. linux/unix 段错误捕获_转

    3. C/C++捕获段错误,打印出错的具体位置(精确到哪一行)_转

    4. gdb调试段错误及使用

  • 相关阅读:
    诡异的Integer
    你已经创建了多少个对象?
    静态方法中使用非静态化数据
    静态化初始块的执行顺序
    java中的多构造函数以及类字段的初始化顺序
    java中类的构造方法出错点
    程序员修炼之道读后感
    纯随机数发生器,以及函数重载的问题
    递归判断回文
    使用jQuery操作DOM元素
  • 原文地址:https://www.cnblogs.com/embedded-linux/p/6081824.html
Copyright © 2020-2023  润新知