使用memleak檢查和調試內存泄漏

小型的嵌入式應用中常常會出現許多內存問題,不少狀況下難以定位問題出如今哪裏。linux

我在 sourceforge 上找了些檢測 C 內存泄漏的工具,感受比較易用的是 memleak,下面就來簡要介紹一下它的使用。函數

下載獲得的 memleak 壓縮包大小不到 15 kB,解壓後只有兩個文件:memleak.c 和 memleak.h。在使用過程當中只須要包含頭文件 memleak.h 就可使用 memleak 提供的幾個易用而有效的內存檢測函數了。工具

memleak的原理是利用C語言的宏調用來替代原有的函數調用,好比咱們在代碼中調用malloc(s),實際是調用了:dbg_malloc(s),這個宏定義在 memleak.h 中給出:
#define malloc(s) (FILE_LINE, dbg_malloc(s))this

memleak 維護了一個鏈表,在這個鏈表中保存着程序中對內存函數調用的記錄,這些函數包括:malloc、calloc、realloc、free。每次調用這些函數時,就會更新這個鏈表。
有了這個表,咱們就能夠在適當的位置調用 memleak 提供的函數,顯示一些重要的信息,包括 malloc、calloc、realloc、free調用的次數,申請及分配的內存數,調用的文件和位置等等,信息很是詳細。有了這些功能,咱們就很容易定位內存使用的錯誤源。spa

因爲 memleak 在某些交叉編譯器下不能正常編譯經過,這裏我將 memleak.c 中的結構體 struct head 修改以下:.net

struct head
{
struct head *addr;
size_t size;
char *file;
unsigned line;
/* two addresses took the same space as an address and an integer on many archs => usable */
union lf {
struct { struct head*prev, *next; } list;
struct { char *file; unsigned line; } free;
} u;
};
memleak.c 文件中其它調用到 head 中共用體 u 的地方也要作相應的修改。
修改後的文件能夠 點擊這裏下載

 

memleak 提供瞭如下幾個函數接口:調試

extern void dbg_init(int history_length);
extern int dbg_check_addr(char *msg, void *ptr, int opt);
extern void dbg_mem_stat(void);
extern void dbg_zero_stat(void);
extern void dbg_abort(char *msg);
extern void dbg_heap_dump(char *keyword);
extern void dbg_history_dump(char *keyword);
extern void dbg_catch_sigsegv(void);
詳細的介紹請查看 memleak.c 頭部的註釋或查看源代碼理解。

 

下面舉個簡單的例子:code

#include 
#include 
#include "memleak.h"
int main(void)
{
char * s, * t;
dbg_init(10);
s = (char *)malloc(100);    // 申請 100 bytes
t = (char *)malloc(11);     // 再申請 11 bytes
free(s);                    // 釋放 100 bytes
s = (char *)malloc(80);     // 從新申請 80 bytes
dbg_heap_dump("");          // 顯示調用棧
dbg_mem_stat();             // 顯示調用統計
free(t);                    // 釋放 11 bytes
free(s);                    // 釋放 80 bytes
dbg_mem_stat();             // 再次顯示調用統計
return 0;
}
編輯後保存爲 test.c,與 memleak.c 和 memleak.h 放於同一目錄下。
而後編寫一 Makefile:
CC = gcc
EXEC = test
CSRC = test.c memleak.c
OBJS = $(patsubst %.c,%.o, $(CSRC))
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(OBJS) $(LDFLAGS) $(LDLIBS) -o $@
$(OBJS): %.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
-rm -f $(EXEC) *.elf *.gdb *.o
也保存於同一目錄,在該目錄下 make 編譯,執行 ./test 後輸出以下:
***** test.c:14: heap dump start
(alloc: test.c:11 size: 11)
(alloc: test.c:13 size: 80)
***** test.c:14: heap dump end
test.c:15: m: 3, c: 0, r: 0, f: 1, mem: 91
test.c:18: m: 3, c: 0, r: 0, f: 3, mem: 0
怎麼樣,很簡單吧?

 

memleak 中還有一個函數 dbg_catch_sigsegv(void),能夠綁定系統出現 SIGSEGV 信號時的處理函數,咱們能夠經過修改 memleak.c 中的 sigsegv_handler,自定義這個 SIGSEGV 信號處理函數。不知道 uClinux 下的 SIGSEGV 信號是否也存在,有的話調試一些內存問題就更容易了。blog

最後從網上摘來一段 SIGSEGV 的介紹:接口

SIGSEGV --- Segment Fault. The possible cases of your encountering this error are:
1.buffer overflow --- usually caused by a pointer reference out of range.
2.stack overflow --- please keep in mind that the default stack size is 8192K.
3.illegal file access --- file operations are forbidden on our judge system.
相關文章
相關標籤/搜索