GDB與Valgrind ,調試C++代碼內存的工具

2021-03-02 C語言與C++編程

來自:HappenLee

連結:https://www.cnblogs.com/happenlee/p/9931423.html

寫 C++的同學想必有太多和內存打交道的血淚經驗了,常常被 C++的內存問題攪的焦頭爛額。(寫 core 的經驗了)有很多同學一見到 core 就兩眼一抹黑,不知所措了。筆者 入"坑"C++之後,在調試 C++代碼的過程之中,學習了不少調試代碼內存的工具。希望借這個機會來介紹一下筆者常用的工具,GDB,Valgrind等等,相信大家通過好好運用這些工具,能更好的馴服內存這匹"野馬"。

1、利用 GDB 調試 CoreDump

CoreDump時一個二進位的文件,進程發生錯誤崩潰時,內核會產生一個瞬時的快照,記錄該進程的內存、運行堆棧狀態等信息保存在core文件之中。做個簡單的類比,core 文件相當於飛機運行時的"黑匣子",能夠幫助我們更好的調試 C++程序的問題。OK,接下來筆者將介紹一下如果利用GDB 來調試 CoreDump的文件。

首先我們先確定一下作業系統是否會產生 CoreDump 文件。通過ulimit -c獲取 core 文件的限制大小:

上面顯示筆者電腦的 core 文件的大小是0,我們需要調整一下。通過ulimit調整為無限制。當然這種調整是臨時的,reboot 之後就恢復為0了。

ulimit -c ulimited

如果需要永久修改,可以通過/etc/security/limits.conf 來修改 core 文件的大小。

#include <unistd.h>
#include <thread>

void core() {
    char* ch = nullptr;
    *ch = 'a';
}

int main() {
    auto t1 = std::thread(core);
    sleep(5);
    return 0;
}

core 文件列出了兩個線程的信息。我們需要判斷對應的問題代碼的定位,接下來我們一起來梳理一下:

用info thread查看線程的運行情況,在這裡我們就可以判斷代碼 core 在什麼線程之中了,如果還是無法確定,可以通過thread apply all bt列出更加詳盡的堆棧信息。

通過上述信息可以確認,thread 1的代碼存在問題。我們通過thread 1切換到 thread 1,用bt顯示堆棧信息繼續追查:

之後我們來看看令人生疑的棧內容,這裡顯然棧0是我們懷疑的代碼,用frame 1查看。

好了,這裡我們找到了引起問題罪魁禍首的代碼,訪問了空指針。

小結

程序運行的 core 文件是我們調試代碼十分重要依據,通過 GDB 可以很好的給出我們修改代碼的線索和參考,熟悉掌握GDB 的調試技巧,能夠大大解放我們調試問題代碼的生產力。

2、利用Valgrind判斷內存洩露

