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

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

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

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

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

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

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

在linux系統中,運行程序若發生崩潰,會產生相應的信號,例如訪問空指針會觸發SIGSEGV(signum:11)。

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

在自定義的handler函數中,可以使用backtrace函數,來打印程序調用棧信息。

最后使用addr2line函數,將地址轉換為可讀的函數名和行號。

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

使用addr2line工具,將地址轉換為可讀的函數名和行號,實例如下:

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

2.一些要用到的函數

2.1 signal

2.1.1 函數原型

在 C 和 C++ 中,signal 函數用于設置信號處理方式。

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

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

參數說明:

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

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

SIGSEGV(11):段錯誤

SIGILL(4):非法指令

SIGTERM(15):終止信號

SIGFPE(8):浮點異常

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

返回值:

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

2.1.2 常見信號列表

signum

信號名稱

默認行為

觸發場景

1

SIGHUP

終止程序

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

2

SIGINT

終止程序(Ctrl+C)

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

3

SIGQUIT

終止程序并生成 Core 文件

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

4

SIGILL

終止程序并生成 Core 文件

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

5

SIGTRAP

終止程序并生成 Core 文件

觸發斷點陷阱(如調試器設置的斷點),用于程序調試時的中斷

6

SIGABRT

終止程序并生成 Core 文件

通常是由進程自身調用 C標準函數庫 的 abort() 函數來觸發

7

SIGBUS

終止程序并生成 Core 文件

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

8

SIGFPE

終止程序并生成 Core 文件

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

9

SIGKILL

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

系統或用戶發送kill -9命令

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

10

SIGUSR1

終止程序

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

11

SIGSEGV

終止程序并生成 Core 文件

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

12

SIGUSR2

終止程序

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

13

SIGPIPE

終止程序

向已關閉的管道或套接字寫入數據(如 TCP 連接斷開后繼續發送數據)

14

SIGALRM

終止程序

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

15

SIGTERM

終止程序(可捕獲)

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

16

SIGSTKFLT

終止程序

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

17

SIGCHLD

忽略信號

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

18

SIGCONT

繼續運行暫停的進程

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

19

SIGSTOP

暫停進程(不可捕獲)

系統或用戶發送kill -STOP命令,用于暫停進程執行,無法被忽略或處理

信號分類:

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

SIGKILL(9)

SIGSTOP(19)

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

SIGUSR1(10)

SIGUSR2(12)

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

SIGBUS(7)

SIGSEGV(11)

  • ...

默認行為的差異:

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

2.2 backtrace

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

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

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

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

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

  • 返回值

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

失?。悍祷?0(極罕見,通常僅在內存不足時發生)。

2.3 backtrace_symbols

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

void *const *buffer:backtrace返回的函數地址數組

int size:backtrace返回的實際幀數

  • 返回值

成功:返回指向字符串數組的指針,每個元素對應一個堆棧幀(需用 free() 釋放)

失?。悍祷?nbsp;NULL,并設置 errno

2.4 backtrace_symbols_fd

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

void *const *buffer:同 backtrace_symbols

int size:同 backtrace_symbols

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

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

3.實例代碼

3.1 主函數

//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>

//<---信號處理函數添加到這里

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 信號處理函數

#define MAX_STACK_FRAMES 100

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

    // [backtrace] 獲取當前調用堆棧中的函數地址
    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] 將函數地址轉換為可讀的字符串
    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]); //打印每一個函數地址
    }
    free(symbols);
    
    // [backtrace_symbols_fd] 將函數地址直接輸出到文件
    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 是一個用于將程序地址(如內存地址)轉換為源代碼位置(文件名和行號)的工具。以下是其常用參數的詳細含義:

參數

含義

說明

-e

--exe=FILE

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

-p

--pretty-print

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

-C

--demangle[=style]

還原 C++ 符號名

(如將 _Z3foov 轉換為 foo())。

-i

--inlines

顯示內聯函數的調用信息(包括原始函數和內聯位置)。

-f

--functions

顯示函數名

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

3.4 測試結果

圖片圖片

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

具體的調用棧關系為:

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

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

圖片圖片

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

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

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

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

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

格式:函數名+0x偏移量

作用:指示崩潰發生在該函數的具體位置。

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

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

格式:0xXXXXXXXX

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

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

圖片圖片

4.總結

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

責任編輯:武曉燕 來源: 碼農愛學習
相關推薦

2017-08-02 14:37:31

LinuxKdump內核崩潰

2021-11-01 12:31:25

Go程序日志

2023-12-26 15:06:00

Linux內核轉儲

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網卡

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參數瀏覽器

2013-06-03 09:34:14

崩潰程序程序算法

2021-04-01 11:28:44

C++ LinuxWindows

2018-11-13 12:52:50

Linux內核棧回溯

2020-07-15 08:11:05

Linuxc++程序

2023-03-19 17:29:01

2019-09-29 00:25:11

CC++內存泄漏
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产日韩欧美 | 风间由美一区二区三区在线观看 | 九九热在线视频观看这里只有精品 | 欧美日韩在线视频一区 | 91原创视频| 521av网站| 狠狠的日 | 中文字幕在线免费视频 | 日日摸日日添日日躁av | 99re6热在线精品视频播放 | 在线成人福利 | 国产高清一区二区三区 | 91成人在线| 国产一区二区三区在线免费观看 | 欧一区 | 一本一道久久a久久精品综合 | 精品久久久久久 | 超碰免费在线 | av在线一区二区三区 | 亚洲欧美国产精品一区二区 | 黄色骚片 | 国产精品久久精品 | 亚洲性网 | 欧美人妇做爰xxxⅹ性高电影 | 福利网站在线观看 | 国产精品久久久久久久久免费桃花 | 在线观看亚洲欧美 | 国产精品亚洲视频 | 一本一道久久a久久精品综合蜜臀 | 中文日韩在线 | 九九伦理片 | 国产一区在线免费 | 欧美视频1 | 久久成人亚洲 | 欧美日韩国产在线 | 一区二区视频在线 | 成人亚洲精品 | 国产精品精品视频一区二区三区 | 99精品在线观看 | 成人国产精品久久久 | 成人在线视频免费看 |