valgrind使用

文章目录

  • 简介
  • 安装
  • 如何使用valgrind来检测内存错误?
  • 如何使用其它的工具?
  • 总结

简介

Valgrind是一个工具集,包含了许多调试与性能分析的工具。其中使用最多的是Memcheck,它能帮你检测C/C++中的内存问题,避免程序崩溃或不可预知错误。

其它的工具实际使用率低,具体如下:

  • Cachegrind
    一个缓存和分支预测分析器。它能够模拟CPU中的一级缓存L1、D1和二级缓存,且能够精确指出程序中cache的命中和丢失。它还可以给出每行代码,每个函数,每个模块,和整个程序的内存引用次数以及指令数,有利于优化程序。
  • Callgrind
    相当于Cachegrind的一个扩展。它除了能够给出Cachegrind提供的所有信息之外,还可以给出程序的调用图。
  • Massif
    堆分析器。会生成一张表示程序运行过程中堆内存使用情况的图,包括在运行过程中哪个模块占用的堆内存最多等信息。
  • Helgrind
    线程调试器。用于检测多线程程序中出现的数据竞争问题,如访问临界资源不加锁、死锁等。
  • DRD
    类似于Helgrind,但使用了不同的分析技术,因此可能会发现不同的问题。
  • DHAT
    一种不同类型的堆分析器。

  • 工具集还在持续更新。

下载地址:Valgrind: Current Releases

官网文档:Valgrind: Table of Contents


安装

tar xjf valgrind-3.22.0.tar.bz2
cd valgrind-3.22.0

./configure
make -j4
make install

man valgrind

其实官方文档内容较多,应该选择阅读自己关注的工具文档,比如Memcheck文档:

在这里插入图片描述


如何使用valgrind来检测内存错误?

常见的内存问题有非法读写、非法释放、使用未初始化或无法寻址的变量、释放和分配函数不匹配、源目地址与目标地址相互覆盖等等。这里就不一一列举各个场景,主要介绍如何使用valgrind来检测程序。

首先,编写一个内存非法访问的程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
#if 0 // memcheck只检查堆空间的内存问题,如果是栈空间的错误会检测不出。
    int arr[5] = {1, 2, 3, 4, 5};
#else
    int *arr = (int *)malloc(5 * sizeof(int));
#endif
    arr[-1] = 0;

    printf("run ok.\n");

    return 0;
}

编译时,尽量别使用编译优化选项,编译器优化可能会改变代码的内存访问方式,导致检测报告不准确。

Valgrind的使用方式很简单:

valgrind [options] prog-and-args

它的选项有很多,除了--tool=<toolname>,用来指定工具外,感觉其它的都不怎么常用。
Valgrind如果没有使用--tool选项,就会默认使用memcheck工具来检测内存错误。

检测结果:

[root@localhost c]# valgrind ./a.out
==20219== Memcheck, a memory error detector
==20219== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==20219== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==20219== Command: ./a.out
==20219==
==20219== Invalid write of size 4
==20219==    at 0x4005F4: main (in /home/ch/c/a.out)
==20219==  Address 0x520e03c is 4 bytes before a block of size 20 alloc'd
==20219==    at 0x4C36081: malloc (vg_replace_malloc.c:442)
==20219==    by 0x4005E7: main (in /home/ch/c/a.out)
==20219==
run ok.
==20219==
==20219== HEAP SUMMARY:
==20219==     in use at exit: 20 bytes in 1 blocks
==20219==   total heap usage: 2 allocs, 1 frees, 1,044 bytes allocated
==20219==
==20219== LEAK SUMMARY:
==20219==    definitely lost: 20 bytes in 1 blocks
==20219==    indirectly lost: 0 bytes in 0 blocks
==20219==      possibly lost: 0 bytes in 0 blocks
==20219==    still reachable: 0 bytes in 0 blocks
==20219==         suppressed: 0 bytes in 0 blocks
==20219== Rerun with --leak-check=full to see details of leaked memory
==20219==
==20219== For lists of detected and suppressed errors, rerun with: -s
==20219== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

报告也很简明:

# 这是列出内存错误
==20219== Invalid write of size 4

