Linux:Valgrind使用

97 篇文章 7 订阅

Linux:Valgrind使用

本着实践主义和实用主义,开始学习。

测试环境:CentOS 7

[test1280@localhost 20170504]$ uname -a
Linux localhost.localdomain 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

valgrind的man手册是这么介绍的:

a suite of tools for debugging and profiling programs

一套用于调试和分析程序的工具

valgrind命令使用方式:

valgrind [valgrind-options] [your-program] [your-program-options]

下面是我们公司里一个大牛写的脚本,大家可以参考下:

文件名:valmem

#!/bin/sh
# $Id$
#
if [ -z "$1" ]; then
    echo "Usage: `basename $0` prog"
    exit 1
fi
valgrind --tool=memcheck \
--leak-check=full \
--time-stamp=yes \
--show-reachable=yes \
--track-origins=yes \
--trace-children=no \
--leak-resolution=med \
--track-fds=yes \
--log-file=myvalmem.log \
"$@"

解释下各个含义:

–tool=memcheck:

--tool=<toolname> [default: memcheck]
Run the Valgrind tool called toolname, e.g. memcheck, cachegrind, callgrind, helgrind, drd, massif, lackey, none, exp-sgcheck, exp-bbv, exp-dhat, etc.
# 默认是memcheck

–leak-check:

--leak-check=<no|summary|yes|full> [default: summary]
When enabled, search for memory leaks when the client program finishes.
If set to summary, it says how many leaks occurred.
If set to full or yes, each individual leak will be shown in detail and/or counted as an error.
# 看得仔细一些,把每个都单独列出来。

–time-stamp:

--time-stamp=<yes|no> [default: no]
When enabled, each message is preceded with an indication of the elapsed wallclock time since startup, expressed as days, hours, minutes, seconds and milliseconds.
# 说白了就是个时间戳。
# 此处要注意,是自启动进程以来的时间。

–show-reachable:

--show-reachable=<yes|no> , --show-possibly-lost=<yes|no>
These options provide an alternative way to specify the leak kinds to show:
--show-reachable=no --show-possibly-lost=yes is equivalent to --show-leak-kinds=definite,possible.
--show-reachable=no --show-possibly-lost=no is equivalent to --show-leak-kinds=definite.
--show-reachable=yes is equivalent to --show-leak-kinds=all.
# 我们这里是yes,代表显示所有类型。

–track-origins:

--track-origins=<yes|no> [default: no]
When set to yes, Memcheck keeps track of the origins of all uninitialised values.
Then, when an uninitialised value error is reported, Memcheck will try to show the origin of the value.
# 跟踪所有未初始化值的起始位置

–trace-children:

--trace-children=<yes|no> [default: no]
When enabled, Valgrind will trace into sub-processes initiated via the exec system call. This is necessary for multi-process programs.
# 启用后,Valgrind将跟踪通过exec系统调用启动的子进程。 这对于多进程程序是必需的。

–leak-resolution:

--leak-resolution=<low|med|high> [default: high]

–track-fds=yes:

--track-fds=<yes|no> [default: no]
When enabled, Valgrind will print out a list of open file descriptors on exit or on request.
# 追踪文件描述符。

–log-file:

--log-file=<filename>
Specifies that Valgrind should send all of its messages to the specified file.
# 后面还以一堆,有三种形式啥啥啥的。
# 这里使用的是直接写一个名字,对,就是你心里想的那样。

脚本使用方式:

