gdb用法
gdb 的用法
在Linux應用程序開發中,最常用的調試器是gdb,它可以在程序中設置斷點、查看變量值、一步一步跟蹤程序的執行過程。利用調試器的這些功能可以方便地找出程序中存在的非語法錯誤。
一、啟動和退出gdb
gdb調試的對象是可執行文件,而不是程序的源代碼。
如果要使一個可執行文件可以被gdb調試,那么在使用編譯器gcc編譯程序時需要加入-g選項。-g選項告訴gcc在編譯程序時加入調試信息,這樣才可以調試這個被編譯的程序。
例:計算1~100的和,應該輸出5050
# cat -n test.c
???? 1? #include <stdio.h>
???? 2
???? 3?int get_sum(int n)
???? 4??{
???? 5???int sum = 0, i;
???? 6???for (i=0;i<n;i++)
???? 7????sum +=i;
???? 8????return sum;
???? 9???}
???10
???11? int main()
???12??? {
???13??? int i = 100, result;
???14??? result = get_sum(i);
???15???printf("1+2+...+%d=%d\n",i,result);
???16??? return 0;
???17? }
編譯并運行該程序:
# gcc -g test.c -o test
# ./test
1+2+...+100=4950
?
(一)、調試一個程序的命令格式
格式:gdb?<可執程序文件名>
例:#gdb test
或? #gdb
?? ?(gdb) file test
?
(二)、退出
??? (gdb) quit
?
二、顯示和查找程序的源代碼
在調試時,一般要查看程序的源代碼。list 命令用于列出程序的源代碼,使用格式如下:
l?list? 顯示10行代碼,若再次運行該命令則顯示接下來的10行代碼
l?list 5,10? 顯示源代文件test.c中的第5行到第10行的代碼,
l?listtest.c:5,10? 顯示源文件test.c中第5行到第10行的代碼,在調試含有多個源文件的程序時使用
l?listget_sum? 顯示get_sum函數周圍的代碼
l?listtest.c:get_sum 顯示源文件test.c中get_sum函數周圍的代碼,在調試含多個源文件的程序時使用
l?search? <字符串> 用來從當前行向后查找第一個匹配的字符串
l?reverse-search<字符串> 用來從當前行向前查找第一個匹配的字符串
?
三、執行程序和獲得幫助
使用gdb test或(gdb) fieltest只是裝入程序,程序并沒有運行,如果要使程序運行,在gdb提示符下輸入run即可。
(gdb) run
Starting program:/tmp/test
1+2+...+100=4950
Program exitednormally.
?
如果想要詳細了解gdb某個命令的使用方法,可以使用help命令
(gdb)help list
(gdb)help all
?
四、設置和管理斷點
在調試程序時,往往需要程序在運行到某行、某個函數或某個條件發生時暫停下來,然后查看此時程序的狀態,如各個變量的值、某個表達式的值等。為此,可以設置斷點(break)。斷點使程序運行到某個位置時暫停下來,以便檢查和分析程序。
1、 以行號設置斷點
在gdb中,大部分都是使用break命令為程序設置斷點。而指定斷點時,最常用的是為某行設置斷點。例:
(gdb) break 7
Breakpoint 1 at0x80483c0: file test.c, line 7.
?
??? 然后我們輸入run命令運行程序:
(gdb) run
Starting program:/tmp/test
Breakpoint 1,get_sum (n=100) at test.c:7
7????????? sum +=i;
可以看到,程序運行完第6行的指令后就暫停了,第7行的代碼并沒有執行而是被gdb的斷點中斷了。此時,我們可以查看各個變量和表達式的值,以了解程序的當前狀態。
?
2、以函數名設置斷點
在break命令后跟上函數名,就可以為函數設置斷點。例:
(gdb) break ?get_sum
Breakpoint 1 at0x80483aa: file test.c, line 5.
(gdb) run
Starting program:/tmp/test
Breakpoint 1,get_sum (n=100) at test.c:5
5???????? int sum = 0,i;
可以看到,程序在第5行停了上來。
?
3、以條件表達式設置斷點
程序在運行過程中,當某個條件滿足時,程序在某行中斷暫停執行
方法1 命令格式:break行號或函數名?if? 條件
例:
(gdb) clear
Deleted breakpoint1
(gdb) break7 if i==99
Breakpoint 2 at0x80483c0: file test.c, line 7.
(gdb) run
The program beingdebugged has been started already.
Start it from thebeginning? (y or n) y
Starting program:/tmp/test
Breakpoint 2,get_sum (n=100) at test.c:7
7????????? sum +=i;
? ??可以看到,運行程序后在i==99時,程序中斷在第7行。
?
方法2? watch?<條件表達式>
例:(gdb) watchi==99
No symbol"i" in current context.
(gdb) list 6
1?????? #include <stdio.h>
2
3?????? int get_sum(int n)
4??????? {
5???????? int sum = 0,i;
6???????? for (i=0;i<n;i++)
7????????? sum +=i;
8????????? return sum;
9???????? }
10
(gdb) break 6
Breakpoint 1 at0x80483b1: file test.c, line 6.
?(gdb) run
Starting program:/tmp/test
Breakpoint 1,get_sum (n=100) at test.c:6
6???????? for (i=0;i<n;i++)
Gdb運行后,以命令watch i==99設置條件斷點,但是失敗了,gdb提示在當前程序的上下文中沒有符號i,這是因為此時test程序沒有運行,變量i還沒有被定義。
為了解決這個問題,首先在第6行設置斷點,然后使用run命令運行程序,程序暫停在第6行,此時第5行的語句已經被執行,所以變量i已經定義。這時就可以使用watch i==99設置斷點了。
?
4、查看當前設置的斷點
使用info breakpoints命令可以查看當前所有的中斷點,例如:
(gdb) break 7
Breakpoint 1 at0x80483c0: file test.c, line 7.
(gdb) break 15 ifresult=5050
Breakpoint 2 at0x8048405: file test.c, line 15.
(gdb) info breakpoints
Num Type?????????? Disp Enb Address??? What
1?? breakpoint???? keep y??0x080483c0 in get_sum at test.c:7
2?? breakpoint???? keep y??0x08048405 in main at test.c:15
? ??????stop only if result = 5050
其中:Num? 表示斷點的編號
????? Type?說明類型,類型為breakpoint是指中斷
????? Disp?指明中斷點在生效一次后是否失去作用,是則為disp,不是則為keep
????? Enb?說明中斷點是否有效,有效則為y,無效則為n
????? Address列? 說明中斷所處的內存地址
????? What列? 列出中斷發生在哪個函數的第幾行
????? Stop only if result==5050? 說明這是一個條件中斷
?
5、使中斷失效或有效
disable?<斷點編號>?可以使某個斷點失效,程序運行到該斷點時不會停下來而是繼續運行
enable?<斷點編號>?可以使某個斷點恢復有效
例:
(gdb) info breakpoints
Num Type?????????? Disp Enb Address??? What
1??breakpoint???? keep y?? 0x080483c0 in get_sum at test.c:7
2??breakpoint???? keep y?? 0x08048405 in main at test.c:15
???????stop only if result = 5050
(gdb) disable 2
(gdb) info breakpoints
Num Type?????????? Disp Enb Address??? What
1??breakpoint???? keep y?? 0x080483c0 in get_sum at test.c:7
2??breakpoint???? keep n?? 0x08048405 in main at test.c:15
???????stop only if result = 5050
(gdb) enable 2
(gdb) info breakpoints
Num Type?????????? Disp Enb Address??? What
1??breakpoint???? keep y?? 0x080483c0 in get_sum at test.c:7
2??breakpoint???? keep y?? 0x08048405 in main at test.c:15
???????stop only if result = 5050
?
6、刪除斷點
l?clear?? 刪除程序中所有的斷點
l?clear<行號>??刪除此行的斷點
l?clear<函數名>?刪除該函數的斷點
l?delete<斷點編號>?刪除指定編號的斷點。如果一次要刪除多個斷點,各個斷點編號以空格隔開
例:
(gdb) break 6
Breakpoint 1 at0x80483b1: file test.c, line 6.
(gdb) break 7
Breakpoint 2 at0x80483c0: file test.c, line 7.
(gdb) break 8 ifsum==5050
Breakpoint 3 at0x80483cf: file test.c, line 8.
(gdb) info breakpoints
Num Type?????????? Disp Enb Address??? What
1?? breakpoint???? keep y??0x080483b1 in get_sum at test.c:6
2?? breakpoint???? keep y??0x080483c0 in get_sum at test.c:7
3?? breakpoint???? keep y??0x080483cf in get_sum at test.c:8
??????? stop only if sum == 5050
(gdb) clear 6
Deleted breakpoint1
(gdb) info breakpoints
Num Type?????????? Disp Enb Address??? What
2?? breakpoint???? keep y??0x080483c0 in get_sum at test.c:7
3?? breakpoint???? keep y??0x080483cf in get_sum at test.c:8
??????? stop only if sum == 5050
(gdb) delete 2 3
(gdb) info breakpoints
No breakpoints orwatchpoints.
?
7、display <表示式>?? 在每次程序停在斷點位置時,自動顯示表達式中的內容
8、commands
? >命令
? >end??????結束命令輸入
作用:指定在程序到達斷點位置時需要執行的調試器命令
?
五、查看和設置變量的值
當程序執行到中斷點暫停執行時,需要查看變量或表達式的值,借此了解程序的執行狀態
1、print命令
作用:print命令一般用來打印變量或表達式的值,也可以用來打印內存中從某個變量開始的一段
????? 存區域的內容,還可以用來對某個變量進行賦值。
格式:
print? <變量或表達式>? 打印變量或表達式的當前值,gdb會用偽變量($n)來保存輸出值以備用
?Print? <變量=值>;?? 對變量進行賦值
?Print? <表達式@要打印的值的個數n>? 打印以表達式值開始的n個數
例:
(gdb) break 7
Breakpoint 4 at0x80483c0: file test.c, line 7.
(gdb) run
Starting program:/tmp/test
Breakpoint 4,get_sum (n=100) at test.c:7
7????????? sum += i;
(gdb)print i<n?? 打印出i<n表達式的值,顯然這個表達式為真,因此值為1
$1 = 1
(gdb) print i
$2 = 0
(gdb) print sum
$3 = 0
(gdb) print i=200
$4 = 200
(gdb) continue
Continuing.
1+2+...+100=200
Program exitednormally.
?
2、whatis 命令
作用:用來顯示某個變量或表達式值的數據類型
格式:whatis?<變量或表達式>
例:
(gdb) break 7
Note: breakpoint 4also set at pc 0x80483c0.
Breakpoint 5 at0x80483c0: file test.c, line 7.
(gdb) run
Starting program:/tmp/test
?
Breakpoint 4,get_sum (n=100) at test.c:7
7????????? sum += i;
(gdb) whatis i
type = int
(gdb) whatis sum+0.5
type = double
?
3、set 命令
作用:用來給變量賦值
格式:set?variable 變量=值?? 相當于print 變量=值
例:set variable i=200,相當于print i=200
六、控制程序的執行
當程序執行到指定的中斷點,查看了變量或表達式的值后,可以讓程序繼續運行。也可以讓程序一步一步地執行,或可以讓程序地直運行下去直到下一個斷點或運行完為止。
1、?continue命令
作用:讓程序繼續運行,直到下一個斷點或運行完為止。
格式:continue
?
2、?kill命令
作用:用于結束當前程序的調試
例:(gdb) kill
Kill the program being debugged? (y or n) y
?
3、?next和step命令
作用:一次一條地執行程序代碼
next和step的區別:
1)、如果遇到函數調用,next會把該函數調用當作一條語句來執行,再次輸入next會執行函數調用
??? 后的語句
2)、step則會跟蹤進入函數,一次一條地執行函數內的代碼,直到函數的代碼執行完,才執行函數調
??? 用后的語句
例:next語句的用法
(gdb) list 1,17
1??????#include <stdio.h>
2
3??????int get_sum(int n)
4??????{
5????????int sum = 0,i;
6????????for(i=0;i<n;i++)
7?????????sum += i;
8????????return sum;
9??????}
10
11?????int main()
12?????{
13???????int i=100,result;
14???????result = get_sum(i);
15???????printf("1+2+...+%d=%d\n",i,result);
16???????return 0;
17?? ???}
(gdb) break 13
Breakpoint 1 at 0x80483f0: file test.c, line13.
(gdb) run
Starting program: /tmp/test
?
Breakpoint 1, main () at test.c:13
13???????int i=100,result;
(gdb) next
14???????result = get_sum(i);
(gdb) next
15???????printf("1+2+...+%d=%d\n",i,result);
(gdb) next
1+2+...+100=4950
16???????return 0;
(gdb) next
17?????}
(gdb) next
0x4003328b in __libc_start_main () from/lib/libc.so.6
(gdb) next
Single stepping until exit from function__libc_start_main,
which has no line number information.
Program exited normally.
?
例:step語句的用法
(gdb) list 1,17
1?????? #include <stdio.h>
2
3?????? int get_sum(int n)
4?????? {
5???????? int sum = 0,i;
6???????? for(i=0;i<n;i++)
7????????? sum += i;
8???????? return sum;
9?????? }
10
11????? int main()
12????? {
13??????? int i=100,result;
14??????? result = get_sum(i);
15???????printf("1+2+...+%d=%d\n",i,result);
16??????? return 0;
17????? }
(gdb) break 13
Breakpoint 1 at 0x80483f0: filetest.c, line 13.
(gdb) run
Starting program: /tmp/test
?
Breakpoint 1, main () at test.c:13
13????? ??int i=100,result;
(gdb) step
14??????? result = get_sum(i);
(gdb) step
get_sum (n=100) at test.c:5
5???????? int sum = 0,i;
(gdb) step
6???????? for(i=0;i<n;i++)
(gdb) step
7????????? sum += i;
(gdb) step
6???????? for(i=0;i<n;i++)
(gdb) step
7????????? sum += i;
?(gdb) step
6???????? for(i=0;i<n;i++)
(gdb) step
7????? ????sum += i;
?
4、?nexti和stepi命令
作用:用來單步執行一條機器指令,注意不是單步執行一行語句。單步執行一行語句的命令是
????? next和step命令
????????? 通常一條語句由多條機器指令構成
注意:nexti和next類似,不會跟蹤進入函數內部去執行
????? Stepi和step類似,跟蹤進入函數執行。
?
第二部分? 程序調試
一、常用調試技巧
1、代碼檢查-試運行-出錯法:
先運行程序,并觀察其輸出結果,如果不能正常工作,修改程序后重新嘗試
2、取樣法:在程序中增加一些語句來獲得更多關于程序內部運行情況的信息
3、受控執行法:直接檢查程序的執行情況
?
程序調試可以分為5個階段
l? 測試:找出程序中存在的缺陷或錯誤
l? 固化:讓程序的錯誤可重現
l? 定位:確定相關的代碼行
l? 糾正:修改代碼糾正錯誤
l? 驗證:確定修改解決了問題
二、有漏洞的程序
如下程序是通過“冒泡排序算法”對一個類型為item的結構數組進行排序,排序方法為基于結構中的成員key以升序排列數組成員。程序用一個樣本來數組來測試該程序。
程序:debug2.c
/*?? 1?*/? typedef struct {
/*?? 2?*/????? char *data;
/*?? 3?*/????? int key;
/*?? 4?*/? } item;
/*?? 5? *///給結構體賦值
/*?? 6?*/? item array[] = {
/*?? 7?*/????? {"bill", 3},
/*?? 8?*/????? {"neil", 4},
/*?? 9?*/????? {"john", 2},
/*?10? */????? {"rick", 5},
/*?11? */????? {"alex", 1},
/*?12? */? };
/*?13? */
/*?14? */? sort(a,n)//默認返回值是int,函數聲明,若無item *a會有警告
/*?15? */? item *a;//聲明上面a是指針
/*?16? */? {
/*?17? */????? int i = 0, j = 0;
/*?18? */????? int s = 1;
/*?19? */
/*?20? */????? for(; i < n && s != 0; i++) {
/*?21? */????????????? s = 0;
/*?22? */????????????? for(j = 0; j < n; j++) {
/*?23? */????????????????????? if(a[j].key >a[j+1].key) {
/*?24? */????????????????????????????? item t = a[j];
/*?25? */????????????????????????????? a[j] = a[j+1];
/*?26? */????????????????????????????? a[j+1] = t;
/*?27? */????????????????????????????? s++;
/*?28? */????????????????????? }
/*?29? */????????????? }
/*?30? */????????????? n--;
/*?31? */????? }
/*?32? */? }
/*?33? */? #include <stdio.h>
/*?34? */? main()
/*?35? */? {
/*?36? */????????? int i;
/*?37? */????????? sort(array,5);
/*?38? */????????? for(i = 0; i < 5; i++)
/*?39? */????????????????? printf("array[%d] = {%s,%d}\n",
/*?40? */?????? ??????????????????i, array[i].data,array[i].key);
/*?41? */? }
編譯:cc –o debug2 debug2.c
輸出結果:
# ./debug2
array[0] = {john, 2}
array[1] = {alex, 1}
array[2] = {(null), 0}
array[3] = {bill, 3}
array[4] = {neil, 4}
我們希望的結果是:
array[0] = {alex, 1}
array[1] = {john, 2}
array[2] = {bill, 3}
array[3] = {neil, 4}
array[4] = {rick, 5}
?
三、程序調試
(一)、捕獲數組訪問錯誤
如果想捕獲數組方面的錯誤,最好的方法是增加數組元素的大小,因為這樣同時也增加了錯誤的大小。如果只是在數組的結尾之后讀取一個字節,可能會看不到有錯誤發生,因為分配給程序的內存大小會取整數到操作系統的特定邊界,一般分配的內存大小以8K為單位遞增。
如果增加數組元素的大小,如在debug.c程序中將item結構中的成員data擴大為一個可以容納4096個字符的數組,對不存在的數組進行訪問時,內存地址就有可能落在分配給這個程序的內存之外的地方,因為數組的每個元素大小為4K,所以錯誤使用的內存將落在數組結尾之后0K~4K范圍內。
1.????測試:將item結構中的成員data擴大為可容納4096個字符的數組
/*?? 1?*/? typedef struct {
/*?? 2?*/????? char *data;?????? _____________________
/*?? 3?*/????? int key;
/*?? 4?*/? } item;
?
2.????編譯:cc –o debug3 debug3.c
3.????執行結果:#./debug3
________________________________________________________
(二)、代碼檢查
當程序的運行情況和預期不同時,最好重新閱讀程序。
編譯器可以用來幫助完成代碼檢查
gcc? -Wall?-pedantic –ansi
??? 使用man gcc,閱讀這三個參數的含義
-ansi? Thisturns off certain features of GCC that are incompatible
?????????? with ISO C90 (when compiling C code)
-pedantic
?????????? Issueall the warnings demanded by strict ISO C and ISO C++;
??????????reject all programs that use forbidden extensions, and some otherprograms that do not follow
ISOC and ISO C++.
-Wall
This enables all thewarnings about constructions that some users consider questionable and that areeasy
to avoid(or modify toprevent the warning), even in conjunction with macros.
使用gcc的三個選項重新編譯debug2.c
#gcc –Wall –pedantic –ansi –odebug2 debug2.c
debug2.c:15:warning:return typedefaults to “int”
debug2.c: in function “sort”
debug2.c:32: warning:controlreaches end of non-void function
debug2.c: At top level:
debug2.c:35:warning:return typedefaults to “int”
debug2.c:in function “main”
debug2.c:41:warning:controlreaches end of non-void function
?
(三)、取樣法
在程序中添加一些代碼來收集更多與程序運行相關的信息的方法。
取樣法的常見做法是,在程序中添加printf函數調用以打印出變量在程序運行的不同階段的值,但需要注意,無論何時程序發生了改動,這一過程都將帶來更多的編輯和編譯次數,而且,在程序錯誤被修復后,我們還要把這些額外的代碼刪除掉。
程序調試時最常用的取樣方法有:
1、用C語言的預處理器有選擇地包括取樣代碼,這樣只需要重新編譯程序就可以包含或去除調試代碼。
?? 實現方法: #ifdef? DEBUG
???????? ????????Printf(“variable x has value =%d\n”,x);
?????????????#endif
?? 在編譯程序時加上編譯器標志-DDEBUG。
?????? 如果加上這個標志,就定義了DEBUG符號,從而可以在程序中包含額外的調試代碼,如果未
?????? 加上該標志,這些調試將被刪除。
?? 還可以用數值調試宏來完成更復雜的調試應用,例:
????? #define?BASIC_DEBUG 1
????? #define?EXTRA_DEBUG 2
?????#define? SUPER_DEBUG 4
?
????? #if (DEBUG & EXTRA_DEBUG)
???????? Prinf ();
????? #endif
這樣,當編譯器標志-DDEBUG=5,將啟用BASIC_DEBUG和SUPER_DEBUG,但不包括EXTRA_DEBUG。標志-DDEBUG=0將禁用所有的調試信息。
C語言預處理器定義的一些宏可以幫助調試:
C語言預處理器定義的宏
| 宏 | 說明 |
| __LINE__ | 代表當前行號的十進制常數 |
| __FILE__ | 代表當前文件名的字符串 |
| __DATE__ | mmm dd yyyy格式的字符串,代表當前日期 |
| __TIME__ | hh:mm:ss格式的字符串,代表當前時間 |
例:cinfo.c程序
#include <stdio.h>
#include <stdlib.h>
?
int main()
{
#ifdef DEBUG
???printf("Compiled: " __DATE__ " at " __TIME__"\n");
???printf("This is line %d of file %s\n", __LINE__, __FILE__);
#endif
???printf("hello world\n");
???exit(0);
}
編譯:cc –o cinfo –DDEBUG cinfo.c
執行# ./cinfo
Compiled: Jul 23 2012 at 07:45:37
This is line 8 of file cinfo.c
hello world
?
2、使用printf函數幫助調試
在程序中增加一個用為調試標志的全局變量,這使得用戶要吧在命令行上通過-d選項切換是否啟用調試模式,即使程序已經發布了,仍然可以這么做,訪方法同時還會在程序中增加一個用于記錄調試信息的函數,例:
if(debug){
??? sprint(msg, …);
??? write_debug(msg);
??? }
應該將調試信息輸出到標準錯誤輸出stderr。
當然這樣做也有一個明顯的不足,就是程序長度會有所增加。程序的長度可能會增加20%~30%。
(四)、程序的受權執行---使用gdb進行調試
使用gdb運行debug3.c程序
編譯:#cc –g –o debug3 debug3.c
調試:#gdb debug3
(gdb) run
Starting program:/root/course/chapter10/debug3
?
Program receivedsignal SIGSEGV, Segmentation fault.
0x0804845f in sort(a=0x80497e0, n=5) at debug3.c:23
23????? /*?23? */????????????????????? if(a[j].key >a[j+1].key) {
可以看到程序停在了debug3.c的第23行
?
1、棧跟蹤---backtrace(縮寫為bt)
(gdb) backtrace
#0? 0x0804845f in sort (a=0x80497e0, n=5) atdebug3.c:23
#1? 0x08048573 in main () at debug3.c:37
??? 可以看出sort函數由同一個文件中的main函數在第37行調用的
?
2、? 檢查變量
(gdb) print j
$1 = 4???????? 將值4賦給了偽變量$1,后續的命令將輸出的結果依次頑抗到保存到$2, $3等中
局部變量j的值是4意味著程序嘗試執行的這樣的一條指令:
if(a[4].key > a[4+1].key)
我們傳遞給sort函數的數組array只有5個元素,它們的下標從0~4,說明這條語句讀的是一個不存在的數組元素array[5]。循環計數器變量j取了一個錯誤的值。
?
??? 打印數組元素中的值
(gdb) print a[3]
$3 = {data ="alex", '\0' <repeats 4091 times>, key = 1}
?
3、? 更改源代碼----更改后的源程序為debug4.c
將第22行代碼由for(j=0; j<n; j++)
???????? 更改為:for(j=0; j<n-1; j++)
重新編譯debug4.c??? #cc –g –o debug4? debug4.c,編程完成后重新執行:
array[0] = {john, 2}
array[1] = {alex, 1}
array[2] = {bill, 3}
array[3] = {neil, 4}
array[4] = {rick, 5}??? 程序運行仍然不正確
?
四、調試程序debug4
在sort()函數中有兩個循環。外層循環針對每個數組元素執行一次,它的循環計數變量是i。內層循環的作用是交換相鄰的兩個元素。總的效果是讓比較小的元素像“氣泡”一樣“冒”到數組的頂部。外層循環每執行一次,數組中的最大的元素就會“下沉”到數組的底部。我們可以通過在外層循環中停止此程序的運行并檢查數組的狀態來核實這一點
1.???? 設置斷點
#gdb debug4
(gdb) break 22
Breakpoint 1 at0x804841e: file debug4.c, line 22.
?
2.設置到達斷點時顯示的內容
(gdb) display array[0]@5
?
3. 設置程序運行到達斷點時,顯示要查看的數據,然后繼續執行
(gdb) commands
Type commands forwhen breakpoint 1 is hit, one per line.
End with a linesaying just "end".
>cont
>end
?
4、設置完成后運行程序
(gdb) run
Starting program:/root/course/chapter10/debug4
?
Breakpoint 1, sort (a=0x8049800,n=5) at debug4.c:22
22????? /*?22? */????????????? for(j = 0; j < n-1; j++) {
1: array[0]@5 = {{data ="bill", '\0' <repeats 4091 times>, key = 3}, {
???data = "neil", '\0' <repeats 4091 times>, key = 4}, {
???data = "john", '\0' <repeats 4091 times>, key = 2}, {
???data = "rick", '\0' <repeats 4091 times>, key = 5}, {
???data = "alex", '\0' <repeats 4091 times>, key = 1}}
?
Breakpoint 1, sort (a=0x8049800,n=4) at debug4.c:22
22????? /*?22? */????????????? for(j = 0; j < n-1; j++) {
1: array[0]@5 = {{data ="bill", '\0' <repeats 4091 times>, key = 3}, {
???data = "john", '\0' <repeats 4091 times>, key = 2}, {
???data = "neil", '\0' <repeats 4091 times>, key = 4}, {
???data = "alex", '\0' <repeats 4091 times>, key = 1}, {
???data = "rick", '\0' <repeats 4091 times>, key = 5}}
?
Breakpoint 1, sort (a=0x8049800,n=3) at debug4.c:22
22????? /*?22? */????????????? for(j = 0; j < n-1; j++) {
1: array[0]@5 = {{data ="john", '\0' <repeats 4091 times>, key = 2}, {
???data = "bill", '\0' <repeats 4091 times>, key = 3}, {
???data = "alex", '\0' <repeats 4091 times>, key = 1}, {
???data = "neil", '\0' <repeats 4091 times>, key = 4}, {
???data = "rick", '\0' <repeats 4091 times>, key = 5}}
array[0] = {john, 2}
array[1] = {alex, 1}
array[2] = {bill, 3}
array[3] = {neil, 4}
array[4] = {rick, 5}
Program exitedwith code 025.?? ---不常見的通出碼,因為程序本身未調用exit函數,并且也沒有從main函數返回一個值,該退出碼沒有實際意義。
??? 好象
?
5、用調試器打補丁
用調試器打補丁:通過將斷點的設置與相應的操作結合起來來嘗試修改程序,而不需要改變程序的源
??????????????? 碼和重新編譯。
針對debug4,需要在程序的第30行中斷程序,增加變量n的值,以使程序執行到第30行時,n的值并未發生變化
???
重新開始執行這個程序,首先必須刪除剛才設置的斷點和display命令的內容。可以用info查看曾經設置過的斷點及display命令的內容
(gdb) info display
Auto-display expressions now ineffect:
Num Enb Expression
1:?? y?array[0]@5
?
(gdb) info break
Num????Type?????????? Disp EnbAddress??? What
1??????breakpoint???? keep y?? 0x08048417 in sort at debug4.c:21
???????breakpoint already hit 3 times
???????cont
?
??? 禁用上述斷點與display,可以在今后必要的時候重新啟用這些保留的設置
(gdb) disable break 1
(gdb) disable display 1
(gdb) break 30
Breakpoint 2 at0x8048535: file debug4.c, line 30.
?
(gdb) commands 2
Type commands forwhen breakpoint 2 is hit, one per line.
End with a linesaying just "end".
>set variable n=n+1
>cont
>end
(gdb) run
Starting program:/root/course/chapter10/debug4
Breakpoint 2, sort (a=0x8049800, n=5) atdebug4.c:30
30?????/*? 30? */????????????? n--;
Breakpoint 2, sort (a=0x8049800, n=5) atdebug4.c:30
30?????/*? 30? */????????????? n--;
Breakpoint 2, sort (a=0x8049800, n=5) atdebug4.c:30
30?????/*? 30? */????????????? n--;
Breakpoint 2, sort (a=0x8049800, n=5) atdebug4.c:30
30?????/*? 30? */????????????? n--;
Breakpoint 2, sort (a=0x8049800, n=5) atdebug4.c:30
30?????/*? 30? */????????????? n--;
array[0] = {alex, 1}
array[1] = {john, 2}
array[2] = {bill, 3}
array[3] = {neil, 4}
array[4] = {rick, 5}
Program exited with code 025.
?
五、斷言
1、斷言
在程序的編寫過程中,有時系統的內部邏輯需要被確認沒有錯誤。X/Open提供了assert宏,它的作用是測試某個假設是否成立,如果不成立就停止程序的運行
#include <assert.h>
Void assert (int expression);
assert宏對表達式進行求值,如果結果非零,它就往標準錯誤寫一些診斷信息,然后調用abort函數結束程序的運行。
例:程序assert.c定義了一個函數,它的參數必須是一個正數,它用斷來來保護程序不受非法參數的影響
???? 1?#include <stdio.h>
???? 2?#include <math.h>
???? 3?#include <assert.h>
???? 4?#include <stdlib.h>
???? 5
???? 6?double my_sqrt(double x)
???? 7? {
???? 8?????assert(x >= 0.0);
???? 9?????return sqrt(x);
???10? }
???11
???12? int main()
???13? {
???14????? printf("sqrt +2 =%g\n", my_sqrt(2.0));
???15????? printf("sqrt -2 =%g\n", my_sqrt(-2.0));
???16????? exit(0);
???17? }
編譯: # cc -o assert assert.c-lm
執行: # ./assert
sqrt +2 = 1.41421
assert: assert.c:8: my_sqrt:Assertion `x >= 0.0' failed.
Aborted
試圖用一個負數來調用函數my_sqrt時,斷言失敗了,aasert宏給出了發生斷言沖突的文件名和行號,還給出了失敗的條件。程序被一個abort中斷陷阱終止了運行。這就是assert調用abort的結果
?
2.????使用-DNDEBUG選項編譯
使用-DNDEBUG選項重新編譯這個程序,斷言功能將被排除在編譯結果之外。當在my_sqrt()中調用sqrt函數時,得到的將是一個NaN(不是一個數字),表明一個無效結果
編譯: # cc -o assert -DNDEBUG ?assert.c-lm
執行: # ./assert
sqrt +2 = 1.41421
sqrt -2 = nan
總結
- 上一篇: 深度好文:select与非阻塞IO
- 下一篇: 遇到的一些小的tips