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

在嵌入式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:忽略該信號

返回值:

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

2.1.2 常見信號列表

signum信號名稱默認行為觸發場景
1SIGHUP終止程序終端連接斷開(如 SSH 會話結束),或用戶登出時通知進程重新加載配置
2SIGINT終止程序(Ctrl+C)用戶在終端按下 Ctrl+C,請求中斷當前進程
3SIGQUIT終止程序并生成 Core 文件用戶按下 Ctrl+\,通常用于強制退出并生成調試用的 Core 文件
4SIGILL終止程序并生成 Core 文件進程執行非法指令(如無效的機器碼),通常由程序編譯錯誤或硬件異常導致
5SIGTRAP終止程序并生成 Core 文件觸發斷點陷阱(如調試器設置的斷點),用于程序調試時的中斷
6SIGABRT終止程序并生成 Core 文件通常是由進程自身調用 C標準函數庫 的 abort() 函數來觸發
7SIGBUS終止程序并生成 Core 文件硬件總線錯誤(如訪問未對齊的內存地址,或內存映射文件錯誤)
8SIGFPE終止程序并生成 Core 文件發生算術錯誤(如除零、溢出、精度錯誤),例如1/0運算
9SIGKILL強制終止程序(不可捕獲)系統或用戶發送kill -9命令,用于強制終止無響應的進程,無法被忽略或處理
10SIGUSR1終止程序用戶自定義信號 1,可由程序自定義處理邏輯(如日志刷新、狀態通知)
11SIGSEGV終止程序并生成 Core 文件訪問無效內存地址(如空指針解引用、越界訪問),是最常見的程序崩潰原因之一
12SIGUSR2終止程序用戶自定義信號 2,用途與SIGUSR1類似,供程序開發者自由定義功能
13SIGPIPE終止程序向已關閉的管道或套接字寫入數據(如 TCP 連接斷開后繼續發送數據)
14SIGALRM終止程序定時器超時(由alarm()setitimer()函數觸發),用于超時控制
15SIGTERM終止程序(可捕獲)系統或用戶發送kill命令(默認),請求進程正常退出,程序可自定義處理邏輯
16SIGSTKFLT終止程序棧溢出錯誤(僅在某些架構上存在,如 x86),通常與硬件相關的棧異常有關
17SIGCHLD忽略信號子進程狀態改變(如終止或暫停),父進程可通過wait()系列函數獲取子進程信息
18SIGCONT繼續運行暫停的進程當進程被暫停(如SIGSTOP)后,用于恢復其執行,默認行為為繼續運行
19SIGSTOP暫停進程(不可捕獲)系統或用戶發送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() 釋放)
    • 失敗:返回 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();return 0;
}

3.2 信號處理函數