[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ ll
total 24
-rwxrwxr-x. 1 test1280 test1280 8500 May  4 02:24 main
-rw-rw-r--. 1 test1280 test1280   66 May  4 02:19 main.c
-rw-r--r--. 1 test1280 test1280 1527 May  4 02:35 myvalmem.log
-rwxr-xr-x. 1 test1280 test1280  291 May  4 02:29 valmem

valmem是脚本名字;main是可执行文件。

PS:你当然可以使用命令行加上一长串的选项去使用valgrind(或alias),使用脚本只不过看起来简洁点。

下面开始各种各样的测试!


Illegal read / Illegal write errors

非法读写错误。

可能出现的情景:

对一个已经释放(free)的地址进行读写。

Sample 1:

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

void fun2()
{
    char *p = (char *)malloc(10);
    free(p);
    char ch = *p; 
}

void fun1()
{
    fun2();
}

int main()
{
    fun1();
    return 0;
}
[test1280@localhost 20170504]$ !g
gcc -o main main.c 
[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 3017== Memcheck, a memory error detector
==00:00:00:00.000 3017== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 3017== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 3017== Command: ./main
==00:00:00:00.000 3017== Parent PID: 3016
==00:00:00:00.000 3017== 
==00:00:00:00.247 3017== Invalid read of size 1
==00:00:00:00.247 3017==    at 0x4005A6: fun2 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005BB: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005CB: main (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==  Address 0x51f6040 is 0 bytes inside a block of size 10 free'd
==00:00:00:00.247 3017==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.247 3017==    by 0x4005A1: fun2 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005BB: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005CB: main (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==  Block was alloc'd at
==00:00:00:00.247 3017==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.247 3017==    by 0x400591: fun2 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005BB: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005CB: main (in /home/test1280/20170504/main)
==00:00:00:00.247 3017== 
==00:00:00:00.267 3017== 
==00:00:00:00.267 3017== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.267 3017== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.268 3017==    <inherited from parent>
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== Open file descriptor 2: /dev/pts/0
==00:00:00:00.268 3017==    <inherited from parent>
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== Open file descriptor 1: /dev/pts/0
==00:00:00:00.268 3017==    <inherited from parent>
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== Open file descriptor 0: /dev/pts/0
==00:00:00:00.268 3017==    <inherited from parent>
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== HEAP SUMMARY:
==00:00:00:00.268 3017==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.268 3017==   total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== All heap blocks were freed -- no leaks are possible
==00:00:00:00.268 3017== 
==00:00:00:00.268 3017== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.268 3017== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

关键是这个地方:

==00:00:00:00.247 3017== Invalid read of size 1
==00:00:00:00.247 3017==    at 0x4005A6: fun2 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005BB: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005CB: main (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==  Address 0x51f6040 is 0 bytes inside a block of size 10 free'd
==00:00:00:00.247 3017==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.247 3017==    by 0x4005A1: fun2 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005BB: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.247 3017==    by 0x4005CB: main (in /home/test1280/20170504/main)

Use of uninitialised values

使用未初始化的值,有两种情况:

Local variables in procedures which have not been initialised.

局部变量未初始化。

Sample 2:

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

int main()
{
    int num;
    int result;
    if (num == 1)
        result = 1;
    else
        result = 2;
    return 0;
}
[test1280@localhost 20170504]$ !g
gcc -o main main.c 
[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 3253== Memcheck, a memory error detector
==00:00:00:00.000 3253== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 3253== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 3253== Command: ./main
==00:00:00:00.000 3253== Parent PID: 3252
==00:00:00:00.000 3253== 
==00:00:00:00.280 3253== Conditional jump or move depends on uninitialised value(s)
==00:00:00:00.280 3253==    at 0x4004F8: main (in /home/test1280/20170504/main)
==00:00:00:00.280 3253==  Uninitialised value was created by a stack allocation
==00:00:00:00.280 3253==    at 0x4004F0: main (in /home/test1280/20170504/main)
==00:00:00:00.280 3253== 
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.299 3253== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.299 3253==    <inherited from parent>
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== Open file descriptor 2: /dev/pts/0
==00:00:00:00.299 3253==    <inherited from parent>
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== Open file descriptor 1: /dev/pts/0
==00:00:00:00.299 3253==    <inherited from parent>
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== Open file descriptor 0: /dev/pts/0
==00:00:00:00.299 3253==    <inherited from parent>
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== HEAP SUMMARY:
==00:00:00:00.299 3253==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.299 3253==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== All heap blocks were freed -- no leaks are possible
==00:00:00:00.299 3253== 
==00:00:00:00.299 3253== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.299 3253== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

关键是这个地方:

==00:00:00:00.280 3253== Conditional jump or move depends on uninitialised value(s)
==00:00:00:00.280 3253==    at 0x4004F8: main (in /home/test1280/20170504/main)
==00:00:00:00.280 3253==  Uninitialised value was created by a stack allocation
==00:00:00:00.280 3253==    at 0x4004F0: main (in /home/test1280/20170504/main)

The contents of heap blocks (allocated with malloc, new, or a similar function) before you (or a constructor) write something there.

动态malloc/new出来的空间,没有向里面写入数据直接读取也会出现这个log。

Sample 3:

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

int main()
{
    int *pNum = (int *)malloc(sizeof(int));
    int result;
    if (*pNum == 1)
        result = 1;
    else
        result = 0;
    free(pNum);

    return 0;
}
[test1280@localhost 20170504]$ !g
gcc -o main main.c 
[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 3438== Memcheck, a memory error detector
==00:00:00:00.000 3438== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 3438== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 3438== Command: ./main
==00:00:00:00.000 3438== Parent PID: 3437
==00:00:00:00.000 3438== 
==00:00:00:00.228 3438== Conditional jump or move depends on uninitialised value(s)
==00:00:00:00.228 3438==    at 0x40059F: main (in /home/test1280/20170504/main)
==00:00:00:00.228 3438==  Uninitialised value was created by a heap allocation
==00:00:00:00.228 3438==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.228 3438==    by 0x400591: main (in /home/test1280/20170504/main)
==00:00:00:00.228 3438== 
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.247 3438== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.247 3438==    <inherited from parent>
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== Open file descriptor 2: /dev/pts/0
==00:00:00:00.247 3438==    <inherited from parent>
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== Open file descriptor 1: /dev/pts/0
==00:00:00:00.247 3438==    <inherited from parent>
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== Open file descriptor 0: /dev/pts/0
==00:00:00:00.247 3438==    <inherited from parent>
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== HEAP SUMMARY:
==00:00:00:00.247 3438==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.247 3438==   total heap usage: 1 allocs, 1 frees, 4 bytes allocated
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== All heap blocks were freed -- no leaks are possible
==00:00:00:00.247 3438== 
==00:00:00:00.247 3438== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.247 3438== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

To see information on the sources of uninitialised data in your program, use the –track-origins=yes option. This makes Memcheck run more slowly, but can make it much easier to track down the root causes of uninitialised value errors.

可以使用–track-origins=yes来追踪根源(未初始化值)。


Use of uninitialised or unaddressable values in system calls

在系统调用中使用未初始化或不可寻址的值。

Sample 4:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char* arr  = malloc(10);
    int*  arr2 = malloc(sizeof(int));
    write( 1 /* stdout */, arr, 10 );
    exit(arr2[0]);
}
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 3750== Memcheck, a memory error detector
==00:00:00:00.000 3750== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 3750== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 3750== Command: ./main
==00:00:00:00.000 3750== Parent PID: 3749
==00:00:00:00.000 3750== 
==00:00:00:00.242 3750== Syscall param write(buf) points to uninitialised byte(s)
==00:00:00:00.242 3750==    at 0x4F1CA30: __write_nocancel (in /usr/lib64/libc-2.17.so)
==00:00:00:00.242 3750==    by 0x4005F9: main (in /home/test1280/20170504/main)
==00:00:00:00.242 3750==  Address 0x51f6040 is 0 bytes inside a block of size 10 alloc'd
==00:00:00:00.242 3750==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.242 3750==    by 0x4005D1: main (in /home/test1280/20170504/main)
==00:00:00:00.242 3750==  Uninitialised value was created by a heap allocation
==00:00:00:00.242 3750==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.242 3750==    by 0x4005D1: main (in /home/test1280/20170504/main)
==00:00:00:00.242 3750== 
==00:00:00:00.250 3750== Syscall param exit_group(status) contains uninitialised byte(s)
==00:00:00:00.250 3750==    at 0x4EF2789: _Exit (in /usr/lib64/libc-2.17.so)
==00:00:00:00.250 3750==    by 0x4E6DE2A: __run_exit_handlers (in /usr/lib64/libc-2.17.so)
==00:00:00:00.250 3750==    by 0x4E6DEB4: exit (in /usr/lib64/libc-2.17.so)
==00:00:00:00.250 3750==    by 0x400606: main (in /home/test1280/20170504/main)
==00:00:00:00.250 3750==  Uninitialised value was created by a heap allocation
==00:00:00:00.250 3750==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.250 3750==    by 0x4005DF: main (in /home/test1280/20170504/main)
==00:00:00:00.250 3750== 
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.262 3750== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.262 3750==    <inherited from parent>
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== Open file descriptor 2: /dev/pts/0
==00:00:00:00.262 3750==    <inherited from parent>
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== Open file descriptor 1: /dev/pts/0
==00:00:00:00.262 3750==    <inherited from parent>
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== Open file descriptor 0: /dev/pts/0
==00:00:00:00.262 3750==    <inherited from parent>
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== HEAP SUMMARY:
==00:00:00:00.262 3750==     in use at exit: 14 bytes in 2 blocks
==00:00:00:00.262 3750==   total heap usage: 2 allocs, 0 frees, 14 bytes allocated
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== 4 bytes in 1 blocks are still reachable in loss record 1 of 2
==00:00:00:00.262 3750==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.262 3750==    by 0x4005DF: main (in /home/test1280/20170504/main)
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== 10 bytes in 1 blocks are still reachable in loss record 2 of 2
==00:00:00:00.262 3750==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.262 3750==    by 0x4005D1: main (in /home/test1280/20170504/main)
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== LEAK SUMMARY:
==00:00:00:00.262 3750==    definitely lost: 0 bytes in 0 blocks
==00:00:00:00.262 3750==    indirectly lost: 0 bytes in 0 blocks
==00:00:00:00.262 3750==      possibly lost: 0 bytes in 0 blocks
==00:00:00:00.262 3750==    still reachable: 14 bytes in 2 blocks
==00:00:00:00.262 3750==         suppressed: 0 bytes in 0 blocks
==00:00:00:00.262 3750== 
==00:00:00:00.262 3750== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.262 3750== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

Because the program has (a) written uninitialised junk from the heap block to the standard output, and (b) passed an uninitialised value to exit.

从未初始化的块中读数据(然后写到标准输出);传递一个未初始化的值到exit系统调用。


Illegal frees

非法的释放。

Sample 5:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void fun1()
{
    char *p = (char *)malloc(10);
    free(p);
    free(p);
    p = NULL;
}

int main()
{
    fun1();
    return 0;
}
[test1280@localhost 20170504]$ !g
gcc -o main main.c 
[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 3894== Memcheck, a memory error detector
==00:00:00:00.000 3894== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 3894== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 3894== Command: ./main
==00:00:00:00.000 3894== Parent PID: 3893
==00:00:00:00.000 3894== 
==00:00:00:00.263 3894== Invalid free() / delete / delete[] / realloc()
==00:00:00:00.263 3894==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.263 3894==    by 0x4005AD: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==    by 0x4005C5: main (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==  Address 0x51f6040 is 0 bytes inside a block of size 10 free'd
==00:00:00:00.263 3894==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.263 3894==    by 0x4005A1: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==    by 0x4005C5: main (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==  Block was alloc'd at
==00:00:00:00.263 3894==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.263 3894==    by 0x400591: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==    by 0x4005C5: main (in /home/test1280/20170504/main)
==00:00:00:00.263 3894== 
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.282 3894== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.282 3894==    <inherited from parent>
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== Open file descriptor 2: /dev/pts/0
==00:00:00:00.282 3894==    <inherited from parent>
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== Open file descriptor 1: /dev/pts/0
==00:00:00:00.282 3894==    <inherited from parent>
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== Open file descriptor 0: /dev/pts/0
==00:00:00:00.282 3894==    <inherited from parent>
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== HEAP SUMMARY:
==00:00:00:00.282 3894==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.282 3894==   total heap usage: 1 allocs, 2 frees, 10 bytes allocated
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== All heap blocks were freed -- no leaks are possible
==00:00:00:00.282 3894== 
==00:00:00:00.282 3894== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.282 3894== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

关键是这个地方:

==00:00:00:00.263 3894== Invalid free() / delete / delete[] / realloc()
==00:00:00:00.263 3894==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.263 3894==    by 0x4005AD: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==    by 0x4005C5: main (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==  Address 0x51f6040 is 0 bytes inside a block of size 10 free'd
==00:00:00:00.263 3894==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.263 3894==    by 0x4005A1: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==    by 0x4005C5: main (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==  Block was alloc'd at
==00:00:00:00.263 3894==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.263 3894==    by 0x400591: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.263 3894==    by 0x4005C5: main (in /home/test1280/20170504/main)

我有一个新malloc出来的地址空间,假设我不释放首地址(free的是被移动过的指针):

Sample 6:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void fun1()
{
    char *p = (char *)malloc(10);
    p += 6;
    free(p);
    p = NULL;
}

int main()
{
    fun1();
    return 0;
}
[test1280@localhost 20170504]$ !g
gcc -o main main.c 
[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 4055== Memcheck, a memory error detector
==00:00:00:00.000 4055== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 4055== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 4055== Command: ./main
==00:00:00:00.000 4055== Parent PID: 4054
==00:00:00:00.000 4055== 
==00:00:00:00.234 4055== Invalid free() / delete / delete[] / realloc()
==00:00:00:00.234 4055==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.234 4055==    by 0x4005A6: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.234 4055==    by 0x4005BE: main (in /home/test1280/20170504/main)
==00:00:00:00.234 4055==  Address 0x51f6046 is 6 bytes inside a block of size 10 alloc'd
==00:00:00:00.234 4055==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.234 4055==    by 0x400591: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.234 4055==    by 0x4005BE: main (in /home/test1280/20170504/main)
==00:00:00:00.234 4055== 
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.254 4055== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.254 4055==    <inherited from parent>
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== Open file descriptor 2: /dev/pts/0
==00:00:00:00.254 4055==    <inherited from parent>
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== Open file descriptor 1: /dev/pts/0
==00:00:00:00.254 4055==    <inherited from parent>
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== Open file descriptor 0: /dev/pts/0
==00:00:00:00.254 4055==    <inherited from parent>
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== HEAP SUMMARY:
==00:00:00:00.254 4055==     in use at exit: 10 bytes in 1 blocks
==00:00:00:00.254 4055==   total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==00:00:00:00.254 4055==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.254 4055==    by 0x400591: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.254 4055==    by 0x4005BE: main (in /home/test1280/20170504/main)
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== LEAK SUMMARY:
==00:00:00:00.254 4055==    definitely lost: 10 bytes in 1 blocks
==00:00:00:00.254 4055==    indirectly lost: 0 bytes in 0 blocks
==00:00:00:00.254 4055==      possibly lost: 0 bytes in 0 blocks
==00:00:00:00.254 4055==    still reachable: 0 bytes in 0 blocks
==00:00:00:00.254 4055==         suppressed: 0 bytes in 0 blocks
==00:00:00:00.254 4055== 
==00:00:00:00.254 4055== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.254 4055== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

关键部分:

==00:00:00:00.234 4055== Invalid free() / delete / delete[] / realloc()
==00:00:00:00.234 4055==    at 0x4C2ACDD: free (vg_replace_malloc.c:530)
==00:00:00:00.234 4055==    by 0x4005A6: fun1 (in /home/test1280/20170504/main)
==00:00:00:00.234 4055==    by 0x4005BE: main (in /home/test1280/20170504/main)
==00:00:00:00.234 4055==  Address 0x51f6046 is 6 bytes inside a block of size 10 alloc'd

可见实际上是空间没有被释放。


When a heap block is freed with an inappropriate deallocation function

申请和释放的函数不匹配。

In C++ it’s important to deallocate memory in a way compatible with how it was allocated.

  1. If allocated with malloc, calloc, realloc, valloc or memalign, you must deallocate with free.
  2. If allocated with new, you must deallocate with delete.
  3. If allocated with new[], you must deallocate with delete[].

The worst thing is that on Linux apparently it doesn’t matter if you do mix these up, but the same program may then crash on a different platform, Solaris for example.

So it’s best to fix it properly.

In some C++ implementations, delete[] must be used for objects allocated by new[] because the compiler stores the size of the array and the pointer-to-member to the destructor of the array’s content just before the pointer actually returned.

delete doesn’t account for this and will get confused, possibly corrupting the heap.

Sample 7:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void fun1()
{
    char *p = (char *)malloc(10);
    delete p;
}

int main()
{
    fun1();
    return 0;
}
[test1280@localhost 20170504]$ g++ -o main main.c
[test1280@localhost 20170504]$ ./valmem ./main
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 4194== Memcheck, a memory error detector
==00:00:00:00.000 4194== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 4194== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 4194== Command: ./main
==00:00:00:00.000 4194== Parent PID: 4193
==00:00:00:00.000 4194== 
==00:00:00:00.351 4194== Mismatched free() / delete / delete []
==00:00:00:00.352 4194==    at 0x4C2B18D: operator delete(void*) (vg_replace_malloc.c:576)
==00:00:00:00.352 4194==    by 0x400691: fun1() (in /home/test1280/20170504/main)
==00:00:00:00.352 4194==    by 0x40069C: main (in /home/test1280/20170504/main)
==00:00:00:00.352 4194==  Address 0x5a17040 is 0 bytes inside a block of size 10 alloc'd
==00:00:00:00.352 4194==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.352 4194==    by 0x400681: fun1() (in /home/test1280/20170504/main)
==00:00:00:00.352 4194==    by 0x40069C: main (in /home/test1280/20170504/main)
==00:00:00:00.352 4194== 
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.374 4194== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.374 4194==    <inherited from parent>
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== Open file descriptor 2: /dev/pts/0
==00:00:00:00.374 4194==    <inherited from parent>
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== Open file descriptor 1: /dev/pts/0
==00:00:00:00.374 4194==    <inherited from parent>
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== Open file descriptor 0: /dev/pts/0
==00:00:00:00.374 4194==    <inherited from parent>
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== HEAP SUMMARY:
==00:00:00:00.374 4194==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.374 4194==   total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== All heap blocks were freed -- no leaks are possible
==00:00:00:00.374 4194== 
==00:00:00:00.374 4194== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.374 4194== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

关键是:

==00:00:00:00.351 4194== Mismatched free() / delete / delete []
==00:00:00:00.352 4194==    at 0x4C2B18D: operator delete(void*) (vg_replace_malloc.c:576)
==00:00:00:00.352 4194==    by 0x400691: fun1() (in /home/test1280/20170504/main)
==00:00:00:00.352 4194==    by 0x40069C: main (in /home/test1280/20170504/main)
==00:00:00:00.352 4194==  Address 0x5a17040 is 0 bytes inside a block of size 10 alloc'd
==00:00:00:00.352 4194==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.352 4194==    by 0x400681: fun1() (in /home/test1280/20170504/main)
==00:00:00:00.352 4194==    by 0x40069C: main (in /home/test1280/20170504/main)

Overlapping source and destination blocks

源地址块和目的地址块重叠。

The following C library functions copy some data from one memory block to another (or something similar): memcpy, strcpy, strncpy, strcat, strncat.

The blocks pointed to by their src and dst pointers aren’t allowed to overlap.

The POSIX standards have wording along the lines “If copying takes place between objects that overlap, the behavior is undefined.” Therefore, Memcheck checks for this.

For example, the obvious way to implement memcpy is by copying from the first byte to the last.

很明显的一种方式是从头开始拷贝,直到结尾。

However, the optimisation guides of some architectures recommend copying from the last byte down to the first.

但实际有可能从最后开始向前拷贝。

Also, some implementations of memcpy zero dst before copying, because zeroing the destination’s cache line(s) can improve performance.

也有可能某个实现直接把dest指向的块先清零了,这样可能提高一些效率。


Fishy argument values

参数是一个Fishy值。

Sample 8:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>

void fun1()
{
    int m = -1; 
    size_t i = m;
    void *p = malloc(i);
    printf("%u\n", i); 
}

int main()
{
    fun1();
    return 0;
}
[test1280@localhost 20170504]$ !g
g++ -o main main.c
[test1280@localhost 20170504]$ ./valmem ./main
4294967295
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 4804== Memcheck, a memory error detector
==00:00:00:00.000 4804== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 4804== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 4804== Command: ./main
==00:00:00:00.000 4804== Parent PID: 4803
==00:00:00:00.000 4804== 
==00:00:00:00.288 4804== Argument 'size' of function malloc has a fishy (possibly negative) value: -1
==00:00:00:00.288 4804==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.288 4804==    by 0x400663: fun1() (in /home/test1280/20170504/main)
==00:00:00:00.288 4804==    by 0x400688: main (in /home/test1280/20170504/main)
==00:00:00:00.288 4804== 
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.323 4804== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.323 4804==    <inherited from parent>
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== Open file descriptor 2: /dev/pts/0
==00:00:00:00.323 4804==    <inherited from parent>
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== Open file descriptor 1: /dev/pts/0
==00:00:00:00.323 4804==    <inherited from parent>
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== Open file descriptor 0: /dev/pts/0
==00:00:00:00.323 4804==    <inherited from parent>
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== HEAP SUMMARY:
==00:00:00:00.323 4804==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.323 4804==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== All heap blocks were freed -- no leaks are possible
==00:00:00:00.323 4804== 
==00:00:00:00.323 4804== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.323 4804== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

关键是:

==00:00:00:00.288 4804== Argument 'size' of function malloc has a fishy (possibly negative) value: -1
==00:00:00:00.288 4804==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==00:00:00:00.288 4804==    by 0x400663: fun1() (in /home/test1280/20170504/main)
==00:00:00:00.288 4804==    by 0x400688: main (in /home/test1280/20170504/main)

注意,实际上没有分配内存!

当我们把数字减小一倍(哈哈,纯属好奇):

Sample 9:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>

void fun1()
{
    int m = -1; 
    size_t i = m;
    void *p = malloc(i/2);
    printf("%u\n", i); 
}

int main()
{
    fun1();
    return 0;
}
[test1280@localhost 20170504]$ cat valmem.log 
==00:00:00:00.000 5052== Memcheck, a memory error detector
==00:00:00:00.000 5052== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==00:00:00:00.000 5052== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==00:00:00:00.000 5052== Command: ./main
==00:00:00:00.000 5052== Parent PID: 5051
==00:00:00:00.000 5052== 
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== FILE DESCRIPTORS: 4 open at exit.
==00:00:00:00.326 5052== Open file descriptor 3: /home/test1280/20170504/valmem.log
==00:00:00:00.326 5052==    <inherited from parent>
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== Open file descriptor 2: /dev/pts/0
==00:00:00:00.326 5052==    <inherited from parent>
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== Open file descriptor 1: /dev/pts/0
==00:00:00:00.326 5052==    <inherited from parent>
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== Open file descriptor 0: /dev/pts/0
==00:00:00:00.326 5052==    <inherited from parent>
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== HEAP SUMMARY:
==00:00:00:00.326 5052==     in use at exit: 0 bytes in 0 blocks
==00:00:00:00.326 5052==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== All heap blocks were freed -- no leaks are possible
==00:00:00:00.326 5052== 
==00:00:00:00.326 5052== For counts of detected and suppressed errors, rerun with: -v
==00:00:00:00.326 5052== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

看到了吗?没有分配出来,也没有报Fishy值参数。


下面介绍definitely lost、indirectly lost、possibly lost以及still reachable。

首先官方的图:

这里写图片描述

Case (1):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char *p = malloc(sizeof(int));
    exit(0);
}
==00:00:00:00.283 6424== LEAK SUMMARY:
==00:00:00:00.283 6424==    definitely lost: 0 bytes in 0 blocks
==00:00:00:00.283 6424==    indirectly lost: 0 bytes in 0 blocks
==00:00:00:00.283 6424==      possibly lost: 0 bytes in 0 blocks
==00:00:00:00.283 6424==    still reachable: 4 bytes in 1 blocks
==00:00:00:00.283 6424==         suppressed: 0 bytes in 0 blocks

p指向的空间直接可达

Case (2):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char **p;
    p = malloc(sizeof(void *));
    *p = malloc(256);
    exit(0);
}
==00:00:00:00.253 6489== LEAK SUMMARY:
==00:00:00:00.253 6489==    definitely lost: 0 bytes in 0 blocks
==00:00:00:00.253 6489==    indirectly lost: 0 bytes in 0 blocks
==00:00:00:00.253 6489==      possibly lost: 0 bytes in 0 blocks
==00:00:00:00.253 6489==    still reachable: 264 bytes in 2 blocks
==00:00:00:00.253 6489==         suppressed: 0 bytes in 0 blocks

p指向的空间是直接可达,*p指向的空间是间接可达可达空间包括直接可达和间接可达

为什么是264呢?

因为p指向的空间是动态malloc出来的,我实际试验了一下,在我的机器上一个void *占8字节;
*p指向的空间也是动态malloc出来的,大小是256字节。

所以:一共是256+8=264B.

Case (3):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char *p = malloc(10);
    p = NULL;
    exit(0);
}
==00:00:00:00.248 6709== LEAK SUMMARY:
==00:00:00:00.248 6709==    definitely lost: 10 bytes in 1 blocks
==00:00:00:00.248 6709==    indirectly lost: 0 bytes in 0 blocks
==00:00:00:00.248 6709==      possibly lost: 0 bytes in 0 blocks
==00:00:00:00.248 6709==    still reachable: 0 bytes in 0 blocks
==00:00:00:00.248 6709==         suppressed: 0 bytes in 0 blocks

p指向的空间直接丢失了,也就是我们从Heap上malloc出来的那10个字节。

Case (4):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char **p = malloc(sizeof(void *));
    *p = malloc(100);
    p = NULL;
    exit(0);
}
==00:00:00:00.259 6856== LEAK SUMMARY:
==00:00:00:00.259 6856==    definitely lost: 8 bytes in 1 blocks
==00:00:00:00.259 6856==    indirectly lost: 100 bytes in 1 blocks
==00:00:00:00.259 6856==      possibly lost: 0 bytes in 0 blocks
==00:00:00:00.259 6856==    still reachable: 0 bytes in 0 blocks
==00:00:00:00.259 6856==         suppressed: 0 bytes in 0 blocks

p指向的空间是从Heap上malloc出来的,在程序退出时已经找不到p指向的空间了,所以*p是直接丢失的(8B);
*p指向的空间也是从Heap上malloc出来的,在程序退出时*p显然是不可访问的,所以*p指向的空间是间接丢失的(100B)。

其余的情况(4 to 9)涉及到“interior-pointer”:

There are two ways a block can be reached. The first is with a “start-pointer”, i.e. a pointer to the start of the block. The second is with an “interior-pointer”, i.e. a pointer to the middle of the block.

其中比较常见的一个是:

The pointer might have originally been a start-pointer and have been moved along deliberately (or not deliberately) by the program. In particular, this can happen if your program uses tagged pointers, i.e. if it uses the bottom one, two or three bits of a pointer, which are normally always zero due to alignment, in order to store extra information.

网上对于“interior-pointer”的介绍是这样子的:

所谓的interior-pointer是指向block内的指针,也就是我们常说的野指针。

所以很多Possible lost并不是真正的Memory leak。

例如(官网的一个对的解释):

It might be a pointer to the inner char array of a C++ std::string. For example, some compilers add 3 words at the beginning of the std::string to store the length, the capacity and a reference count before the memory containing the array of characters. They return a pointer just after these 3 words, pointing at the char array.

其实大家可以多试试,就能知道各种各样的情况(泄露的情形)。

资料来源:

0.http://valgrind.org/docs/manual/manual.html
1.https://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/
3.http://zhenhua2000.blog.51cto.com/3167594/768342
4.http://blog.csdn.net/byrsongqq/article/details/5881375

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值