Linux GDB 調(diào)試命令入門

即使經(jīng)驗非常豐富的程序員,在編寫程序的時候也避免不了出錯粥谬。
程序中的語法錯誤通掣馗可以在翻譯階段就能被診斷出來,
但邏輯錯誤卻很難被發(fā)現(xiàn)和糾正漏策,
比如在解決問題時使用了錯誤或者不完備的思路派哲。
在這種情況下調(diào)試可能是唯一辦法。

通過設(shè)置適當(dāng)?shù)臄帱c哟玷,你可以觀察結(jié)果并和預(yù)期的結(jié)果進(jìn)行比較以縮小問題代碼的范圍狮辽,并最終發(fā)現(xiàn)問題所在一也。

什么是調(diào)試?

調(diào)試(Debug)喉脖,就是讓代碼一步一步執(zhí)行椰苟,跟蹤程序的運行過程。
比如树叽,可讓程序停在某個地方舆蝴,查看當(dāng)前所有變量的值,或者內(nèi)存中的數(shù)據(jù)题诵;
也可以讓程序一次只執(zhí)行一條或者幾條語句洁仗,查看到底執(zhí)行了哪些分支。

在調(diào)試過程中性锭,可以監(jiān)控程序的每一個細(xì)節(jié)赠潦,包括變量的值、函數(shù)的調(diào)用過程草冈、內(nèi)存中數(shù)據(jù)她奥、線程的調(diào)度等,發(fā)現(xiàn)隱藏的錯誤或者低效的代碼怎棱。

編譯器可以發(fā)現(xiàn)程序的語法錯誤哩俭,
調(diào)試可以發(fā)現(xiàn)程序的邏輯錯誤。
所謂邏輯錯誤是指代碼思路或者設(shè)計上的缺陷拳恋。

學(xué)習(xí)調(diào)試也可以增加編程的功力凡资,能更加了解自己的程序,變量是什么時候賦值的谬运、內(nèi)存是什么時候分配的隙赁。

調(diào)試是每個程序員必須掌握的技能,沒有選擇的余地吩谦!

調(diào)試需要借助專業(yè)的輔助軟件——調(diào)試器(Debugger)鸳谜。

現(xiàn)在主流C/C++調(diào)試器有下面幾種:
1) Remote Debugger
Remote Debugger 是 VC/VS 自帶的調(diào)試器,與整個IDE無縫銜接式廷,使用非常方便咐扭,初學(xué)者建議使用該調(diào)試器,本教程也以 VS2010 為例講解調(diào)試技巧滑废。
2) WinDbg
Windows 下的調(diào)試器蝗肪,功能甚至超越了 Remote Debugger,它還有一個命令行版本(cdb.exe)蠕趁,但是這個命令行版本的調(diào)試器指令比較復(fù)雜薛闪,不建議初學(xué)者使用。
3) LLDB
XCode 自帶的調(diào)試器俺陋,Mac OS X 下開發(fā)必備調(diào)試器豁延。
4) GDB
Linux 下使用最多的一款調(diào)試器昙篙,也有 Windows 的移植版。

關(guān)于 GDB

GDB 是 Linux 下的 C/C++ 程序調(diào)試工具诱咏,開源免費苔可,功能強(qiáng)大,命令眾多.

GDB 調(diào)試器圖標(biāo)長成這樣


GDB 調(diào)試器圖標(biāo).png

Windows C/C++ 開發(fā)的一定要熟悉 Visual Studio袋狞、
Java 開發(fā)的要熟悉 Eclipse 或 IntelliJ IDEA焚辅、
Android 開發(fā)的要熟悉 Android Studio、
iOS 開發(fā)的要熟悉 XCode 一樣苟鸯,
Linux C/C++ 開發(fā)要熟悉 GDB同蜻。

雖然 Linux 系統(tǒng)下編寫 C/C++ 代碼的 IDE 可以自由選擇,但是調(diào)試生成的 C/C++ 程序一定是直接或者間接使用 GDB早处。

調(diào)試是開發(fā)流程中一重要環(huán)節(jié)
從事 Linux C/C++ 開發(fā)人員熟練使用 GDB 調(diào)試是一項基本要求湾蔓。



GDB 入門

以下面的代碼為例在 Linux 系統(tǒng)下來講解 GBD 的調(diào)試流程:

int main (void) {
    unsigned long long int n, sum;

    n = 1;
    sum = 0;

    while (n <= 100)  {
        sum = sum + n;
        n = n + 1;
    }

    return 0;
}

將代碼保存到 /demo/main.c,表示當(dāng)前用戶的主目錄陕赃,也即 home 目錄卵蛉。

首先使用 GCC 來編譯 main.c,編譯的時候使用-g選項么库,目的是向可執(zhí)行程序中加入調(diào)試信息,包括源代碼甘有、符號表等诉儒,GDB 需要這些額外的信息來完成調(diào)試工作。

