valgrind 性能測試工具學習使用


一、valgrind簡介

Valgrind工具套件提供了許多調試和分析工具,可幫助您使程序更快,更正確。這些工具中最受歡迎的是Memcheck。它可以檢測許多與C和C ++程序中常見的內存相關的錯誤,這些錯誤可能導致崩潰和不可預測的行為。

標准配置提供了許多有用的工具:

  1. Memcheck是一個內存錯誤檢測器。可以幫助你使程序更加正確。——這是valgrind應用最廣泛的工具,一個重量級的內存檢查器,能夠發現開發中絕大多數內存錯誤使用情況,比如:使用未初始化的內存,使用已經釋放了的內存,內存訪問越界等。
  2. Cachegrind是一個緩存和分支預測分析器。可以幫助程序運行的更快。——它主要用來檢查程序中緩存使用出現的問題。
  3. Callgrind是一個生成緩存分析器的調用圖。和Cachegrind有一些重疊,也收集了Cachegrind沒有的一些信息。——它主要用來檢查程序中函數調用過程中出現的問題。
  4. Helgrind是一個線程錯誤檢測器。可以幫助多線程程序更准確。——它主要用來檢查多線程程序中出現的競爭問題。
  5. DRD也是線程錯誤檢測器。他與Helgrind類似,但是用不同的分析技術,因此可能會發現不同的問題。
  6. Massif時一個堆分析器。可以幫助使程序使用更少的內存。——它主要用來檢查程序中堆棧使用中出現的問題。
  7. DHAT時一種不同類型的對分析器。可以幫助了解塊壽命,塊利用率和效率低下的問題。
  8. SGcheck時一種實驗工具,可以檢測堆棧和全局數組的溢出。它的功能與Memcheck的功能互補:SGcheck發現Memcheck無法解決的問題,反之亦然。
  9. 小工具: Lackey是一個示例工具,用於說明一些儀器基礎知識
  10. 小工具:Nulgrind是最小的Valgrind工具,不進行分析或檢測,僅用於測試目的。是最簡單的Valgrind工具。它不執行任何程序或程序分析,只需正常運行即可。它主要用於Valgrind的開發人員進行調試和回歸測試。盡管如此,您可以使用Nulgrind運行程序。它們的運行速度比正常情況慢5倍左右,沒有任何有用的效果。Note that you need to use the option --tool=none to run Nulgrind (ie. not --tool=nulgrind).
  11. extension ------> 可以利用core提供的功能,自己編寫特定的內存調試工具(不在我學習的范圍中)

 二、Ubuntu16.04下安裝valgrind

Valgrind 安裝,去官網下載: http://valgrind.org/downloads/current.html#current

我當前下載的版本為valgrind-3.15.0.tar.bz2

# tar -jxf valgrind-3.15.0.tar.bz2   ——解壓

# cd /home/ranxf/valgrind-3.15.0

