Valgrind——程序分析工具

目录

Valgrind

一.摘要

Valgrind是运行在linux上的程序分析工具,它包含很多小工具: memcheck(内存泄露检查工具)等。

二.安装Valgrind

注意: 这里通过源码安装 Valgrind 3.18.1版本, 因为apt方式安装的版本过旧,会有一些使用上问题

(1) 打开网址:https://valgrind.org/downloads/current.html#current, 这里以下载Valgrind 3.18.1(tar.bz2)压缩包为例

(2)编译安装

shell 复制代码
./configure --prefix=[你自己的安装目录]    设置环境变量时需要用到该地址
make
make install
vim ~/.bashrc  在最后一行添加 PATH=${PATH}:/valgrind/bin [你自己的安装目录]
source ~/.bashrc

(3) 测试安装版本

shell 复制代码
valgrind --version

显示如下信息则说明安装成功

shell 复制代码
valgrind-3.18.1

(4) Qt creator中设置新安装的Valgrind路径和参数

三,简单上手和分析

参考:Linux 性能分析valgrind(一)之memcheck使用

命令(以下程序均可以使用此命令):

powershell 复制代码
valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full  --show-leak-kinds=all ./your_app arg1 arg2

--log-file :报告文件名。如果没有指定,输出到stderr

--tool=memcheck:指定Valgrind使用的工具。Valgrind是一个工具集,包括Memcheck、Cachegrind、Callgrind等多个工具。memcheck是缺省项。

--leak-check: 指定如何报告内存泄漏(memcheck能检查多种内存使用错误,内存泄漏是其中常见的一种),可选值有:

  • no 不报告
  • summary 显示简要信息,有多少个内存泄漏。summary是缺省值。
  • yes 和 full 显示每个泄漏的内存在哪里分配。

--show-leak-kinds : 指定显示内存泄漏的类型的组合。类型包括definite, indirect, possible,reachable。也可以指定all或none。缺省值是definite,possible。 运行一段时间后想停止进程不要kill掉,需要ctrl + c来结束,输出的log会在上述命令中的valgrind.log中。

程序1(C程序):使用未初始化的内存

c 复制代码
#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p; 

    char c = *p; 

    printf("\n [%c]\n",c); 

    return 0;
}

Valgrind重点结果信息: 使用未初始化的变量 , 无效的读( 读取没有分配地址空间的区域数据 )

Bash 复制代码
==73374== Use of uninitialised value of size 8
==73374==    at 0x400513: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==73374== 
==73374== Invalid read of size 1
==73374==    at 0x400513: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==73374==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

程序2(C程序):在内存被释放后进行读/写

c 复制代码
#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = malloc(1);
    *p = 'a'; 

    char c = *p; 

    printf("\n [%c]\n",c); 

    free(p);
    c = *p;
    return 0;
}

Valgrind重点结果信息:

Bash 复制代码
==74181== Invalid read of size 1
==74181==    at 0x4005E3: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==74181==  Address 0x520a040 is 0 bytes inside a block of size 1 free'd
==74181==    at 0x4C3195F: free (vg_replace_malloc.c:872)
==74181==    by 0x4005DE: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==74181==  Block was alloc'd at
==74181==    at 0x4C2F075: malloc (vg_replace_malloc.c:381)
==74181==    by 0x4005A8: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)

程序3(C程序): 内存泄露

c 复制代码
#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = malloc(1);
    *p = 'a'; 

    char c = *p; 

    printf("\n [%c]\n",c); 

    return 0;
}

Valgrind重点结果信息:直接泄露

Bash 复制代码
==74814== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==74814==    at 0x4C2F075: malloc (vg_replace_malloc.c:381)
==74814==    by 0x400558: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)

程序4(C++程序): 不匹配使用malloc free 和 new delete

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include<iostream> 

int main(void)
{
    char *p = (char*)malloc(1);
    *p = 'a'; 

    char c = *p; 

    printf("\n [%c]\n",c);
    delete p;
    return 0;
}

Valgrind重点结果信息:

Bash 复制代码
==75341==    by 0x400683: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==75341==  Address 0x5b20c80 is 0 bytes inside a block of size 1 alloc'd
==75341==    at 0x4C2F075: malloc (vg_replace_malloc.c:381)
==75341==    by 0x400648: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)

程序5(C程序): 两次释放内存

c 复制代码
#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
    char *p = (char*)malloc(1);
    *p = 'a'; 

    char c = *p;
    printf("\n [%c]\n",c);
    free(p);
    free(p);
    return 0;
}

Valgrind重点结果信息:

Bash 复制代码
==76126== Invalid free() / delete / delete[] / realloc()
==76126==    at 0x4C3195F: free (vg_replace_malloc.c:872)
==76126==    by 0x4005EA: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==76126==  Address 0x520a040 is 0 bytes inside a block of size 1 free'd
==76126==    at 0x4C3195F: free (vg_replace_malloc.c:872)
==76126==    by 0x4005DE: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)
==76126==  Block was alloc'd at
==76126==    at 0x4C2F075: malloc (vg_replace_malloc.c:381)
==76126==    by 0x4005A8: main (in /home/bossdog/3Growup/valgrind/test1/a.exe)

