基於c語言中調試工具的用法匯總(不包含gdb)

來源:互聯網
上載者:User

是不是只有編譯的時候才知道程式寫了錯誤?有沒有在未編譯的時候就讓機器幫你檢查錯誤的工具呢?
答案是:有!!

splint工具.用一個最簡單的HELLO WORLD來表述:
=====================================

複製代碼 代碼如下:/*錯誤很明顯*/
#include <stdio.h>

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函數
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 <------對應return語法錯誤
parseerrors.)
*** Cannot continue.

=============================================

cxref

cxref程式分析C原始碼並且產生一個交叉引用。他顯示了每一個符號在程式中何處被提到。他使用標記星號的每一個符號定義位置產生一個排序列表,如下所示:

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

在作者的機子上,前面的輸入在程式的源碼目錄中使用下面的命令來產生的:

$ cxref *.c *.h

但是實際的文法因為版本的不同而不同。查看我們系統的文檔或是man手冊可以得到更多的資訊。

cflow <使用時輸入cflow *.c就可以了.可以馬上搞清除什麼函數調用了什麼.>

cflow程式會輸出一個函數調用樹,這是一個顯示函數調用關係的圖表。這對於查看程式結構來瞭解他是如何操作的以及瞭解對於一個函數有哪些影響是十分有用的。一些版本的cflow可以同時作用於目標檔案與原始碼。查看手冊頁我們可以瞭解更為詳細的操作。

下面是由一個cflow版本(cflow-2.0)所獲得的例子輸出,這個版本的cflow版本是由Marty Leisner維護的,並且可以網上得到。

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 {}

從這個輸出中我們可以看到main函數調用show_all_lists,而show_all_lists調用display_list,display_list本身調用printf。

這個版本cflow的一個選項就是-i,這會產生一個反轉的流程圖。對於每一個函數,cflow列出調用他的其他函數。這聽起來有些複雜,但是實際上並不是這樣。下面是一個例子。

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}

例如,這告訴我們調用exit的函數有main,show_all_lists與usage。

使用prof/gprof執行效能測試

當我們試著追蹤一個程式的效能問題時一個十分有用的技術就是執行效能測試(execution profiling)。通常被特殊的編譯器選項以及輔助程式所支援,一個程式的效能顯示他在哪裡花費時間。

prof程式(以及其GNU版本gprof)會由效能測試程式運行時所產生的執行追蹤檔案中輸出報告。一個可執行檔效能測試是由指定-p選項(對prof)或是-pg選項(對gprof)所產生的:

$ cc -pg -o program program.c

這個程式是使用一個特殊版本的C庫進行連結的並且被修改來包含監視代碼。對於不同的系統結果也許不同,但是通常是由安排頻繁被中斷的程式以及記錄執行位置來做到的。監視資料被寫入目前的目錄中的一個檔案,mon.out(對於gprof為gmon.out)。

$ ./program
$ ls -ls
2 -rw-r--r-- 1 neil users 1294 Feb 4 11:48 gmon.out

然後用命令:gprof ./program可以查看到下面的報告

prof/gprof程式讀取這些監視資料並且產生一個報告。查看其手冊頁可以詳細瞭解其程式選項。下面以gprof輸出作為一個例子:

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]

記憶體調試

富含bug而且難於跟蹤調試的一個地區就是動態記憶體分配。如果我們編譯一個使用malloc與free來分配記憶體的程式,很重要的一點就是我們要跟蹤我們所分配的記憶體塊,並且保證不要使用已經釋放的記憶體塊。

通常,記憶體是由malloc分配並且賦給一個指標變數的。如果指標變數被修改了,而又沒有其他的指標來指向這個記憶體塊,他就會變為不可訪問的記憶體塊。這就是一個記憶體泄露,而且會使得我們程式尺寸變大。如果我們泄露了大量的記憶體,那麼我們的系統就會變慢並且會最終用盡記憶體。

如 果我們在超出一個分配的記憶體塊的結束部分(或是在一個記憶體塊的開始部分)寫入資料,我們很有可能會破壞malloc庫來跟蹤分配所用的資料結構。在這種情 況下,在將來的某個時刻,調用malloc,或者甚至是free,就會引起段錯誤,而我們的程式就會崩潰。跟蹤錯誤發生的精確點是非常困難的,因為很可能 他在引起崩潰的事件發生以前很一段時間就已經發生了。

