成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何使用Backtrace定位Linux程序的崩潰位置

系統(tǒng) Linux
本篇介紹了如何使用backtrace工具來定位Linux應(yīng)用程序崩潰的位置信息,首先通過signal捕獲崩潰信息,然后通過backtrace記錄崩潰時(shí)的堆棧調(diào)用信息,最后使用addr2line來顯示對(duì)應(yīng)的崩潰時(shí)的代碼行號(hào)。?

在嵌入式Linux開發(fā)中,特別是復(fù)雜軟件,多人協(xié)作開發(fā)時(shí),當(dāng)某人無意間寫了一個(gè)代碼bug導(dǎo)致程序崩潰,但又不知道崩潰的具體位置時(shí),單純靠走讀代碼,很難快速的定位問題。

本篇就來介紹一種方法,使用backtrace工具,來輔助定位程序崩潰的位置信息。

backtrace是 C/C++ 中用于獲取程序調(diào)用棧信息的函數(shù),借助backtrace可以排查崩潰并定位代碼行號(hào)。

1.backtrace分析程序崩潰的原理

在linux系統(tǒng)中,運(yùn)行程序若發(fā)生崩潰,會(huì)產(chǎn)生相應(yīng)的信號(hào),例如訪問空指針會(huì)觸發(fā)SIGSEGV(signum:11)。

這時(shí)可以使用signal函數(shù)來捕獲這個(gè)信息,捕獲信號(hào)后,支持自定義的handler函數(shù)進(jìn)行一些處理。

在自定義的handler函數(shù)中,可以使用backtrace函數(shù),來打印程序調(diào)用棧信息。

最后使用addr2line函數(shù),將地址轉(zhuǎn)換為可讀的函數(shù)名和行號(hào)。

使用backtrace分析程序崩潰,需要在編譯時(shí)使用 -g 選項(xiàng)生成的調(diào)試信息。

使用addr2line工具,將地址轉(zhuǎn)換為可讀的函數(shù)名和行號(hào),實(shí)例如下:

addr2line -e 程序名 -f -C 0x400526
# 輸出:
main
/path/to/main.c:42

2.一些要用到的函數(shù)

2.1 signal

2.1.1 函數(shù)原型

在 C 和 C++ 中,signal 函數(shù)用于設(shè)置信號(hào)處理方式。

其原型定義在 <signal.h> 頭文件中:

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

參數(shù)說明:

  • int signum:信號(hào)編號(hào)(整數(shù)),如:

SIGINT(2):中斷信號(hào)(Ctrl+C)

SIGSEGV(11):段錯(cuò)誤

SIGILL(4):非法指令

SIGTERM(15):終止信號(hào)

SIGFPE(8):浮點(diǎn)異常

  • sighandler_t handler:信號(hào)處理函數(shù)指針,有三種取值:
  • 用戶定義函數(shù):void handler(int signum) 類型的函數(shù)
  • SIG_DFL:默認(rèn)處理(如終止程序)
  • SIG_IGN:忽略該信號(hào)

返回值:

  • 成功:返回之前的信號(hào)處理函數(shù)指針
  • 失敗:返回 SIG_ERR,并設(shè)置 errno(如 EINVAL 表示無效信號(hào))

2.1.2 常見信號(hào)列表

signum

信號(hào)名稱

默認(rèn)行為

觸發(fā)場景

1

SIGHUP

終止程序

終端連接斷開(如 SSH 會(huì)話結(jié)束),或用戶登出時(shí)通知進(jìn)程重新加載配置

2

SIGINT

終止程序(Ctrl+C)

用戶在終端按下 Ctrl+C,請(qǐng)求中斷當(dāng)前進(jìn)程

3

SIGQUIT

終止程序并生成 Core 文件

用戶按下 Ctrl+\,通常用于強(qiáng)制退出并生成調(diào)試用的 Core 文件

4

SIGILL

終止程序并生成 Core 文件

進(jìn)程執(zhí)行非法指令(如無效的機(jī)器碼),通常由程序編譯錯(cuò)誤或硬件異常導(dǎo)致

5

SIGTRAP

終止程序并生成 Core 文件

觸發(fā)斷點(diǎn)陷阱(如調(diào)試器設(shè)置的斷點(diǎn)),用于程序調(diào)試時(shí)的中斷

6

SIGABRT

終止程序并生成 Core 文件

通常是由進(jìn)程自身調(diào)用 C標(biāo)準(zhǔn)函數(shù)庫 的 abort() 函數(shù)來觸發(fā)

7

SIGBUS

終止程序并生成 Core 文件

