linux下valgrind memcheck 简单使用 C++开发

C++开发使用valgrind memcheck使程序更快更正确

valgrind是提供一系列工具,可以帮助我们是我们的程序更正确,运行的更快,其中比较受欢迎的就是memcheck,接下来是我在这方面的学习总结:

更详细的可以参见:valgrind 用户手册

程序在操作内存时可能犯的错误主要有以下几种:

  • 内存泄漏
  • 使用未初始化的变量
  • 释放内存的方式不对
  • 访问了不应该访问的内存
  • 内存拷贝移动src和dst存在内存重叠
  • 把负值传给内存分配函数

CMakeLists.txt 编写如下:

cmake_minimum_required(VERSION 3.11)

set(CMAKE_CXX_FLAGS "-O2 -g -std=c++11 -pipe -Wall -W -fstrict-aliasing -Wno-invalid-offsetof -Wno-unused-parameter -fno-omit-frame-pointer")

add_executable(memcheck_leak memcheck_leak.cpp)
add_executable(memcheck_uninit memcheck_uninit.cpp)
add_executable(memcheck_errfree memcheck_errfree.cpp)
add_executable(memcheck_visiterr memcheck_visiterr.cpp)
add_executable(memcheck_strncpyerr memcheck_strncpyerr.cpp)
add_executable(memcheck_illegalalloc memcheck_illegalalloc.cpp)

内存泄露 测试代码如下

为了让valgrind的输出结果更加清晰直观,我们编译时加上-g,memcheck_leak.cpp 编写如下:

# memcheck_leak.cpp
#include <iostream>

void leak_mem_func(size_t size) {
    auto ptr = new char[size];

    (void)ptr;
}

void no_leak_mem_func(size_t size) {
    auto ptr = new char[size];

    delete[] ptr;
}

int main(int argc, char **argv) {
    std::cout << "start ..." << std::endl;

    leak_mem_func(10); 

    no_leak_mem_func(20);

    std::cout << "stop ..." << std::endl;
    return 0;
}

执行如下命令编译程序

cmake .  # 如果没有增删文件,一般只需要执行一次
make
执行程序

正常情况下我们执行程序的命令如下:

# memcheck_leak 为本次测试的编译产出
./memcheck_leak

当使用memcheck时需要执行如下命令:

# 让自己的程序,在valgrind提供的虚拟内核中运行
valgrind --leak-check=yes memcheck_leak

# 执行如上命令后产出如下:
==21928== Memcheck, a memory error detector
==21928== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21928== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==21928== Command: ./memcheck_leak
==21928== 
start ...
stop ...
==21928== 
==21928== HEAP SUMMARY:
==21928==     in use at exit: 10 bytes in 1 blocks
==21928==   total heap usage: 2 allocs, 1 frees, 30 bytes allocated    #2次内存分配,1次内存释放
==21928== 
==21928== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1    #内存泄露的调用栈,以及内存使用场景
==21928==    at 0x4C2A8E8: operator new[](unsigned long) (vg_replace_malloc.c:423)
==21928==    by 0x4007D4: leak_mem_func (memcheck_test.cpp:4)
==21928==    by 0x4007D4: main (memcheck_test.cpp:18)
==21928== 
==21928== LEAK SUMMARY:                               # 对程序的内存泄露情况进行汇总
==21928==    definitely lost: 10 bytes in 1 blocks
==21928==    indirectly lost: 0 bytes in 0 blocks
==21928==      possibly lost: 0 bytes in 0 blocks
==21928==    still reachable: 0 bytes in 0 blocks
==21928==         suppressed: 0 bytes in 0 blocks
==21928== 
==21928== For counts of detected and suppressed errors, rerun with: -v
==21928== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

使用未初始化的变量 测试代码如下

为了让valgrind的输出结果更加清晰直观,我们编译时加上-g,memcheck_leak.cpp 编写如下:

# memcheck_uninit.cpp
#include <iostream>

int main(int argc, char **argv) {
    std::cout << "start ..." << std::endl;

    auto var_uinit = new int;

    std::cout << "var_uninit = " << *var_uinit << std::endl;

    delete var_uinit;
    std::cout << "stop ..." << std::endl;
    return 0;
}

执行如下命令编译程序

cmake .  # 如果没有增删文件,一般只需要执行一次
make
执行程序

正常情况下我们执行程序的命令如下:

# memcheck_uninit 为本次测试的编译产出
./memcheck_uninit

当使用memcheck时需要执行如下命令:

# 让自己的程序,在valgrind提供的虚拟内核中运行
valgrind --track-origins=yes  ./memcheck_uninit