# 堆检测汇总,2处分配,1处释放
==20219== HEAP SUMMARY:
==20219==     in use at exit: 20 bytes in 1 blocks
==20219==   total heap usage: 2 allocs, 1 frees, 1,044 bytes allocated

# 泄漏汇总,有20字节绝对泄漏
==20219== LEAK SUMMARY:
==20219==    definitely lost: 20 bytes in 1 blocks

如何使用其它的工具?

我们再演示一个检查线程错误的工具Helgrind。它的说明文档在这:Helgrind: a thread error detector

同样编写一个示例程序data_race.c:

#include <pthread.h>

int var = 0;

void* child_fn ( void* arg ) {
   var++; /* Unprotected relative to parent */ /* this is line 6 */
   return NULL;
}

int main ( void ) {
   pthread_t child;
   pthread_create(&child, NULL, child_fn, NULL);
   var++; /* Unprotected relative to child */ /* this is line 13 */
   pthread_join(child, NULL);
   return 0;
}

这段代码在使用线程访问全局变量时,没有加锁,因此会产生数据竞争的问题。

编译程序,并运行Helgrind进行检测:

[root@localhost c]# gcc data_race.c -lpthread
[root@localhost c]#
[root@localhost c]# valgrind --tool=helgrind ./a.out
==5425== Helgrind, a thread error detector
==5425== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.
==5425== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==5425== Command: ./a.out
==5425==
==5425== ---Thread-Announcement------------------------------------------
==5425==
==5425== Thread #1 is the program's root thread
==5425==
==5425== ---Thread-Announcement------------------------------------------
==5425==
==5425== Thread #2 was created
==5425==    at 0x516DDB2: clone (in /usr/lib64/libc-2.28.so)
==5425==    by 0x4E5803E: create_thread (in /usr/lib64/libpthread-2.28.so)
==5425==    by 0x4E599F5: pthread_create@@GLIBC_2.2.5 (in /usr/lib64/libpthread-2.28.so)
==5425==    by 0x4C44542: pthread_create_WRK (hg_intercepts.c:445)
==5425==    by 0x4C45A49: pthread_create@* (hg_intercepts.c:478)
==5425==    by 0x400656: main (in /home/ch/c/a.out)
==5425==
==5425== ----------------------------------------------------------------
==5425==
==5425== Possible data race during read of size 4 at 0x601030 by thread #1
==5425== Locks held: none
==5425==    at 0x400657: main (in /home/ch/c/a.out)
==5425==
==5425== This conflicts with a previous write of size 4 by thread #2
==5425== Locks held: none
==5425==    at 0x400627: child_fn (in /home/ch/c/a.out)
==5425==    by 0x4C44736: mythread_wrapper (hg_intercepts.c:406)
==5425==    by 0x4E59179: start_thread (in /usr/lib64/libpthread-2.28.so)
==5425==    by 0x516DDC2: clone (in /usr/lib64/libc-2.28.so)
==5425==  Address 0x601030 is 0 bytes inside data symbol "var"
==5425==
==5425== ----------------------------------------------------------------
==5425==
==5425== Possible data race during write of size 4 at 0x601030 by thread #1
==5425== Locks held: none
==5425==    at 0x400660: main (in /home/ch/c/a.out)
==5425==
==5425== This conflicts with a previous write of size 4 by thread #2
==5425== Locks held: none
==5425==    at 0x400627: child_fn (in /home/ch/c/a.out)
==5425==    by 0x4C44736: mythread_wrapper (hg_intercepts.c:406)
==5425==    by 0x4E59179: start_thread (in /usr/lib64/libpthread-2.28.so)
==5425==    by 0x516DDC2: clone (in /usr/lib64/libc-2.28.so)
==5425==  Address 0x601030 is 0 bytes inside data symbol "var"
==5425==
==5425==
==5425== Use --history-level=approx or =none to gain increased speed, at
==5425== the cost of reduced accuracy of conflicting-access information
==5425== For lists of detected and suppressed errors, rerun with: -s
==5425== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

报告中说明了每个线程的创建过程,以及提示出可能有数据竞争的变量,最后给出错误汇总。