# vim README ——查看文檔來確認安裝過程

 0. Clone the code from GIT:
     git clone git://sourceware.org/git/valgrind.git
     There are further instructions at
     http://www.valgrind.org/downloads/repository.html.

 1. cd into the source directory.

  2. Run ./autogen.sh to setup the environment (you need the standard
     autoconf tools to do so).

  3. Continue with the following instructions...

  To install from a tar.bz2 distribution:

  4. Run ./configure, with some options if you wish.  The only interesting
     one is the usual --prefix=/where/you/want/it/installed.

  5. Run "make".

  6. Run "make install", possibly as root if the destination permissions
     require that.

  7. See if it works.  Try "valgrind ls -l".  Either this works, or it
     bombs out with some complaint.  In that case, please let us know
     (see http://valgrind.org/support/bug_reports.html).

# ./configure --prefix=/workdisk/valgrind/  # 配置安裝路徑
# make  # 從makefile中讀取指令,然后編譯
# make instal  # 從makefile中讀取指令,安裝到指定位置

# vim /etc/profile
export PATH="$PATH:/workdisk/valgrind/bin"(環境變量配置)

三、驗證valgrind是否安裝成功

cd /workdisk/valgrind/bin

# ./valgrind ls -l
==31242== Memcheck, a memory error detector
==31242== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31242== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==31242== Command: ls -l
==31242== 
總用量 836
-rwxr-xr-x 1 root root  44737 8月  27 10:41 callgrind_annotate
-rwxr-xr-x 1 root root  12673 8月  27 10:41 callgrind_control
-rwxr-xr-x 1 root root  33535 8月  27 10:41 cg_annotate
-rwxr-xr-x 1 root root  10418 8月  27 10:41 cg_diff
-rwxr-xr-x 1 root root 315872 8月  27 10:41 cg_merge
-rwxr-xr-x 1 root root  24398 8月  27 10:41 ms_print
-rwxr-xr-x 1 root root  76816 8月  27 10:41 valgrind
-rwxr-xr-x 1 root root 104136 8月  27 10:41 valgrind-di-server
-rwxr-xr-x 1 root root  30176 8月  27 10:41 valgrind-listener
-rwxr-xr-x 1 root root 180680 8月  27 10:41 vgdb
==31242== 
==31242== HEAP SUMMARY:
==31242==     in use at exit: 19,848 bytes in 17 blocks
==31242==   total heap usage: 228 allocs, 211 frees, 102,284 bytes allocated
==31242== 
==31242== LEAK SUMMARY:
==31242==    definitely lost: 0 bytes in 0 blocks
==31242==    indirectly lost: 0 bytes in 0 blocks
==31242==      possibly lost: 0 bytes in 0 blocks
==31242==    still reachable: 19,848 bytes in 17 blocks
==31242==         suppressed: 0 bytes in 0 blocks
==31242== Rerun with --leak-check=full to see details of leaked memory
==31242== 
==31242== For lists of detected and suppressed errors, rerun with: -s
==31242== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
./valgrind --leak-check=full ls -l
==31322== Memcheck, a memory error detector
==31322== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31322== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==31322== Command: ls -l
==31322== 
總用量 836
-rwxr-xr-x 1 root root  44737 8月  27 10:41 callgrind_annotate
-rwxr-xr-x 1 root root  12673 8月  27 10:41 callgrind_control
-rwxr-xr-x 1 root root  33535 8月  27 10:41 cg_annotate
-rwxr-xr-x 1 root root  10418 8月  27 10:41 cg_diff
-rwxr-xr-x 1 root root 315872 8月  27 10:41 cg_merge
-rwxr-xr-x 1 root root  24398 8月  27 10:41 ms_print
-rwxr-xr-x 1 root root  76816 8月  27 10:41 valgrind
-rwxr-xr-x 1 root root 104136 8月  27 10:41 valgrind-di-server
-rwxr-xr-x 1 root root  30176 8月  27 10:41 valgrind-listener
-rwxr-xr-x 1 root root 180680 8月  27 10:41 vgdb
==31322== 
==31322== HEAP SUMMARY:
==31322==     in use at exit: 19,848 bytes in 17 blocks
==31322==   total heap usage: 228 allocs, 211 frees, 102,284 bytes allocated
==31322== 
==31322== LEAK SUMMARY:
==31322==    definitely lost: 0 bytes in 0 blocks
==31322==    indirectly lost: 0 bytes in 0 blocks
==31322==      possibly lost: 0 bytes in 0 blocks
==31322==    still reachable: 19,848 bytes in 17 blocks
==31322==         suppressed: 0 bytes in 0 blocks
==31322== Reachable blocks (those to which a pointer was found) are not shown.
==31322== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==31322== 
==31322== For lists of detected and suppressed errors, rerun with: -s
==31322== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

四、Valgrind 命令參數介紹

用法: valgrind [options] prog-and-args
[options]: 常用選項,適用於所有Valgrind工具

最常用的命令格式:

valgrind --tool=memcheck --leak-check=full ./test

    -h –help 顯示幫助信息。(除了以下參數,可用幫助-h查看更多信息)
    --version 顯示valgrind內核的版本,每個工具都有各自的版本。
    -q –quiet 安靜地運行,只打印錯誤信息。
    -v –verbose 更詳細的信息, 增加錯誤數統計。
    --trace-children=no|yes 跟蹤子線程? [no]
    --track-fds=no|yes 跟蹤打開的文件描述?[no]
    --time-stamp=no|yes 增加時間戳到LOG信息? [no]
    --log-fd=<number> 輸出LOG到描述符文件 [2=stderr]
    --log-file=<file> 將輸出的信息寫入到filename.PID的文件里,PID是運行程序的進行ID
    --log-file-exactly=<file> 輸出LOG信息到 file
    --log-file-qualifier=<VAR> 取得環境變量的值來做為輸出信息的文件名。 [none]
    --log-socket=ipaddr:port 輸出LOG到socket ,ipaddr:port

LOG信息輸出

還是在-h中查看

   --xml=yes 將信息以xml格式輸出,只有memcheck可用
    --num-callers=<number> show <number> callers in stack traces [12]
    --error-limit=no|yes 如果太多錯誤,則停止顯示新錯誤? [yes]
    --error-exitcode=<number> 如果發現錯誤則返回錯誤代碼 [0=disable]
    --db-attach=no|yes 當出現錯誤,valgrind會自動啟動調試器gdb。[no]
    --db-command=<command> 啟動調試器的命令行選項[gdb -nw %f %p]

適用於Memcheck工具的相關選項

   --leak-check=no|summary|full 要求對leak給出詳細信息? [summary]
    --leak-resolution=low|med|high how much bt merging in leak check [low]
    --show-reachable=no|yes show reachable blocks in leak check? [no]

五、測試案例介紹

(一)測試demo

/workdisk# vim /workdisk/testcode/testValgrind.cc  # 在該路徑下生成喊以下內容的testValgrind.cc文件(內存未釋放)

#include <iostream> using namespace std; int main() { int *a = new int(2); //delete a; return 0; }

(二)代碼編譯

root@ranxf-TEST:/workdisk/testcode# ls
testValgrind.cc
root@ranxf-TEST:/workdisk/testcode# g++ -g -o testValgrind testValgrind.cc 
root@ranxf-TEST:/workdisk/testcode# ls
testValgrind  testValgrind.cc

-o:指定生成可執行文件的名稱。使用方法為:g++ -o afile file.cpp file.h ... (可執行文件不可與待編譯或鏈接文件同名,否則會生成相應可執行文件且覆蓋原編譯或鏈接文件),如果不使用-o選項,則會生成默認可執行文件a.out。
-c:只編譯不鏈接,只生成目標文件。
-g:添加gdb調試選項。

(三)代碼單元測試

輸入命令:orkdisk/testcode# /workdisk/valgrind/bin/valgrind --tool=memcheck --leak-check=full ./testValgrind
如果valgrind添加了環境變量,輸入命令可以在任何路徑下簡化為:#valgrind --tool=memcheck --leak-check=full ./testValgrin
root@ranxf-TEST:/workdisk/testcode# /workdisk/valgrind/bin/valgrind --tool=memcheck --leak-check=full ./testValgrind
==12862== Memcheck, a memory error detector    # 內存錯誤檢測器
==12862== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12862== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12862== Command: ./testValgrind
==12862== 
==12862== 
==12862== HEAP SUMMARY:  # 堆摘要
==12862==     in use at exit: 72,708 bytes in 2 blocks # 退出時使用:2個數據塊中有72708個字節
==12862==   total heap usage: 2 allocs, 0 frees, 72,708 bytes allocated
==12862== 
==12862== 4 bytes in 1 blocks are definitely lost in loss record 1 of 2  # 1個塊中的4個字節在丟失記錄1(共2個)中肯定丟失
==12862==    at 0x4C2E4B6: operator new(unsigned long) (vg_replace_malloc.c:344)
==12862==    by 0x400717: main (testValgrind.cc:7)
==12862== 
==12862== LEAK SUMMARY:  泄漏匯總
==12862==    definitely lost: 4 bytes in 1 blocks  肯定泄露:1塊中有4個字節
==12862==    indirectly lost: 0 bytes in 0 blocks  間接泄露:0塊0字節
==12862==      possibly lost: 0 bytes in 0 blocks  可能泄露:0塊0字節
==12862==    still reachable: 72,704 bytes in 1 blocks  仍然可以訪問:1個塊中有72704個字節
==12862==         suppressed: 0 bytes in 0 blocks   抑制:0塊0字節
==12862== Reachable blocks (those to which a pointer was found) are not shown. 不顯示可訪問塊(找到指針的塊)。
==12862== To see them, rerun with: --leak-check=full --show-leak-kinds=all   若要查看它們,請使用以下項重新運行:--leak check=full--show leak kinds=all
==12862== 
==12862== For lists of detected and suppressed errors, rerun with: -s  對於檢測到的和抑制的錯誤列表,請使用-s重新運行。
==12862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)  錯誤摘要:1個上下文中有1個錯誤(禁止:0到0)
root@ranxf-TEST:/workdisk/testcode# 