硬件總線錯(cuò)誤(如訪問未對(duì)齊的內(nèi)存地址,或內(nèi)存映射文件錯(cuò)誤)

8

SIGFPE

終止程序并生成 Core 文件

發(fā)生算術(shù)錯(cuò)誤(如除零、溢出、精度錯(cuò)誤),例如1/0運(yùn)算

9

SIGKILL

強(qiáng)制終止程序(不可捕獲)

系統(tǒng)或用戶發(fā)送kill -9命令

,用于強(qiáng)制終止無響應(yīng)的進(jìn)程,無法被忽略或處理

10

SIGUSR1

終止程序

用戶自定義信號(hào) 1,可由程序自定義處理邏輯(如日志刷新、狀態(tài)通知)

11

SIGSEGV

終止程序并生成 Core 文件

訪問無效內(nèi)存地址(如空指針解引用、越界訪問),是最常見的程序崩潰原因之一

12

SIGUSR2

終止程序

用戶自定義信號(hào) 2,用途與SIGUSR1類似,供程序開發(fā)者自由定義功能

13

SIGPIPE

終止程序

向已關(guān)閉的管道或套接字寫入數(shù)據(jù)(如 TCP 連接斷開后繼續(xù)發(fā)送數(shù)據(jù))

14

SIGALRM

終止程序

定時(shí)器超時(shí)(由alarm()setitimer()函數(shù)觸發(fā)),用于超時(shí)控制

15

SIGTERM

終止程序(可捕獲)

系統(tǒng)或用戶發(fā)送kill命令(默認(rèn)),請(qǐng)求進(jìn)程正常退出,程序可自定義處理邏輯

16

SIGSTKFLT

終止程序

棧溢出錯(cuò)誤(僅在某些架構(gòu)上存在,如 x86),通常與硬件相關(guān)的棧異常有關(guān)

17

SIGCHLD

忽略信號(hào)

子進(jìn)程狀態(tài)改變(如終止或暫停),父進(jìn)程可通過wait()系列函數(shù)獲取子進(jìn)程信息

18

SIGCONT

繼續(xù)運(yùn)行暫停的進(jìn)程

當(dāng)進(jìn)程被暫停(如SIGSTOP)后,用于恢復(fù)其執(zhí)行,默認(rèn)行為為繼續(xù)運(yùn)行

19

SIGSTOP

暫停進(jìn)程(不可捕獲)

系統(tǒng)或用戶發(fā)送kill -STOP命令,用于暫停進(jìn)程執(zhí)行,無法被忽略或處理

信號(hào)分類:

  • 不可捕獲信號(hào):無法通過signalsigaction修改處理方式,只能由系統(tǒng)強(qiáng)制控制。

SIGKILL(9)

SIGSTOP(19)

  • 用戶自定義信號(hào):可由程序自由定義處理邏輯,常用于進(jìn)程間通信或調(diào)試。

SIGUSR1(10)

SIGUSR2(12)

  • 異常信號(hào):通常由程序錯(cuò)誤(如內(nèi)存操作異常)觸發(fā),默認(rèn)會(huì)生成 Core 文件用于調(diào)試。

SIGBUS(7)

SIGSEGV(11)

  • ...

默認(rèn)行為的差異:

  • 多數(shù)信號(hào)的默認(rèn)行為是終止程序,但部分信號(hào)(如SIGCHLD)默認(rèn)會(huì)被忽略,而SIGCONT則用于恢復(fù)進(jìn)程運(yùn)行。

2.2 backtrace

在 C 和 C++ 中,backtrace 函數(shù)用于獲取當(dāng)前程序的調(diào)用堆棧信息,常用于調(diào)試和錯(cuò)誤處理。

其原型定義在 <execinfo.h> 頭文件中:

/* 獲取當(dāng)前調(diào)用堆棧中的函數(shù)地址 */
int backtrace(void **buffer, int size);
  • 參數(shù)

void **buffer:指向存儲(chǔ)函數(shù)地址的數(shù)組的指針。

int size:數(shù)組的最大元素?cái)?shù)(即最多獲取的堆棧幀數(shù))。

  • 返回值

成功:返回實(shí)際獲取的堆棧幀數(shù)(不超過 size)。

失敗:返回 0(極罕見,通常僅在內(nèi)存不足時(shí)發(fā)生)。

2.3 backtrace_symbols

/* 將函數(shù)地址轉(zhuǎn)換為可讀的字符串(如函數(shù)名、偏移量) */
char **backtrace_symbols(void *const *buffer, int size);
  • 參數(shù)

void *const *buffer:backtrace返回的函數(shù)地址數(shù)組

