偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

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

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

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

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

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

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

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

在自定義的handler函數(shù)中,可以使用backtrace函數(shù),來(lái)打印程序調(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ù)說(shuō)明:

  • 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ù)指針
  • 失?。悍祷?nbsp;SIG_ERR,并設(shè)置 errno(如 EINVAL 表示無(wú)效信號(hào))

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

signum

信號(hào)名稱

默認(rèn)行為

觸發(fā)場(chǎng)景

1

SIGHUP

終止程序

終端連接斷開(kāi)(如 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í)行非法指令(如無(wú)效的機(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ù)庫(kù) 的 abort() 函數(shù)來(lái)觸發(fā)

7

SIGBUS

終止程序并生成 Core 文件

硬件總線錯(cuò)誤(如訪問(wèn)未對(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)制終止無(wú)響應(yīng)的進(jìn)程,無(wú)法被忽略或處理

10

SIGUSR1

終止程序

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

11

SIGSEGV

終止程序并生成 Core 文件

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

12

SIGUSR2

終止程序

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

13

SIGPIPE

終止程序

向已關(guān)閉的管道或套接字寫(xiě)入數(shù)據(jù)(如 TCP 連接斷開(kāi)后繼續(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)程可通過(guò)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í)行,無(wú)法被忽略或處理

信號(hào)分類:

  • 不可捕獲信號(hào):無(wú)法通過(guò)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ù)(不超過(guò) size)。

失?。悍祷?0(極罕見(jiàn),通常僅在內(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() 釋放)

失?。悍祷?nbsp;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é)果

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

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ù)

含義

說(shuō)明

-e

--exe=FILE

指定要分析的可執(zhí)行文件或共享庫(kù)(必選參數(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沒(méi)有賦值,直接訪問(wèn)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ù)中,通過(guò)backtrace_symbols打印的信息,與通過(guò)backtrace_symbols_fd保存在backtrace.txt文件中的信息,其實(shí)是一樣的:

圖片圖片

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

當(dāng)然也可以手動(dòng)使用addr2line來(lái)打印行號(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工具來(lái)定位Linux應(yīng)用程序崩潰的位置信息,首先通過(guò)signal捕獲崩潰信息,然后通過(guò)backtrace記錄崩潰時(shí)的堆棧調(diào)用信息,最后使用addr2line來(lái)顯示對(duì)應(yīng)的崩潰時(shí)的代碼行號(hào)。

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)愛(ài)學(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

2011-07-06 13:33:41

iPhone 模擬器 XCode

2014-07-25 11:45:29

Linuxlocate命令

2021-09-06 09:10:31

Linuxncurses定位文本

2011-04-22 17:29:37

Linux網(wǎng)卡

2021-03-06 21:21:11

STM32單片機(jī)追蹤庫(kù)

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

2025-02-27 09:17:41

DeepSeekPythonAI

2013-06-03 09:34:14

崩潰程序程序算法

2021-04-01 11:28:44

C++ LinuxWindows

2018-11-13 12:52:50

Linux內(nèi)核?;厮?/a>

2023-03-19 17:29:01

2019-09-29 00:25:11

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

2025-07-09 02:00:00

點(diǎn)贊
收藏

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