Memcheck將內存泄露分為兩種,一種是肯定的內存泄露(definitely lost),一種可能的內存泄露(possibly lost);

Definitely lost 是指已經不能夠訪問這塊內存;

  Definitely lost又分為兩種:直接的(direct)泄露和間接的(indirect)泄露

    直接泄露是沒有任何指針指向該內存;

    間接泄露是指指向該內存的指針都位於內存泄露處;

Possibly lost 是指仍然存在某個指針能夠訪問某塊內存,但該指針指向的已經不是該內存首地址。

 (四)測試代碼

代碼1. 使用未初始化的內存(程序名為valgrindTestCode1.cpp

#include <iostream>
using namespace std;
int main()
{
    int a[5];
    int i,s=0;
    a[0]=a[1]=a[3]=a[4]=0;
    for(i=0;i<5;i++)
      s=s+a[i];
    if(s==33)
      cout<<"sum is 33"<<endl;
    else
      cout<<"sum is not 33"<<endl;
    return 0;
}

數組a中的第二個元素未進行初始化,但是在for循環中依然訪問鏈數組a的第二個元素。這就是使用未初始化的元素問題。在使用g++編譯器對該程序編譯並運行,結果如下:

root@ranxf-TEST:/workdisk/testcode# g++ -g -o TectCode1 valgrindTestCode1.cpp 
root@ranxf-TEST:/workdisk/testcode# ls
TectCode1  testValgrind  testValgrind.cc  valgrindTestCode1.cpp
root@ranxf-TEST:/workdisk/testcode# ./TectCode1 
sum is not 33

程序在編譯和運行過程中並沒有報出任何錯誤,異常和警告。

但實際上,程序存在一個巨大的隱患,那就是對未初始化的內存進行訪問。我們使用valgrind對該程序進行檢測,結果如下所示:

root@ranxf-TEST:/workdisk/testcode# valgrind ./TectCode1  或者 valgrind --tool=memcheck --leak-check=full ./TectCode1 # valgrind默認為內存錯誤檢測器
==22985== Memcheck, a memory error detector
==22985== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22985== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==22985== Command: ./TectCode1
==22985== 
==22985== Conditional jump or move depends on uninitialised value(s)
==22985==    at 0x400910: main (valgrindTestCode1.cpp:10)  (此處要求我們在進行g++編譯時添加-g參數,不然便給不出具體行數的提示)
==22985== 
sum is not 33
==22985== 
==22985== HEAP SUMMARY:
==22985==     in use at exit: 72,704 bytes in 1 blocks
==22985==   total heap usage: 2 allocs, 1 frees, 73,728 bytes allocated
==22985== 
==22985== LEAK SUMMARY:
==22985==    definitely lost: 0 bytes in 0 blocks
==22985==    indirectly lost: 0 bytes in 0 blocks
==22985==      possibly lost: 0 bytes in 0 blocks
==22985==    still reachable: 72,704 bytes in 1 blocks
==22985==         suppressed: 0 bytes in 0 blocks
==22985== Rerun with --leak-check=full to see details of leaked memory

從上圖結果中可以清晰的看到,提示出1個錯誤,提示信息為:“Conditional jump or move depends on uninitialised value(s)”,並且提示了錯誤位置在源文件test1.cpp中的第10行(此處要求我們在進行g++編譯時添加-g參數,不然便給不出具體行數的提示)

代碼2:內存讀寫越界

 
         
 1 #include <stdlib.h>
 2 #include <iostream>
 3 using namespace std;
 4 int main()
 5 {
 6     int len=4;
 7     int *pt=(int *)malloc(len*sizeof(int));
 8     int *p=pt;
 9     for(int i=0;i<len;i++)
10       p++;
11     *p=5;
12     cout<<"the value of p is"<<*p<<endl;
13     return 0;
14 }
 
        
在程序2中,p指針首先指向了malloc出的4個字節的地址。
之后執行了四次自增運算。也就是p指針自增運算后與初始地址相比偏移了32個字節。
偏移后的地址空間在程序中並沒有申請。所以,p指針變成鏈野指針。
程序中並且又對p的地址空間進行寫操作。這就造成鏈內存讀寫越界的問題。
 ———————————————— 
在使用g++編譯器對該程序編譯並運行,結果如下: root@ranxf-TEST:/workdisk/testcode# g++ -g -o TestCode2 valgrindTestCode2.cpp root@ranxf-TEST:/workdisk/testcode# ./TestCode2 the value of p is5
/workdisk/testcode# valgrind ./TestCode2
==26714== Memcheck, a memory error detector
==26714== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26714== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==26714== Command: ./TestCode2
==26714== 
==26714== Invalid write of size 4 ==26714== at 0x400921: main (valgrindTestCode2.cpp:11) ==26714==  Address 0x5ab7c90 is 0 bytes after a block of size 16 alloc'd
==26714==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
==26714==    by 0x4008F6: main (valgrindTestCode2.cpp:7)
==26714== 
==26714== Invalid read of size 4 ==26714== at 0x40092B: main (valgrindTestCode2.cpp:12) ==26714==  Address 0x5ab7c90 is 0 bytes after a block of size 16 alloc'd
==26714==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
==26714==    by 0x4008F6: main (valgrindTestCode2.cpp:7)
==26714== 
the value of p is5
==26714== 
==26714== HEAP SUMMARY:
==26714==     in use at exit: 72,720 bytes in 2 blocks
==26714==   total heap usage: 3 allocs, 1 frees, 73,744 bytes allocated ==26714== 
==26714== LEAK SUMMARY:
==26714==    definitely lost: 16 bytes in 1 blocks
==26714==    indirectly lost: 0 bytes in 0 blocks
==26714==      possibly lost: 0 bytes in 0 blocks
==26714==    still reachable: 72,704 bytes in 1 blocks
==26714==         suppressed: 0 bytes in 0 blocks
==26714== Rerun with --leak-check=full to see details of leaked memory
==26714== 
==26714== For lists of detected and suppressed errors, rerun with: -s
==26714== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

從上圖結果中可以清晰的看到,提示出2個錯誤,提示信息分別為:“Invalid write of size 4”和“Invalid read of size 4”,並且提示了錯誤位置在源文件test2.cpp中的第11行和第12行。並且檢測提示了:“total heap usage: 3 allocs, 1 frees, 73,744 bytes allocated”。有內存泄漏。

代碼3. 內存覆蓋

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    char x[50];
    int i;
    for(i=0;i<50;i++)
      x[i]=i+1;
    strncpy(x+20,x,20);
    strncpy(x+20,x,21);
    strncpy(x,x+20,20);
    strncpy(x,x+20,21);
    x[39]='\0';
    strcpy(x,x+20);
    x[39]=39;
    x[40]='\0';
    strcpy(x,x+20);
    return 0;
}