此外還建議關(guān)閉編譯器的程序優(yōu)化選項亏掀。編譯器的程序優(yōu)化選項一般有五個級別忱反,從 O0 ~ O4, O0 表示不優(yōu)化滤愕,從 O1 ~ O4 優(yōu)化級別越來越高温算,O4 最高。這樣做的目的是為了調(diào)試的時候间影,符號文件顯示的調(diào)試變量等能與源代碼完全對應(yīng)起來注竿。

使用 GCC 編譯源文件:

$ cd demo
$ gcc main.c -o main.out -g

打開 demo 文件夾,發(fā)現(xiàn)多了一個 mian.out魂贬,說明編譯成功巩割。


1) 啟動 GDB 調(diào)試器

接下來啟動 GDB 并調(diào)試 main.out:

$ gdb main.out -silent
Reading symbols from /home/mozhiyan/demo/main.out...done.

選項-silent用于屏蔽 GDB 的前導(dǎo)信息,否則它會在屏幕上打印一堆免責(zé)條款付燥。

啟動 GDB 后宣谈,輸出的信息表明已經(jīng)讀入了 mian.out 的符號表。
接下來键科,GDB 會顯示自己的提示符(gbd)闻丑,提示并等待你輸入調(diào)試命令漩怎。


2) gdb -b 選項:設(shè)置斷點

調(diào)試一個程序的時候,應(yīng)在我們關(guān)注的地方嗦嗡,或在故障點的前邊設(shè)置一個斷點(Breakpoint)勋锤,讓程序執(zhí)行到這里停下來,這樣我們就可以慢慢地用別的調(diào)試命令進(jìn)行觀察酸钦。

在 GDB 中怪得,設(shè)置斷點的方法很多,包括在指定的內(nèi)存地址處設(shè)置斷點卑硫、在源代碼的某一行設(shè)置斷點徒恋,或者在某個函數(shù)的入口處設(shè)置斷點等等。

設(shè)置斷點的命令是b或者break欢伏,在這里我 們是將 main 函數(shù)的入口處作為斷點:

(gdb) b main
Breakpoint 1 at 0x4004f4: file main.c, line 5.

b 命令在執(zhí)行后返回了斷點的具體信息入挣,也就是說,斷點(main 函數(shù)的入口位置)的內(nèi)存地址為 0x4004f4硝拧,對應(yīng)于源文件的第 5 行(也就是說,main 函數(shù)位于源文件的第 5 行)滋恬。

如果我們用內(nèi)存地址的方式來設(shè)置這個斷點抱究,則可以是:

b * 0x4004f4
星號*意味著是以內(nèi)存地址作為斷點的鼓寺。

如果用源代碼行的形式設(shè)置這個斷點,則可以是

b 5

3) gdb -r 選項:執(zhí)行程序

一旦設(shè)置了斷點敢靡,下一步就是用r或者run命令執(zhí)行被調(diào)試的程序啸胧,執(zhí)行后會自動在第一個斷點處停下來:

Starting program: /home/mozhiyan/demo/main.out
[New Thread 1500.0x1e34]
[New Thread 1500.0x2fb8]

Thread 1 hit Breakpoint 1, main () at main.c:5
5     n = 1;

在運行了被調(diào)試的程序后吓揪,GDB 的輸出信息顯示程序己經(jīng)啟動柠辞,下一個將要執(zhí)行的語句是第 5 行的n = 1;主胧。

注意,這條語句并沒有執(zhí)行焙格,而僅僅是告訴你,再繼續(xù)執(zhí)行程序的話予颤,執(zhí)行的語句會是它冬阳。


4) gdb -p 選項:打印變量的值

在當(dāng)前位置 變量 n 和 sum 己經(jīng)分配,但并沒有開始賦值驳庭。此時氯窍,這兩個變量的值會是多少呢?我們可以使用p或者print命令來分別顯示:

(gdb) p n
$1 = 24
(gdb) p sum
$2 = 140737488347344

GDB 的 p 命令用于打印一個表達(dá)式的值,在這里是表達(dá)式 n 和 sum政供。

GDB 先計算表達(dá)式的值鲫骗,并把它保存在一個存儲區(qū)中执泰,存儲區(qū)的名字用外加數(shù)字來表示,并且這個數(shù)字會隨著調(diào)試過程的進(jìn)行而不斷遞增(這意味著存儲區(qū)也是不斷開辟的)排苍。以上学密,第一個 p 命令執(zhí)行后腻暮,GDB 的回應(yīng)是1 = 16毯侦,意思是表達(dá)式 n 的值保存在 $1 中具垫,其內(nèi)容為 16侈离。