四.Qt中使用Valgrind

CMakeLists.txt

cpp 复制代码
cmake_minimum_required(VERSION 3.5)
project(test LANGUAGES C)
add_executable(test main.c)

main.c

cpp 复制代码
#include <stdlib.h>

void f(void)
{
    int* x = malloc(10 * sizeof(int));
    x[10] = 0;        // problem 1: heap block overrun
}                    // problem 2: memory leak -- x not freed

int main(void)
{
    for(int i=0;i<100;i++)
    {
        f();
    }

    f();
    f();
    f();

    return 0;
}

点击 Tool -> Valgrind Memory Analyzer 选项

查看分析结果:

可以看到,同一个位置去调用一个内存泄露函数,被统计为1次record.,点击后面的蓝色字体可以看到具体内存泄漏的代码位置。

五.内存泄露分析

valgrind 将内存泄漏分为 4 类。 参考

  • 明确泄漏(definitely lost):内存还没释放,但已经没有指针指向内存,内存已经不可访问
  • 间接泄漏(indirectly lost):泄漏的内存指针保存在明确泄漏的内存中,随着明确泄漏的内存不可访问,导致间接泄漏的内存也不可访问
  • 可能泄漏(possibly lost):指针并不指向内存头地址,而是指向内存内部的位置
  • 仍可访达(still reachable):指针一直存在且指向内存头部,直至程序退出时内存还没释放。

PS:这里只展开 definitely lostindirectly lost

明确泄漏: 内存没释放,但已经没有任何指针指向这片内存,内存地址已经丢失

间接泄漏: 间接泄漏就是指针并不直接丢失,但保存指针的内存地址丢失了

cpp 复制代码
struct list {
	struct list *next;
};

int main(int argc, char **argv)
{
	struct list *root;
	
	root = (struct list *)malloc(sizeof(struct list));
	root->next = (struct list *)malloc(sizeof(struct list));
	printf("root %p roop->next %p\n", root, root->next);
	root = NULL;
	return 0;
}

丢失的是 root 指针,导致 root 存储的 next 指针成为了间接泄漏。

可能泄漏: valgrind之所以会怀疑可能泄漏,是因为指针已经偏移,并没有指向内存头,而是有内存偏移,指向内存内部的位置.

有些时候,这并不是泄漏,因为这些程序就是这么设计的,例如为了实现内存对齐,额外申请内存,返回对齐后的内存地址。但更多时候,是我们不小心 p++

仍可访达: 仍可访达 表示在程序退出时,不管是正常退出还是异常退出,内存申请了没释放,都属于仍可访达的泄漏类型. 如果测试的程序是正常退出的,那么这些 仍可访达 的内存就是泄漏,最好修复了。如果测试是长期运行的程序,通过信号提前终止,那么这些内存就大概率并不是泄漏。

其他内存错误

  • 非法读/写内存(Illegal read / Illegal write errors)
  • 使用未初始化的变量(Use of uninitialised values)
  • 系统调用传递不可访问或未初始化内存(Use of uninitialised or unaddressable values in system calls)
  • 非法释放(Illegal frees)
  • 不对应的内存申请和释放(When a heap block is freed with an inappropriate deallocation function)
  • 源地址和目的地址重叠(Overlapping source and destination blocks)
  • 内存申请可疑大小(Fishy argument values)

官方手册

中文手册

相关推荐
网络研究院1 个月前
x64dbg: 用于Windows的开源二进制调试器
网络安全·软件分析·程序分析·动态分析·逆向工程·调试器·反汇编程序
_whitepure2 个月前
内存泄漏详解
java·jvm·内存泄漏·解决内存泄漏
烛秋2 个月前
GCC8 编译优化 BUG 导致的内存泄漏
c++·内存泄漏·jemalloc·编译优化·gcc8
LabVIEW开发3 个月前
LabVIEW程序内存泄漏分析与解决方案
算法·labview·labview开发·内存泄漏
dvlinker5 个月前
【C++软件调试技术】C++软件开发维护过程中典型调试问题的解答与总结
c++·内存泄漏·线程堵塞·gdi对象泄漏·软件调试技术·软件异常·堆内存被破坏
Bear Coding5 个月前
windbg托管内存泄漏排查
windbg·内存泄漏·托管堆
原来是图哥6 个月前
android中单例模式为什么会引起内存泄漏?
android·java·单例模式·内存泄漏
MyZony6 个月前
排查 dotNET Core 程序内存暴涨的问题
linux·gc·垃圾回收·内存泄漏·.net 6·dump·.net 5
@forever@7 个月前
C/C++内存管理
c语言·开发语言·c++·操作系统·内存管理·内存泄漏·c++基础