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

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

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

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

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

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

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

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

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

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

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

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

使用addr2line工具,將地址轉(zhuǎn)換為可讀的函數(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è)置信號處理方式。

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

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

參數(shù)說明:

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

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

SIGSEGV(11):段錯誤

SIGILL(4):非法指令

SIGTERM(15):終止信號

SIGFPE(8):浮點異常

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

返回值:

  • 成功:返回之前的信號處理函數(shù)指針
  • 失?。悍祷?nbsp;SIG_ERR,并設(shè)置 errno(如 EINVAL 表示無效信號)

2.1.2 常見信號列表

signum

信號名稱

默認行為

觸發(fā)場景

1

SIGHUP

終止程序

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

2

SIGINT

終止程序(Ctrl+C)

用戶在終端按下 Ctrl+C,請求中斷當前進程

3

SIGQUIT

終止程序并生成 Core 文件

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

4

SIGILL

終止程序并生成 Core 文件

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

5

SIGTRAP

終止程序并生成 Core 文件

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

6

SIGABRT

終止程序并生成 Core 文件

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

7

SIGBUS

終止程序并生成 Core 文件

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

8

SIGFPE

終止程序并生成 Core 文件

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

9

SIGKILL

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

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

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

10

SIGUSR1

終止程序

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

11

SIGSEGV

終止程序并生成 Core 文件

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

12

SIGUSR2

終止程序

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

13

SIGPIPE

終止程序

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

14

SIGALRM

終止程序

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

15

SIGTERM

終止程序(可捕獲)

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

16

SIGSTKFLT

終止程序

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

17

SIGCHLD

忽略信號

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

18

SIGCONT

繼續(xù)運行暫停的進程

當進程被暫停(如SIGSTOP)后,用于恢復(fù)其執(zhí)行,默認行為為繼續(xù)運行

19

SIGSTOP

暫停進程(不可捕獲)

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

信號分類:

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

SIGKILL(9)

SIGSTOP(19)

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

SIGUSR1(10)

SIGUSR2(12)

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

SIGBUS(7)

SIGSEGV(11)

  • ...

默認行為的差異:

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

2.2 backtrace

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

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

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

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

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

  • 返回值

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

失?。悍祷?0(極罕見,通常僅在內(nèi)存不足時發(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ù)組的指針,每個元素對應(yīng)一個堆棧幀(需用 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é)果

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

3.實例代碼

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>

//<---信號處理函數(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 信號處理函數(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ù)默認行為

    // [backtrace] 獲取當前調(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]); //打印每一個函數(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 是一個用于將程序地址(如內(nèi)存地址)轉(zhuǎn)換為源代碼位置(文件名和行號)的工具。以下是其常用參數(shù)的詳細含義:

參數(shù)

含義

說明

-e

--exe=FILE

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

-p

--pretty-print

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

-C

--demangle[=style]

還原 C++ 符號名

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

-i

--inlines

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

-f

--functions

顯示函數(shù)名

(默認僅顯示地址對應(yīng)的行號)。

3.4 測試結(jié)果

圖片圖片

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

具體的調(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信號被捕獲后,在SignalHandler函數(shù)中的backtrace被處理

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

圖片圖片

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

當然也可以手動使用addr2line來打印行號信息,只是效率較低。

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

  • 圓括號 (...) 中的地址

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

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

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

  • 方括號 [...] 中的地址

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

格式:0xXXXXXXXX

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

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

圖片圖片

4.總結(jié)

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

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

2017-08-02 14:37:31

LinuxKdump內(nèi)核崩潰

2021-11-01 12:31:25

Go程序日志

2023-12-26 15:06:00

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

2011-07-06 13:33:41

iPhone 模擬器 XCode

2010-03-02 13:28:58

Linux setit

2014-07-25 11:45:29

Linuxlocate命令

2021-09-06 09:10:31

Linuxncurses定位文本

2011-04-22 17:29:37

Linux網(wǎng)卡

2025-02-27 09:17:41

DeepSeekPythonAI

2015-11-26 10:53:45

LinuxWindowsMac OS

2021-03-06 21:21:11

STM32單片機追蹤庫

2010-06-02 10:42:39

Linux mysql

2017-02-06 18:42:37

Linuxgdb程序

2024-10-10 09:55:51

JavaScript參數(shù)瀏覽器

2013-06-03 09:34:14

崩潰程序程序算法

2021-04-01 11:28:44

C++ LinuxWindows

2018-11-13 12:52:50

Linux內(nèi)核棧回溯

2020-07-15 08:11:05

Linuxc++程序

2023-03-19 17:29:01

2019-09-29 00:25:11

CC++內(nèi)存泄漏
點贊
收藏

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