当前位置: 首页 > news >正文

Linux下如何进行内存泄漏分析

前言

正文

一、环境的安装

1、tar –xf valgrind-3.17.0.tar.bz2
2、cd valgrind-3.17.0
3./configure         // 运行配置脚本生成makefile文件,可以--help查看配置项,自行按需配置,比如修改编译工具、修改安装路径等
4、make
5、make install        //安装生成可执行文件,可执行文件的路径有参数--prefix指定,需要在PATH中添加环境变量;若不加参数--prefix指定,仅使用默认配置,则会自动关联

安装完后可以使用:
valgrind --help查看使用方法 或测试一下是否安装成功

二、使用

1、工具的基本介绍

(1)Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
(2)Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
(3)Cachegrind。它主要用来检查程序中缓存使用出现的问题。
(4)Helgrind。它主要用来检查多线程程序中出现的竞争问题。
(5)Massif。它主要用来检查程序中堆栈使用中出现的问题。
(6)Extension。可以利用core提供的功能,自己编写特定的内存调试工具

2、常用选项

(1)适用于所有Valgrind工具
–tool=< name > 最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。
-h --help 显示帮助信息。
–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

(2)LOG信息输出
–xml=yes 将信息以xml格式输出,只有memcheck可用
–num-callers=< number > show < numbe r> 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]

(3)适用于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]
更详细的使用信息详见帮助文件、man手册或官网:http://valgrind.org/docs/manual/manual-core.html

(4)注意:
① valgrind不会自动的检查程序的每一行代码,只会检查运行到的代码分支,所以单元测试或功能测试用例很重要;
② 可以把valgrind看成是一个sandbox,通过valgrind运行的程序实际上是运行在valgrind的sandbox中的,所以,不要测试性能,会让你失望的,建议只做功能测试
③ 编译代码时,建议增加-g -o0选项,不要使用-o1、-o2选项

3、memcheck工具的原理

–tool=< name > : 使用的工具名称
–log-file=< file > : 输出的日志的名称
valgrind --tool=memcheck --log-file=log.txt --leak-check=yes  ./test

说明:使用memcheck工具对test程序进行包含内存泄漏的检查,并将日志保存到log.txt

当待分析程序片段第一次被执行时,valgrind会将代码片段交给工具——比如内存调试时使用的memcheck处理,工具会在代码中插入一些辅助分析的代码片段。新的代码会在valgrind模拟出的CPU上执行。然后valgrind会结合之前读取到的待执行程序和其所关联的库文件的调试信息,输出分析结果。

因为有新插入的代码逻辑,valgrind运行下的程序都比其独立运行时要慢。视选择的工具不同,其效率可能是正常值的1/4~1/50。所以使用valgrind做性能分析时,一般不使用绝对数据,而使用相同环境下的相对数据进行对比。

为了让valgrind读取出准确的调试信息,待分析程序最好使用-O0禁止编译器优化,以及使用-g让编译器把行号信息编入到文件中

4、memcheck示例

编译: g++ -O0 -g test.c -o test_g

ValGrind分析:valgrind --tool=memcheck ./test_g

1、写违例

#include <stdlib.h>int main() {const int array_count = 4;int* p = (int *)malloc(array_count * sizeof(int));p[array_count] = 0; // Illegal read free(p);return 0;
}

在这里插入图片描述

第1显示有4个字节被违例写入,第2显示写入的位置在分配的16个字节之后。

2、读违例

#include <stdlib.h>int main() {const int array_count = 4;int* p = (int *)malloc(array_count * sizeof(int));int error_num = p[array_count]; // Illegal readfree(p);return 0;
}

错误的位置和上例一样,区别在于这次是读取不合法的地址的数据。使用valgrind分析显示

在这里插入图片描述

第一行显示有4个字节被违例读取,第三行显示读取的位置在分配的16个字节之后。

3、使用未初始化变量

#include <stdlib.h>
#include <stdio.h>int main() {const int array_count = 4;int* p = (int*)malloc(array_count * sizeof(int));printf("%d",  p[array_count - 1]);free(p);int undefine_num;printf("%d", undefine_num);return 0;
}

第7行和第11行分别访问了堆上、栈上未初始化的变量。valgrind分析显示
在这里插入图片描述

这里面就会提醒第7行和第11行访问了未初始化的变量。

5、Valgrind 的局限

Valgrind 不对静态数组 (分配在栈上) 进行边界检查。如果在程序中声明了一个数组:

int main(int argc, char *argv[])
{char x[10];x[11] = 'a';return 0;
}

Valgrind 则不会警告你,你可以把数组改为动态在堆上分配的数组,这样就可能进行边界检查了。这个方法好像有点得不偿失的感觉。

Valgrind 占用了更多的内存—可达两倍于你程序的正常使用量。如果你用 Valgrind 来检测使用大量内存的程序就会遇到问题,它可能会用很长的时间来运行测试。大多数情况下,这都不是问题,即使速度慢也仅是检测时速度慢,如果你用 Valgrind 来检测一个正常运行时速度就很慢的程序,这下问题就大了。Valgrind 不可能检测出你在程序中犯下的所有错误—如果你不检查缓冲区溢出,Valgrind 也不会告诉你代码写了它不应该写的内存。

参考

http://www.lryc.cn/news/514107.html

相关文章:

  • Colyseus Metadata 详解
  • C语言day5:shell脚本
  • 微记录-Linux字符设备的write函数如何避免文件系统重复调用?
  • 本地调试自定义Maven Plugin步骤
  • 二、github基础
  • 如何在 Vue 2 中使用 Swiper 5.4.5 处理静态与后端数据不能切换问题
  • request.getSession().getAttribute(Constants.ADMIN_ID)
  • 线性回归模型的构建与训练
  • 【JavaWeb后端学习笔记】MySQL的常用函数(字符串函数,数值函数,日期函数,流程函数)
  • 【推送】主流的服务端推送技术的对比
  • 直观解读 JuiceFS 的数据和元数据设计(一)
  • nginx配置文件没有语法颜色
  • PCB层叠结构设计
  • 电子应用设计方案83:智能 AI 打印机系统设计
  • windows安装rsync Shell语句使用rsync
  • Django 模型
  • CentOS — 压缩解压
  • OpenGL变换矩阵和输入控制
  • LCS最长公共子序列C++实现
  • 深入刨析数据结构之排序(上)
  • 【无重复字符的最长子串】
  • Vue3+Element Plus的表格分页实战
  • vue项目搭建规范
  • Mac iTerm2集成DeepSeek AI
  • 检索增强生成(RAG)
  • 【第二部分--Python之基础】03 容器类型的数据
  • 【人工智能机器学习基础篇】——深入详解深度学习之复杂网络结构:卷积神经网络(CNN)、循环神经网络(RNN)、生成对抗网络(GAN)等概念及原理
  • MySQL 入门教程
  • 【sql】CAST(GROUP_CONCAT())实现一对多对象json输出
  • QT:控件属性及常用控件(1)------核心控件及属性