相信大家都想過(guò)我們寫(xiě)錯(cuò)程序之后是不是只有編譯的時(shí)候才能發(fā)現(xiàn),那么我們?cè)谖淳幾g的時(shí)候有沒(méi)有什么辦法幫我們檢查錯(cuò)誤呢?現(xiàn)在我們就去看看c語(yǔ)言中調(diào)試工具的用法。
splint工具.用一個(gè)最簡(jiǎn)單的HELLO WORLD來(lái)表述:
=====================================
?
int main(void)
{
?? print("hello world/n", s);
?? return
}
-----------------------------------------------------
casio$ splint -strict foo.c
Splint 3.1.1 --- 03 Nov 2006
?
foo.c: (in function main)
foo.c:5:2: Unrecognized identifier: print <-------找到print不是printf
Identifier used in code has not been declared. (Use -unrecog to inhibit
warning)
foo.c:5:25: Unrecognized identifier: s <-------未定義變量s
foo.c:5:2: Statement has no effect (possible undected modification through call
to unconstrained function print): print("hello wor... <---------不存在prinf函數(shù)
Statement has no visible effect --- no values are modified. It may modify
something through a call to an unconstrained function. (Use -noeffectuncon to
inhibit warning)
foo.c:7:2: Parse Error. (For help on parse errors, see splint -help <------對(duì)應(yīng)return語(yǔ)法錯(cuò)誤
parseerrors.)
*** Cannot continue.
=============================================
cxref
cxref程序分析C源代碼并且生成一個(gè)交叉引用。他顯示了每一個(gè)符號(hào)在程序中何處被提到。他使用標(biāo)記星號(hào)的每一個(gè)符號(hào)定義位置生成一個(gè)排序列表,如下所示:
SYMBOL FILE FUNCTION LINE
BASENID prog.c — *12 *96 124 126 146 156 166
BINSIZE prog.c — *30 197 198 199 206
BUFMAX prog.c — *44 45 90
BUFSIZ /usr/include/stdio.h — *4
EOF /usr/include/stdio.h — *27
argc prog.c — 36
prog.c main *37 61 81
argv prog.c — 36
prog.c main *38 61
calldata prog.c — *5
prog.c main 64 188
calls prog.c — *19
prog.c main 54
在作者的機(jī)子上,前面的輸入在程序的源碼目錄中使用下面的命令來(lái)生成的:
$ cxref *.c *.h
但是實(shí)際的語(yǔ)法因?yàn)榘姹镜牟煌煌2榭次覀兿到y(tǒng)的文檔或是man手冊(cè)可以得到更多的信息。
cflow <使用時(shí)輸入cflow *.c就可以了.可以馬上搞清除什么函數(shù)調(diào)用了什么.>
cflow程序會(huì)輸出一個(gè)函數(shù)調(diào)用樹(shù),這是一個(gè)顯示函數(shù)調(diào)用關(guān)系的圖表。這對(duì)于查看程序結(jié)構(gòu)來(lái)了解他是如何操作的以及了解對(duì)于一個(gè)函數(shù)有哪些影響是十分有用的。一些版本的cflow可以同時(shí)作用于目標(biāo)文件與源代碼。查看手冊(cè)頁(yè)我們可以了解更為詳細(xì)的操作。
下面是由一個(gè)cflow版本(cflow-2.0)所獲得的例子輸出,這個(gè)版本的cflow版本是由Marty Leisner維護(hù)的,并且可以網(wǎng)上得到。
1 file_ungetc {prcc.c 997}
2 main {prcc.c 70}
3 getopt {}
4 show_all_lists {prcc.c 1070}
5 display_list {prcc.c 1056}
6 printf {}
7 exit {}
8 exit {}
9 usage {prcc.c 59}
10 fprintf {}
11 exit {}
從這個(gè)輸出中我們可以看到main函數(shù)調(diào)用show_all_lists,而show_all_lists調(diào)用display_list,display_list本身調(diào)用printf。
這個(gè)版本cflow的一個(gè)選項(xiàng)就是-i,這會(huì)生成一個(gè)反轉(zhuǎn)的流程圖。對(duì)于每一個(gè)函數(shù),cflow列出調(diào)用他的其他函數(shù)。這聽(tīng)起來(lái)有些復(fù)雜,但是實(shí)際上并不是這樣。下面是一個(gè)例子。
19 display_list {prcc.c 1056}
20 show_all_lists {prcc.c 1070}
21 exit {}
22 main {prcc.c 70}
23 show_all_lists {prcc.c 1070}
24 usage {prcc.c 59}
...
74 printf {}
75 display_list {prcc.c 1056}
76 maketag {prcc.c 487}
77 show_all_lists {prcc.c 1070}
78 main {prcc.c 70}
...
99 usage {prcc.c 59}
100 main {prcc.c 70}
例如,這告訴我們調(diào)用exit的函數(shù)有main,show_all_lists與usage。
使用prof/gprof執(zhí)行性能測(cè)試
當(dāng)我們?cè)囍粉櫼粋€(gè)程序的性能問(wèn)題時(shí)一個(gè)十分有用的技術(shù)就是執(zhí)行性能測(cè)試(execution profiling)。通常被特殊的編譯器選項(xiàng)以及輔助程序所支持,一個(gè)程序的性能顯示他在哪里花費(fèi)時(shí)間。
prof程序(以及其GNU版本gprof)會(huì)由性能測(cè)試程序運(yùn)行時(shí)所生成的執(zhí)行追蹤文件中輸出報(bào)告。一個(gè)可執(zhí)行的性能測(cè)試是由指定-p選項(xiàng)(對(duì)prof)或是-pg選項(xiàng)(對(duì)gprof)所生成的:
$ cc -pg -o program program.c
這個(gè)程序是使用一個(gè)特殊版本的C庫(kù)進(jìn)行鏈接的并且被修改來(lái)包含監(jiān)視代碼。對(duì)于不同的系統(tǒng)結(jié)果也許不同,但是通常是由安排頻繁被中斷的程序以及記錄執(zhí)行位置來(lái)做到的。監(jiān)視數(shù)據(jù)被寫(xiě)入當(dāng)前目錄中的一個(gè)文件,mon.out(對(duì)于gprof為gmon.out)。
$ ./program
$ ls -ls
2 -rw-r--r-- 1 neil users 1294 Feb 4 11:48 gmon.out
然后用命令:gprof ./program可以查看到下面的報(bào)告
prof/gprof程序讀取這些監(jiān)視數(shù)據(jù)并且生成一個(gè)報(bào)告。查看其手冊(cè)頁(yè)可以詳細(xì)了解其程序選項(xiàng)。下面以gprof輸出作為一個(gè)例子:
cumulative self self total
time seconds seconds calls ms/call ms/call name
18.5 0.10 0.10 8664 0.01 0.03 _doscan [4]
18.5 0.20 0.10 mcount (60)
14.8 0.28 0.08 43320 0.00 0.00 _number [5]
9.3 0.33 0.05 8664 0.01 0.01 _format_arg [6]
7.4 0.37 0.04 112632 0.00 0.00 _ungetc [8]
7.4 0.41 0.04 8757 0.00 0.00 _memccpy [9]
7.4 0.45 0.04 1 40.00 390.02 _main [2]
3.7 0.47 0.02 53 0.38 0.38 _read [12]
3.7 0.49 0.02 w4str [10]
1.9 0.50 0.01 26034 0.00 0.00 _strlen [16]
1.9 0.51 0.01 8664 0.00 0.00 strncmp [17]
內(nèi)存調(diào)試
富含bug而且難于跟蹤調(diào)試的一個(gè)區(qū)域就是動(dòng)態(tài)內(nèi)存分配。如果我們編譯一個(gè)使用malloc與free來(lái)分配內(nèi)存的程序,很重要的一點(diǎn)就是我們要跟蹤我們所分配的內(nèi)存塊,并且保證不要使用已經(jīng)釋放的內(nèi)存塊。
通常,內(nèi)存是由malloc分配并且賦給一個(gè)指針變量的。如果指針變量被修改了,而又沒(méi)有其他的指針來(lái)指向這個(gè)內(nèi)存塊,他就會(huì)變?yōu)椴豢稍L問(wèn)的內(nèi)存塊。這就是一個(gè)內(nèi)存泄露,而且會(huì)使得我們程序尺寸變大。如果我們泄露了大量的內(nèi)存,那么我們的系統(tǒng)就會(huì)變慢并且會(huì)最終用盡內(nèi)存。
如 果我們?cè)诔鲆粋€(gè)分配的內(nèi)存塊的結(jié)束部分(或是在一個(gè)內(nèi)存塊的開(kāi)始部分)寫(xiě)入數(shù)據(jù),我們很有可能會(huì)破壞malloc庫(kù)來(lái)跟蹤分配所用的數(shù)據(jù)結(jié)構(gòu)。在這種情 況下,在將來(lái)的某個(gè)時(shí)刻,調(diào)用malloc,或者甚至是free,就會(huì)引起段錯(cuò)誤,而我們的程序就會(huì)崩潰。跟蹤錯(cuò)誤發(fā)生的精確點(diǎn)是非常困難的,因?yàn)楹芸赡?他在引起崩潰的事件發(fā)生以前很一段時(shí)間就已經(jīng)發(fā)生了。
不必奇怪的是,有一些工具,商業(yè)或是自由的,可以有助于處理這兩種問(wèn)題類(lèi)型。例如,有許多不同的malloc與free版本,其中的一些包含額外的代碼在分配與回收上進(jìn)行檢測(cè)嘗試檢測(cè)一個(gè)內(nèi)存塊被釋放兩次或是其他一些濫用類(lèi)型的情況。
ElectricFence
ElectricFence 庫(kù)是由Bruce Perens開(kāi)發(fā)的,并且在一些Linux發(fā)行版本中作為一個(gè)可選的組件來(lái)提供,例如RedHat,而且已經(jīng)可以在網(wǎng)絡(luò)上獲得。他嘗試使用Linux的虛 擬內(nèi)存工具來(lái)保護(hù)malloc與free所使用的內(nèi)存,從而在內(nèi)存被破壞時(shí)終止程序。
試驗(yàn)--ElectricFence
下面的程序,efence.c,使用malloc分配一個(gè)內(nèi)存塊,然后在超出塊結(jié)束處寫(xiě)入數(shù)據(jù)。讓我們看一下會(huì)發(fā)生什么情況。
當(dāng)我們編譯運(yùn)行這個(gè)程序時(shí),我們并不會(huì)看到進(jìn)一步的行為。然而,似乎malloc所分配的內(nèi)存區(qū)域有一些問(wèn)題,而我們實(shí)際上已經(jīng)遇到了麻煩。
?
$ cc -o efence efence.c
$ ./efence
$
然而,如果我們使用ElectricFence庫(kù),libefence.a來(lái)鏈接這個(gè)程序,我們就會(huì)得到一個(gè)即時(shí)的響應(yīng)。
$ cc -o efence efence.c -lefence
$ ./efence
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <[email protected]>
Segmentation fault
$
在調(diào)試器下運(yùn)行可以定位這個(gè)問(wèn)題:
$ cc -g -o efence efence.c -lefence
$ gdb efence
(gdb) run
Starting program: /home/neil/BLP3/chapter10/efence
[New Thread 1024 (LWP 1869)]
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <[email protected]>
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 1024 (LWP 1869)]
0x080484ad in main () at efence.c:10
10 ptr[1024] = 0;
(gdb)
工作原理
Electric替換malloc并且將函數(shù)與計(jì)算機(jī)處理器的虛擬內(nèi)存特性相關(guān)聯(lián)來(lái)阻止非法的內(nèi)存訪問(wèn)。當(dāng)這樣的訪問(wèn)發(fā)生時(shí),就會(huì)拋出一個(gè)段錯(cuò)誤信息從而可以終止程序。
valgrind
valgrind是一個(gè)可以檢測(cè)我們已經(jīng)討論過(guò)的許多問(wèn)題的工具。事實(shí)上,他可以檢測(cè)數(shù)據(jù)訪問(wèn)錯(cuò)誤與內(nèi)存泄露。也許他并沒(méi)有被包含在我們的Linux發(fā)行版本中,但是我們可以在http://developer.kde.org/~sewardj處得到。
程序并不需要使用valgrind重新編譯,而我們甚至可以調(diào)用一個(gè)正在運(yùn)行的程序的內(nèi)存訪問(wèn)。他很值得一看,他已經(jīng)用在主要的開(kāi)發(fā)上,包含KDE版本3。
試驗(yàn)--valgrind
下面的程序,checker.c,分配一些內(nèi)存,讀取超過(guò)那塊內(nèi)存限制的位置,在其結(jié)束處之外寫(xiě)入數(shù)據(jù),然后使其不能訪問(wèn)。
要使用valgrind,我們只需要簡(jiǎn)單的運(yùn)行valgrind命令,傳遞我們希望檢測(cè)的選項(xiàng),其后是使用其參數(shù)運(yùn)行的程序。
?
當(dāng)我們使用valgrind來(lái)運(yùn)行我們的程序時(shí),我們可以看到診斷出許多問(wèn)題:
$ valgrind --leak-check=yes -v ./checker
==3436== valgrind-1.0.4, a memory error detector for x86 GNU/Linux.
==3436== Copyright (C) 2000-2002, and GNU GPL'd, by Julian Seward.
==3436== Estimated CPU clock rate is 452 MHz
==3436== For more details, rerun with: -v
==3436==
==3436== Invalid read of size 1
==3436== at 0x8048397: main (checker.c:10)
==3436== by 0x402574F2: __libc_start_main (in /lib/libc.so.6)
==3436== by 0x80482D1: exit@@GLIBC_2.0 (in /home/neil/BLP3/chapter10/checker)
==3436== Address 0x42AD1424 is 0 bytes after a block of size 1024 alloc'd
==3436== at 0x4003CA75: malloc (vg_clientfuncs.c:100)
==3436== by 0x8048389: main (checker.c:6)
==3436== by 0x402574F2: __libc_start_main (in /lib/libc.so.6)
==3436== by 0x80482D1: exit@@GLIBC_2.0 (in /home/neil/BLP3/chapter10/checker)
==3436==
==3436== Invalid write of size 1
==3436== at 0x80483A4: main (checker.c:13)
==3436== by 0x402574F2: __libc_start_main (in /lib/libc.so.6)
==3436== by 0x80482D1: exit@@GLIBC_2.0 (in /home/neil/BLP3/chapter10/checker)
==3436== Address 0x42AD1424 is 0 bytes after a block of size 1024 alloc'd
==3436== at 0x4003CA75: malloc (vg_clientfuncs.c:100)
==3436== by 0x8048389: main (checker.c:6)
==3436== by 0x402574F2: __libc_start_main (in /lib/libc.so.6)
==3436== by 0x80482D1: exit@@GLIBC_2.0 (in /home/neil/BLP3/chapter10/checker)
==3436==
==3436== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
==3436== malloc/free: in use at exit: 1024 bytes in 1 blocks.
==3436== malloc/free: 1 allocs, 0 frees, 1024 bytes allocated.
==3436== For counts of detected errors, rerun with: -v
==3436== searching for pointers to 1 not-freed blocks.
==3436== checked 3468724 bytes.
==3436==
==3436== definitely lost: 1024 bytes in 1 blocks.
==3436== possibly lost: 0 bytes in 0 blocks.
==3436== still reachable: 0 bytes in 0 blocks.
==3436==
==3436== 1024 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3436== at 0x4003CA75: malloc (vg_clientfuncs.c:100)
==3436== by 0x8048389: main (checker.c:6)
==3436== by 0x402574F2: __libc_start_main (in /lib/libc.so.6)
==3436== by 0x80482D1: exit@@GLIBC_2.0 (in /home/neil/BLP3/chapter10/checker)
==3436==
==3436== LEAK SUMMARY:
==3436== definitely lost: 1024 bytes in 1 blocks.
==3436== possibly lost: 0 bytes in 0 blocks.
==3436== still reachable: 0 bytes in 0 blocks.
==3436== Reachable blocks (those to which a pointer was found) are not shown.
==3436== To see them, rerun with: --show-reachable=yes
==3436== $
這里我們可以看到錯(cuò)誤的讀取與寫(xiě)入已經(jīng)被捕獲,而所關(guān)注的內(nèi)存塊與他們被分配的位置相關(guān)聯(lián)。我們可以使用調(diào)試器在出錯(cuò)點(diǎn)斷開(kāi)程序。
valgrind 有許多選項(xiàng),包含特定的錯(cuò)誤類(lèi)型表達(dá)式與內(nèi)存泄露檢測(cè)。要檢測(cè)我們的例子泄露,我們必須使用一個(gè)傳遞給valgrind的選項(xiàng)。當(dāng)程序結(jié)束時(shí)要檢測(cè)內(nèi)存泄 露,我們需要指定 --leak-check=yes。我們可以使用valgrind --help得到一個(gè)選項(xiàng)列表。
工作原理
我們的程序在valgrind的控制下執(zhí)行,這會(huì)檢測(cè)我們程序所執(zhí)行的各種動(dòng)作,并且執(zhí)行許多檢測(cè),包括內(nèi)存訪問(wèn)。如果程序訪問(wèn)一個(gè)已分配的內(nèi)存塊并且訪問(wèn) 是非法的,valgrind就會(huì)輸出一條信息。在程序結(jié)束時(shí),一個(gè)垃圾收集例程就會(huì)運(yùn)行來(lái)檢測(cè)是否在存在分配的內(nèi)存塊沒(méi)有被釋放。這些孤兒內(nèi)存也會(huì)被報(bào)告。
以上就是關(guān)于c語(yǔ)言中調(diào)試工具的用法,學(xué)習(xí)c語(yǔ)言中的調(diào)試工具對(duì)于我們的學(xué)習(xí)和工作都是有一定幫助的哦。
新聞熱點(diǎn)
疑難解答
圖片精選