亡羊補牢不如未雨綢繆,與其等到出現程序崩潰時使用 GDB 來調試解決,不如事前確認代碼之中可能引發的問題。所以筆者接下來要介紹一款來自大不列顛的C++代碼分析神器:Valgrind。(Valgrind的作者也通過開發Valgrind獲得了第二屆Google-O'Reilly開原始碼大獎~~~)


Valgrind 十分強大,適用於內存分析,洩漏檢測、鎖分析,性能評估。筆者也只掌握了一些基本的入門使用。希望這裡能夠拋磚引玉,更多複雜的用法煩請參考官方文檔http://valgrind.org/docs/manual/QuickStart.html。


Valgrind的安裝

Valgrind的安裝很簡單,筆者的發行版帶了對應的 deb 包。通過 apt-get 的包管理工具就可以直接安裝了,其他的發行版也可以作為參考。

sudo apt-get install valgrind


Valgrind的使用

與 GDB 類似,Valgrind 同樣推薦使用-g作為編譯參數。能夠更好的對代碼進行分析。這裡我們依舊使用之前的例子進行測試:

valgrind ./untitiled

下面是 Valgrind 的分析結果

這裡有顯示Invalid write of size 1,說明這裡有一個不合法的寫入,並且寫入了1個字節的內容。也就是指的是我們之前代碼之中寫入空指針的行為。

接下來我們要展示 Valgrind更加強大的功能。它展示了程序的內存使用情況,並且給出總結:


這裡列出了多種的內存洩露情況:

definitely lost:肯定的內存洩漏,這表示在程序退出時,有內存沒有回收,但是也沒有指針指向該內存。這種情況最為嚴重。

indirectly lost:間接的內存洩漏,如類之中定義的指針指向的內存沒有回收。這種情況和上述相同。

possibly lost: 可能出現內存洩漏。這種情況需要仔細排查,可能代碼沒有問題,也可能有異常的內存洩露。

still reachable:程序沒主動釋放內存,在退出時候該內存仍能訪問到。這種情況一般問題不大,因為程序退出之後作業系統會回收程序的內存,所以這種情況一般問題不大。

這裡沒有給出具體洩露的內容,需要加入參數--leak-check=full將完整的結果列印出來,會指出對應的引起內存洩露的具體代碼,可以繼續深入分析。

代碼調優

這裡進行代碼調優的時,需要利用qcachegrind來進行分析。首先筆者先進行安裝:

sudo apt-get install qcachegrind 

之後我們調用Valgrind來生成運行數據:

valgrind --tool=callgrind -v main(需要分析的程序)

運行之後在目錄下生成對應的分析數據,我們用qcachegrind 打開,這裡用的代碼是筆者之前實現的 SkipList。

qcachegrind callgrind.out.29235 

接下來我們來分析對應的結果

上圖顯示了各個函數的被調用的耗時百分比,我們可以選取對性能感興趣的函數來進行深入分析。我們下面繼續分析其中一個函數被調用和它使用函數的性能情況

所以通過上述數據,我們可以給出性能分析的證據和線索,依據這些信息來更好的優化我們代碼的性能。

3、小結

本文介紹了亡羊補牢的工具 GDB,也簡介了未雨綢繆的Valgrind 。通過上述工具對C++程序更加深入分析。工欲善其事,必先利其器,希望大家也能好好掌握這些提供生產力的工具,讓 C++不再惱人。

●編號408,輸入編號直達本文

●輸入m獲取文章目錄

分享C/C++技術文章

相關焦點

  • C/C++程序調試和內存檢測
    (2)取樣法:在程序中增加一些代碼,收集更多與程序運行時的行為相關的信息。使用條件編譯,可以清楚的辨別哪些是調試代碼,有利於調試後的代碼整理。這些信息包括符號和原始碼行號,調試器將利用這些信息向用戶顯示程序已經執行到的原始碼的位置。-g標誌是對程序調試性編譯時常用的一個選項。調試信息的加入使可執行程序的長度成倍的增長、容量增加,程序運行時的內存數量還是和原來一樣,程序調試結束後,最好還是將調試信息從程序的發行版中刪除。
  • 談談如何利用 valgrind 排查內存錯誤
    導讀Valgrind 最為開發者熟知和廣泛使用的工具莫過於 Memcheck,它是檢查 c/c++ 程序內存錯誤的神器,報告結果非常之精準。本文主要分享作者在使用該神器解決內存問題的過程中積累的一些實戰經驗,希望幫助你快速定位問題甚至在編碼階段就規避這些問題。
  • 用valgrind定位內存洩漏
    緣起年前,寫了使用mtrace定位內存洩漏,在留言中,有讀者提到了希望介紹valgrind,那好,今天就介紹使用valgrind定位內存洩漏。大約2-3年前,楊同學讓我幫做模擬面試,他求職的是C++崗位,我問了這樣一個問題:在你的項目中,你是如何定位內存洩漏的呢?
  • 【開源工具】推薦一款Linux下內存檢測工具:valgrind
    你的工作越接近系統,你就需要面對越多的內存問題。有時這些問題非常瑣碎, 常見的內存問題主要有下面幾種:內存方面的問題定位一直是個老大難,對於自己寫的demo來說,簡單去檢查代碼中malloc/new和free/delete的匹配對數就基本能定位到問題,但是對於數以萬甚至十萬行的工程時,僅靠肉眼檢查來定位問題那就非常困難了。所以我們需要利用工具幫助我們找出問題所在。
  • Linux平臺中調試C/C++內存洩漏方法 (騰訊和MTK面試的時候問到的)
    實際上 不同的系統都帶有內存監視工具,我們可以從監視工具收集一段時間內的堆棧內存信息,觀測增長趨勢,來確定是否有內存洩漏。在 Linux 平臺可以用 ps 命令,來監視內存的使用,比如下面的命令 (觀測指定進程的VSZ值):ps -aux2. 靜態分析包括手動檢測和靜態工具分析,這是代價最小的調試方法。
  • GDB調試實戰三完整的數據流程
    這裡需要注意的是,如果不調試了,要馬上停止這個進程,否則會產生大量的垃圾日誌。二、啟動調試按照上面兩篇文件的方法啟動啟動進程:「nohup ./gdbTest >1.log 2>&1 &」。
  • 使用gdb+python查看C/C++進程內存分配情況
    內存使用和管理在C/C++程序中是一個無法繞開的問題, 在gdb支持python script以後, 我們就可以使用gdb這個新的特性來幫助我們查看在glibc ptmalloc算法中管理的內存的情況。為了方便, 下面我們主要針對x64環境。在可以查看內存分配情況以前, 我們當然需要知道ptmalloc算法大致是一個什麼樣子的。
  • 原來gdb的底層調試原理這麼簡單
    「程序」描述的是一個靜態的概念,就是一堆數據躺著硬碟上,而「進程」描述的是動態的過程,是這個程序被讀取、加載到內存上之後,在作業系統中有一個任務控制塊(一個數據結構),專門用來管理這個進程的。鋪墊了半天,終於輪到主角登場了,那就是系統調用函數 ptrace(其中的參數後面會解釋),正是在它的幫助下,gdb 才擁有了強大的調試能力。函數原型是:
  • GDB 調試 .NET 程序實錄 - .NET 調用 .so 出現問題怎麼解決
    GC.EndNoGCRegion();工具調試經過提示,知道可以使用 GDB 調試 .so,於是馬上 Google 查找資料,經過一段時間後,學會了使用這些工具查詢異常堆棧信息。GDBGNU Debugger,也稱為 gdb,是用於 UNIX系統調試 C 和 C ++ 程序的最流行的調試器。
  • 如何通過wrap malloc定位C/C++程序的內存洩漏
    我們可以review代碼,但從海量代碼裡找到隱藏的問題,這如同大海撈針,往往兩手空空。所以,我們需要藉助工具,比如valgrind,但這些找內存洩漏的工具,往往對你使用動態內存的方式有某種期待,或者說約束,比如常駐內存的對象會被誤報出來,然後真正有用的信息會掩蓋在誤報的汪洋大海裡。
  • 用圖文帶你徹底弄懂GDB調試原理
    「程序」描述的是一個靜態的概念,就是一堆數據躺著硬碟上,而「進程」描述的是動態的過程,是這個程序被讀取、加載到內存上之後,在作業系統中有一個任務控制塊(一個數據結構),專門用來管理這個進程的。鋪墊了半天,終於輪到主角登場了,那就是系統調用函數ptrace(其中的參數後面會解釋),正是在它的幫助下,gdb才擁有了強大的調試能力。
  • Linux C/C++ 開發人員要熟練掌握 GDB 調試代碼塊
    一、啟動GDB調試使用 GDB 調試程序一般有三種方式: gdb filename gdb attach pid gdb filename corename1、直接調試目標程序2、附加進程3、調試 core
  • GDB調試還不會?看這篇就夠了!
    當然,想要獲取電子版的,也可以在公眾號【編程珠璣】後臺回復【GDB調試指南】,即可獲取PDF版本。啟動調試前言GDB(GNU Debugger)是UNIX及UNIX-like下的強大調試工具,可以調試ada, c, c++, asm, minimal, d, fortran, objective-c, go, java,pascal等語言。
  • GDB入門教程之如何使用GDB啟動調試
    對於本地的某個二進位文件 demo ( GDB 也支持遠程調試),若其啟動時不需要命令行參數,則可以在shell下使用命令 gdb demo 進入 GDB,並輸入 run (縮寫形式 r) 啟動對demo的調試。若 demo 程序啟動時需要命令行參數,則可以在使用 gdb demo 命令進入GDB後,使用命令 run arg1 arg2...
  • 你還在用GDB調試程序嗎?
    本文主要講述如何使用Python來提高你的GDB調試技能, 讓你從繁重的重複的工作裡面掙脫出來呼吸新鮮空氣。首先,第一件事,使用gdb7.x以上的版本,最好9.x的。因為Python的支持是從gdb7.0(2009年?)開始的。
  • 獨家|Linux進程內存用量分析之堆內存篇
    本文將介紹幾種內存洩漏檢測工具,並通過實際例子介紹一種分析堆內存佔用量的工具和方法,幫助定位內存膨脹問題。背景進程的內存管理是每一個開發者必須要考慮的問題,對於C++程序進程來說,出現問題很多情況下都與內存掛鈎。進程崩潰問題通常可以使用gdb等調試工具輕鬆排查並解決。
  • GDB簡明教程:快速入門
    列印方法簡單方便,但對於一些隱藏比較深的bug,或者一些跟內存相關的bug,此時再用「列印大法」可能就愛莫能助了。此時,我們經常使用的另一種方法是:單步調試。所謂單步調試,就是我們可以獲取到控制程序運行的權限,在人工操作下,程序可以「放慢腳步」,一步一步地執行,甚至可以暫停執行,方便我們觀察各個變量、內存、寄存器的值,看是否符合我們的預期。
  • Linux 系統內核的調試
    儘管缺乏一種內置的調試內核的有效方法,但是 Linux 系統在內核發展的過程中也逐漸形成了一些監視內核代碼和錯誤跟蹤的技術。同時,許多的補丁程序應運而生,它們為標準內核附加了內核調試的支持。儘管這些補丁有些並不被 Linux 官方組織認可,但他們確實功能完善,十分強大。調試內核問題時,利用這些工具與方法跟蹤內核執行情況,並查看其內存和數據結構將是非常有用的。
  • C/C++開發:CLion與Valgrind
    簡單使用安裝完成後,您可以選擇New Project從頭開始項目;在已有代碼的情況下,可以選擇New CMake Project from sources,將Eclipse項目變為CMake項目。與Valgrind結合使用對於使用unix終端的用戶,Valgrind可方便地用於檢查內存洩漏等問題,然而這一軟體在Windows上不受支持。
  • 簡要記錄丨VSCode 搭建基礎 C/C++ 編譯環境
    但是 VSCode 可以通過安裝各種擴展插件,實現代碼編譯、調試、運行等功能。  我覺得首先概念要正確,然後才能好好正視這個軟體,參考知乎回答解釋如下:VSC 只是一個純文本編輯器( editor ),不是 IDE (集成開發環境),不含編譯器( compiler )和許多其它功能,所以編譯器要自己裝好。