linux調試工具glibc的演示分析

一)MALLOC_CHECK_

GNU的標準庫(glibc)可以通過內置的調試特性對動態內存進行調試,它就是MALLOC_CHECK_環境變量,
它在默認情況下是不設定的,在老的版本默認這個值爲0,新的版本默認值爲2,但有一個矛盾,如果設定爲空,它將會打印出長長的跟蹤信息,這比設爲2更詳細.

MALLOC_CHECK_有三種設定,即:
MALLOC_CHECK_=0 ----- 關閉所有檢查.
MALLOC_CHECK_=1 ----- 當有錯誤被探測到時,在標準錯誤輸出(stderr)上打印錯誤信息.
MALLOC_CHECK_=2 ----- 當有錯誤被探測到時,不顯示錯誤信息,直接進行中斷.


我們用下面的小程序做一下測試,源程序如下:
#include <stdio.h>
#include <stdlib.h>

int main (int argc,char *argv[])
{
        int i;
        char* p = (char *)malloc(10);
        char* pt = p;

        for (i = 0;i < 10;i++)
        {
                p[i] = 'z';
        }
        free (p);
        free(pt);
        return 0;
}
gcc double-free.c -o double-free

注:這個程序會釋放兩次指針.
echo $MALLOC_CHECK_

我們在MALLOC_CHECK_默認設定的情況下,執行test程序,輸出如下的信息:
 ./test 
*** glibc detected *** ./test: double free or corruption (fasttop): 0x0890f008 ***
======= Backtrace: =========
/lib/libc.so.6[0x175f7d]
/lib/libc.so.6(cfree+0x90)[0x1795d0]
./test[0x80483dc]
/lib/libc.so.6(__libc_start_main+0xdc)[0x125dec]
./test[0x8048301]
======= Memory map: ========
00110000-00247000 r-xp 00000000 08:01 3704502    /lib/libc-2.5.so
00247000-00249000 r-xp 00137000 08:01 3704502    /lib/libc-2.5.so
00249000-0024a000 rwxp 00139000 08:01 3704502    /lib/libc-2.5.so
0024a000-0024d000 rwxp 0024a000 00:00 0 
00b51000-00b6a000 r-xp 00000000 08:01 3704501    /lib/ld-2.5.so
00b6a000-00b6b000 r-xp 00018000 08:01 3704501    /lib/ld-2.5.so
00b6b000-00b6c000 rwxp 00019000 08:01 3704501    /lib/ld-2.5.so
00bf3000-00bf4000 r-xp 00bf3000 00:00 0          [vdso]
00dab000-00db6000 r-xp 00000000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
00db6000-00db7000 rwxp 0000a000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
08048000-08049000 r-xp 00000000 08:01 327681     /root/test
08049000-0804a000 rw-p 00000000 08:01 327681     /root/test
0890f000-08930000 rw-p 0890f000 00:00 0 
b7e00000-b7e21000 rw-p b7e00000 00:00 0 
b7e21000-b7f00000 ---p b7e21000 00:00 0 
b7f26000-b7f27000 rw-p b7f26000 00:00 0 
b7f3b000-b7f3c000 rw-p b7f3b000 00:00 0 
bfdcf000-bfde4000 rw-p bfdcf000 00:00 0          [stack]
Aborted

這裏我們調整MALLOC_CHECK_爲0,再次運行程序,如下:
export MALLOC_CHECK_=0
./test 
注:我們看到程序沒有任何輸出.

我們將MALLOC_CHECK_調整爲1,再次運行程序,如下:
export MALLOC_CHECK_=1
./test 
malloc: using debugging hooks
*** glibc detected *** ./test: free(): invalid pointer: 0x0811e008 ***
注:我們看到每次運行程序都會有malloc: using debugging hooks的輸出,同時程序檢測到free()兩次釋放的問題.

我們將MALLOC_CHECK_調整爲2,再次運行程序,如下:
export MALLOC_CHECK_=2
./test 
Aborted
注:我們看到程序只輸出了Aborted,並中斷了程序的運行.



二)用mtrace查找內存泄露

mtrace是由glibc提供的一個工具,在Redhat中將它打包在glibc-utils包中.

我們安裝此包,如下:
rpm -ivh /mnt/Server/glibc-utils-2.5-12.i386.rpm

mtrace的主要作用是查找內存泄露,爲了應用mtrace程序,必須在代碼中使用glibc提供的函數mtrace和muntrace.另外,必須設置一個文件的名字給環境變量MALLOC_TRACE,因爲glibc利用它爲mtrace程序存儲數據.
當執行完代碼後,數據將會存在這個確認的文件中,每執行一次程序,這個文件的內容都會被重寫.

我們用下面的代碼進行測試,如下:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mcheck.h>