# 执行如上命令后产出如下:
==22571==    by 0x4ECCFED: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib64/libstdc++.so.6.0.19)
==22571==    by 0x400847: main (memcheck_uninit.cpp:8)
==22571==  Uninitialised value was created by a heap allocation #指出使用了为初始化的堆分配空间
==22571==    at 0x4C2A243: operator new(unsigned long) (vg_replace_malloc.c:334)
==22571==    by 0x400827: main (memcheck_uninit.cpp:6)
==22571== 
var_uninit = 0
stop ...
==22571== 
==22571== HEAP SUMMARY:
==22571==     in use at exit: 0 bytes in 0 blocks
==22571==   total heap usage: 1 allocs, 1 frees, 4 bytes allocated
==22571== 
==22571== All heap blocks were freed -- no leaks are possible
==22571== 
==22571== For counts of detected and suppressed errors, rerun with: -v
==22571== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)

释放内存的方式不对 测试代码如下

为了让valgrind的输出结果更加清晰直观,我们编译时加上-g,memcheck_leak.cpp 编写如下:

# memcheck_errfree.cpp

#include <iostream>
void C_free(void *ptr) {
    free(ptr);
}

int main(int argc, char **argv) {
    std::cout << "start ..." << std::endl;

    auto var_ptr = new int;

    C_free(var_ptr);

    auto int_arr = new int[10];

    delete int_arr;
    std::cout << "stop ..." << std::endl;
    return 0;
}

执行如下命令编译程序

cmake .  # 如果没有增删文件,一般只需要执行一次
make
执行程序

正常情况下我们执行程序的命令如下:

# memcheck_errfree 为本次测试的编译产出
./memcheck_errfree

当使用memcheck时需要执行如下命令:

# 让自己的程序,在valgrind提供的虚拟内核中运行
valgrind ./memcheck_errfree

# 执行如上命令后产出如下:
==22957== Memcheck, a memory error detector
==22957== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22957== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==22957== Command: ./memcheck_errfree
==22957== 
start ... # 用c语言的free去释放new分配的空间
==22957== Mismatched free() / delete / delete []
==22957==    at 0x4C2AD1D: free (vg_replace_malloc.c:530)
==22957==    by 0x40086C: C_free (memcheck_errfree.cpp:4)
==22957==    by 0x40086C: main (memcheck_errfree.cpp:12)
==22957==  Address 0x5a19040 is 0 bytes inside a block of size 4 alloc'd
==22957==    at 0x4C2A243: operator new(unsigned long) (vg_replace_malloc.c:334)
==22957==    by 0x400864: main (memcheck_errfree.cpp:10)
==22957== # operator delete(void*) 去释放 operator new[](unsigned long)分配的空间
==22957== Mismatched free() / delete / delete []
==22957==    at 0x4C2B1CD: operator delete(void*) (vg_replace_malloc.c:576)
==22957==    by 0x40087E: main (memcheck_errfree.cpp:16)
==22957==  Address 0x5a19090 is 0 bytes inside a block of size 40 alloc'd
==22957==    at 0x4C2A8E8: operator new[](unsigned long) (vg_replace_malloc.c:423)
==22957==    by 0x400876: main (memcheck_errfree.cpp:14)
==22957== 
stop ...
==22957== 
==22957== HEAP SUMMARY:
==22957==     in use at exit: 0 bytes in 0 blocks
==22957==   total heap usage: 2 allocs, 2 frees, 44 bytes allocated
==22957== 
==22957== All heap blocks were freed -- no leaks are possible
==22957== 
==22957== For counts of detected and suppressed errors, rerun with: -v
==22957== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
[fas@localhost valgrind_memcheck]$ vim memcheck_errfree.cpp

访问了不应该访问的内存 测试代码如下

为了让valgrind的输出结果更加清晰直观,我们编译时加上-g,memcheck_leak.cpp 编写如下:

# memcheck_visiterr.cpp

#include <iostream>

void visit_err(void *ptr) {
    int value = *((int *)ptr);
    // will core
    std::cout << "visit err addr value = " << value << std::endl;
}

int main(int argc, char **argv) {
    std::cout << "start ..." << std::endl;

    auto var_ptr = (int*)1000;

    visit_err(var_ptr);
    std::cout << "stop ..." << std::endl;
    return 0;
}

执行如下命令编译程序

cmake .  # 如果没有增删文件,一般只需要执行一次
make
执行程序

正常情况下我们执行程序的命令如下:

# memcheck_visiterr 为本次测试的编译产出
./memcheck_visiterr

当使用memcheck时需要执行如下命令:

# 让自己的程序,在valgrind提供的虚拟内核中运行
valgrind ./memcheck_visiterr