在代碼3中,strncpy(x+20,x,21),strncpy(x,x+20,21),strcpy(x,x+20)這三條語句,在進行字符串復制過程中,復制與被復制的字符串空間存在交集。
通過復制操作,會改變初始字符串空間中的值。這就是內存覆蓋的問題。在使用g++編譯器對該程序編譯並運行,結果如下: root@ranxf-TEST:/workdisk/testcode# g++ -g -o TestCode3 valgrindTestCode3.cpp root@ranxf-TEST:/workdisk/testcode# ./TestCode3 root@ranxf-TEST:/workdisk/testcode#

 

可以從上圖中發現,程序在編譯和運行過程中並沒有報出任何錯誤,異常和警告。
但實際上存在內存覆蓋的問題,當我們不注意,再去使用原始字符串時,已經不是原來那個字符串了。
我們使用valgrind對該程序進行檢測,結果如下所示: root@ranxf
-TEST:/workdisk/testcode# valgrind ./TestCode3 ==30101== Memcheck, a memory error detector ==30101== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==30101== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==30101== Command: ./TestCode3 ==30101== ==30101== Source and destination overlap in strncpy(0x1fff0003c9, 0x1fff0003b5, 21) ==30101== at 0x4C31706: __strncpy_sse2_unaligned (vg_replace_strmem.c:555) ==30101== by 0x400648: main (valgrindTestCode3.cpp:11) ==30101== ==30101== Source and destination overlap in strncpy(0x1fff0003b5, 0x1fff0003c9, 21) ==30101== at 0x4C31706: __strncpy_sse2_unaligned (vg_replace_strmem.c:555) ==30101== by 0x400680: main (valgrindTestCode3.cpp:13) ==30101== ==30101== Source and destination overlap in strcpy(0x1fff0003a0, 0x1fff0003b4) ==30101== at 0x4C311C6: strcpy (vg_replace_strmem.c:513) ==30101== by 0x4006BA: main (valgrindTestCode3.cpp:18) ==30101== ==30101== ==30101== HEAP SUMMARY: ==30101== in use at exit: 0 bytes in 0 blocks ==30101== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==30101== ==30101== All heap blocks were freed -- no leaks are possible ==30101== ==30101== For lists of detected and suppressed errors, rerun with: -s ==30101== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

