根據定義,調試工具是那些那些使我們能夠監測、控制和糾正其他程式的程式。我們為什麼應該用調試工具呢? 在有些情況下,運作一些程式的時候我們會被卡住,我們需要明白究竟發生了什麼。 例如,我們正在運作應用程式,它産生了一些錯誤消息。要修複這些錯誤,我們應該先找出為什麼産生這些錯誤的消息和這些錯誤消息從哪裡産生的。 一個應用程式可能突然挂起,我們必須了解其他什麼程序同時在運作。我們可能還必須弄清楚某個程序挂起的時候在做什麼。為了剖析這些細節, 我們需要調試工具的幫助。

有幾個linux下的使用者空間調試工具和技術,它們用來分析使用者空間的問題相當有用。它們是:
'print' 語句
查詢 (/proc, /sys 等)
跟蹤 (strace/ltrace)
valgrind (memwatch)
gdb
讓我們一個個地了解。
<a target="_blank"></a>
這是一個基本的原始的調試問題的方法。 我們可以在程式中插入print語句來了解控制流和變量值。 雖然這是一個簡單的技術, 但它有一些缺點。 程式需要進行編輯以添加'print'語句,然後必須重新編譯,重新運作來獲得輸出。 如果要調試的程式相當大,這是一個耗時的方法。
在某些情況下,我們需要弄清楚在一個運作在核心中的程序的狀态和記憶體映射。為了獲得這些資訊,我們不需要在核心中插入任何代碼。 相反,可以用 /proc 檔案系統。
/proc 是一個僞檔案系統,系統一啟動運作就收集着運作時系統的資訊 (cpu資訊, 記憶體容量等)。
'ls /proc'的輸出
正如你看到的, 系統中運作的每一個程序在/proc檔案系統中有一個以程序id命名的項。每個程序的細節資訊可以在程序id對應的目錄下的檔案中獲得。
'ls /proc/pid'的輸出
解釋/proc檔案系統内的所有條目超出了本文的範圍。一些有用的列舉如下:
/proc/cmdline -> 核心指令行
/proc/cpuinfo -> 關于處理器的品牌,型号資訊等
/proc/filesystems -> 檔案系統的核心支援的資訊
/proc/<pid>/cmdline -> 指令行參數傳遞到目前程序
/proc/<pid>/mem -> 目前程序持有的記憶體
/proc/<pid>/status -> 目前程序的狀态
strace的和ltrace是兩個在linux中用來追蹤程式的執行細節的跟蹤工具。
strace攔截和記錄系統調用及其接收的信号。對于使用者,它顯示了系統調用、傳遞給它們的參數和傳回值。strace的可以附着到已在運作的程序或一個新的程序。它作為一個針對開發者和系統管理者的診斷、調試工具是很有用的。它也可以用來當做一個通過跟蹤不同的程式調用來了解系統的工具。這個工具的好處是不需要源代碼,程式也不需要重新編譯。
使用strace的基本文法是:
strace 指令
strace有各種各樣的參數。可以檢檢視strace的手冊頁來獲得更多的細節。
strace的輸出非常長,我們通常不會對顯示的每一行都感興趣。我們可以用'-e expr'選項來過濾不想要的資料。
用 '-p pid' 選項來綁到運作中的程序.
用'-o'選項,指令的輸出可以被重定向到檔案。
strace過濾成隻有系統調用的輸出
ltrace跟蹤和記錄一個程序的動态(運作時)庫的調用及其收到的信号。它也可以跟蹤一個程序所作的系統調用。它的用法是類似與strace。
ltrace command
'-i' 選項在調用庫時列印指令指針。
'-s' 選項被用來現實系統調用和庫調用
所有可用的選項請參閱ltrace手冊。
ltrace捕捉'strcmp'庫調用的輸出
valgrind是一套調試和分析工具。它的一個被廣泛使用的預設工具——'memcheck'——可以攔截malloc(),new(),free()和delete()調用。換句話說,它在檢測下面這些問題非常有用:
記憶體洩露
重釋放
通路越界
使用未初始化的記憶體
使用已經被釋放的記憶體等。
它直接通過可執行檔案運作。
valgrind也有一些缺點,因為它增加了記憶體占用,會減慢你的程式。它有時會造成誤報和漏報。它不能檢測出靜态配置設定的數組的通路越界問題。
使用指令行安裝需要解壓縮和解包下載下傳的檔案。
tar -xjvf valgring-x.y.z.tar.bz2 (where x.y.z is the version number you are trying to install)
進入新建立的目錄(的valgrind-xyz)内運作以下指令:
./configure
make
make install
讓我們通過一個小程式(test.c)來了解valgrind怎麼工作的:
#include <stdio.h>
void f(void)
{
int x = malloc(10 * sizeof(int));
x[10] = 0;
}
int main()
f();
return 0;
編譯程式:
gcc -o test -g test.c
現在我們有一個可執行檔案叫做'test'。我們現在可以用valgrind來檢測記憶體錯誤:
valgrind –tool=memcheck –leak-check=yes test
這是valgrind呈現錯誤的輸出:
valgrind顯示堆溢出和記憶體洩漏的輸出
正如我們在上面看到的消息,我們正在試圖通路函數f未配置設定的記憶體以及配置設定尚未釋放的記憶體。
gdb是來自自由軟體基金會的調試器。它對定位和修複代碼中的問題很有幫助。當被調試的程式運作時,它給使用者控制權去執行各種動作, 比如:
啟動程式
停在指定位置
停在指定的條件
檢查所需資訊
改變程式中的資料 等。
你也可以将一個崩潰的程式coredump附着到gdb并分析故障的原因。
gdb提供很多選項來調試程式。 然而,我們将介紹一些重要的選擇,來感受如何開始使用gdb。
為了用gdb調試程式,必須使用gcc的'-g'選項進行編譯。這将以作業系統的本地格式産生調試資訊,gdb利用這些資訊來工作。
下面是一個簡單的程式(example1.c)執行被零除用來顯示gdb的用法:
#include
int divide()
int x=5, y=0;
return x / y;
divide();
展示gdb用法的例子
通過在指令行中執行'gdb'來啟動gdb:
調用 gdb
調用後, 它将等待終端指令并執行,直到退出。
如果一個程序已經在運作,你需要将gdb連接配接到它上面,可以通過指定程序id來實作。假設程式已經崩潰,要分析問題的原因,則用gdb分析core檔案。
一旦你在gdb裡面,使用'run'指令來啟動程式進行調試。
使用'set args'給你的程式傳參數,當程式下次運作時将獲得該參數。'show args'将顯示傳遞給程式的參數。
每當程式停止,任何人想明白的第一件事就是它為什麼停止,以及怎麼停在那裡的。該資訊被稱為反向跟蹤。由程式産生每個函數調用和局部變量,傳遞的參數,調用位置等資訊一起存儲在堆棧内的資料塊種,被稱為一幀。我們可以使用gdb來檢查所有這些資料。 gdb從最底層的幀開始給這些幀編号。
bt: 列印整個堆棧的回溯
bt 列印n個幀的回溯
frame : 切換到指定的幀,并列印該幀
up : 上移'n'個幀
down : 下移'n'個幀 ( n預設是1)
程式的資料可以在裡面gdb使用'print'指令進行檢查。例如,如果'x'是調試程式内的變量,'print x'會列印x的值。
源碼可以在gdb中列印。預設情況下,'list'指令會列印10行代碼。
list : 列出'linenum'行周圍的源碼
list : 從'function'開始列出源碼
disas : 顯示該函數機器代碼
使用gdb,我們可以在必要的地方設定斷點,觀察點等來停止程式。
break : 在'location'設定一個斷點。當在程式執行到這裡時斷點将被擊中,控制權被交給使用者。
watch : 當'expr'被程式寫入而且它的值發生變化時gdb将停止
catch : 當'event'發生時gdb停止
disable : 禁用指定斷點
enable : 啟用指定斷點
delete : 删除 斷點/觀察點/捕獲點。 如果沒有傳遞參數預設操作是在所有的斷點
step: 一步一步執行程式
continue: 繼續執行程式,直到執行完畢
用'quit'指令還從gdb中退出。
gdb還有更多的可用選項。裡面gdb使用help選項了解更多詳情。
在gdb中獲得幫助
在這篇文章中,我們已經看到不同類型的linux使用者空間的調試工具。總結以上所有内容,如下是什麼時候使用該什麼的快速指南:
基本調試,獲得關鍵變量 - print 語句
擷取有關檔案系統支援,可用記憶體,cpu,運作程式的核心狀态等資訊 - 查詢 /proc 檔案系統
最初的問題診斷,系統調用或庫調用的相關問題,了解程式流程 – strace / ltrace
應用程式記憶體空間的問題 – valgrind
檢查應用程式運作時的行為,分析應用程式崩潰 – gdb
----------------------------------------------------------------------------------------------------------------------------