注意,在你的計算機(jī)上卦碾,變量 n 和 sum 的當(dāng)前值可能和這里顯示的不同。這很好理解起宽,內(nèi)存是反復(fù)使用的,當(dāng)一個程序終止后燎含,它占用的內(nèi)存會分配給其他程序使用宾濒;當(dāng)一個變量不再使用后屏箍,它占用的內(nèi)存也會重新分配绘梦,并成為另一個變量。因為變量 n 和 sum 剛剛分配赴魁,還沒有往里面保存任何數(shù)值卸奉,故它們的內(nèi)容是隨機(jī)的颖御,是其他程序或者變量用過的垃圾值榄棵。

順便說一下疹鳄,既然 $1 是 GDB 用于保存計算結(jié)果的內(nèi)部存儲區(qū)的名字,那么我們也可以用 p 命令來打印它:

(gdb) p $1
$3 = 24

5) gdb -n 選項:單步調(diào)試

下面腺怯,我們將通過單步執(zhí)行程序,來看一看變量 n 和 sum 賦值后的值呛占。調(diào)試命令n或者next用于繼續(xù)執(zhí)行源文件中的下一行晾虑。

(gdb) n
6     sum = 0;

執(zhí)行 n 命令后,實際執(zhí)行的是第 5 行n = 1;,GDB 顯示下一個即將執(zhí)行的源代碼行走贪,也就是第 6 行的sum = 0;佛猛。

因為此時己經(jīng)往變量 n 寫入了 1,所以我們可繼續(xù)用 p 命令來觀察它現(xiàn)在的存儲值:

(gdb) p n
$4 = 1

顯然經(jīng)賦值后坠狡,變量 n 的值己經(jīng)變成 1继找。

繼續(xù)執(zhí)行下一條指令,實際執(zhí)行的是第 6 行sum = 0逃沿。執(zhí)行后婴渡,GDB 停下并顯示下一條即將執(zhí)行的源代碼行,也即第 8 行的while (n <= 100)凯亮,第 7 行為空行边臼,所以直接跳過了:

(gdb) n
8     while (n <= 100)

剛才執(zhí)行的語句是往變量 sum 保存數(shù)值 0,故我們可以再次用 p 命令來觀察變量 sum 現(xiàn)在的存儲值假消,可發(fā)現(xiàn)它己經(jīng)變成 0:

(gdb) p sum
$5 = 0

繼續(xù)用 n 命令執(zhí)行下一個源代碼行柠并,則將計算 while 語句的控制表達(dá)式,并根據(jù)該表達(dá)式的值決定是否進(jìn)入循環(huán)體富拗,執(zhí)行后 GDB 顯示下一條即將執(zhí)行的源代碼行是第 10 行:

(gdb) n
10    sum = sum + n;

進(jìn)入循環(huán)體之后臼予,我們想再看看變量 n 和 sum 的當(dāng)前值。但這次使用 p 命令的方法不一樣啃沪,這次是用花括號將表達(dá)式 n 和 sum 圍住以形成一個集合粘拾。GDB 允許用這種方式來一次性地打印多個表達(dá)式的值:

(gdb) p {n, sum}
$6 = {1, 0}

顯然,變量 n 和 sum 此時的值依然分別為 1 和 0创千。

繼續(xù)用 n 命令執(zhí)行第 10 行缰雇,執(zhí)行后 GDB 停留在即將執(zhí)行的第 11 行:

(gdb) n
11    n = n + 1;

注意,第 10 行己經(jīng)執(zhí)行完畢追驴,但第 11 行還沒有執(zhí)行械哟。猜猜看,變量 n 和 sum 此時的值是多少殿雪?猜測之后戒良,用 p 命令看看結(jié)果是否如你所想:

(gdb) p {n, sum}
$7 = {1, 1}

繼續(xù)用 n 命令執(zhí)行下一個源代碼行,這將執(zhí)行第 11 行的n = n + 1;冠摄,執(zhí)行后控制又回到了循環(huán)的起始處,也即第 8 行:
(gdb) n
8 while (n <= 100)
此時几缭,變量 n 和 sum 的值各自會是多少河泳?使用 p 命令打印一下就知道了:
(gdb) p {n, sum}
$8 = {2, 1}
因為現(xiàn)在處于一個循環(huán)體內(nèi),如果繼續(xù)用 n 命令往下執(zhí)行年栓,則其過程與前面相比大同小異拆挥。前面己經(jīng)循環(huán)過一次,本次循環(huán)完整的調(diào)試過程如下:

(gdb) n
10    sum = sum + n;
(gdb) n
11    n = n + 1;
(gdb) n
8    while (n <= 100)
(gdb) p {n, sum}
$9 = {3, 3}

顯然,第二次循環(huán)過后纸兔,變量 n 的值為 3惰瓜,變量 sum 的值也是 3装盯。