当然,部分工具给出的报告,还需要借助一个解释工具,将报告转换成易读的模式。比如Massif的输出报告需要通过ms_print转变可读模式;Callgrind需要借助callgrind_annotate;Cachegrind需要借助cg_annotate等等。当然,这些工具在安装时会自带。

Callgrind能显示程序调用图,并且能跟踪线程。接下来演示它的使用过程。
同样,先编写一个示例程序,该程序创建了两个线程,分别使用不同的方法统计质数,有快有慢,相互对比耗时。

#include <pthread.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/time.h>
#include <math.h>
#include <unistd.h>

#define RANGE 1000000

bool is_prime_1(int n) {
    if (n <= 1)
      return false;

    for(int i = 2; i <= sqrt(n); ++i)
        if (n % i == 0) return false;

    return true;
}

// 优化算法
bool is_prime_2(int n) {
    if (n <= 3)
        return n > 1;

    // 只有6x-1和6x+1的数才有可能是质数
    if (n % 6 != 1 && n % 6 != 5)
        return false;

    // 判断这些数能否被小于sqrt(n)的奇数整除
    for (int i = 5; i <= sqrt(n); i += 6)
        if (n % i == 0 || n % (i + 2) == 0)
            return false;

    return true;
}

void* fun1(void *arg) {
    int i = 0;
    int count = 0;
    struct timeval begin, end;

    gettimeofday(&begin, 0);
    for (i = 0; i < RANGE; i++)
        if (is_prime_1(i))
            ++count;
    gettimeofday(&end, 0);
    long seconds = end.tv_sec - begin.tv_sec;
    long microseconds = end.tv_usec - begin.tv_usec;
    double elapsed = seconds + microseconds*1e-6;
    printf("use func1111111 static prime, count is %d,  time comsumed: %lf\n\n", count, elapsed);

    return NULL;
}

void* fun2(void *arg) {
    int i = 0;
    int count = 0;
    struct timeval begin, end;

    gettimeofday(&begin, 0);
    for (i = 0; i < RANGE; i++)
        if (is_prime_2(i))
            ++count;
    gettimeofday(&end, 0);
    long seconds = end.tv_sec - begin.tv_sec;
    long microseconds = end.tv_usec - begin.tv_usec;
    double elapsed = seconds + microseconds*1e-6;
    printf("use func222222 static prime, count is %d,  time comsumed: %lf\n\n", count, elapsed);

    return NULL;
}

int main() {
    pthread_t t1, t2;

    pthread_create(&t1, NULL, fun1, NULL);
    pthread_join(t1, NULL);

    pthread_create(&t2, NULL, fun2, NULL);
    pthread_join(t2, NULL);

    return 0;
}

编译后,同样使用Callgrind进行检测,输出报表。

[root@localhost c]# gcc demo.c -lpthread -lm
[root@localhost c]#
[root@localhost c]# valgrind --tool=callgrind ./a.out

Callgrind有一个选项--separate-threads=yes,它会拆分线程,输出多个callgrind.out.pid文件,一个线程对应一个

输出的报告还不能直接读,因为它描述的是调用关系以及CPU占比,是以图的形式展示的。所以还需要通过gprof2dot.py脚本转成.dot文件。并且,.dot文件也不是最终的报告,它的内容是DOT Language,一种语言,用来告诉Graphviz怎样画图的

python gprof2dot.py -f callgrind -n10 -s callgrind.out.6204 > test.dot

gprof2dot.py顾名思义,是一个将gprof分析结果转换为图形化调用图的Python脚本。可以通过pip intall gprof2dot下载(当然,你得解决pip源的问题)。或者直接在官网下载:gprof2dot · PyPI

我怕我扯的越来越远,再啰嗦一句,gprof是Linux/Unix系统下一个性能分析工具,用于分析和显示程序的运行情况。Man文档:GNU gprof

Graphviz是一个开源的图形可视化工具包,主要功能是根据描述图的数据生成图形。感兴趣的同学可以访问它的官网:Graphviz

一般linux的都有安装Graphviz工具包,我们最终使用它有有向图工具dot,生成最后的调用关系图。

dot -Tpng test.dot -o test.png

最后,将图片下载下来,就长这样:

在这里插入图片描述

