天天看點

unix之gdb

 Linux 包含了一個叫 gdb 的 GNU 調試程式. gdb 是一個用來調試 C 和 C++ 程式的強力調試器. 它使你能在程式運作時觀察程式的内部結構和記憶體的使用情況. 以下是 gdb 所提供的一些功能: 

它使你能監視你程式中變量的值. 

它使你能設定斷點以使程式在指定的代碼行上停止執行. 

它使你能一行行的執行你的代碼. 

    在指令行上鍵入 gdb 并按Enter鍵就可以運作 gdb 了, 如果一切正常的話, gdb 将被啟動并且你将在螢幕上看到類似的内容: 

GDB is free software and you are welcome to distribute copies of it 

under certain conditions; type "show copying" to see the conditions.

There is absolutely no warranty for GDB; type "show warranty" for details.

GDB 4.14 (i486-slakware-linux), Copyright 1995 Free Software Foundation, Inc.

(gdb)

    當你啟動 gdb 後, 你能在指令行上指定很多的選項. 你也可以以下面的方式來運作 gdb : 

gdb <fname>

    當你用這種方式運作 gdb , 你能直接指定想要調試的程式. 這将告訴gdb 裝入名為 fname 的可執行檔案. 你也可以用 gdb 去檢查一個因程式異常終止而産生的 core 檔案, 或者與一個正在運作的程式相連. 你可以參考 gdb 指南頁或在指令行上鍵入 gdb -h 得到一個有關這些選項的說明的簡單清單.

為調試編譯代碼(Compiling Code for Debugging)

    為了使 gdb 正常工作, 你必須使你的程式在編譯時包含調試資訊. 調試資訊包含你程式裡的每個變量的類型和在可執行檔案裡的位址映射以及源代碼的行号. gdb 利用這些資訊使源代碼和機器碼相關聯. 

    在編譯時用 -g 選項打開調試選項.

gdb 基本指令

     gdb 支援很多的指令使你能實作不同的功能. 這些指令從簡單的檔案裝入到允許你檢查所調用的堆棧内容的複雜指令, 表27.1列出了你在用 gdb 調試時會用到的一些指令. 想了解 gdb 的詳細使用請參考 gdb 的指南頁.

表 27.1. 基本 gdb 指令

命   令 描 述 

file 裝入想要調試的可執行檔案. 

kill 終止正在調試的程式. 

list 列出産生執行檔案的源代碼的一部分. 

next 執行一行源代碼但不進入函數内部. 

step 執行一行源代碼而且進入函數内部. 

run 執行目前被調試的程式 

quit 終止 gdb 

watch 使你能監視一個變量的值而不管它何時被改變. 

break 在代碼裡設定斷點, 這将使程式執行到這裡時被挂起. 

make 使你能不退出 gdb 就可以重新産生可執行檔案. 

shell 使你能不離開 gdb 就執行 UNIX shell 指令.

     gdb 支援很多與 UNIX shell 程式一樣的指令編輯特征. 你能象在 bash 或 tcsh裡那樣按 Tab 鍵讓 gdb 幫你補齊一個唯一的指令, 如果不唯一的話 gdb 會列出所有比對的指令. 你也能用光标鍵上下翻動曆史指令.

gdb 應用舉例

    本節用一個執行個體教你一步步的用 gdb 調試程式. 被調試的程式相當的簡單, 但它展示了 gdb 的典型應用.

    下面列出了将被調試的程式. 這個程式被稱為 greeting , 它顯示一個簡單的問候, 再用反序将它列出. 

#include <stdio.h>

main ()

{

char my_string[] = "hello there";

my_print (my_string);

my_print2 (my_string);

}

void my_print (char *string)

printf ("The string is %s\n", string);

void my_print2 (char *string)

char *string2;

int size, i;

size = strlen (string);

string2 = (char *) malloc (size + 1);

for (i = 0; i < size; i++)

    string2[size - i] = string[i];