你可能己經(jīng)發(fā)現(xiàn)了曲稼,我們現(xiàn)在進(jìn)退維谷:如果繼續(xù)用 n 命令執(zhí)行下面,則將陷入循環(huán)碟案,直到變量 n 的值等于 101氏捞。好在這也算不上什么大的問題棉饶,我們可以在循環(huán)語句的后面設(shè)置斷點垦页,然后命令程序一直執(zhí)行紊册,直至到達(dá)這個斷點赋续。


6) gdb -l 選項:列出源文件內(nèi)容

為了搞清楚 while 語句的下一條語句的行號男翰,我們需要列出源文件的內(nèi)容,這需要使用l或者list命令:

(gdb) l
3     unsigned long long int n, sum;
4
5     n = 1;
6     sum = 0;
7
8     while (n <= 100)
9     {
10        sum = sum + n;
11        n = n + 1;
12    }
l 命令默認(rèn)每次顯示 10 行源代碼纽乱,但我們關(guān)心的那一行顯然還沒有出來蛾绎。為此,可繼續(xù)使用 l 命令來顯示后面的行:
(gdb) l
13
14        return 0;
15    }

好了鸦列,我們己經(jīng)知道 while 語句之后是 return 語句租冠,它的行號是 14,現(xiàn)在就可以用 b 命令設(shè)置一個新的斷點:

(gdb) b 14
Breakpoint 2 at 0x40051a: file main.c, line 14.

7) gdb -c 選項:繼續(xù)執(zhí)行程序

現(xiàn)在敛熬,可以用一個新的命令c或者continue來持續(xù)執(zhí)行程序肺稀,直至遇到斷點或者程序結(jié)束。因為己經(jīng)設(shè)置斷點应民,故程序?qū)⒊掷m(xù)執(zhí)行话原,在第 14 行處停下:

(gdb) c
Continuing.

Breakpoint 2, main () at main.c:14
14     return 0;

非常好,既然己經(jīng)退出了 while 循環(huán)诲锹,說明累加過程己經(jīng)成功結(jié)束繁仁,變量 sum 的值就是累加結(jié)果。我們來看看它到底是多少:

(gdb) p {n, sum}
$10 = {101, 5050}

顯然归园,變量 n 的當(dāng)前值是 101黄虱,變量 sum 的當(dāng)前值是 5050,和數(shù)學(xué)計算的結(jié)果一模 一樣庸诱。

  1. gdb -q 選項:退出調(diào)試

本次調(diào)試即將結(jié)束捻浦,我們可以先用 c 命令讓程序“跑完全程”,然后再用q或者quit結(jié)束本次調(diào)試工作桥爽,這將使得調(diào)試器 GDB 結(jié)束運行并返回到操作系統(tǒng):

(gdb) c
Continuing.
[Inferior 1 (process 2814) exited normally]
(gdb) q
[c.biancheng.net demo]$
關(guān)鍵是要動手嘗試朱灿,
嘗試之前需要知道一些基本信息,
希望這篇漏洞百出的文章能有一點點作用钠四。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盗扒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌侣灶,老刑警劉巖甸祭,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異褥影,居然都是意外死亡池户,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進(jìn)店門伪阶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煞檩,“玉大人,你說我怎么就攤上這事栅贴≌迮龋” “怎么了?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵檐薯,是天一觀的道長凝赛。 經(jīng)常有香客問我,道長坛缕,這世上最難降的妖魔是什么墓猎? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮赚楚,結(jié)果婚禮上毙沾,老公的妹妹穿的比我還像新娘。我一直安慰自己宠页,他們只是感情好左胞,可當(dāng)我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著举户,像睡著了一般烤宙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俭嘁,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天躺枕,我揣著相機(jī)與錄音,去河邊找鬼供填。 笑死拐云,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的近她。 我是一名探鬼主播慨丐,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泄私!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤晌端,失蹤者是張志新(化名)和其女友劉穎捅暴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咧纠,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡蓬痒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了漆羔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梧奢。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖演痒,靈堂內(nèi)的尸體忽然破棺而出亲轨,到底是詐尸還是另有隱情,我是刑警寧澤鸟顺,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布惦蚊,位于F島的核電站,受9級特大地震影響讯嫂,放射性物質(zhì)發(fā)生泄漏蹦锋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一欧芽、第九天 我趴在偏房一處隱蔽的房頂上張望莉掂。 院中可真熱鬧,春花似錦千扔、人聲如沸憎妙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尚氛。三九已至,卻和暖如春洞渤,著一層夾襖步出監(jiān)牢的瞬間阅嘶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工载迄, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留讯柔,地道東北人。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓护昧,卻偏偏與公主長得像魂迄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惋耙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,926評論 2 361