# 执行如上命令后产出如下:
==23427== Memcheck, a memory error detector
==23427== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==23427== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==23427== Command: ./memcheck_visiterr
==23427== 
start ... # 指出那里产生非法地址,那里访问了该非法地址
==23427== Invalid read of size 4
==23427==    at 0x400B01: visit_err(void*) (memcheck_visiterr.cpp:4)
==23427==    by 0x4009C4: main (memcheck_visiterr.cpp:14)  
==23427==  Address 0x3e8 is not stack'd, malloc'd or (recently) free'd
==23427== 
==23427== 
==23427== Process terminating with default action of signal 11 (SIGSEGV)
==23427==  Access not within mapped region at address 0x3E8
==23427==    at 0x400B01: visit_err(void*) (memcheck_visiterr.cpp:4)
==23427==    by 0x4009C4: main (memcheck_visiterr.cpp:14)
==23427==  If you believe this happened as a result of a stack
==23427==  overflow in your program's main thread (unlikely but
==23427==  possible), you can try to increase the size of the
==23427==  main thread stack using the --main-stacksize= flag.
==23427==  The main thread stack size used in this run was 8388608.
==23427== 
==23427== HEAP SUMMARY:
==23427==     in use at exit: 0 bytes in 0 blocks
==23427==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==23427== 
==23427== All heap blocks were freed -- no leaks are possible
==23427== 
==23427== For counts of detected and suppressed errors, rerun with: -v
==23427== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
段错误(吐核)

内存拷贝移动src和dst存在内存重叠 测试代码如下

为了让valgrind的输出结果更加清晰直观,我们编译时加上-g,memcheck_leak.cpp 编写如下:

# memcheck_strncpyerr.cpp

#include <iostream>
#include <string.h>
#include <memory.h>

int main(int argc, char **argv) {
    std::cout << "start ..." << std::endl;

    char a[] = "abcdefghijk";  
    strncpy(a + 1, a, 5);  
    printf("%s\n", a); 

    std::cout << "stop ..." << std::endl;
    return 0;
}

执行如下命令编译程序

cmake .  # 如果没有增删文件,一般只需要执行一次
make
执行程序

正常情况下我们执行程序的命令如下:

# memcheck_strncpyerr 为本次测试的编译产出
./memcheck_strncpyerr

当使用memcheck时需要执行如下命令:

# 让自己的程序,在valgrind提供的虚拟内核中运行
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./memcheck_strncpyerr

# 执行如上命令后产出如下:
==25296== Memcheck, a memory error detector
==25296== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==25296== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==25296== Command: ./memcheck_strncpyerr
==25296== 
start ...  # 指出内存存在重叠的位置
==25296== Source and destination overlap in strncpy(0x1ffefffc06, 0x1ffefffc05, 5)
==25296==    at 0x4C2CD73: strncpy (vg_replace_strmem.c:549)
==25296==    by 0x4007F2: main (memcheck_strncpyerr.cpp:9)
==25296== 
aaaaaaghijk
stop ...
==25296== 
==25296== HEAP SUMMARY:
==25296==     in use at exit: 0 bytes in 0 blocks
==25296==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==25296== 
==25296== All heap blocks were freed -- no leaks are possible
==25296== 
==25296== For counts of detected and suppressed errors, rerun with: -v
==25296== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)  

内存拷贝移动src和dst存在内存重叠 测试代码如下

为了让valgrind的输出结果更加清晰直观,我们编译时加上-g,memcheck_leak.cpp 编写如下:

# memcheck_illegalalloc.cpp

#include <iostream>
#include <memory.h>

int main(int argc, char **argv) {
    std::cout << "start ..." << std::endl;

    int size = -3; 

    char *iptr = (char*) malloc(size);

    if (iptr != nullptr) {
        std::cout << "iptr != nullptr" << std::endl;
        // 内存虽然未分配成功,但是存在使用代码的情况下valgrind就会报错
        iptr[0] = 0;
    } else {
        std::cout << "iptr == nullptr" << std::endl;
    }   

    free(iptr);
    std::cout << "stop ..." << std::endl;
    return 0;
}

执行如下命令编译程序

cmake .  # 如果没有增删文件,一般只需要执行一次
make
执行程序

正常情况下我们执行程序的命令如下:

# memcheck_illegalalloc 为本次测试的编译产出
./memcheck_illegalalloc

当使用memcheck时需要执行如下命令:

# 让自己的程序,在valgrind提供的虚拟内核中运行
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./memcheck_illegalalloc

# 执行如上命令后产出如下:
==25736== Memcheck, a memory error detector
==25736== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==25736== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==25736== Command: ./memcheck_illegalalloc
==25736== 
start ...  # 指出出现fishy内存分配参数的地方
==26650== Memcheck, a memory error detector
==26650== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26650== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==26650== Command: ./memcheck_illegalalloc
==26650== 
start ...
==26650== Argument 'size' of function malloc has a fishy (possibly negative) value: -3
==26650==    at 0x4C29C23: malloc (vg_replace_malloc.c:299)
==26650==    by 0x4007D3: main (memcheck_illegalalloc.cpp:13)
==26650== 
iptr == nullptr
stop ...
==26650== 
==26650== HEAP SUMMARY:
==26650==     in use at exit: 0 bytes in 0 blocks
==26650==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==26650== 
==26650== All heap blocks were freed -- no leaks are possible
==26650== 
==26650== For counts of detected and suppressed errors, rerun with: -v
==26650== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

猜你喜欢

转载自blog.csdn.net/u010154685/article/details/80216326