#define MAX_STACK_FRAMES 100void 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/shif [ $# -lt 2 ]; thenecho "example: myaddr2line.sh test backtrace.log"exit 1
fiBIN_FILE=$1
BACK_TRACE_FILE=$2lines=$(cat $BACK_TRACE_FILE | grep ${BIN_FILE})
for line in ${lines}; doaddr=$(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來顯示對應的崩潰時的代碼行號。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/89859.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/89859.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/89859.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

十大排序算法匯總

好的&#xff0c;下面為你整理一篇面試全覆蓋、極其深入的十大排序算法總結博客&#xff0c;涵蓋算法原理、復雜度、穩定性、應用場景、工程實踐、C與Python實現&#xff08;含詳細注釋&#xff09;&#xff0c;并對比分析各種排序的優缺點與適用情境。內容力求結構清晰、講解透…

零基礎 “入坑” Java--- 七、數組(二)

文章目錄 一、數組轉字符串二、數組的拷貝三、求數組中元素的平均值四、查找數組中指定元素&#xff08;順序查找&#xff09;五、數組排序&#xff08;冒泡排序&#xff09;六、查找數組中指定元素&#xff08;二分查找&#xff09;七、判斷兩個數組中的元素是否相等八、填充數…

【C++ 真題】P1104 生日

P1104 生日 題目描述 cjf 君想調查學校 OI 組每個同學的生日&#xff0c;并按照年齡從大到小的順序排序。但 cjf 君最近作業很多&#xff0c;沒有時間&#xff0c;所以請你幫她排序。 輸入格式 輸入共有 n 1 n 1 n1 行&#xff0c; 第 1 1 1 行為 OI 組總人數 n n n&…

Oracle DB和PostgreSQL,OpenGauss主外鍵一致性的區別

針對于unique索引在主外鍵上的表現&#xff0c;o和PG的行為確實不一致&#xff0c;測試樣例&#xff1a;PG:測試1&#xff1a;test# CREATE TABLE gdb_editingtemplates ( objectid INTEGER NOT NULL, globalid VARCHAR(38) DEFAULT {00000000-0000-0000-0000-000000000000} …

06.自動化測試概念

自動化測試概念 1. 自動化1.1 回歸測試1.2 自動化分類 1.3 自動化測試金字塔2. web自動化測試3.Selenium 1. 自動化 ? **自動化測試&#xff08;Automated Testing&#xff09;&#xff1a;**是指使用軟件工具或腳本來自動執行測試任務&#xff0c;代替人工進行重復性、繁瑣的…

頁面登錄數據的加密(前端+后端)

本加密過程使用的 AESRSA概要1.使用AES對傳輸數據進行加密AES為對稱加密,加密和解決所需要的key是一樣的,所以攔截到AES key就可以直接解密,所以需要結果RSA進行加密2.對AES的key進行RSA加密RSA為非對稱加密,客戶端只能獲取到publicKey(公鑰),而解密只能使用服務器的privateKey…

PC端基于SpringBoot架構控制無人機(一):初識無人機控制

一、無人機飛控系統的概述飛控&#xff08;Flight Controller&#xff09;是無人機最為核心的組成部分之一&#xff0c;負責實現無人機的自主飛行控制和穩定飛行。飛控系統的功能決定了無人機的飛行性能&#xff0c;包括飛行的穩定性、操控的響應速度、導航的精確度等。通過飛控…

QT6 源(154)模型視圖架構里的列表視圖 QListView:先學習屬性部分,

&#xff08;1&#xff09;屬性總圖&#xff0c;以及測試程序的框架 &#xff1a; 開始屬性的學習 &#xff1a; &#xff08;2&#xff09; 繼續屬性學習 &#xff1a; &#xff08;3&#xff09; 謝謝

MySQL——9、事務管理

事務管理 1、什么是事務&#xff1f;2、事務常見操作方式3、事務隔離級別4、數據庫并發場景4.1、讀-寫4.2、RR與RC的本質區別 1、什么是事務&#xff1f; mysql是基于CS模式的&#xff0c;是一套網絡服務&#xff0c;所以我們是可以在本地連接上遠程服務器的mysql服務端的。my…

Python之面向對象詳解(一篇足矣)

目錄 一、初階面向對象 1. 初識面向對象 1.1 對象和self 1.2 常見成員 1.3 應用示例 將數據封裝到一個對象&#xff0c;便于以后使用。 將數據封裝到對象中&#xff0c;在方法中對原始數據進行加工處理。 根據類創建多個對象&#xff0c;在方法中對對象中的數據進行修改…

【Qt】qml組件對象怎么傳遞給c++

將QML組件對象傳遞給C的方法 在QML和C之間傳遞完整的組件對象需要特殊處理&#xff0c;因為QML組件是動態創建的JavaScript對象。以下是幾種有效的方法&#xff1a; 1. 使用QObject指針傳遞 C端設置 // MyClass.h #include <QObject> #include <QQuickItem>cla…

Java基礎 集合框架 List框架

list架構 list接口list 核心特性以及擴展Collection的體現 抽象類 AbstractList抽象類 AbstractSequentialList (簡化鏈表的順序訪問)AbstractSequentialList 核心特點自定義實現示例代碼講解其實現原理AbstractSequentialList 總結與AbstractList的對比 List 實現類 ArrayList…

2025年6月28和29日復習和預習(C++)

學習筆記大綱?一、預習部分&#xff1a;數組基礎?&#xff08;一&#xff09;核心知識點?數組的創建&#xff1a;掌握一維數組的聲明方式&#xff0c;如int arr[5];&#xff08;創建一個包含 5 個整數的數組&#xff09;。重點在于理解數組長度需為常量&#xff0c;且在聲明…

【centos8服務如何給服務器開發3306端口】

在 CentOS 8 中開放 MySQL 默認端口 3306&#xff0c;需要配置防火墻和 SELinux。以下是詳細步驟&#xff1a; 1. 開放防火墻端口&#xff08;Firewalld&#xff09; CentOS 8 默認使用 firewalld 管理防火墻&#xff0c;執行以下命令開放 3306 端口&#xff1a; # 開放 TCP 33…

python系列之:使用md5和sha256完成簽名認證,調用接口

python系列之:使用md5和sha256完成簽名認證,調用接口 MD5簽名和sha256簽名認證md5認證代碼sha256認證代碼拼接簽名生成簽名拼接url調用接口MD5簽名和sha256簽名認證 MD5簽名認證 算法特性: 生成128位(16字節)的哈希值計算速度快已被證明存在碰撞漏洞(不同輸入可能產生相同…

SpringBatch配置與入門實例

通過對SpringBatch基礎概念的了解&#xff0c;參考&#xff1a;SpringBatch使用介紹 任何技術用起來之后&#xff0c;再去探究內部細節的原理&#xff0c;才會事半功倍。下面記錄一下筆者在SpringBoot項目中集成SpringBatch&#xff0c;并且通過一個小的實例展示如何簡單使用它…

spdlog 項目介紹與二次封裝

目錄 介紹 二次封裝 介紹 spdlog 是C開源的第三方日志庫&#xff0c;整個項目在 spdlog 命名空間中。 在 spdlog 命名空間的 level 命名空間里定義了枚舉類型&#xff0c;把日志分為了 5 個等級&#xff1a;trace debug info warn err critical enum level_enum : in…

shell編程之awk命令詳解

1. awk 教程 1.1 調用 awk awk 是一種強大的文本處理工具&#xff0c;在 Linux 系統中廣泛應用于日志分析、數據處理等場景。調用 awk 主要有以下三種方式&#xff1a; 1.1.1 命令行方式 基本語法為&#xff1a; awk (-F filed-separator) commands input-files其中&#…

服務器需要備案嗎?在哪些地區需要備案?

&#x1f3af; 服務器是否需要備案&#xff1f; 是否需要備案&#xff0c;關鍵看以下兩個因素&#xff1a; 服務器所在地&#xff08;機房位置&#xff09; 網站面向的訪問群體&#xff08;境內或境外&#xff09; &#x1f3f7; 中國大陸&#xff08;境內&#xff09;服務器…

HarmonyOS學習3---ArkUI

1、組件 1.1、基礎組件 1.2、布局容器 1.3、頁面導航 1.4、其他組件 2、ArkTs/C混合開發&#xff0c;高性能編碼 3、布局能力&交互歸一 4、實時開發預覽