int main (int argc,char *argv[])
{
        setenv("MALLOC_TRACE","output",1);
        mtrace();
        int i;
        char* p = (char *)malloc(10);
        char* pt = p;

        for (i = 0;i < 10;i++)
        {
                p[i] = 'z';
        }
        return 0;
}

編譯:
gcc test.c -o test

注:程序用setenv函數設定環境變量MALLOC_TRACE

運行程序:
./test 

這時在當前目錄下生成了一個名爲output的文件,如下:
cat output 
= Start
@ ./test:[0x80483f2] + 0x82ba438 0xa
@ /lib/libc.so.6:(clearenv+0x7c)[0xb9910c] - 0x82ba008
@ /lib/libc.so.6:(tdestroy+0x47)[0xc39b77] - 0x82ba090
@ /lib/libc.so.6:(tdestroy+0x4f)[0xc39b7f] - 0x82ba0b0

用mtrace查找內存泄露,它告訴我們memory not freed
mtrace output 
- 0x082ba008 Free 3 was never alloc'd 0xb9910c
- 0x082ba090 Free 4 was never alloc'd 0xc39b77
- 0x082ba0b0 Free 5 was never alloc'd 0xc39b7f

Memory not freed:
-----------------
   Address     Size     Caller
0x082ba438      0xa  at 0x80483f2




三)使用memusage收集內存統計數據

memusage不需要在代碼中做出任何指示.這個工具也來自由glibc-utils包.它以柱形顯示程序佔用了多少內存.它默認輸出到標準輸出中,用ASCII文本顯示一個繪成圖畫似的柱形.如下:
memusage awk 'BEGIN{print "hello world"}'
hello world

Memory usage summary: heap total: 7487, heap peak: 6891, stack peak: 8624
         total calls   total memory   failed calls
 malloc|         58           7487              0
realloc|          0              0              0  (nomove:0, dec:0, free:0)
 calloc|          0              0              0
   free|         15            797
Histogram for block sizes:
    0-15             27  46% ==================================================
   16-31              7  12% ============
   32-47              2   3% ===
   48-63              6  10% ===========
   64-79              1   1% =
   80-95              1   1% =
   96-111             1   1% =
  112-127             4   6% =======
  160-175             1   1% =
  176-191             2   3% ===
  192-207             1   1% =
  208-223             2   3% ===
  384-399             1   1% =
  480-495             1   1% =
 4000-4015            1   1% =
 



四)使用Electric Fence檢測內存泄漏

Electric Fence用一些巧妙的技術來檢測程序在堆內存區上的溢出,不需要用Electric Fence來修改代碼,相反,它提供一個動態庫,這個庫有多個動態分配函數.
一個名爲ef的腳本被用來處理環境變量LD_PRELOAD的設置,我們可以用ef命令來調用程序.

下面是安裝Electric Fence,如下:
rpm -ivh /mnt/Server/ElectricFence-2.2.2-20.2.2.i386.rpm

我們下面用一個小程序做測試,源代碼如下:
#include <string.h>

int
main (int argc, char *argv[])
{
        int *ptr = new int;
        memset(ptr, 0, sizeof(int) + 1);
        delete ptr;
}

編譯:
g++ new-corrupt.cpp -o new-corrupt
注:這個小程序會導致邊界溢出.

執行程序:
./new-corrupt
注:程序沒有任何指示.


我們用ef執行這個程序,如下:
ef ./new-corrupt   

  Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <[email protected]>
/usr/bin/ef: line 20:  4148 Segmentation fault      ( export LD_PRELOAD=libefence.so.0.0; exec $* )
注:此時有輸出信息,它告訴我們出現了Segmentation {敏感詞}t,並且指明在哪行出現的問題.

我們也可以將electric fence和gdb聯用,如下:
編譯程序,同時指定-g選項
g++ -g new-corrupt.cpp -o new-corrupt 

用gdb打開程序,如下:
gdb ./new-corrupt
GNU gdb Red Hat Linux (6.5-16.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) set environment LD_PRELOAD libefence.so.0.0              /*設置環境變量LD_PRELOAD爲libefence.so.0.0*/
(gdb) run                                                      /*運行程序*/
Starting program: /root/new-corrupt 

  Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <[email protected]>

  Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <[email protected]>

Program received signal SIGSEGV, Segmentation fault.
0x0804849d in main () at new-corrupt.cpp:7                     /*檢查出在調用memset函數時導致越界*/
7               memset(ptr, 0, sizeof(int) + 1);
(gdb) quit

下面我們在gdb中不指定環境變量,我們看到gdb沒有打印出相關的錯誤信息.
gdb ./new-corrupt
GNU gdb Red Hat Linux (6.5-16.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) run
Starting program: /root/new-corrupt 

Program exited normally.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章