linux valgrind內存泄漏

valgrind

Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and threading bugs, and profile your programs in detail. You can also use Valgrind to build new tools.
Valgrind包含下列工具:

    1、memcheck:檢查程序中的內存問題,如泄漏、越界、非法指針等(最常用)

    2、callgrind:檢測程序代碼的運行時間和調用過程,以及分析程序性能。(配合gprof使用)

    3、cachegrind:分析CPU的cache命中率、丟失率,用於進行代碼優化。

    4、helgrind:用於檢查多線程程序的競態條件。

    5、massif:堆棧分析器,指示程序中使用了多少堆內存等信息。

    6、lackey:

    7、nulgrind:

這幾個工具的使用是通過命令:valgrind [options] prog-and-args
valgrind --tool=name 程序名來分別調用的,當不指定tool參數時默認是 --tool=memcheck

下載&安裝

平臺支持,內核版本>3.0,glibc 2.5.X以上

http://valgrind.org/info/platforms.html

工具概述:

http://valgrind.org/info/tools.html

下載地址:

http://valgrind.org/downloads/current.html#current
tar jxvf valgrind.tar.bz2
./autogen.sh 

修改configure,查找armv7* 
armv7*改爲armv7*|arm*)

./configure --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc CPP=arm-linux-gnueabihf-cpp CXX=arm-linux-gnueabihf-g++ --prefix=/home/valgrind

--prefix是安裝路徑,要與target存放位置一直

make && make install

tar zcvf /home/valgrind.tar.gz ./valgrind/
tar zxvf valgrind.tar.gz /home


PATH="/usr/local/bin:/usr/bin:/bin:/home/valgrind/bin"
export VALGRIND_LIB="/home/valgrind/lib/valgrind"

備忘:AM335X最新SDK文件系統默認含valgrind,而SDK5.X安裝valgrind3.12無法使用"ld-linux缺失"

測試代碼1

//測試代碼
void test()
{
int *ptr=malloc(sizeof(int)*10);//嚴格來說:動態內存ptr必須memset初始化
ptr[10]=7;// error1:內存越界 
memcpy(ptr+1,ptr,5);// error2:踩內存  
free(ptr);//
free(ptr);// error3:重複釋放  
int *p1;// error4:指針變量沒初始化
*p1=1;// error5:非法指針  
}
int main(void)
{
test();
return 0;
}

運行結果:

# valgrind --leak-check=full ./test_test 
==1083== Memcheck, a memory error detector
==1083== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1083== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==1083== Command: ./test_test
==1083== 
==1083== Invalid write of size 4   //內存越界
==1083==    at 0x104E6: test (in /mnt/nfs/home/root/test/test_test)
==1083==  Address 0x4978050 is 0 bytes after a block of size 40 alloc'd
==1083==    at 0x483D850: malloc (in /usr/lib/valgrind/vgpreload_memcheck-arm-linux.so)
==1083== 
==1083== Source and destination overlap in memcpy(0x497802c, 0x4978028, 5)//內存飛踩
==1083==    at 0x48431F8: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-arm-linux.so)
==1083== 
==1083== Invalid free() / delete / delete[] / realloc()
==1083==    at 0x483EE90: free (in /usr/lib/valgrind/vgpreload_memcheck-arm-linux.so)
==1083==  Address 0x4978028 is 0 bytes inside a block of size 40 free'd
==1083==    at 0x483EE90: free (in /usr/lib/valgrind/vgpreload_memcheck-arm-linux.so)
==1083==  Block was alloc'd at
==1083==    at 0x483D850: malloc (in /usr/lib/valgrind/vgpreload_memcheck-arm-linux.so)
==1083== 
==1083== Use of uninitialised value of size 4//指針變量沒初始化
==1083==    at 0x10506: test (in /mnt/nfs/home/root/test/test_test)
==1083== 
==1083== Invalid write of size 4//內存越界
==1083==    at 0x10506: test (in /mnt/nfs/home/root/test/test_test)
==1083==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==1083== 
==1083== 
==1083== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==1083==  Access not within mapped region at address 0x0
==1083==    at 0x10506: test (in /mnt/nfs/home/root/test/test_test)
==1083==  If you believe this happened as a result of a stack
==1083==  overflow in your program's main thread (unlikely but
==1083==  possible), you can try to increase the size of the
==1083==  main thread stack using the --main-stacksize= flag.
==1083==  The main thread stack size used in this run was 8388608.
==1083== 
==1083== HEAP SUMMARY:
==1083==     in use at exit: 0 bytes in 0 blocks
==1083==   total heap usage: 1 allocs, 2 frees, 40 bytes allocated
==1083== 
==1083== All heap blocks were freed -- no leaks are possible
==1083== 
==1083== For counts of detected and suppressed errors, rerun with: -v
==1083== Use --track-origins=yes to see where uninitialised values come from
==1083== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 0 from 0)