图片上显示的占比为CPU占比,然后就可以根据热点进行程序优化了。如果你不喜欢.png,也可以生成其它格式的文件,dot支持的类型有很多,比如换成svg:

# -T 指定生成的类型
# -o 指定输出文件名
dot -Tsvg test.dot -o test.svg

更多类型,请参考:Output Formats | Graphviz


总结

Valgrind的工具有很多,不可能三言两语全说清楚。只有实际使用上时,才能有更深入的了解。但我希望你永远不需要它,原因懂的都懂。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/345587.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JS之打地鼠案例

需要素材的同学可以私信我 效果图&#xff1a; 上代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title><style>* {margin: 0;padding: 0;}.box {position: relative;width: 320px;heigh…

【Java程序员面试专栏 专业技能篇】MySQL核心面试指引(三):性能优化策略

关于MySQL部分的核心知识进行一网打尽,包括三部分:基础知识考察、核心机制策略、性能优化策略,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 本篇Blog为第三部分:性能优化策略,子节点表示追问或同级提问 读写分离 分布式数据库的…

【学网攻】 第(4)节 -- 交换机划分Vlan

文章目录 【学网攻】 第(1)节 -- 认识网络 【学网攻】 第(2)节 -- 交换机认识及使用【学网攻】 第(3)节 -- 交换机配置聚合端口 前言 网络已经成为了我们生活中不可或缺的一部分&#xff0c;它连接了世界各地的人们&#xff0c;让信息和资源得以自由流动。随着互联网的发展&am…

大模型微调实战笔记

大模型三要素 1.算法&#xff1a;模型结构&#xff0c;训练方法 2.数据&#xff1a;数据和模型效果之间的关系&#xff0c;token分词方法 3.算力&#xff1a;英伟达GPU&#xff0c;模型量化 基于大模型对话的系统架构 基于Lora的模型训练最好用&#xff0c;成本低好上手 提…

vue3和vite项目在scss中因为本地图片,不用加~

看了很多文章说要加~&#xff0c;真的好坑哦&#xff0c;我的加了~反而出不来了&#xff1a; 304 Not Modified 所以需要去掉~&#xff1a; /* 默认dark主题 */ :root[themered] {--bg-color: #0d1117;--text-color: #f0f6fc;--backImg: url(/assets/images/redBg.png); }/* …

代码随想录算法训练营第四十二天|01背包问题、01背包问题(滚动数组)、416. 分割等和子集

题目&#xff1a;01背包问题 文章链接&#xff1a;代码随想录 视频链接&#xff1a;LeetCode:背包问题 题目链接&#xff1a;卡码题目链接 图释&#xff1a; //二维dp数组实现 #include <bits/stdc.h> using namespace std;int n, bagweight;// bagweight代表行李箱空…

【Java并发】聊聊Future如何提升商品查询速度

java中可以通过new thread、实现runnable来进行实现线程。但是唯一的缺点是没有返回值、以及抛出异常&#xff0c;而callable就可以解决这个问题。通过配合使用futuretask来进行使用。 并且Future提供了对任务的操作&#xff0c;取消&#xff0c;查询是否完成&#xff0c;获取结…

SpringBoot整合ElasticSearch实现分页查询

本文使用SpringBoot整合ElasticSearch实现分页查询 文章目录 环境准备分页查询方式一方式二 本文小结 环境准备 还是继续使用spring-boot-starter-data-elasticsearch来实现分页查询操作 <!-- spring-boot-starter-data-elasticsearch--> <dependency><groupId&…

QT解析json数据

QT解析json数据 头文件jsonObjectToMapparseJson结果 头文件 #include <QFile> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> #include <QJsonValue>jsonObjectToMap 将Json对象转换成map QVariantMap MainWindow…

数据分析-Pandas如何用图把数据展示出来

数据分析-Pandas如何用图把数据展示出来 俗话说&#xff0c;一图胜千语&#xff0c;对人类而言一串数据很难立即洞察出什么&#xff0c;但如果展示图就能一眼看出来门道。数据整理后&#xff0c;如何画图&#xff0c;画出好的图在数据分析中成为关键的一环。 数据表&#xff…

详解JavaScript异步编程之Promise