int size:backtrace返回的實(shí)際幀數(shù)

  • 返回值

成功:返回指向字符串?dāng)?shù)組的指針,每個(gè)元素對(duì)應(yīng)一個(gè)堆棧幀(需用 free() 釋放)

失敗:返回 NULL,并設(shè)置 errno

2.4 backtrace_symbols_fd

/* 將函數(shù)地址直接輸出到文件 */
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
  • 參數(shù)

void *const *buffer:同 backtrace_symbols

int size:同 backtrace_symbols

int fd:文件描述符(如 STDERR_FILENO),用于輸出結(jié)果

  • 返回值:無(直接輸出到文件)

3.實(shí)例代碼

3.1 主函數(shù)

//g++ -g test.cpp -o test
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <csignal>
#include <string.h>
#include <fcntl.h>
#include <vector>

//<---信號(hào)處理函數(shù)添加到這里

void TestFun()
{
    printf("[%s] in\n", __func__);
std::vector<int> a;
printf("[%s] a[1]=%d\n", __func__, a[1]);
}

int main()
{
    std::vector<int> vSignalType = {SIGILL, SIGSEGV, SIGABRT};                             
    for (int &signalType : vSignalType)
    {
        if (SIG_ERR == signal(signalType, SignalHandler))
        {
            printf("[%s] signal for signalType:%d err\n", __func__, signalType);
        }
    }

 TestFun();
return0;
}

3.2 信號(hào)處理函數(shù)

#define MAX_STACK_FRAMES 100

void SignalHandler(int signum)
{
    printf("[%s] signum:%d(%s)\n", __func__, signum, strsignal(signum));
    signal(signum, SIG_DFL); //恢復(fù)默認(rèn)行為

    // [backtrace] 獲取當(dāng)前調(diào)用堆棧中的函數(shù)地址
    void *buffer[MAX_STACK_FRAMES];
    size_t size = backtrace(buffer, MAX_STACK_FRAMES);
    printf("[%s] backtrace() return %zu address. Stack trace:\n", __func__, size);
    
    // [backtrace_symbols] 將函數(shù)地址轉(zhuǎn)換為可讀的字符串
    char **symbols = (char **) backtrace_symbols(buffer, size);
    if (symbols == NULL) 
    {
        printf("[%s] backtrace_symbols() null\n", __func__);
        return;
    }

    for (size_t i = 0; i < size; ++i)
    {
        printf("#%d %s\n", (int)i, symbols[i]); //打印每一個(gè)函數(shù)地址
    }
    free(symbols);
    
    // [backtrace_symbols_fd] 將函數(shù)地址直接輸出到文件
    int fd = open("backtrace.txt", O_CREAT | O_WRONLY, S_IRWXU | S_IRWXG | S_IRWXO);
    if (fd >= 0)
    {
        backtrace_symbols_fd(buffer, size, fd);
        close(fd);
    }
}

3.3 addr2line解析backtrace信息

#!/bin/sh

