【linux 段错误跟踪-addr2line&nm】

本文介绍了如何在Linux项目中遇到偶发段错误时,通过编写backtrace功能并结合addr2line工具,以及利用nm命令来高效定位问题。重点讲解了nm命令的作用和addr2line在调试中的应用,以及在特定编译选项下解决backtrace返回0的问题和通过地址定位崩溃源代码的方法。
摘要由CSDN通过智能技术生成

前言:

平时出现段错误多数是必现的,自己通常通过打印日志来定位,最近项目中出现一个偶现的段错误,由于没有方向加日志的方法来复现效率太低,所以痛定思痛决定搞一个高效跟踪段错误的手段,经过大量查阅资料,写了一个backtrace的功能加入项目,利用addr2line或nm从而高效定位段错误的问题!!!

必要知识介绍:

nm 是一个 Linux 系统下的命令行工具,用于显示二进制目标文件(可执行文件或库文件)中的符号(Symbol),包括函数和变量名等。

nm 可以用于检查二进制文件的符号表信息,它可以列出二进制文件中包含的所有符号、这些符号所处的节(节是编译后二进制文件中包含的一个段)以及它们的类型(如函数、变量等)。nm 常用于调试和检查程序的二进制文件。

nm 命令的常见用法如下:

nm <file>:查看文件(如可执行文件或库文件)中包含的所有符号。

nm -u <file>:列出未定义符号的名称,这些符号是需要在连接时动态解析的符号,通常需要通过静态或共享库来定义。

nm -g <file>:只列出全局符号的名称,全局符号是可被其他模块所引用的符号,多次定义时会引发重定义错误。

nm -D <file>:只列出动态符号的名称,动态符号是在动态链接时解析的符号,如共享库中导出的符号。

nm -C <file>:将 C++ 操作符和函数名还原为可读的形式。

T:该符号是一个函数,并且是在该文件中定义的。

t:该符号是一个函数,并且是在该文件中局部定义的(即只能在该文件中访问)。

D:该符号是一个已初始化的全局变量或静态变量,并且是在该文件中定义的。

d:该符号是一个已初始化的局部变量或静态变量,并且是在该文件中定义的。

B:该符号是一个未初始化的全局变量或静态变量,并且是在该文件中定义的。

b:该符号是一个未初始化的局部变量或静态变量,并且是在该文件中定义的。

U:该符号是一个未定义的符号,需要在链接时解析,通常是在其他文件或库文件中被定义的符号

add2line
addr2line是一个GNU调试工具,用于将程序计数器(PC)地址转换为对应的源文件名、函数名和行号。addr2line可通过调试信息,将内存地址映射到源代码行号,并在开发人员调试应用程序时帮助找到问题所在。addr2line通常与交叉编译器一起使用,用于在代码嵌入式或远程设备上汇报错误的处理信息。

其中,参数说明如下:

-C: 此选项用于修复函数名。addr2line可以对编译器生成的能够适应C++的名称进行反向演绎,使名称更易于读取和理解。
-f: 此选项用于输出函数名称。
-p: 此选项用于调解指针地址为C++名称。
-H: 此选项用于输出全部文件名、函数名称、和行号。
-s: 此选项用于输出文件名和行号。
-v: 此选项用于详细输出,比如唯一的文件和否则可能无法报告的错误。
-V: 此选项用于输出程序版本信息。
-e filename: 此选项用于指定可执行文件。
-j section: 此选项用于指定要查找的节。
-a address: 此选项用于指定要查找的内存地址

实例代码:

#include "mg_backtrace.h"
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

#define BACKTRACE_MAX_NUM 32    //嵌套数,大型项目一般也不会超过50个,32个够用

void mg_signal_handle(int sig)
{
    int i         = 0;
    void *buf[32] = {0};

    size_t size = backtrace(buf, BACKTRACE_MAX_NUM);
    printf("sig = %d, size:%d!!!!!\n", sig, size);
    /*
    说明:
    1. backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组。

    2. 参数buffer应该是从backtrace函数获取的指针数组,size是该数组中的元素个数(backtrace的返回值)。

    3. 函数返回值是一个指向字符串数组的指针,它的大小同buffer相同。每个字符串包含了一个相对于
       buffer中对应元素的可打印信息。它包括函数名、函数的偏移地址、实际的返回地址。

    4. 目前,只有使用ELF二进制格式的程序才能获取函数名称和偏移地址。在其他系统,只有16进制的返回地址能被
       获取。另外,你可能需要传递相应的符号给链接器,以能支持函数名功能。(比如,在使用GNU ld链接器的系统
       中,器支持-rdynamic的话,建议将其加上!)

    5. 该函数的返回值是通过malloc函数申请的空间,因此调用者必须使用free函数来释放指针。

    6. 如果不能为字符串获取足够的空间函数的返回值将会为NULL。
    */

    char **strings = backtrace_symbols(buf, size);
    if (strings == NULL)
    {
        exit(EXIT_FAILURE);
    }
 
    for (i = 0; i < (int)size; i++)
    {
       printf("[%d]  %s\n", i, strings[i]);
       printf("backtrace : [%d]  %x\n", i, (unsigned int)buf[i]);
    }

    free(strings);
    exit(-1);

}

void mg_signal_register(void)
{
    signal(SIGSEGV, mg_signal_handle);
}
 

问题一:

编译后模拟尝试 结果 backtrace返回0

解决方案:

arm平台编译选项加上 -rdynamic -funwind-tables -ffunction-sections

问题二:

还不是最终崩溃的地方,第4和第5行是最开始的调用处

利用addr2line定位最终崩溃处,如没有addr2line,可以用nm定性分析,定位到崩溃函数内

addr2line 0x50250 -e /moorgen/bin/moorgenService/moorgenService -f

addr2line 0x4b510 -e /moorgen/bin/moorgenService/moorgenService -f

最终定位到shcpProtocol.c 504行就是段错误处!

跟踪结果体现了调用过程shcp_client_recv_cb->shcpSocketEvent->schpDataEvent->shcpConnectProc(504行最终崩溃的地方)

nm /moorgen/bin/moorgenService/moorgenService 

崩溃的地址0x4b510处于0x0004b4e8和0x0004e540之间则说明崩溃函数为shcpConnectProc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值