測試代碼2

參考例程/examples/test-parser

valgrind運行結果:

#valgrind --leak-check=full ./test_test-parser  
==915== Conditional jump or move depends on uninitialised value(s)
==915==    at 0x15B9C: SCPI_NumberToStr (in /mnt/nfs/home/root/test/test_test-parser)
.
.
P1=7.291226319893 //隨機值
P2=
0
.
.
==1624== HEAP SUMMARY:
==1624==     in use at exit: 0 bytes in 0 blocks
==1624==   total heap usage: 2 allocs, 2 frees, 4,101 bytes allocated
==1624== 
==1624== All heap blocks were freed -- no leaks are possible
==1624== 
==1624== For counts of detected and suppressed errors, rerun with: -v
==1624== Use --track-origins=yes to see where uninitialised values come from
==1624== ERROR SUMMARY: 1779 errors from 126 contexts (suppressed: 0 from 0)

報錯原因:變量沒有初始化

//MAIN.C
TEST_SCPI_INPUT("meas:volt:dc? 0.01 V, Default\r\n");//該命令沒報錯!DMM_MeasureVoltageDcQ對param1 param2賦了值
TEST_SCPI_INPUT("meas:volt:dc?\r\n");//<----報錯點
TEST_SCPI_INPUT("meas:volt:dc? def, 0.00001\r\n");
TEST_SCPI_INPUT("meas:volt:dc? 0.00001\r\n");

TEST_SCPI_INPUT("meas:volt:dc?\r\n");---->DMM_MeasureVoltageDcQ-->param1, param2未初始化

修改examples/test-parser/main.c

static scpi_result_t DMM_MeasureVoltageDcQ(scpi_t * context) {
    scpi_number_t param1, param2;
    char bf[15];
    fprintf(stderr, "meas:volt:dc\r\n"); /* debug command name */
    memset(&param1,0,sizeof(param1));//<-----添加這兩行即可解決所有報錯
    memset(&param2,0,sizeof(param2));//<-----
    /* read first parameter if present */
    if (!SCPI_ParamNumber(context, scpi_special_numbers_def, &param1, FALSE)) {
        /* do something, if parameter not present */
        fprintf(stderr, "\t error no param1\r\n");
    }

    /* read second paraeter if present */
    if (!SCPI_ParamNumber(context, scpi_special_numbers_def, &param2, FALSE)) {
        /* do something, if parameter not present */
        fprintf(stderr, "\t error no param2\r\n");
    }


    SCPI_NumberToStr(context, scpi_special_numbers_def, &param1, bf, 15);
    fprintf(stderr, "\tP1=%s\r\n", bf);


    SCPI_NumberToStr(context, scpi_special_numbers_def, &param2, bf, 15);
    fprintf(stderr, "\tP2=%s\r\n", bf);

    SCPI_ResultDouble(context, 0);

    return SCPI_RES_OK;
}

​ 結論:

​ 1.valgrind只能檢測程序中被使用或調用的函數/變量所存在的問題

​ 2.

gprof和gprof2dot.py

​ gprof簡介

每個函數的調用次數,每個函數消耗的處理器時間。
函數的調用關係,每個函數調用花費了多少時間。

gprof2dot.py

生成調用關係圖:展示調用關係、次數、消耗時間

如何生成可視化調用關係圖


//PC端安裝graphviz軟件
#sudo apt-get install graphviz
//makefile添加 -pg選項,運行可執行程序後生成gmon.out 
#./test_test-parser
#gprof test_test-parser gmon.out //在終端打印
//生成調用關係圖
#valgrind --tool=callgrind ./test_test-parser //生成callgrind.out.XXX
#gprof2dot.py -f callgrind callgrind.out.XXX |dot -Tpng -o report.png//嵌入式環境該命令在PC上執行

gprof2dot.py下載地址

https://github.com/jrfonseca/gprof2dot

接上文補充幾點可能無法產生gmon.out文件的情況:

  1,程序不是從main return或exit()退出,則可能不生成gmon.out。
  2,程序如果崩潰,可能不生成gmon.out。
  3,測試發現在虛擬機上運行,可能不生成gmon.out。
  4,程序忽略SIGPROF信號!一定不能捕獲、忽略SIGPROF信號。man手冊對SIGPROF的解釋是:profiling timer expired. 如果忽略這個信號,gprof的輸出則是:Each sample counts as 0.01 seconds. no time accumulated.
  5,如果程序運行時間非常短,則gprof可能無效。

參考文章:

https://e2e.ti.com/support/processors/f/791/t/217947

https://www.cnblogs.com/CodingTheFuture/p/9864960.html

https://blog.csdn.net/u014717036/article/details/50762252

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