Linux學(xué)習(xí)gdb調(diào)試教程
GNU調(diào)試器(GNU Debugger, GDB)是一個(gè)開(kāi)源的源代碼級(jí)的調(diào)試工具,其性能優(yōu)良、功能齊全、易于使用,在嵌入式系統(tǒng)開(kāi)發(fā)過(guò)程中扮演著越來(lái)越重要的角色。接下來(lái)是小編為大家收集的Linux學(xué)習(xí)gdb調(diào)試教程,希望能幫到大家。
Linux學(xué)習(xí)gdb調(diào)試教程
一.gdb常用命令:
命令 | 描述 |
---|---|
backtrace(或bt) | 查看各級(jí)函數(shù)調(diào)用及參數(shù) |
finish | 連續(xù)運(yùn)行到當(dāng)前函數(shù)返回為止,然后停下來(lái)等待命令 |
frame(或f) 幀編號(hào) | 選擇棧幀 |
info(或i) locals | 查看當(dāng)前棧幀局部變量的值 |
list(或l) | 列出源代碼,接著上次的位置往下列,每次列10行 |
list 行號(hào) | 列出從第幾行開(kāi)始的源代碼 |
list 函數(shù)名 | 列出某個(gè)函數(shù)的源代碼 |
next(或n) | 執(zhí)行下一行語(yǔ)句 |
print(或p) | 打印表達(dá)式的值,通過(guò)表達(dá)式可以修改變量的值或者調(diào)用函數(shù) |
quit(或q) |
退出gdb 調(diào)試環(huán)境 |
set var | 修改變量的值 |
start |
開(kāi)始執(zhí)行程序,停在main 函數(shù)第一行語(yǔ)句前面等待命令 |
step(或s) | 執(zhí)行下一行語(yǔ)句,如果有函數(shù)調(diào)用則進(jìn)入到函數(shù)中 |
二.gdb學(xué)習(xí)小例:
#include <stdio.h>
int add_range(int low, int high)
{
int i, sum;
for (i = low; i <= high; i++)
sum = sum + i;
return sum;
}
int main(void)
{
int result[100];
result[0] = add_range(1, 10);
result[1] = add_range(1, 100);
printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
return 0;
}
add_range函數(shù)從low加到high,在main函數(shù)中首先從1加到10,把結(jié)果保存下來(lái),然后從1加到100,再把結(jié)果保存下來(lái),最后打印的兩個(gè)結(jié)果是:
result[0]=55
result[1]=5105
第一個(gè)結(jié)果正確[20],第二個(gè)結(jié)果顯然不正確,在小學(xué)我們就聽(tīng)說(shuō)過(guò)高斯小時(shí)候的故事,從1加到100應(yīng)該是5050。一段代碼,第一次運(yùn)行結(jié)果是對(duì)的,第二次運(yùn)行卻不對(duì),這是很常見(jiàn)的一類錯(cuò)誤現(xiàn)象,這種情況不應(yīng)該懷疑代碼而應(yīng)該懷疑數(shù)據(jù),因?yàn)榈谝淮魏偷诙芜\(yùn)行的都是同一段代碼,如果代碼是錯(cuò)的,那為什么第一次的結(jié)果能對(duì)呢?然而第一次和第二次運(yùn)行時(shí)相關(guān)的數(shù)據(jù)卻有可能不同,錯(cuò)誤的數(shù)據(jù)會(huì)導(dǎo)致錯(cuò)誤的結(jié)果。在動(dòng)手調(diào)試之前,讀者先試試只看代碼能不能看出錯(cuò)誤原因,只要前面幾章學(xué)得扎實(shí)就應(yīng)該能看出來(lái)。
在編譯時(shí)要加上-g選項(xiàng),生成的可執(zhí)行文件才能用gdb進(jìn)行源碼級(jí)調(diào)試:
$ gcc -g main.c -o main
$ gdb main
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb)
-g選項(xiàng)的作用是在可執(zhí)行文件中加入源代碼的信息,比如可執(zhí)行文件中第幾條機(jī)器指令對(duì)應(yīng)源代碼的第幾行,但并不是把整個(gè)源文件嵌入到可執(zhí)行文件中,所以在調(diào)試時(shí)必須保證gdb能找到源文件。gdb提供一個(gè)類似Shell的命令行環(huán)境,上面的(gdb)就是提示符,在這個(gè)提示符下輸入help可以查看命令的類別:
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
也可以進(jìn)一步查看某一類別中有哪些命令,例如查看files類別下有哪些命令可用:
(gdb) help files
Specifying and examining files.
List of commands:
add-shared-symbol-files -- Load the symbols from shared objects in the dynamic linker's link map
add-symbol-file -- Load symbols from FILE
add-symbol-file-from-memory -- Load the symbols out of memory from a dynamically loaded object file
cd -- Set working directory to DIR for debugger and program being debugged
core-file -- Use FILE as core dump for examining memory and registers
directory -- Add directory DIR to beginning of search path for source files
edit -- Edit specified file or function
exec-file -- Use FILE as program for getting contents of pure memory
file -- Use FILE as program to be debugged
forward-search -- Search for regular expression (see regex(3)) from last line listed
generate-core-file -- Save a core file with the current state of the debugged process
list -- List specified function or line
...
現(xiàn)在試試用list命令從第一行開(kāi)始列出源代碼:
(gdb) list 1
1 #include <stdio.h>
2
3 int add_range(int low, int high)
4 {
5 int i, sum;
6 for (i = low; i <= high; i++)
7 sum = sum + i;
8 return sum;
9 }
10
一次只列10行,如果要從第11行開(kāi)始繼續(xù)列源代碼可以輸入
(gdb) list
也可以什么都不輸直接敲回車,gdb提供了一個(gè)很方便的功能,在提示符下直接敲回車表示重復(fù)上一條命令。
(gdb) (直接回車)
11 int main(void)
12 {
13 int result[100];
14 result[0] = add_range(1, 10);
15 result[1] = add_range(1, 100);
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
17 return 0;
18
gdb的很多常用命令有簡(jiǎn)寫(xiě)形式,例如list命令可以寫(xiě)成l,要列一個(gè)函數(shù)的源代碼也可以用函數(shù)名做參數(shù):
(gdb) l add_range
1 #include <stdio.h>
2
3 int add_range(int low, int high)
4 {
5 int i, sum;
6 for (i = low; i <= high; i++)
7 sum = sum + i;
8 return sum;
9 }
10
現(xiàn)在退出gdb的環(huán)境:
(gdb) quit
我們做一個(gè)實(shí)驗(yàn),把源代碼改名或移到別處再用gdb調(diào)試,這樣就列不出源代碼了:
$ mv main.c mian.c
$ gdb main
...
(gdb) l
5 main.c: No such file or directory.
in main.c
可見(jiàn)gcc的-g選項(xiàng)并不是把源代碼嵌入到可執(zhí)行文件中的,在調(diào)試時(shí)也需要源文件?,F(xiàn)在把源代碼恢復(fù)原樣,我們繼續(xù)調(diào)試。首先用start命令開(kāi)始執(zhí)行程序:
$ gdb main
...
(gdb) start
Breakpoint 1 at 0x80483ad: file main.c, line 14.
Starting program: /home/akaedu/main
main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb)
gdb停在main函數(shù)中變量定義之后的第一條語(yǔ)句處等待我們發(fā)命令,gdb列出的這條語(yǔ)句是即將執(zhí)行的下一條語(yǔ)句。我們可以用next命令(簡(jiǎn)寫(xiě)為n)控制這些語(yǔ)句一條一條地執(zhí)行:
(gdb) n
15 result[1] = add_range(1, 100);
(gdb) (直接回車)
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb) (直接回車)
result[0]=55
result[1]=5105
17 return 0;
用n命令依次執(zhí)行兩行賦值語(yǔ)句和一行打印語(yǔ)句,在執(zhí)行打印語(yǔ)句時(shí)結(jié)果立刻打出來(lái)了,然后停在return語(yǔ)句之前等待我們發(fā)命令。雖然我們完全控制了程序的執(zhí)行,但仍然看不出哪里錯(cuò)了,因?yàn)殄e(cuò)誤不在main函數(shù)中而在add_range函數(shù)中,現(xiàn)在用start命令重新來(lái)過(guò),這次用step命令(簡(jiǎn)寫(xiě)為s)鉆進(jìn)add_range函數(shù)中去跟蹤執(zhí)行:
(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Breakpoint 2 at 0x80483ad: file main.c, line 14.
Starting program: /home/akaedu/main
main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) s
add_range (low=1, high=10) at main.c:6
6 for (i = low; i <= high; i++)
這次停在了add_range函數(shù)中變量定義之后的第一條語(yǔ)句處。在函數(shù)中有幾種查看狀態(tài)的辦法,backtrace命令(簡(jiǎn)寫(xiě)為bt)可以查看函數(shù)調(diào)用的棧幀:
(gdb) bt
#0 add_range (low=1, high=10) at main.c:6
#1 0x080483c1 in main () at main.c:14
可見(jiàn)當(dāng)前的add_range函數(shù)是被main函數(shù)調(diào)用的,main傳進(jìn)來(lái)的參數(shù)是low=1, high=10。main函數(shù)的棧幀編號(hào)為1,add_range的棧幀編號(hào)為0?,F(xiàn)在可以用info命令(簡(jiǎn)寫(xiě)為i)查看add_range函數(shù)局部變量的值:
(gdb) i locals
i = 0
sum = 0
如果想查看main函數(shù)當(dāng)前局部變量的值也可以做到,先用frame命令(簡(jiǎn)寫(xiě)為f)選擇1號(hào)棧幀然后再查看局部變量:
(gdb) f 1
#1 0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) i locals
result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
注意到result數(shù)組中有很多元素具有雜亂無(wú)章的值,我們知道未經(jīng)初始化的局部變量具有不確定的值。到目前為止一切正常。用s或n往下走幾步,然后用print命令(簡(jiǎn)寫(xiě)為p)打印出變量sum的值:
(gdb) s
7 sum = sum + i;
(gdb) (直接回車)
6 for (i = low; i <= high; i++)
(gdb) (直接回車)
7 sum = sum + i;
(gdb) (直接回車)
6 for (i = low; i <= high; i++)
(gdb) p sum
class="main">
Linux學(xué)習(xí)gdb調(diào)試教程
Linux學(xué)習(xí)gdb調(diào)試教程
Linux學(xué)習(xí)gdb調(diào)試教程
Linux學(xué)習(xí)gdb調(diào)試教程
一.gdb常用命令:
命令 | 描述 |
---|---|
backtrace(或bt) | 查看各級(jí)函數(shù)調(diào)用及參數(shù) |
finish | 連續(xù)運(yùn)行到當(dāng)前函數(shù)返回為止,然后停下來(lái)等待命令 |
frame(或f) 幀編號(hào) | 選擇棧幀 |
info(或i) locals | 查看當(dāng)前棧幀局部變量的值 |
list(或l) | 列出源代碼,接著上次的位置往下列,每次列10行 |
list 行號(hào) | 列出從第幾行開(kāi)始的源代碼 |
list 函數(shù)名 | 列出某個(gè)函數(shù)的源代碼 |
next(或n) | 執(zhí)行下一行語(yǔ)句 |
print(或p) | 打印表達(dá)式的值,通過(guò)表達(dá)式可以修改變量的值或者調(diào)用函數(shù) |
quit(或q) |
退出gdb 調(diào)試環(huán)境 |
set var | 修改變量的值 |
start |
開(kāi)始執(zhí)行程序,停在main 函數(shù)第一行語(yǔ)句前面等待命令 |
step(或s) | 執(zhí)行下一行語(yǔ)句,如果有函數(shù)調(diào)用則進(jìn)入到函數(shù)中 |
二.gdb學(xué)習(xí)小例:
#include <stdio.h>
int add_range(int low, int high)
{
int i, sum;
for (i = low; i <= high; i++)
sum = sum + i;
return sum;
}
int main(void)
{
int result[100];
result[0] = add_range(1, 10);
result[1] = add_range(1, 100);
printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
return 0;
}
add_range函數(shù)從low加到high,在main函數(shù)中首先從1加到10,把結(jié)果保存下來(lái),然后從1加到100,再把結(jié)果保存下來(lái),最后打印的兩個(gè)結(jié)果是:
result[0]=55
result[1]=5105
第一個(gè)結(jié)果正確[20],第二個(gè)結(jié)果顯然不正確,在小學(xué)我們就聽(tīng)說(shuō)過(guò)高斯小時(shí)候的故事,從1加到100應(yīng)該是5050。一段代碼,第一次運(yùn)行結(jié)果是對(duì)的,第二次運(yùn)行卻不對(duì),這是很常見(jiàn)的一類錯(cuò)誤現(xiàn)象,這種情況不應(yīng)該懷疑代碼而應(yīng)該懷疑數(shù)據(jù),因?yàn)榈谝淮魏偷诙芜\(yùn)行的都是同一段代碼,如果代碼是錯(cuò)的,那為什么第一次的結(jié)果能對(duì)呢?然而第一次和第二次運(yùn)行時(shí)相關(guān)的數(shù)據(jù)卻有可能不同,錯(cuò)誤的數(shù)據(jù)會(huì)導(dǎo)致錯(cuò)誤的結(jié)果。在動(dòng)手調(diào)試之前,讀者先試試只看代碼能不能看出錯(cuò)誤原因,只要前面幾章學(xué)得扎實(shí)就應(yīng)該能看出來(lái)。
在編譯時(shí)要加上-g選項(xiàng),生成的可執(zhí)行文件才能用gdb進(jìn)行源碼級(jí)調(diào)試:
$ gcc -g main.c -o main
$ gdb main
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb)
-g選項(xiàng)的作用是在可執(zhí)行文件中加入源代碼的信息,比如可執(zhí)行文件中第幾條機(jī)器指令對(duì)應(yīng)源代碼的第幾行,但并不是把整個(gè)源文件嵌入到可執(zhí)行文件中,所以在調(diào)試時(shí)必須保證gdb能找到源文件。gdb提供一個(gè)類似Shell的命令行環(huán)境,上面的(gdb)就是提示符,在這個(gè)提示符下輸入help可以查看命令的類別:
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
也可以進(jìn)一步查看某一類別中有哪些命令,例如查看files類別下有哪些命令可用:
(gdb) help files
Specifying and examining files.
List of commands:
add-shared-symbol-files -- Load the symbols from shared objects in the dynamic linker's link map
add-symbol-file -- Load symbols from FILE
add-symbol-file-from-memory -- Load the symbols out of memory from a dynamically loaded object file
cd -- Set working directory to DIR for debugger and program being debugged
core-file -- Use FILE as core dump for examining memory and registers
directory -- Add directory DIR to beginning of search path for source files
edit -- Edit specified file or function
exec-file -- Use FILE as program for getting contents of pure memory
file -- Use FILE as program to be debugged
forward-search -- Search for regular expression (see regex(3)) from last line listed
generate-core-file -- Save a core file with the current state of the debugged process
list -- List specified function or line
...
現(xiàn)在試試用list命令從第一行開(kāi)始列出源代碼:
(gdb) list 1
1 #include <stdio.h>
2
3 int add_range(int low, int high)
4 {
5 int i, sum;
6 for (i = low; i <= high; i++)
7 sum = sum + i;
8 return sum;
9 }
10
一次只列10行,如果要從第11行開(kāi)始繼續(xù)列源代碼可以輸入
(gdb) list
也可以什么都不輸直接敲回車,gdb提供了一個(gè)很方便的功能,在提示符下直接敲回車表示重復(fù)上一條命令。
(gdb) (直接回車)
11 int main(void)
12 {
13 int result[100];
14 result[0] = add_range(1, 10);
15 result[1] = add_range(1, 100);
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
17 return 0;
18
gdb的很多常用命令有簡(jiǎn)寫(xiě)形式,例如list命令可以寫(xiě)成l,要列一個(gè)函數(shù)的源代碼也可以用函數(shù)名做參數(shù):
(gdb) l add_range
1 #include <stdio.h>
2
3 int add_range(int low, int high)
4 {
5 int i, sum;
6 for (i = low; i <= high; i++)
7 sum = sum + i;
8 return sum;
9 }
10
現(xiàn)在退出gdb的環(huán)境:
(gdb) quit
我們做一個(gè)實(shí)驗(yàn),把源代碼改名或移到別處再用gdb調(diào)試,這樣就列不出源代碼了:
$ mv main.c mian.c
$ gdb main
...
(gdb) l
5 main.c: No such file or directory.
in main.c
可見(jiàn)gcc的-g選項(xiàng)并不是把源代碼嵌入到可執(zhí)行文件中的,在調(diào)試時(shí)也需要源文件?,F(xiàn)在把源代碼恢復(fù)原樣,我們繼續(xù)調(diào)試。首先用start命令開(kāi)始執(zhí)行程序:
$ gdb main
...
(gdb) start
Breakpoint 1 at 0x80483ad: file main.c, line 14.
Starting program: /home/akaedu/main
main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb)
gdb停在main函數(shù)中變量定義之后的第一條語(yǔ)句處等待我們發(fā)命令,gdb列出的這條語(yǔ)句是即將執(zhí)行的下一條語(yǔ)句。我們可以用next命令(簡(jiǎn)寫(xiě)為n)控制這些語(yǔ)句一條一條地執(zhí)行:
(gdb) n
15 result[1] = add_range(1, 100);
(gdb) (直接回車)
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb) (直接回車)
result[0]=55
result[1]=5105
17 return 0;
用n命令依次執(zhí)行兩行賦值語(yǔ)句和一行打印語(yǔ)句,在執(zhí)行打印語(yǔ)句時(shí)結(jié)果立刻打出來(lái)了,然后停在return語(yǔ)句之前等待我們發(fā)命令。雖然我們完全控制了程序的執(zhí)行,但仍然看不出哪里錯(cuò)了,因?yàn)殄e(cuò)誤不在main函數(shù)中而在add_range函數(shù)中,現(xiàn)在用start命令重新來(lái)過(guò),這次用step命令(簡(jiǎn)寫(xiě)為s)鉆進(jìn)add_range函數(shù)中去跟蹤執(zhí)行:
(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Breakpoint 2 at 0x80483ad: file main.c, line 14.
Starting program: /home/akaedu/main
main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) s
add_range (low=1, high=10) at main.c:6
6 for (i = low; i <= high; i++)
這次停在了add_range函數(shù)中變量定義之后的第一條語(yǔ)句處。在函數(shù)中有幾種查看狀態(tài)的辦法,backtrace命令(簡(jiǎn)寫(xiě)為bt)可以查看函數(shù)調(diào)用的棧幀:
(gdb) bt
#0 add_range (low=1, high=10) at main.c:6
#1 0x080483c1 in main () at main.c:14
可見(jiàn)當(dāng)前的add_range函數(shù)是被main函數(shù)調(diào)用的,main傳進(jìn)來(lái)的參數(shù)是low=1, high=10。main函數(shù)的棧幀編號(hào)為1,add_range的棧幀編號(hào)為0?,F(xiàn)在可以用info命令(簡(jiǎn)寫(xiě)為i)查看add_range函數(shù)局部變量的值:
(gdb) i locals
i = 0
sum = 0
如果想查看main函數(shù)當(dāng)前局部變量的值也可以做到,先用frame命令(簡(jiǎn)寫(xiě)為f)選擇1號(hào)棧幀然后再查看局部變量:
(gdb) f 1
#1 0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) i locals
result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
注意到result數(shù)組中有很多元素具有雜亂無(wú)章的值,我們知道未經(jīng)初始化的局部變量具有不確定的值。到目前為止一切正常。用s或n往下走幾步,然后用print命令(簡(jiǎn)寫(xiě)為p)打印出變量sum的值:
(gdb) s
7 sum = sum + i;
(gdb) (直接回車)
6 for (i = low; i <= high; i++)
(gdb) (直接回車)
7 sum = sum + i;
(gdb) (直接回車)
6 for (i = low; i <= high; i++)
(gdb) p sum
$1 = 3
第一次循環(huán)i是1,第二次循環(huán)i是2,加起來(lái)是3,沒(méi)錯(cuò)。這里的$1表示gdb保存著這些中間結(jié)果,$后面的編號(hào)會(huì)自動(dòng)增長(zhǎng),在命令中可以用$1、$2、$3等編號(hào)代替相應(yīng)的值。由于我們本來(lái)就知道第一次調(diào)用的結(jié)果是正確的,再往下跟也沒(méi)意義了,可以用finish命令讓程序一直運(yùn)行到從當(dāng)前函數(shù)返回為止:
(gdb) finish
Run till exit from #0 add_range (low=1, high=10) at main.c:6
0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
Value returned is $2 = 55
返回值是55,當(dāng)前正準(zhǔn)備執(zhí)行賦值操作,用s命令賦值,然后查看result數(shù)組:
(gdb) s
15 result[1] = add_range(1, 100);
(gdb) p result
$3 = {55, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
第一個(gè)值55確實(shí)賦給了result數(shù)組的第0個(gè)元素。下面用s命令進(jìn)入第二次add_range調(diào)用,進(jìn)入之后首先查看參數(shù)和局部變量:
(gdb) s
add_range (low=1, high=100) at main.c:6
6 for (i = low; i <= high; i++)
(gdb) bt
#0 add_range (low=1, high=100) at main.c:6
#1 0x080483db in main () at main.c:15
(gdb) i locals
i = 11
sum = 55
由于局部變量i和sum沒(méi)初始化,所以具有不確定的值,又由于兩次調(diào)用是挨著的,i和sum正好取了上次調(diào)用時(shí)的值,原來(lái)這跟例 3.7 “驗(yàn)證局部變量存儲(chǔ)空間的分配和釋放”是一樣的道理,只不過(guò)我這次舉的例子設(shè)法讓局部變量sum在第一次調(diào)用時(shí)初值為0了。i的初值不是0倒沒(méi)關(guān)系,在for循環(huán)中會(huì)賦值為0的,但sum如果初值不是0,累加得到的結(jié)果就錯(cuò)了。好了,我們已經(jīng)找到錯(cuò)誤原因,可以退出gdb修改源代碼了。如果我們不想浪費(fèi)這次調(diào)試機(jī)會(huì),可以在gdb中馬上把sum的初值改為0繼續(xù)運(yùn)行,看看這一處改了之后還有沒(méi)有別的Bug:
(gdb) set var sum=0
(gdb) finish
Run till exit from #0 add_range (low=1, high=100) at main.c:6
0x080483db in main () at main.c:15
15 result[1] = add_range(1, 100);
Value returned is $4 = 5050
(gdb) n
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb) (直接回車)
result[0]=55
result[1]=5050
17 return 0;
這樣結(jié)果就對(duì)了。修改變量的值除了用set命令之外也可以用print命令,因?yàn)閜rint命令后面跟的是表達(dá)式,而我們知道賦值和函數(shù)調(diào)用也都是表達(dá)式,所以也可以用print命令修改變量的值或者調(diào)用函數(shù):
(gdb) p result[2]=33
$5 = 33
(gdb) p printf("result[2]=%d\n", result[2])
result[2]=33
看了“Linux學(xué)習(xí)gdb調(diào)試教程”還想看:
3.PS入門教程
、等編號(hào)代替相應(yīng)的值。由于我們本來(lái)就知道第一次調(diào)用的結(jié)果是正確的,再往下跟也沒(méi)意義了,可以用finish命令讓程序一直運(yùn)行到從當(dāng)前函數(shù)返回為止:(gdb) finish
Run till exit from #0 add_range (low=1, high=10) at main.c:6
0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
Value returned is
GNU調(diào)試器(GNU Debugger, GDB)是一個(gè)開(kāi)源的源代碼級(jí)的調(diào)試工具,其性能優(yōu)良、功能齊全、易于使用,在嵌入式系統(tǒng)開(kāi)發(fā)過(guò)程中扮演著越來(lái)越重要的角色。接下來(lái)是小編為大家收集的Linux學(xué)習(xí)gdb調(diào)試教程,希望能幫到大家。
Linux學(xué)習(xí)gdb調(diào)試教程
一.gdb常用命令:
命令 | 描述 |
---|---|
backtrace(或bt) | 查看各級(jí)函數(shù)調(diào)用及參數(shù) |
finish | 連續(xù)運(yùn)行到當(dāng)前函數(shù)返回為止,然后停下來(lái)等待命令 |
frame(或f) 幀編號(hào) | 選擇棧幀 |
info(或i) locals | 查看當(dāng)前棧幀局部變量的值 |
list(或l) | 列出源代碼,接著上次的位置往下列,每次列10行 |
list 行號(hào) | 列出從第幾行開(kāi)始的源代碼 |
list 函數(shù)名 | 列出某個(gè)函數(shù)的源代碼 |
next(或n) | 執(zhí)行下一行語(yǔ)句 |
print(或p) | 打印表達(dá)式的值,通過(guò)表達(dá)式可以修改變量的值或者調(diào)用函數(shù) |
quit(或q) |
退出gdb 調(diào)試環(huán)境 |
set var | 修改變量的值 |
start |
開(kāi)始執(zhí)行程序,停在main 函數(shù)第一行語(yǔ)句前面等待命令 |
step(或s) | 執(zhí)行下一行語(yǔ)句,如果有函數(shù)調(diào)用則進(jìn)入到函數(shù)中 |
二.gdb學(xué)習(xí)小例:
#include <stdio.h>
int add_range(int low, int high)
{
int i, sum;
for (i = low; i <= high; i++)
sum = sum + i;
return sum;
}
int main(void)
{
int result[100];
result[0] = add_range(1, 10);
result[1] = add_range(1, 100);
printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
return 0;
}
add_range函數(shù)從low加到high,在main函數(shù)中首先從1加到10,把結(jié)果保存下來(lái),然后從1加到100,再把結(jié)果保存下來(lái),最后打印的兩個(gè)結(jié)果是:
result[0]=55
result[1]=5105
第一個(gè)結(jié)果正確[20],第二個(gè)結(jié)果顯然不正確,在小學(xué)我們就聽(tīng)說(shuō)過(guò)高斯小時(shí)候的故事,從1加到100應(yīng)該是5050。一段代碼,第一次運(yùn)行結(jié)果是對(duì)的,第二次運(yùn)行卻不對(duì),這是很常見(jiàn)的一類錯(cuò)誤現(xiàn)象,這種情況不應(yīng)該懷疑代碼而應(yīng)該懷疑數(shù)據(jù),因?yàn)榈谝淮魏偷诙芜\(yùn)行的都是同一段代碼,如果代碼是錯(cuò)的,那為什么第一次的結(jié)果能對(duì)呢?然而第一次和第二次運(yùn)行時(shí)相關(guān)的數(shù)據(jù)卻有可能不同,錯(cuò)誤的數(shù)據(jù)會(huì)導(dǎo)致錯(cuò)誤的結(jié)果。在動(dòng)手調(diào)試之前,讀者先試試只看代碼能不能看出錯(cuò)誤原因,只要前面幾章學(xué)得扎實(shí)就應(yīng)該能看出來(lái)。
在編譯時(shí)要加上-g選項(xiàng),生成的可執(zhí)行文件才能用gdb進(jìn)行源碼級(jí)調(diào)試:
$ gcc -g main.c -o main
$ gdb main
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb)
-g選項(xiàng)的作用是在可執(zhí)行文件中加入源代碼的信息,比如可執(zhí)行文件中第幾條機(jī)器指令對(duì)應(yīng)源代碼的第幾行,但并不是把整個(gè)源文件嵌入到可執(zhí)行文件中,所以在調(diào)試時(shí)必須保證gdb能找到源文件。gdb提供一個(gè)類似Shell的命令行環(huán)境,上面的(gdb)就是提示符,在這個(gè)提示符下輸入help可以查看命令的類別:
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.
也可以進(jìn)一步查看某一類別中有哪些命令,例如查看files類別下有哪些命令可用:
(gdb) help files
Specifying and examining files.
List of commands:
add-shared-symbol-files -- Load the symbols from shared objects in the dynamic linker's link map
add-symbol-file -- Load symbols from FILE
add-symbol-file-from-memory -- Load the symbols out of memory from a dynamically loaded object file
cd -- Set working directory to DIR for debugger and program being debugged
core-file -- Use FILE as core dump for examining memory and registers
directory -- Add directory DIR to beginning of search path for source files
edit -- Edit specified file or function
exec-file -- Use FILE as program for getting contents of pure memory
file -- Use FILE as program to be debugged
forward-search -- Search for regular expression (see regex(3)) from last line listed
generate-core-file -- Save a core file with the current state of the debugged process
list -- List specified function or line
...
現(xiàn)在試試用list命令從第一行開(kāi)始列出源代碼:
(gdb) list 1
1 #include <stdio.h>
2
3 int add_range(int low, int high)
4 {
5 int i, sum;
6 for (i = low; i <= high; i++)
7 sum = sum + i;
8 return sum;
9 }
10
一次只列10行,如果要從第11行開(kāi)始繼續(xù)列源代碼可以輸入
(gdb) list
也可以什么都不輸直接敲回車,gdb提供了一個(gè)很方便的功能,在提示符下直接敲回車表示重復(fù)上一條命令。
(gdb) (直接回車)
11 int main(void)
12 {
13 int result[100];
14 result[0] = add_range(1, 10);
15 result[1] = add_range(1, 100);
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
17 return 0;
18
gdb的很多常用命令有簡(jiǎn)寫(xiě)形式,例如list命令可以寫(xiě)成l,要列一個(gè)函數(shù)的源代碼也可以用函數(shù)名做參數(shù):
(gdb) l add_range
1 #include <stdio.h>
2
3 int add_range(int low, int high)
4 {
5 int i, sum;
6 for (i = low; i <= high; i++)
7 sum = sum + i;
8 return sum;
9 }
10
現(xiàn)在退出gdb的環(huán)境:
(gdb) quit
我們做一個(gè)實(shí)驗(yàn),把源代碼改名或移到別處再用gdb調(diào)試,這樣就列不出源代碼了:
$ mv main.c mian.c
$ gdb main
...
(gdb) l
5 main.c: No such file or directory.
in main.c
可見(jiàn)gcc的-g選項(xiàng)并不是把源代碼嵌入到可執(zhí)行文件中的,在調(diào)試時(shí)也需要源文件。現(xiàn)在把源代碼恢復(fù)原樣,我們繼續(xù)調(diào)試。首先用start命令開(kāi)始執(zhí)行程序:
$ gdb main
...
(gdb) start
Breakpoint 1 at 0x80483ad: file main.c, line 14.
Starting program: /home/akaedu/main
main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb)
gdb停在main函數(shù)中變量定義之后的第一條語(yǔ)句處等待我們發(fā)命令,gdb列出的這條語(yǔ)句是即將執(zhí)行的下一條語(yǔ)句。我們可以用next命令(簡(jiǎn)寫(xiě)為n)控制這些語(yǔ)句一條一條地執(zhí)行:
(gdb) n
15 result[1] = add_range(1, 100);
(gdb) (直接回車)
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb) (直接回車)
result[0]=55
result[1]=5105
17 return 0;
用n命令依次執(zhí)行兩行賦值語(yǔ)句和一行打印語(yǔ)句,在執(zhí)行打印語(yǔ)句時(shí)結(jié)果立刻打出來(lái)了,然后停在return語(yǔ)句之前等待我們發(fā)命令。雖然我們完全控制了程序的執(zhí)行,但仍然看不出哪里錯(cuò)了,因?yàn)殄e(cuò)誤不在main函數(shù)中而在add_range函數(shù)中,現(xiàn)在用start命令重新來(lái)過(guò),這次用step命令(簡(jiǎn)寫(xiě)為s)鉆進(jìn)add_range函數(shù)中去跟蹤執(zhí)行:
(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Breakpoint 2 at 0x80483ad: file main.c, line 14.
Starting program: /home/akaedu/main
main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) s
add_range (low=1, high=10) at main.c:6
6 for (i = low; i <= high; i++)
這次停在了add_range函數(shù)中變量定義之后的第一條語(yǔ)句處。在函數(shù)中有幾種查看狀態(tài)的辦法,backtrace命令(簡(jiǎn)寫(xiě)為bt)可以查看函數(shù)調(diào)用的棧幀:
(gdb) bt
#0 add_range (low=1, high=10) at main.c:6
#1 0x080483c1 in main () at main.c:14
可見(jiàn)當(dāng)前的add_range函數(shù)是被main函數(shù)調(diào)用的,main傳進(jìn)來(lái)的參數(shù)是low=1, high=10。main函數(shù)的棧幀編號(hào)為1,add_range的棧幀編號(hào)為0?,F(xiàn)在可以用info命令(簡(jiǎn)寫(xiě)為i)查看add_range函數(shù)局部變量的值:
(gdb) i locals
i = 0
sum = 0
如果想查看main函數(shù)當(dāng)前局部變量的值也可以做到,先用frame命令(簡(jiǎn)寫(xiě)為f)選擇1號(hào)棧幀然后再查看局部變量:
(gdb) f 1
#1 0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) i locals
result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
注意到result數(shù)組中有很多元素具有雜亂無(wú)章的值,我們知道未經(jīng)初始化的局部變量具有不確定的值。到目前為止一切正常。用s或n往下走幾步,然后用print命令(簡(jiǎn)寫(xiě)為p)打印出變量sum的值:
(gdb) s
7 sum = sum + i;
(gdb) (直接回車)
6 for (i = low; i <= high; i++)
(gdb) (直接回車)
7 sum = sum + i;
(gdb) (直接回車)
6 for (i = low; i <= high; i++)
(gdb) p sum
$1 = 3
第一次循環(huán)i是1,第二次循環(huán)i是2,加起來(lái)是3,沒(méi)錯(cuò)。這里的$1表示gdb保存著這些中間結(jié)果,$后面的編號(hào)會(huì)自動(dòng)增長(zhǎng),在命令中可以用$1、$2、$3等編號(hào)代替相應(yīng)的值。由于我們本來(lái)就知道第一次調(diào)用的結(jié)果是正確的,再往下跟也沒(méi)意義了,可以用finish命令讓程序一直運(yùn)行到從當(dāng)前函數(shù)返回為止:
(gdb) finish
Run till exit from #0 add_range (low=1, high=10) at main.c:6
0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
Value returned is $2 = 55
返回值是55,當(dāng)前正準(zhǔn)備執(zhí)行賦值操作,用s命令賦值,然后查看result數(shù)組:
(gdb) s
15 result[1] = add_range(1, 100);
(gdb) p result
$3 = {55, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
第一個(gè)值55確實(shí)賦給了result數(shù)組的第0個(gè)元素。下面用s命令進(jìn)入第二次add_range調(diào)用,進(jìn)入之后首先查看參數(shù)和局部變量:
(gdb) s
add_range (low=1, high=100) at main.c:6
6 for (i = low; i <= high; i++)
(gdb) bt
#0 add_range (low=1, high=100) at main.c:6
#1 0x080483db in main () at main.c:15
(gdb) i locals
i = 11
sum = 55
由于局部變量i和sum沒(méi)初始化,所以具有不確定的值,又由于兩次調(diào)用是挨著的,i和sum正好取了上次調(diào)用時(shí)的值,原來(lái)這跟例 3.7 “驗(yàn)證局部變量存儲(chǔ)空間的分配和釋放”是一樣的道理,只不過(guò)我這次舉的例子設(shè)法讓局部變量sum在第一次調(diào)用時(shí)初值為0了。i的初值不是0倒沒(méi)關(guān)系,在for循環(huán)中會(huì)賦值為0的,但sum如果初值不是0,累加得到的結(jié)果就錯(cuò)了。好了,我們已經(jīng)找到錯(cuò)誤原因,可以退出gdb修改源代碼了。如果我們不想浪費(fèi)這次調(diào)試機(jī)會(huì),可以在gdb中馬上把sum的初值改為0繼續(xù)運(yùn)行,看看這一處改了之后還有沒(méi)有別的Bug:
(gdb) set var sum=0
(gdb) finish
Run till exit from #0 add_range (low=1, high=100) at main.c:6
0x080483db in main () at main.c:15
15 result[1] = add_range(1, 100);
Value returned is $4 = 5050
(gdb) n
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb) (直接回車)
result[0]=55
result[1]=5050
17 return 0;
這樣結(jié)果就對(duì)了。修改變量的值除了用set命令之外也可以用print命令,因?yàn)閜rint命令后面跟的是表達(dá)式,而我們知道賦值和函數(shù)調(diào)用也都是表達(dá)式,所以也可以用print命令修改變量的值或者調(diào)用函數(shù):
(gdb) p result[2]=33
$5 = 33
(gdb) p printf("result[2]=%d\n", result[2])
result[2]=33
看了“Linux學(xué)習(xí)gdb調(diào)試教程”還想看:
3.PS入門教程
= 55返回值是55,當(dāng)前正準(zhǔn)備執(zhí)行賦值操作,用s命令賦值,然后查看result數(shù)組:
(gdb) s
15 result[1] = add_range(1, 100);
(gdb) p result
= {55, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
第一個(gè)值55確實(shí)賦給了result數(shù)組的第0個(gè)元素。下面用s命令進(jìn)入第二次add_range調(diào)用,進(jìn)入之后首先查看參數(shù)和局部變量:
(gdb) s
add_range (low=1, high=100) at main.c:6
6 for (i = low; i <= high; i++)
(gdb) bt
#0 add_range (low=1, high=100) at main.c:6
#1 0x080483db in main () at main.c:15
(gdb) i locals
i = 11
sum = 55
由于局部變量i和sum沒(méi)初始化,所以具有不確定的值,又由于兩次調(diào)用是挨著的,i和sum正好取了上次調(diào)用時(shí)的值,原來(lái)這跟例 3.7 “驗(yàn)證局部變量存儲(chǔ)空間的分配和釋放”是一樣的道理,只不過(guò)我這次舉的例子設(shè)法讓局部變量sum在第一次調(diào)用時(shí)初值為0了。i的初值不是0倒沒(méi)關(guān)系,在for循環(huán)中會(huì)賦值為0的,但sum如果初值不是0,累加得到的結(jié)果就錯(cuò)了。好了,我們已經(jīng)找到錯(cuò)誤原因,可以退出gdb修改源代碼了。如果我們不想浪費(fèi)這次調(diào)試機(jī)會(huì),可以在gdb中馬上把sum的初值改為0繼續(xù)運(yùn)行,看看這一處改了之后還有沒(méi)有別的Bug:
(gdb) set var sum=0
(gdb) finish
Run till exit from #0 add_range (low=1, high=100) at main.c:6
0x080483db in main () at main.c:15
15 result[1] = add_range(1, 100);
Value returned is = 5050
(gdb) n
16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb) (直接回車)
result[0]=55
result[1]=5050
17 return 0;
這樣結(jié)果就對(duì)了。修改變量的值除了用set命令之外也可以用print命令,因?yàn)閜rint命令后面跟的是表達(dá)式,而我們知道賦值和函數(shù)調(diào)用也都是表達(dá)式,所以也可以用print命令修改變量的值或者調(diào)用函數(shù):
(gdb) p result[2]=33
= 33
(gdb) p printf("result[2]=%d\n", result[2])
result[2]=33
看了“Linux學(xué)習(xí)gdb調(diào)試教程”還想看:
3.PS入門教程
Linux學(xué)習(xí)gdb調(diào)試教程
精選文章
-
linux下gdb常用的調(diào)試命令
用gdb調(diào)試程序時(shí),常常很困惑一些命令的使用,要么是不知道這個(gè)命令,要么忘了命令的使用方法。接下來(lái)是小編為大家收集的linux下gdb常用的調(diào)試命令,
-
linux tar命令詳解
你還在為不知道linux tar命令詳解而煩惱么?接下來(lái)是小編為大家收集的linux tar命令詳解,希望能幫到大家。 linux tar命令詳解 apt-cache search # (package 搜索包)
-
Linux GDB常用命令介紹
你還在為不知道Linux GDB常用命令介紹而煩惱么?接下來(lái)是小編為大家收集的Linux GDB常用命令介紹,希望能幫到大家。 Linux GDB常用命令介紹 1.啟動(dòng)和退出gdb
-
Linux環(huán)境下使用gdb反匯編C語(yǔ)言程序
你還在為不知道Linux環(huán)境下使用gdb反匯編C語(yǔ)言程序而煩惱么?接下來(lái)是小編為大家收集的Linux環(huán)境下使用gdb反匯編C語(yǔ)言程序,希望能幫到大家。 Linux環(huán)境下使