valgrind - memcheck内存检查

valgrind - memcheck内存检查

简介

Valgrind工具套件提供了许多调试和分析工具, 用以提高程序的效率和正确性. 这些工具中最受欢迎的是Memcheck, 它可以检测出许多与内存有关的错误, 这些错误在C和C++程序中很常见, 并可能导致崩溃和不可预测的行为.

valgrind官网: https://valgrind.org/
valgrind手册: https://valgrind.org/docs/manual/manual.html

使用valgrind --tool=memcheck

(一般linux上都自带有valgrind, 这里我也省去了安装valgrind的步骤)
(当前所使用的valgrind版本为3.13.0)

valgrind对需要检测的程序有要求, 一般使用-g在debug环境下使用valgrind, 以下是不通编译选项对valgrind结果的影响:
-g: 能够获取准确的行号
-O0: 速度会慢一些
-O1: 通常情况下没什么问题, 但可能会有行号错误
-O2: 不建议对使用O2及以上优化编译出来的程序使用

使用方式

若程序启动方式是: ./program arg1 arg2
则对程序使用valgrind的方式: valgrind --tool=memcheck --leak-check=yes ./program arg1 arg2

常用选项

--tool=memcheck
指定valgrind所使用的工具是memcheck做内存检查, memcheck是valgrind的默认工具.
(valgrind配套有不同的工具, memcheck内存检测工具是最常用工具, 目前这里对valgrind的使用也仅限于memcheck)

注意: 以下选项都是针对memcheck工具的
--leak-check=<no|summary|yes|full> [default: summary]
指定检测内存泄漏. 默认summary会对检测到的内存泄漏做统计说明. 如果需要了解更详细的内存泄漏信息, 则使用full或者yes.

--show-leak-kinds=<all|definite|indirect|possible|reachable> [default: definite,possible]
指定关注何种类型的内存泄漏. 当--leck-check=full时使用, 它会将内存泄漏分类过滤并显示.

--leak-resolution=<low|med|high> [default: high]
指定输出结果中关注内存泄漏的级别. 默认为high, 则关注所有内存泄漏相关的问题.

--track-origins=<yes|no> [default: no]
指定追溯问题源. 默认不启用. 启用它会很耗时.

结果说明

对于一般的内存检测问题, 可以通过valgrind提供的信息定位到问题. 这里仅对内存泄漏的结果做说明.

在valgrind手册上看到下图(括号为中文注释), 讲了内存泄漏的集中情况, 以及valgrind如何对内存泄漏的判断.

Pointer chain(指针链)        AAA Leak Case   BBB Leak Case (AAA或BBB处发生泄漏)
-------------               -------------   -------------
(1) RRR ------------> BBB                   DR
(2) RRR ---> AAA ---> BBB   DR              IR
(3) RRR               BBB                   DL
(4) RRR      AAA ---> BBB   DL              IL
(5) RRR ------?-----> BBB                   (y)DR, (n)DL
(6) RRR ---> AAA -?-> BBB   DR              (y)IR, (n)DL
(7) RRR -?-> AAA ---> BBB   (y)DR, (n)DL    (y)IR, (n)IL
(8) RRR -?-> AAA -?-> BBB   (y)DR, (n)DL    (y,y)IR, (n,y)IL, (_,n)DL
(9) RRR      AAA -?-> BBB   DL              (y)IL, (n)DL

Pointer chain legend:(指针链图示)
- RRR: a root set node or DR block(根节点或能够直接访问的内存块)
- AAA, BBB: heap blocks(堆内存块)
- --->: a start-pointer(指针指向)
- -?->: an interior-pointer(内部指针)

Leak Case legend:(泄漏类型图示)
- DR: Directly reachable(可直接访问)
- IR: Indirectly reachable(可间接访问)
- DL: Directly lost(直接丢失)
- IL: Indirectly lost(间接丢失)
- (y)XY: it's XY if the interior-pointer is a real pointer(如果内部指针是真实的指针, 则...)
- (n)XY: it's XY if the interior-pointer is not a real pointer(如果内部指针不是真实的指针, 则...)
- (_)XY: it's XY in either case(无论内部指针怎样, 都...)

针对以上9中情况, valgrind会得出4种类型的结果:

  1. "Still reachable": 对应(1)(2)中BBB的两种情况, 表示仍有指针指向这部分内存, 内存还有机会释放.
  2. "Definitely lost": 对应(3)中BBB这种情况, 确定已经没有任何指针指向这部分内存, 内存必然泄漏了.
  3. "Indirectly lost": 对应(4)(9)中BBB的两种情况, RRR已经丢失, 虽然AAA指向BBB, 但AAA自身已经是必然的内存泄露了.
  4. "Possibly lost": 对应(5-8)中BBB的情况, 简单的讲就是还有其他指针能够管理BBB的内存, 无法通过RRR就能判断BBB是否泄露.

这四种结果在内存泄漏的SUMMARY中有所体现:

LEAK SUMMARY:
    definitely lost: 0 bytes in 0 blocks.
    indirectly lost: 0 bytes in 0 blocks.
      possibly lost: 0 bytes in 0 blocks.
    still reachable: 78208 bytes in 2 blocks.
         suppressed: 0 bytes in 0 blocks.

小结

当vargrind真的检查出存在的问题时, 它很有可能帮助你解决了一个bug, 避免了一次崩溃, 所以推荐使用它.
vargrind可能并非完美, 但绝大多数的情况下不会误报, 所以当valgrind发现问题时, 尽量通过它提供的线索找到问题, 而不是质疑它的结果.
此篇关于valgrind的使用, 目前只接触了memcheck方面的, 或许以后有机会在研究下其它几个工具吧.

发表回复

电子邮件地址不会被公开。必填项已用 * 标注