• objdump和backtrace的配合使用


    在程序调试过程中程序崩溃的情况时有发生,把出问题时的调用栈信息打印出来是一种不错的解决办法。

    当然还有一些其他方法:https://www.cnblogs.com/jiangyibo/p/8653720.html

     

    首先,介绍三个函数:

      1.int backtrace(void **buffer,int size);

        该函数用于获取当前线程的调用堆栈信息,信息被存放在buffer中,它是一个指针数组。

        参数size表示buffer中可以存放void*元素的个数,函数返回值是实际获取到的void*元素的个数。

     

      2.char **backtrace_symbols(void *const *buffer, int size);

        backtrace_symbols将backtrace函数获取的信息转化为一个字符串数组,参数buffer是从backtrace函数获取的指针数组,size是该数组中的元素个数(backtrace函数的返回值)。

        函数返回值是一个指向字符串数组的指针,它的大小同buffer相同。

        需要注意的是该函数返回的地址是通过malloc函数申请的空间,为了防止内存泄露,我们要手动调用free来释放这块内存。"free(函数返回的指针)"

      

      3.void backtrace_symbols_fd (void *const *buffer, int size, int fd);

        该函数与backtrace_symbols 函数功能类似,不同的是,这个函数直接把结果输出到文件描述符为fd的文件中,且没有调用malloc,不需要手动释放空间。

     

    测试用例:

      main.c

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 #include <execinfo.h>
     6 
     7 #define Size 128
     8 
     9 void fun(void)
    10 {
    11     int *piTest = NULL;
    12     *piTest = 2;
    13 }
    14 
    15 void signalHandler(int signalId)
    16 {
    17     int i = 0;
    18     int iNum = 0;
    19     void *pBuffer[Size] = {0};
    20     char **pszDebugInfo = NULL;
    21 
    22     iNum = backtrace(pBuffer, Size);
    23     pszDebugInfo = backtrace_symbols(pBuffer, iNum);
    24 
    25     if (pszDebugInfo == NULL)
    26     {
    27         perror("backtrace_symbols");
    28         exit(EXIT_FAILURE);         // 表示没有成功执行程序
    29     }
    30 
    31     for (i = 0; i < iNum; i++)
    32     {
    33         printf(" [%02d] %s
    ", i, pszDebugInfo[i]);
    34     }
    35 
    36     free(pszDebugInfo);
    37 
    38     signal(signalId, SIG_DFL);
    39 
    40     raise(signalId);
    41 }
    42 
    43 int main(int argc, char *argv[])
    44 {
    45     // SIGSEGV是当一个进程执行了一个无效的内存引用,
    46     // 或发生段错误时发送给它的信号
    47     signal(SIGSEGV, signalHandler);
    48 
    49     fun();
    50 
    51     printf("----
    ");
    52 
    53     return 0;
    54 }

     

     

    gcc -g -rdynamic main.c -o main

      -g      "objdump"的参数"-l","-S"要求编译时使用了-g之类的调试编译选项。 

      -rdynamic  该参数是链接选项,不是编译选项。这主要是对可执行程序(elf)而言的,而编译动态库时,即使没有rdynamic选项,默认也会将非静态函数放入动态符号表中(刻意隐藏的函数除外)。默认情况下,可执行程序(非动态库)文件内我们定义的非静态函数,是不放到动态符号表中的,链接时只有加上"-rdynamic"才能将所有非静态函数加到动态符号表中。

    ./main

    objdump -S -l ./main > info.txt

      objdump命令是用查看目标文件或者可执行的目标文件的构成的gcc工具。

      -l    --line-numbers 
      用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。

      -S    --source 
      尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。 

    上图可以看到发生了段错误,从下往上可以看到错误大概是发生在"fun"函数,然后打开info.txt,查找有关地址"0a16"的行号:
      vim info.txt
      命令模式:/0a16

      可以看到"0a16"所在"main.c"的12行,而12号正好是"*piTemp = 2;"。

      这里只举例了可执行文件,同理的动态库(记得加-g)也可以按照这个办法来查找错误,这里就不细说了。

  • 相关阅读:
    c语言 ctype.h中的函数
    sizeof 用法
    [LeetCode] Permutations 解题报告
    [LeetCode] Permutations II 解题报告
    [LeetCode] Next Permutation 解题报告
    [LeetCode] Path Sum II 解题报告
    [LeetCode] Palindrome Number 解题报告
    [LeetCode] Minimum Window Substring 解题报告
    [LeetCode] Partition List 解题报告
    [LeetCode] Pascal's Triangle II 解题报告
  • 原文地址:https://www.cnblogs.com/jiangyibo/p/9507555.html
Copyright © 2020-2023  润新知