提示三個錯誤,紅色標識部分,並提示源文件中的錯誤行號11行,13行和18行

代碼4、動態內存管理錯誤

 1 #include <iostream>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     int i;
 6     char *p=(char *)malloc(10);
 7     char *pt=p;
 8     for(i=0;i<10;i++)
 9     {
10         p[i]='z';
11     }
12     delete p;
13     pt[1]='x';
14     free(pt);
15     return 0;
16 }
在程序4中,使用malloc申請空間,使用delete釋放空間,兩者不匹配。使用malloc申請了10個字節的空間,只釋放了一個字節空間。另外,對釋放的空間仍然進行了讀寫操作。這些是典型的內存管理錯誤問題。在使用g++編譯器對該程序編譯並運行,結果如下


root@ranxf-TEST:/workdisk/testcode# g++ -g -o TestCode4 valgrindTestCode4.cpp 
root@ranxf-TEST:/workdisk/testcode# ./TestCode4
*** Error in `./TestCode4': double free or corruption (fasttop): 0x00000000023dfc20 ***
……
已放棄 (核心已轉儲)


程序在運行中報出內存錯誤,但沒有給出具體信息。我們使用valgrind對該程序進行檢測,結果如下所示

root@ranxf-TEST:/workdisk/testcode# valgrind ./TestCode4
==31851== Memcheck, a memory error detector
==31851== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31851== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==31851== Command: ./TestCode4
==31851== 
==31851== Mismatched free() / delete / delete []
==31851==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
==31851==    by 0x4007E2: main (valgrindTestCode4.cpp:12)
==31851==  Address 0x5ab7c80 is 0 bytes inside a block of size 10 alloc'd
==31851==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
==31851==    by 0x4007A7: main (valgrindTestCode4.cpp:6)
==31851== 
==31851== Invalid write of size 1
==31851==    at 0x4007EB: main (valgrindTestCode4.cpp:13)
==31851==  Address 0x5ab7c81 is 1 bytes inside a block of size 10 free'd
==31851==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
==31851==    by 0x4007E2: main (valgrindTestCode4.cpp:12)
==31851==  Block was alloc'd at
==31851==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
==31851==    by 0x4007A7: main (valgrindTestCode4.cpp:6)
==31851== 
==31851== Invalid free() / delete / delete[] / realloc()
==31851==    at 0x4C2EF90: free (vg_replace_malloc.c:540)
==31851==    by 0x4007F9: main (valgrindTestCode4.cpp:14)
==31851==  Address 0x5ab7c80 is 0 bytes inside a block of size 10 free'd
==31851==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
==31851==    by 0x4007E2: main (valgrindTestCode4.cpp:12)
==31851==  Block was alloc'd at
==31851==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
==31851==    by 0x4007A7: main (valgrindTestCode4.cpp:6)
==31851== 
==31851== 
==31851== HEAP SUMMARY:
==31851==     in use at exit: 72,704 bytes in 1 blocks
==31851==   total heap usage: 2 allocs, 2 frees, 72,714 bytes allocated
==31851== 
==31851== LEAK SUMMARY:
==31851==    definitely lost: 0 bytes in 0 blocks
==31851==    indirectly lost: 0 bytes in 0 blocks
==31851==      possibly lost: 0 bytes in 0 blocks
==31851==    still reachable: 72,704 bytes in 1 blocks
==31851==         suppressed: 0 bytes in 0 blocks
==31851== Rerun with --leak-check=full to see details of leaked memory
==31851== 
==31851== For lists of detected and suppressed errors, rerun with: -s
==31851== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
root@ranxf-TEST:/workdisk/testcode# 
從上圖結果中可以清晰的看到,提示出的3個錯誤。
  使用malloc申請空間,使用delete釋放空間,兩者不匹配問題:
    “Mismatched free() / delete / delete []”;
  使用malloc申請鏈10個字節的空間,只釋放鏈一個字節空間:
    “ Invalid free() / delete / delete[] / realloc()”;
  對釋放空間仍然進行讀寫操作:
    “Invalid write of size 1”;

代碼5:內存泄露

以下代碼中分別存在makefile  test.cpp  tree.cpp  tree.h四個文件中

 1 /* makefile文件
 2 
 3 test:test.o tree.o
 4         g++ -g -o test test.o tree.o
 5 tree.o:tree.cpp tree.h
 6         g++ -g -c tree.cpp -o tree.o
 7 test.o:test.cpp
 8         g++ -g -c test.cpp -o test.o
 9 
10 
/* tree.h文件
12 #ifndef _TREE_
13 #define _TREE_
14 typedef struct _node{
15     struct _node *l;
16     struct _node *r;
17     char v;
18 }node;
19 node *mk(node *l,node *r,char val);
20 void nodefr(node *n);
21 #endif
22 
23 
/ * test.cpp文件
25 #include <iostream>
26 #include "tree.h"
27 int main()
28 {
29         node *tree1,*tree2,*tree3;
30         tree1=mk(mk(mk(0,0,'3'),0,'2'),0,'1');
31         tree2=mk(0,mk(0,mk(0,0,'6'),'5'),'4');
32         tree3=mk(mk(tree1,tree2,'8'),0,'7');
33         return 0;
34 }
35 
/* tree.cpp文件
37 
38 #include <stdlib.h>
39 #include "tree.h"
40 node *mk(node *l,node *r,char val)
41 {
42         node *f=(node *)malloc(sizeof(*f));
43         f->l=l;
44         f->r=r;
45         f->v=val;
46         return f;
47 }
48 void nodefr(node *n)
49 {
50         if(n){
51                 nodefr(n->l);
52                 nodefr(n->r);
53                 free(n);
54         }
55 }

在程序5中,使用malloc申請空間后,沒有使用free函數釋放申請的內存地址造成內存泄漏。在使用g++編譯器對該程序編譯並運行,結果如下:

1 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# ls
2 makefile  test.cpp  tree.cpp  tree.h
3 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# make
4 g++ -g -c test.cpp -o test.o
5 g++ -g -c tree.cpp -o tree.o
6 g++ -g -o test test.o tree.o
7 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# 

root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# ls
makefile  test  test.cpp  test.o  tree.cpp  tree.h  tree.o
root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# ./test

可以從上圖中發現,程序在編譯和運行過程中並沒有報出任何錯誤,異常和警告。但實際上存在內存泄漏的問題。我們使用valgrind對該程序進行檢測,結果如下所示:

 1 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# valgrind ./test
 2 ==6852== Memcheck, a memory error detector
 3 ==6852== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
 4 ==6852== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
 5 ==6852== Command: ./test
 6 ==6852== 
 7 ==6852== 
 8 ==6852== HEAP SUMMARY:
 9 ==6852==     in use at exit: 72,896 bytes in 9 blocks
10 ==6852== total heap usage: 9 allocs, 0 frees, 72,896 bytes allocated 11 ==6852== 
12 ==6852== LEAK SUMMARY:
13 ==6852== definitely lost: 24 bytes in 1 blocks # 提示直接內存泄露 14 ==6852== indirectly lost: 168 bytes in 7 blocks # 提示間接內存泄露 15 ==6852==      possibly lost: 0 bytes in 0 blocks
16 ==6852==    still reachable: 72,704 bytes in 1 blocks
17 ==6852==         suppressed: 0 bytes in 0 blocks
18 ==6852== Rerun with --leak-check=full to see details of leaked memory
19 ==6852== 
20 ==6852== For lists of detected and suppressed errors, rerun with: -s
21 ==6852== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

由此看到:“total heap usage: 8 allocs, 0 frees, 96 bytes allocated”。

提示間接內存泄漏:“indirectly lost: 84 bytes in 7 blocks”;

提示直接內存泄漏:“definitely lost: 12 bytes in 1 blocks”

間接內存泄漏指:指向該內存的指針都位於內存泄漏處;

直接泄漏時指:沒有任何指針指向該內存。

 

本篇中的代碼樣例及相關內容都來自於學習徐曉鑫女士編著的《后台開發核心技術與應用實踐》所得,特此聲明。

程序相關源碼下載地址:https://github.com/XiaoYaoNet/Valgrind_Memset

參考文章:

  valgrind 工具介紹和簡單的使用

  Valgrind工具之內存檢測

  valgrind 用法詳見:http://blog.csdn.net/sduliulun/article/details/7732906


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM