Valgrind 学习篇 -- memcheck

Valgrind 学习篇 – memcheck

瓦尔格隆德

本文只是对memcheck进行学习

学习资料:

  • 官网主页: http://valgrind.org/

  • Valgrind用户手册 : http://valgrind.org/docs/manual/manual.html

  • CSDN 博文 :

https://blog.csdn.net/andylauren/article/details/93189740

https://blog.csdn.net/u010122972/article/details/78214174

一、Valgrind

Valgrind是GPL的系统,用于调试和分析Linux程序。

  1. 自动检测许多内存管理和线程错误;
  2. 执行非常详细的性能分析;
  3. 可以在没有源代码的程序上使用Valgrind
  4. Valgrind可在几种流行的平台上运行,例如 x86/Linux, AMD64/Linux and PPC32/Linux

二、Valgrind的工具套件 (Tool Suite)

2.1 Memcheck

内存检查

Memcheck可检测内存管理问题,并且主要针对C和C++程序。在Memcheck的监视下运行程序时,将检查所有内存读取和写入,并拦截对malloc / new / free / delete的调用。

  • 访问它不应该访问的内存(尚未分配的区域,已释放的区域,堆块末尾的区域,堆栈的不可访问区域)。
  • 以危险的方式使用未初始化的值。
  • 泄漏内存。
  • 释放堆块的不良释放(两次释放,不匹配的释放)。
  • 将重叠的源和目标内存块传递给memcpy()和相关函数。

重叠src和 dst指针 memcpy以及相关功能。

Memcheck会在发生这些错误时立即报告这些错误,并给出发生错误的源代码行号,以及为达到该行而调用的函数的堆栈跟踪。

  1. Memcheck运行的程序比正常运行慢大约10–30倍。
  2. Memcheck不对全局数组或堆栈数组进行边界检查。
  3. 检查所有支持的动态链接库(包括C库,图形库等)中的代码。

2.2 Cachegrind

Cachegrind是一个缓存分析器。它可以对CPU中的I1,D1和L2缓存进行详细的仿真,因此可以准确地指出代码中的缓存未命中源。

2.3 Callgrind

它提供了Cachegrind所做的所有信息,以及有关调用图的其他信息。

2.4 Massif

Massif是堆分析器。包括有关程序的哪些部分负责最多内存分配的信息。

2.5 Helgrind

Helgrind是一个线程调试器,可在多线程程序中查找数据竞争。

2.6 等等其他tool


三、文档

http://valgrind.org/docs/manual/QuickStart.html

用户文档: http://valgrind.org/docs/manual/manual-core.html


四、一个运行示例 (An Example Run)

4.1 安装

安装包网址 : http://www.valgrind.org/downloads/current.html

或者
git clone git://sourceware.org/git/valgrind.git

下载源码、下载文档


tar jxvf valgrind-3.15.0.tar.bz2

cd valgrind-3.15.0/

./configure

make

make install

//验证是否安装成功
valgrind --version

4.2 写源码

#include 
#include 
#include 

int main(int argc, char *argv[]){    
    // int x = 1;
    int x;
    printf("x [%d]\n\n", x);  //1、使用未初始化的变量

    char * str1 = malloc(10);   //2、未释放的内存、内存泄漏

    char * str2 = malloc(sizeof(int));
    free(str2);
    // free(str2);    //3、非法释放


    return 0;
}

4.3 使用Valgrind

valgrind --tool=memcheck --leak-check=full --show-reachable=yes --trace-children=yes --log-file=out ./test

  • –tool=memcheck 默认值, 不写也可以; 使用memcheck工具(内存错误检测器)
  • –leak-check=full 详细显示错误信息
  • –show-reachable=yes 将所有内存泄漏的类型都显示出来
  • –trace-children=yes 多线程的时候使用,会检查每个线程
  • –log-file=log 输出到log 这个文件.

在log文件最后会有个summary,其中对内存泄露进行了分类,总共有五类:
(1) “definitely lost” 意味着你的程序一定存在内存泄露;
(2)”indirectly lost”意味着你的程序一定存在内存泄露,并且泄露情况和指针结构相关.
(3) “possibly lost” 意味着你的程序一定存在内存泄露,除非你是故意进行着不符合常规的操作,例如将指针指向某个已分配内存块的中间位置。
(4) “still reachable” 意味着你的程序可能是没问题的,但确实没有释放掉一些本可以释放的内存。这种情况是很常见的,并且通常基于合理的理由。
(5)”suppressed” 意味着有些泄露信息被压制了。在默认的 suppression 文件中可以看到一些 suppression 相关设置。

版权声明:本文为CSDN博主「深夜虫鸣」的原创文章,遵循 CC 4.0 BY-SA 版权协议,
原文链接:
https://blog.csdn.net/u010122972/article/details/78214174

4.4 看错误报告

注意:需要编译源文件的时候加上 -g

==47085== Memcheck, a memory error detector
==47085== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==47085== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==47085== Command: ./test
==47085== 
==47085== Conditional jump or move depends on uninitialised value(s)
==47085==    at 0x4E998DA: vfprintf (vfprintf.c:1642)
==47085==    by 0x4EA1F25: printf (printf.c:33)
==47085==    by 0x1086FE: main (test1.c:8)
==47085== 
==47085== Use of uninitialised value of size 8      //8个字节的未初始化
==47085==    at 0x4E9586B: _itoa_word (_itoa.c:179)
==47085==    by 0x4E98F0D: vfprintf (vfprintf.c:1642)
==47085==    by 0x4EA1F25: printf (printf.c:33)
==47085==    by 0x1086FE: main (test1.c:8)   //源码具体位置
==47085== 
==47085== Conditional jump or move depends on uninitialised value(s)
==47085==    at 0x4E95875: _itoa_word (_itoa.c:179)
==47085==    by 0x4E98F0D: vfprintf (vfprintf.c:1642)
==47085==    by 0x4EA1F25: printf (printf.c:33)
==47085==    by 0x1086FE: main (test1.c:8)
==47085== 
==47085== Conditional jump or move depends on uninitialised value(s)
==47085==    at 0x4E99014: vfprintf (vfprintf.c:1642)
==47085==    by 0x4EA1F25: printf (printf.c:33)
==47085==    by 0x1086FE: main (test1.c:8)
==47085== 
==47085== Conditional jump or move depends on uninitialised value(s)
==47085==    at 0x4E99B4C: vfprintf (vfprintf.c:1642)
==47085==    by 0x4EA1F25: printf (printf.c:33)
==47085==    by 0x1086FE: main (test1.c:8)
==47085== 
x [0]

==47085== 
==47085== HEAP SUMMARY:
==47085==     in use at exit: 10 bytes in 1 blocks
==47085==   total heap usage: 3 allocs, 2 frees, 1,038 bytes allocated
==47085== 
==47085== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1  //没有释放malloc的内存
==47085==    at 0x4C2FDFB: malloc (vg_replace_malloc.c:309)
==47085==    by 0x108708: main (test1.c:10)
==47085== 
==47085== LEAK SUMMARY:
==47085==    definitely lost: 10 bytes in 1 blocks
==47085==    indirectly lost: 0 bytes in 0 blocks
==47085==      possibly lost: 0 bytes in 0 blocks
==47085==    still reachable: 0 bytes in 0 blocks
==47085==         suppressed: 0 bytes in 0 blocks
==47085== 
==47085== Use --track-origins=yes to see where uninitialised values come from
==47085== For lists of detected and suppressed errors, rerun with: -s
==47085== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 0 from 0)
4.5 错误信息example

反着看,先看最底下的,再看上面的.

==25832== Invalid read of size 4
==25832==    at 0x8048724: BandMatrix::ReSize(int, int, int) (bogon.cpp:45)
==25832==    by 0x80487AF: main (bogon.cpp:66)
==25832==  Address 0xBFFFF74C is not stack'd, malloc'd or free'd
  • 25832 是进程ID

  • 该程序对地址0xBFFFF74C进行了非法的4字节读取, 该地址不是有效的堆栈地址,也不对应于任何当前堆块或最近释放的堆块。读取发生在的第45行 bogon.cpp,从同一文件的第66行调用

检测重复错误的过程是非常昂贵的过程,并且如果您的程序生成大量错误,则可能会成为相当大的性能开销。

–error-limit=no

4.10、使用Valgrind gdbserver和GDB调试程序

在Valgrind下运行的程序不会直接由CPU执行。它在Valgrind提供的合成CPU上运行。这就是为什么调试器在Valgrind上运行时无法调试程序的原因。

在使用Memcheck工具时使用GDB调试程序

valgrind --vgdb=yes --vgdb-error=0 ./test


在另一个shell中,启动GDB:

gdb ./test

//这个命令在valgrind那个线程里面有, 按照提示复制那个
(gdb) target remote | vgdb

退出gdb那个终端, valgrind就回退出

五、Valgrind核心

1、调用 valgrind
valgrind [valgrind-options] 原来的运行命令

最重要的选项是 --tool 决定要运行哪个Valgrind工具。

2、工具选择选项
--tool= [default: memcheck]

toolname, 例如memcheck,cachegrind,callgrind,helgrind

3、Memcheck命令行选项
--leak-check= [default: summary]
	启用后,在客户端程序完成时搜索内存泄漏。如果设置为summary,则表示发生了多少泄漏。如果设置为full或 yes,则每个泄漏都将详细显示和/或计为错误,如选项--show-leak-kinds和 所指定 --errors-for-leak-kinds。
	
	
--show-reachable= , --show-possibly-lost=
这些选项提供了另一种方法来指定要显示的泄漏类型:
	--show-reachable=no --show-possibly-lost=yes等同于 --show-leak-kinds=definite,possible。
	--show-reachable=no --show-possibly-lost=no等同于 --show-leak-kinds=definite。
	--show-reachable=yes等同于 --show-leak-kinds=all。
	
	
--show-leak-kinds= [default: definite,possible]
	full 通过以下方式之一指定要在泄漏搜索中显示的泄漏种类:
	
	用逗号分隔的一个或多个列表 definite indirect possible reachable。
	all指定全套(所有泄漏种类)。等同于 --show-leak-kinds=definite,indirect,possible,reachable。
	none 为空集。


--undef-value-errors= [default: yes]
	控制Memcheck报告是否使用未定义的值错误。如果您不想看到未定义的值错误,请将此设置为no。它还具有一定程度加速Memcheck的副作用。


--track-origins= [default: no]
	控制Memcheck是否跟踪未初始化值的来源。默认情况下,它不是,这意味着尽管它可以告诉您未初始化的值正在以危险的方式使用,但是它无法告诉您未初始化的值是从哪里来的。这通常使得很难找到根本问题。
	
  • 程序退出之前, 内存泄漏类型集:

definitely lost(绝对丢失)、indirectly lost(间接丢失)、 possibly lost(可能丢失)、 still reachable(仍可访问)、 suppressed(抑制)

如果–leak-check=full指定,则Memcheck将提供每个绝对丢失或可能丢失的块的详细信息,包括分配位置。

  • 当–leak-check=full被指定时, --show-leak-kinds=选项控制要显示的泄漏类型集

要显示的泄漏种类的默认值是 --show-leak-kinds=definite,possible。