不必奇怪的是,有一些工具,商業或是自由的,可以有助於處理這兩種問題類型。例如,有許多不同的malloc與free版本,其中的一些包含額外的代碼在分配與回收上進行檢測嘗試檢測一個記憶體塊被釋放兩次或是其他一些濫用類型的情況。

ElectricFence

ElectricFence 庫是由Bruce Perens開發的,並且在一些Linux發行版本中作為一個可選的組件來提供,例如RedHat,而且已經可以在網路上獲得。他嘗試使用Linux的虛 擬記憶體工具來保護malloc與free所使用的記憶體,從而在記憶體被破壞時終止程式。

實驗--ElectricFence

下面的程式,efence.c,使用malloc分配一個記憶體塊,然後在超出塊結束處寫入資料。讓我們看一下會發生什麼情況。

複製代碼 代碼如下:#include <stdio.h>
#include <stdlib.h>
int main()
{
char *ptr = (char *) malloc(1024);
ptr[0] = 0;
/* Now write beyond the block */
ptr[1024] = 0;/*寫非法*/
exit(0);
}

當我們編譯運行這個程式時,我們並不會看到進一步的行為。然而,似乎malloc所分配的記憶體地區有一些問題,而我們實際上已經遇到了麻煩。

$ cc -o efence efence.c
$ ./efence
$

然而,如果我們使用ElectricFence庫,libefence.a來連結這個程式,我們就會得到一個即時的響應。

$ cc -o efence efence.c -lefence
$ ./efence
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Segmentation fault
$

在調試器下運行可以定位這個問題:

$ 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 <bruce@perens.com>
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並且將函數與電腦處理器的虛擬記憶體特性相關聯來阻止非法的記憶體訪問。當這樣的訪問發生時,就會拋出一個段錯誤資訊從而可以終止程式。

valgrind

valgrind是一個可以檢測我們已經討論過的許多問題的工具。事實上,他可以檢測資料訪問錯誤與記憶體泄露。也許他並沒有被包含在我們的Linux發行版本中,但是我們可以在http://developer.kde.org/~sewardj處得到。

程式並不需要使用valgrind重新編譯,而我們甚至可以調用一個正在啟動並執行程式的記憶體訪問。他很值得一看,他已經用在主要的開發上,包含KDE版本3。

實驗--valgrind

下面的程式,checker.c,分配一些記憶體,讀取超過那塊記憶體限制的位置,在其結束處之外寫入資料,然後使其不能訪問。

複製代碼 代碼如下:#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *ptr = (char *) malloc(1024);
char ch;
/* Uninitialized read */
ch = ptr[1024];/*讀非法*/
/* Write beyond the block */
ptr[1024] = 0;/*寫非法*/
/* Orphan the block */
ptr = 0;/*野指標*/
exit(0);
}

要使用valgrind,我們只需要簡單的運行valgrind命令,傳遞我們希望檢測的選項,其後是使用其參數啟動並執行程式。

當我們使用valgrind來運行我們的程式時,我們可以看到診斷出許多問題:

$ 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== $

這裡我們可以看到錯誤的讀取與寫入已經被捕獲,而所關注的記憶體塊與他們被分配的位置相關聯。我們可以使用調試器在出錯點斷開程式。

valgrind 有許多選項,包含特定的錯誤類型運算式與記憶體泄露檢測。要檢測我們的例子泄露,我們必須使用一個傳遞給valgrind的選項。當程式結束時要檢測記憶體泄 露,我們需要指定 --leak-check=yes。我們可以使用valgrind --help得到一個選項列表。

工作原理

我們的程式在valgrind的控制下執行,這會檢測我們程式所執行的各種動作,並且執行許多檢測,包括記憶體訪問。如果程式訪問一個已指派的記憶體塊並且訪問 是非法的,valgrind就會輸出一條資訊。在程式結束時,一個垃圾收集常式就會運行來檢測是否在存在分配的記憶體塊沒有被釋放。這些孤兒記憶體也會被報告。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.