if [ $# -lt 2 ]; then
echo"example: myaddr2line.sh test backtrace.log"
exit 1
fi

BIN_FILE=$1
BACK_TRACE_FILE=$2

lines=$(cat $BACK_TRACE_FILE | grep ${BIN_FILE})
for line in${lines}; do
 addr=$(echo$line | awk -F '(''{print $2}' | awk -F ')''{print $1}')
 addr2line -e ${BIN_FILE} -C -f $addr
done

addr2line 是一個(gè)用于將程序地址(如內(nèi)存地址)轉(zhuǎn)換為源代碼位置(文件名和行號(hào))的工具。以下是其常用參數(shù)的詳細(xì)含義:

參數(shù)

含義

說明

-e

--exe=FILE

指定要分析的可執(zhí)行文件或共享庫(必選參數(shù))。

-p

--pretty-print

以更易讀的格式輸出信息(如添加換行和縮進(jìn))。

-C

--demangle[=style]

還原 C++ 符號(hào)名

(如將 _Z3foov 轉(zhuǎn)換為 foo())。

-i

--inlines

顯示內(nèi)聯(lián)函數(shù)的調(diào)用信息(包括原始函數(shù)和內(nèi)聯(lián)位置)。

-f

--functions

顯示函數(shù)名

(默認(rèn)僅顯示地址對(duì)應(yīng)的行號(hào))。

3.4 測(cè)試結(jié)果

圖片圖片

可以看到,定位到了test.cpp的50行為崩潰的位置,代碼中的vector a沒有賦值,直接訪問vector[1]將會(huì)崩潰。

具體的調(diào)用棧關(guān)系為:

  • main函數(shù),test.cpp的65行:調(diào)用的TestFun函數(shù)
  • TestFun函數(shù),test.cpp的50行:執(zhí)行的printf("[%s] a[1]=%d\n", __func__, a[1]);
  • SignalHandler函數(shù),test.cpp的20行:崩潰觸發(fā)的SIGSEGV信號(hào)被捕獲后,在SignalHandler函數(shù)中的backtrace被處理

SignalHandler函數(shù)中,通過backtrace_symbols打印的信息,與通過backtrace_symbols_fd保存在backtrace.txt文件中的信息,其實(shí)是一樣的:

圖片圖片

使用myaddr2line.sh腳本,可以方便打印所有的行號(hào)信息。

當(dāng)然也可以手動(dòng)使用addr2line來打印行號(hào)信息,只是效率較低。

另外,注意backtrace的地址,圓括號(hào) () 和 方括號(hào) [] 中的地址具有不同含義,分別對(duì)應(yīng) 符號(hào)表中的函數(shù)地址 和 實(shí)際執(zhí)行地址。

  • 圓括號(hào) (...) 中的地址

含義:函數(shù)內(nèi)部的 相對(duì)偏移量(相對(duì)于函數(shù)起始地址)

格式:函數(shù)名+0x偏移量

作用:指示崩潰發(fā)生在該函數(shù)的具體位置。

  • 方括號(hào) [...] 中的地址

含義:指令在 內(nèi)存中的實(shí)際地址(絕對(duì)地址)

格式:0xXXXXXXXX

作用:可直接用于 addr2line 等工具定位源代碼

但在本示例程序測(cè)試中,卻要使用圓括號(hào)中的地址,addr2line才能顯示行號(hào),這里有待再研究。

圖片圖片

4.總結(jié)

本篇介紹了如何使用backtrace工具來定位Linux應(yīng)用程序崩潰的位置信息,首先通過signal捕獲崩潰信息,然后通過backtrace記錄崩潰時(shí)的堆棧調(diào)用信息,最后使用addr2line來顯示對(duì)應(yīng)的崩潰時(shí)的代碼行號(hào)。

責(zé)任編輯:武曉燕 來源: 碼農(nóng)愛學(xué)習(xí)
相關(guān)推薦

2021-11-01 12:31:25

Go程序日志

2017-08-02 14:37:31

LinuxKdump內(nèi)核崩潰

2023-12-26 15:06:00

Linux內(nèi)核轉(zhuǎn)儲(chǔ)

2010-03-02 13:28:58

Linux setit

2014-07-25 11:45:29

Linuxlocate命令

2011-07-06 13:33:41

iPhone 模擬器 XCode

2021-09-06 09:10:31

Linuxncurses定位文本

2011-04-22 17:29:37

Linux網(wǎng)卡

2017-02-06 18:42:37

Linuxgdb程序

2024-10-10 09:55:51

JavaScript參數(shù)瀏覽器

2010-06-02 10:42:39

Linux mysql

2015-11-26 10:53:45

LinuxWindowsMac OS

2013-06-03 09:34:14

崩潰程序程序算法

2025-02-27 09:17:41

DeepSeekPythonAI

2021-03-06 21:21:11

STM32單片機(jī)追蹤庫

2018-11-13 12:52:50

Linux內(nèi)核棧回溯

2021-04-01 11:28:44

C++ LinuxWindows

2019-09-29 00:25:11

CC++內(nèi)存泄漏

2023-03-19 17:29:01

2025-07-09 02:00:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲在线一区 | 亚洲精品一区二区三区蜜桃久 | 热久久久久 | 少妇一级淫片免费观看 | 国产一区二区在线免费 | 免费av网站在线观看 | 午夜免费视频 | 黄色网在线 | 久久久精 | 免费成人黄色 | 亚洲高清免费视频 | 日日干视频 | 日韩欧美在线观看视频 | 在线国产小视频 | 欧美福利影院 | 日韩免费高清视频 | 国产91av在线 | 成人性生活免费视频 | 亚洲福利专区 | 国产成年妇视频 | 精品久久久久久久久久久 | 国产精品欧美精品 | 欧美黑人一区二区三区 | 中文字幕国产在线 | 国产精品视频网站 | 午夜在线影院 | 日本黄a三级三级三级 | 国产中文 | 97成人在线 | 亚洲第一天堂网 | 欧美久久一区 | 91在线精品秘密一区二区 | 国产日韩欧美亚洲 | 日本一级淫片色费放 | 国产视频导航 | 青青草91 | 一级大片免费看 | 国内精品一区二区三区 | 免费毛片网站 | 99视频网 | 国产人妖在线 |