一、前言 JavaScript是⼀⻔典型的异步编程脚本语⾔&#xff0c;在编程过程中会⼤量的出现异步代码的编写&#xff0c;在JS的整个发展历程中&#xff0c;对异步编程的处理⽅式经历了很多个时代&#xff0c;其中最典型也是现今使⽤最⼴泛的时代&#xff0c;就是Promise对象处理异…

当软件开发具备了低代码的开发能力,难以想象会有多“香”

一、前言 低代码开发平台&#xff0c;一个号称能在几分钟的时间里开发出一套公司内部都可使用的应用系统开发工具。 很多人或许都隐隐听说过低代码&#xff0c;因为低代码不仅远名国外&#xff0c;国内的腾讯、阿里、华为、网易、百度等科技巨头也纷纷入局。 那么市面上都有哪些…

全桥RLC模态图具体分析

T0时刻&#xff0c;Q6,Q7,Q1.Q4开通&#xff0c;驱动为高电平&#xff0c;励磁电流线性上升,但是lm电流在to是为负电流&#xff0c;这时刻有给副边提供能量&#xff0c;Ip电流开始上升&#xff0c;这个时候给副边的电流也是从0开始上升,这个能量由励磁电感提供&#xff0c;Co给…

HCIA——27E-mall、MIME;POP3、IMAP的选择,解答

学习目标&#xff1a; 计算机网络 1.掌握计算机网络的基本概念、基本原理和基本方法。 2.掌握计算机网络的体系结构和典型网络协议&#xff0c;了解典型网络设备的组成和特点&#xff0c;理解典型网络设备的工作原理。 3.能够运用计算机网络的基本概念、基本原理和基本方法进行…

整理了一下常用的LaTeX数学公式语法,未完待续

为了方便对应&#xff0c;后面会拆一下 公式代码放入LaTeX编译环境中时&#xff0c;两边需要加入$$: $$公式代码$$ 1&#xff0c;分解示例 L^{A}T_{E}X\,2_{\epsilon} c^{2}a^{2}b^{2} \tau\phi \cos2\pi1 f\, \,a^{x}\,\,b \heartsuit \cos^{2}\theta \sin^{2}\theta 1.0…

Nodejs前端学习Day1

妈的&#xff0c;学vue3需要15.0以上的nodejs 文章目录 前言一、学习目标二、学习目录三、为什么JavaScript可以在浏览器中被执行四、为什么JavaScript可以操作DOM和BOM五、浏览器中的JavaScript运行环境总结 前言 妈的&#xff0c;学vue3需要15.0以上的nodejs 一、学习目标 二…

CNN经典网络模型(五):ResNet简介及代码实现(PyTorch超详细注释版)

目录 一、开发背景 二、网络结构 三、模型特点 四、代码实现 1. model.py 2. train.py 3. predict.py 4. spilit_data.py 五、参考内容 一、开发背景 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的&#xff0c; 斩获2015年ImageNet竞赛…

SE通道注意力机制模块

简介 论文原址&#xff1a;https://arxiv.org/pdf/1709.01507.pdf 在深度学习领域&#xff0c;提升模型的表征能力一直是一个关键的研究方向。SE&#xff08;Squeeze-and-Excitation&#xff09;模块是一种引入通道注意力机制的方法&#xff0c;旨在让神经网络更加关注对当前…

我每天如何使用 ChatGPT

我们都清楚互联网的运作方式——充斥着各种“爆款观点”&#xff0c;极端分裂的意见&#xff0c;恶搞和无知现象屡见不鲜。 最近&#xff0c;大家对于人工智能&#xff08;AI&#xff09;特别是大语言模型&#xff08;LLMs&#xff09;和生成式 AI&#xff08;GenAI&#xff0…

【趣味游戏-08】20240123点兵点将点到谁就是谁(列表倒置reverse)

背景需求&#xff1a; 上个月&#xff0c;看到大4班一个孩子在玩“点兵点将点到谁就是谁”的小游戏&#xff0c;他在桌上摆放两排奥特曼卡片&#xff0c;然后点着数“点兵点将点到谁就是谁”&#xff0c;第10次点击的卡片&#xff0c;拿起来与同伴的卡片进行交换。他是从第一排…