string2[size+1] = `\0';

printf ("The string printed backward is %s\n", string2);

    用下面的指令編譯它:

gcc -o test test.c

    這個程式執行時顯示如下結果: 

The string is hello there

The string printed backward is

    輸出的第一行是正确的, 但第二行列印出的東西并不是我們所期望的. 我們所設想的輸出應該是: 

The string printed backward is ereht olleh

    由于某些原因, my_print2 函數沒有正常工作. 讓我們用 gdb 看看問題究竟出在哪兒, 先鍵入如下指令:

gdb greeting

--------------------------------------------------------------------------------

注意: 記得在編譯 greeting 程式時把調試選項打開. 

    如果你在輸入指令時忘了把要調試的程式作為參數傳給 gdb , 你可以在 gdb 提示符下用 file 指令來載入它:

(gdb) file greeting

    這個指令将載入 greeting 可執行檔案就象你在 gdb 指令行裡裝入它一樣. 

    這時你能用 gdb 的 run 指令來運作 greeting 了. 當它在 gdb 裡被運作後結果大約會象這樣:

(gdb) run

Starting program: /root/greeting

Program exited with code 041

    這個輸出和在 gdb 外面運作的結果一樣. 問題是, 為什麼反序列印沒有工作? 為了找出症結所在, 我們可以在 my_print2 函數的 for 語句後設一個斷點, 具體的做法是在 gdb 提示符下鍵入 list 指令三次, 列出源代碼: 

(gdb) list

技巧: 在 gdb 提示符下按回車健将重複上一個指令. 

    第一次鍵入 list 指令的輸出如下:

1       #include <stdio.h>

2

3       main ()

4       {

5         char my_string[] = "hello there";

6

7         my_print (my_string);

8         my_print2 (my_string);

9       }

10

    如果按下回車, gdb 将再執行一次 list 指令, 給出下列輸出:

11      my_print (char *string)

12      {

13        printf ("The string is %s\n", string);

14      }

15

16      my_print2 (char *string)

17      {

18        char *string2;

19        int size, i;

20

    再按一次回車将列出 greeting 程式的剩餘部分: 

21        size = strlen (string);

22        string2 = (char *) malloc (size + 1);

23        for (i = 0; i < size; i++)

24          string2[size - i] = string[i];

25        string2[size+1] = `\0';

26        printf ("The string printed backward is %s\n", string2);

27      }

    根據列出的源程式, 你能看到要設斷點的地方在第24行, 在 gdb 指令行提示符下鍵入如下指令設定斷點: 

(gdb) break 24

    gdb 将作出如下的響應: 

Breakpoint 1 at 0x139: file greeting.c, line 24

    現在再鍵入 run 指令, 将産生如下的輸出:

Breakpoint 1, my_print2 (string = 0xbfffdc4 "hello there") at greeting.c :24

24 string2[size-i]=string[i]

    你能通過設定一個觀察 string2[size - i] 變量的值的觀察點來看出錯誤是怎樣産生的, 做法是鍵入:

(gdb) watch string2[size - i]

    gdb 将作出如下回應: 

Watchpoint 2: string2[size - i]

    現在可以用 next 指令來一步步的執行 for 循環了:

(gdb) next

    經過第一次循環後, gdb 告訴我們 string2[size - i] 的值是 `h`. gdb 用如下的顯示來告訴你這個資訊:

Watchpoint 2, string2[size - i]

Old value = 0 `\000'

New value = 104 `h'

my_print2(string = 0xbfffdc4 "hello there") at greeting.c:23

23 for (i=0; i<size; i++)

    這個值正是期望的. 後來的數次循環的結果都是正确的. 當 i=10 時, 表達式 string2[size - i] 的值等于 `e`, size - i 的值等于 1, 最後一個字元已經拷到新串裡了. 

    如果你再把循環執行下去, 你會看到已經沒有值配置設定給 string2[0] 了, 而它是新串的第一個字元, 因為 malloc 函數在配置設定記憶體時把它們初始化為空(null)字元. 是以 string2 的第一個字元是空字元. 這解釋了為什麼在列印 string2 時沒有任何輸出了.

    現在找出了問題出在哪裡, 修正這個錯誤是很容易的. 你得把代碼裡寫入 string2 的第一個字元的的偏移量改為 size - 1 而不是 size. 這是因為 string2 的大小為 12, 但起始偏移量是 0, 串内的字元從偏移量 0 到 偏移量 10, 偏移量 11 為空字元保留.

    為了使代碼正常工作有很多種修改辦法. 一種是另設一個比串的實際大小小 1 的變量. 這是這種解決辦法的代碼:

my_print (char *string)

my_print2 (char *string)

int size, size2, i;

size2 = size -1;

    string2[size2 - i] = string[i];

string2[size] = `\0';

本文轉自terryli51CTO部落格,原文連結: http://blog.51cto.com/terryli/520841,如需轉載請自行聯系原作者