==46004== Memcheck, a memory error detector
==46004== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==46004== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==46004== Command: ./test
==46004== 
==46004== 
==46004== HEAP SUMMARY:
==46004==     in use at exit: 10 bytes in 1 blocks
==46004==   total heap usage: 1 allocs, 0 frees, 10 bytes allocated
==46004== 
@1==46004== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==46004==    at 0x4C2FDFB: malloc (vg_replace_malloc.c:309)
@1==46004==    by 0x108662: main (test1.c:9)
==46004== 
==46004== LEAK SUMMARY:
==46004==    definitely lost: 10 bytes in 1 blocks
==46004==    indirectly lost: 0 bytes in 0 blocks
==46004==      possibly lost: 0 bytes in 0 blocks
==46004==    still reachable: 0 bytes in 0 blocks
==46004==         suppressed: 0 bytes in 0 blocks
==46004== 
==46004== For lists of detected and suppressed errors, rerun with: -s
==46004== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  • 程序最后的LEAK SUMMARY是每次都会输出的,没有关系…

--leak-check=full 这个选项主要是会显示出来两个 @1 的内容…

--show-leak-kinds=definite,possible 这个选项就是详细信息只会显示definite、possible的内存泄漏类型的详细信息.

3.1、–leak-check=full

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

如果–leak-check=full指定,则Memcheck将提供每个绝对丢失或可能丢失的块的详细信息,包括分配位置。

#include 
#include 

int main(int argc, char *argv[]){
	char * str1 = malloc(10);   //2、未释放的内存、内存泄漏
	
    return 0;
}

加上这个主要就是多了这些错误信息, 可以显示行号等信息.

==54477== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==54477==    at 0x4C2FDFB: malloc (vg_replace_malloc.c:309)
==54477==    by 0x108662: main (test1.c:9)
3.2、–track-origins=yes

valgrind --tool=memcheck --track-origins=yes ./test

–tool=memcheck 可以省略,默认选项

–track-origins=yes 更加详细的显示错误

#include 

int main(int argc, char *argv[]){
    
    // int x = 1;
    int x;

    printf("x [%d]\n\n", x);  //使用未初始化的变量

    return 0;
}


1、编译您的程序-g以包含调试信息,以便Memcheck的错误消息包含确切的行号。

10、基本选项

适用于所有工具

-h --help     
	对核心和所选工具的所有选项显示帮助。


--version
	显示Valgrind内核的版本号。


-v, --verbose
	更加冗长。提供有关程序各个方面的额外信息,


--trace-children-skip=patt1,patt2,...
	仅当--trace-children=yes指定时,此选项才有效。它允许一些孩子被跳过。


--trace-children= [default: no]
	启用后,Valgrind将跟踪通过exec系统调用启动的子进程。这对于多进程程序是必需的。

	请注意,Valgrind确实会跟踪到fork的子进程(由于fork创建了相同的进程副本,因此很难做到这一点,因此此选项的名称可谓是错误的。然而,大多数fork的子进程都会立即调用exec。
	
	
--vgdb= [default: yes]
	当--vgdb=yes或--vgdb=full被指定时,Valgrind将提供“gdbserver”功能; 这使外部GNU GDB调试器在Valgrind上运行时可以控制和调试程序。 --vgdb=full会产生巨大的性能开销,但会提供更精确的断点和观察点。

11、与错误相关的选项

--xml= [default: no]
	启用后,输出的重要部分(例如,工具错误消息)将采用XML格式,而不是纯文本。还必须使用的一个 --xml-fd,--xml-file或 --xml-socket到指定的XML将被发送。

--xml-fd= [default: -1, disabled]
	指定Valgrind应该将其XML输出发送到指定的文件描述符。它必须与结合使用 --xml=yes。

--xml-file=
	指定Valgrind应该将其XML输出发送到指定文件。

--xml-socket=
	指定Valgrind应该在指定的IP地址将其XML输出发送到指定的端口。

欢迎关注公众号:
Valgrind 学习篇 -- memcheck_第1张图片

你可能感兴趣的:(C语言,c语言)