valgrind,memcheck的使用

news2025/1/17 9:07:03

一,valgrind介绍

​ valgrind是一个开源的,检测内存泄漏的工具,通常在linux下使用,除此之外,他还能检测内存管理错误,线程bug等错误。粗浅的来讲,valgrind由两部分构成,一部分用来模拟cpu和内核,被称为framework(框架),一部分是他用来检测各种错误信息的插件工具。其中的插件包括:

memcheck:是valgrind最重要的工具之一,是一个重量级的内存检查器,它可以帮我们检测是否有使用未初始化的内存,以及内存泄漏,访问越界之类的问题。

callgrind:可以用来统计函数的互相调用情况。可以用来统计某个函数被调用了几次,以及他们的调用关系。

cachegrind:cache分析器,可以精准的指出cache的丢失与命中。还可以统计cache命中与丢失的次数,让我们了解我们程序堆栈的使用情况。

helgrind:主要用来检测线程资源的竞争问题,比如POSIX pthreads API的误用。由锁排序导致的死锁。以及数据的竞争问题等。

massif:堆栈分析器。它可以统计堆和栈使用的内存大小是多少。

extension:可以利用core的特性自己编辑的内存调试工具。

在这里插入图片描述

ps:valgrind相当于一个沙盒,指定的程序会在他模拟的cpu和内核中去运行,所以我们尽量不要使用他来进行性能测试,这是不合理的!。

二,valgrind通用命令

#指定使用的工具
valgrind --tool=toolname program

#指定输出日志的名字
--log-file=logname.log

#打印帮助信息。
-h --help 
 
#显示valgrind的版本
--version 
显示valgrind内核的版本,每个工具都有各自的版本。 

#只打印错误信息
-q --quiet 

#打印更加详细的信息。
-v --verbose 

三,memcheck的使用

​ memcheck是valgrind的默认工具,可以与 valgrind program 一起运行,不指定工具时,便默认使用memcheck,即 valgrind --tool=memcheck。运行 Memcheck 时程序运行速度要比正常运行时慢 10-30 倍。这也是为什么我们一般使用它进行功能测试,而不是性能测试的原因。

1,选项

#启用后(=yes 或 =full 或 =summary),Memcheck会在客户端程序完成后搜索内存泄漏。其默认值为summary,它输出找到的泄漏数。其他可能的值为 yes 和 full,这两个选项都会给出每个泄漏的详细情况。
#禁用(=no)禁用内存泄漏检查。 
--leak-check=

#启用后(将其设定为 =yes),Memcheck 会报告使用未定义值报告的错误。
#禁用时(将其设定为 =no),则不会报告未定义值错误。默认启用这个选项。禁用该选项会稍稍提高 Memcheck 速度。
--undef-value-errors=

#可让用户指定一个或者多个 Memcheck 检查时应该忽略的范围。多个范围使用逗号分开,例如:--ignore-ranges=0xPP-0xQQ,0xRR-0xSS。 
--ignore-ranges=

#输出结果为。。。文件
--log-file=filename

#是否打印间接泄漏日志
--show-reachable=yes.
     

2,错误类型

1.非法读写错误[invalid read of size 4] :读写了被释放的内存,没有被分配的内存等,还有内存访问越界等问题导致的。

2.使用未初始化的内存[Conditional jump or move depends on uninitialised value]:使用了没有初始化的值,例如使用没有被初始化的局部变量;或使用了没有初始化的堆。

3.[syscall param write(buf) points to uninitilaised bytes]:buffer不可用,例如在使用snprintf()时,写入的字符串大于存储的buffer,就会爆出这个错误。

4.非法释放[invalid free()]:重复释放内存块(即double free),或者指针没有指向内存的起始位置。

5.使用不适当的释放函数释放[Mismatched free()/delete/delete[]]:

  • 如果与分配 malloc, calloc, realloc, valloc或者 memalign,必须使用free。
  • 如果分配new,必须使用delete。

6.内存块重叠[source and destination overlap in memcpy(,)]:比如使用 memcpy 函数时源地址和目标地址发生重叠。

7.Fishy argument values[Argument ‘size’ of function malloc has a fishy (possibly negative) value: -3]:需要的内存不合理,例如需要-1的内存,或者很大的内存,无法分配。

3,使用实例与分析

#例如执行./a.out  -p 32903 -i 127.0.0.1 -b 0 -t 10这样的程序。
#那么需要用valgrind的时候,用如下命令
valgrind --tool=memcheck --leak-check=yes --log-file=check.log ./${OBJ_NAME}.o  -p 32903 -i 127.0.0.1 -b 0 -t 10 



valgrind 将内存泄漏分为 4 类:并且默认情况下,只会打印明确泄漏 和可能泄漏,如果需要打印 间接泄漏,需要加上选项 --show-reachable=yes.

明确泄漏(definitely lost):表示某段内存还没有被释放,但是已经没有指针指向它了,所以明确了这里一定发生了内存释放。
间接泄漏(indirectly lost):某一段内存没有被释放,指向它的指针仍然存在,但是,存放这个指针的内存已经被释放了。
可能泄漏(possibly lost):指针存在,但并不指向内存头地址,而是指向内存内部的位置。
仍可访达(still reachable):指针一直存在并指向首地址,直至程序退出时内存还没被释放。例如指针ptr=malloc()了一段内存,直至程序结束,ptr仍然指向malloc分配的内存的首地址,并且该内存仍然没有没释放。

明确泄漏和间接泄漏毫无疑问是必须修复的,间接泄漏往往在明确泄漏被修复后便解决了,可能泄漏要看我们的函数是不是那样设计的,需要我们检查后定论,而任可访达,也需要我们判断,如果程序正常退出后,仍然有仍可访达,那么也是需要修复的,一般是在程序的结尾忘记了free()或者close()某些句柄造成的。如果程序,被信号强行终止,那么就可能就并不是泄漏,只是程序还未走到free()的地方便结束了程序造成的。

#日志输出后,我们一般先看HEAP SUMMARY(堆总结内存) 以及 LEAK SUMMARY(内存泄漏总结)
==27801== HEAP SUMMARY:
==27801==   in use at exit: 215,528 bytes in 379 blocks
==27801==   total heap usage: 801 allocs, 422 frees, 274,889 bytes allocated
==27801== 	#分配了801次内存,但是只释放了422次。


#leak summary会总结所有类型的内存泄漏,泄漏多少字节,在多少个blocks里面泄漏了。
==27756== LEAK SUMMARY:
==27756==    definitely lost: 520 bytes in 1 blocks
==27756==    indirectly lost: 61,800 bytes in 173 blocks
==27756==    possibly lost: 152,896 bytes in 204 blocks
==27756==    still reachable: 312 bytes in 1 blocks
==27756==    suppressed: 0 bytes in 0 blocks
#统计了使用valgrind的某些参数取消了特定库的某些错误,会被归结到suppressed。

格式
at {地址、函数名、模块或代码行}
by {地址、函数名、代码行}
by …{逐层依次显示调用堆栈}
Address 0x??? {描述地址的相对关系}
#例:明确泄漏分析
#泄漏情况和泄漏类型
==27703== 62,320 (520 direct, 61,800 indirect) bytes in 1 blocks are definitely lost in loss record 116 of 117
==27703==    at 0x48483D0: malloc (vg_replace_malloc.c:262)    #at...在哪里分配的内存
==27703==    by 0x48CC3B3: sqlite3MemMalloc (sqlite3.c:23980)  #by...分配的内存都在哪里被使用了
==27703==    by 0x48B380F: mallocWithAlarm (sqlite3.c:27862)
==27703==    by 0x48B3917: sqlite3Malloc (sqlite3.c:27892)
==27703==    by 0x49925AB: openDatabase (sqlite3.c:28134)
==27703==    by 0x487345F: sqlite3_open_database_table (in /home/。。。/。。。/。。。/。。。/libname.so)#(显示是哪一个库的哪一个函数发生的内存泄漏)
#最后使用这段内存的函数是sqlite3_open_database_table,一般就是这里,或者是调用了这个函数后忘记释放了内存导致的。

四,一些使用经验

1、sqlite3_get_table()释放

int sqlite3_get_table(
  sqlite3 *db,              /* Database handle */
  const char *Sql,         /* SQL query to be executed */
  char ***Result,        /* Pointer to array of result rows */
  int *nRow,               /* Number of result rows written here */
  int *nColumn,            /* Number of result columns written here */
  char **Errmsg           /* Error msg written here */
);

这个函数会执行 SQL 语句并将结果存储在一个二维字符数组中,然后返回指向这个数组的指针。它在内部会使用malloc来分配足够的内存来容纳查询结果。由于sqlite3_get_table使用了malloc,所以在使用完查询结果后,必须调用sqlite3_free_table () 来释放由sqlite3_get_table 分配的内存(即需要释放掉result结果),以避免内存泄漏问题。这个函数会释放整个结果数组及其内部的字符串数组所分配的内存。除此之外,如果在调用 sqlite3_get_table函数时出现了错误(比如语法错误、查询失败等),则 Errmsg指向的内存将被写入错误信息。在处理完错误后,调用者需要调用sqlite3_free释放Errmsg 指向的内存,以避免内存泄漏。

2、getaddrinfo()释放

int getaddrinfo(const char *node, const char *service,
                	const struct addrinfo *hints, struct addrinfo **res);

函数是用于执行主机名到 IP 地址以及服务名到端口号的转换的函数,getaddrinfo 函数会返回一个 addrinfo 结构体链表,因为是使用链表存储,每个节点表示一个可能的主机名和服务名对应的 IP 地址和端口号。这些 addrinfo 结构体的内存是在 getaddrinfo 函数内部使用 malloc 动态分配的。因此,在使用 getaddrinfo 函数时,需要注意在使用完返回的结果后,调用 freeaddrinfo 函数来释放动态分配的内存,避免内存泄漏。

3,else省略导致的内存泄漏

#根据以下信息,我们可以了解到内存泄漏大抵是发生在sqlite3_open_database_table函数中,又检查了自己的主函数后发现已经free过db了在程序结尾处,所以我们基本确定是函数内部的问题。在进行更改。
==9359== 62,320 (520 direct, 61,800 indirect) bytes in 1 blocks are definitely lost in loss record 116 of 117 
==9359==    at 0x48483D0: malloc (vg_replace_malloc.c:262)
==9359==    by 0x48CC3B3: sqlite3MemMalloc (sqlite3.c:23980)
==9359==    by 0x48B380F: mallocWithAlarm (sqlite3.c:27862)
==9359==    by 0x48B3917: sqlite3Malloc (sqlite3.c:27892)
==9359==    by 0x49925AB: openDatabase (sqlite3.c:28134)
==9359==    by 0x48734C7: sqlite3_open_database_table (in /home/。。。/。。。/lib/libmylib.so)
==9359== 
//改前:
==27756== LEAK SUMMARY:
==27756==    definitely lost: 520 bytes in 1 blocks
==27756==    indirectly lost: 61,800 bytes in 173 blocks
==27756==    possibly lost: 152,896 bytes in 204 blocks
==27756==    still reachable: 312 bytes in 1 blocks
==27756==    suppressed: 0 bytes in 0 blocks

//更改之前的代码
int sqlite3_open_database_table(const char *db_name)
{   
    int                         rv = -1; 
    char                        sql_str[SQL_COMMAND_LEN] = {0};
    char                        *err_msg = NULL;

    if ( db_name == NULL )
    {   
        log_error("%s() Invalid input arguments\n", __func__);
        return -1; 
    }   
    
    //if database has exist, just open it 
    if ( 0 == access(db_name, F_OK) )
    {   
        if ( SQLITE_OK != sqlite3_open(db_name, &db) )
        {   
            log_error("open file %s error\n", db_name);
            return -1; 
        }   
        else
        {   
            log_info("open database file '%s' success\n", db_name);
        }   
    }   
  
    //注意,我们这里省略了else,导致这里这里无论如何都会被执行,也就是又多malloc了一个db,
    //正是这个原因,导致了内存泄漏。如果一定要这么用,需要free两次。
    if (  sqlite3_open(db_name, &db) != SQLITE_OK )
    {   
        log_error("create open file %s error\n", db_name);
        return -1; 
    }   
    
    //database has open,create table and open it
    snprintf(sql_str, SQL_COMMAND_LEN,
            "CREATE TABLE IF NOT EXISTS %s(dev_name text,meas_time text,temp real);", TABLE_NAME);
    
    if ( sqlite3_exec(db, sql_str, 0, 0, &err_msg) != SQLITE_OK )
    {   
        log_error("create table: %s failure:%s\n", TABLE_NAME, err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        unlink(db_name);
        return -1; 
    }   
    
    sqlite3_free(err_msg);

    return 0;
}


//改后:
==5477== LEAK SUMMARY:
==5477==    definitely lost: 0 bytes in 0 blocks
==5477==    indirectly lost: 0 bytes in 0 blocks
==5477==    possibly lost: 152,896 bytes in 204 blocks
==5477==    still reachable: 360 bytes in 2 blocks
==5477==    suppressed: 0 bytes in 0 blocks

//更改后的代码
int sqlite3_open_database_table(const char *db_name)
{   
    int                         rv = -1; 
    char                        sql_str[SQL_COMMAND_LEN] = {0};
    char                        *err_msg = NULL;

    if ( db_name == NULL )
    {   
        log_error("%s() Invalid input arguments\n", __func__);
        return -1; 
    }   
    
    //if database has exist, just open it 
    if ( 0 == access(db_name, F_OK) )
    {   
        if ( SQLITE_OK != sqlite3_open(db_name, &db) )
        {   
            log_error("open file %s error\n", db_name);
            return -1; 
        }   
        else
        {   
            log_info("open database file '%s' success\n", db_name);
        }   
    }   
    else//正常的判断,加入else后,sqlite3——open只执行一个,所以只释放一次即可。
    {   
        if (  sqlite3_open(db_name, &db) != SQLITE_OK )
        {   
            log_error("create open file %s error\n", db_name);
            return -1; 
        }   
    
        //database has open,create table and open it
        snprintf(sql_str, SQL_COMMAND_LEN,
                "CREATE TABLE IF NOT EXISTS %s(dev_name text,meas_time text,temp real);", TABLE_NAME);
    }   
    
    if ( sqlite3_exec(db, sql_str, 0, 0, &err_msg) != SQLITE_OK )
    {   
        log_error("create table: %s failure:%s\n", TABLE_NAME, err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        unlink(db_name);
        return -1; 
    }   

    sqlite3_free(err_msg);

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

配置IPSSL证书需要几步

在互联网的世界中,数据安全和用户信任成为了网站管理员和公司所追求的关键因素。为了保护网站的数据安全并提高用户的信任度,HTTPS加密通信已经成为了一种必要手段。而配置IPSSL证书则是实现HTTPS加密通信的重要步骤之一。 让我们来了解一下什么是IPSSL…

Linux入门攻坚——20、systemd、(sysvinit、upstart重温)

再一次讲到Linux系统启动流程: POST --> Boot Sequence --> Bootloader(grub) --> kernel initramfs(initrd) --> rootfs --> /sbin/init 对于init,即系统内核加载完毕后(加载kernel和切换根文件系统)运行…

是德KEYSIGHT E4980A 20Hz至2MHz精密LCR表

是德KEYSIGHT E4980A 20Hz至2MHz精密LCR表 Keysight(原Agilent) E4980A精密LCR表在20Hz至20MHz范围内提供最高的精度及重复性测量,支持LAN、USB及GPIB计算机连接。E4980A不仅为低抗阻情况下提供快速测量及卓越的性能,在高抗阻情况下亦如此, …

Go 到底是哪里没有做好,我请问呢?

没有引导好并发理念 从历史背景来看,在 Go 诞生的那个年代,并发编程是一个比较新颖的理念。许多其他编程语言、论文甚至书籍都写过关于并发编程的内容。并发编程还没有成为主流思想。 Go 团队发明了 “goroutine” 这个词,Go 让协程的使用变…

掌握未来通信技术:5G核心网基础入门

🔥个人主页:Quitecoder 🔥专栏:5GC笔记仓 朋友们大家好,本篇文章是我们新内容的开始,我们本篇进入5GC的学习,希望大家多多支持! 目录 一.核心网的演进2G核心网2.5G核心网3G核心网4G…

考到HCIP工资一般多少? 有IP证书的进来自测下!

在IT行业,华为认证网络工程师(HCIP)作为中级认证,属于网络工程师们都会考虑的证书。 它不仅代表了个人在网络技术方面的专业水平,还可能影响到你的薪资水平。 那么,考取HCIP认证后的工资一般是多少&#…

12 JavaScript学习: 字符串

JavaScript 字符串 JavaScript 字符串是一种用于存储和操作文本数据的数据类型。字符串可以包含字母、数字、符号和空格等字符。在 JavaScript 中,字符串可以使用单引号()或双引号(")来定义。 例如:…

栈和队列的概念、表示和实现

文章目录 前言一、栈1.定义和特点2.栈的抽象类型定义3.顺序栈的表示4. 顺序栈基本操作的实现 二、队列1.定义和特点2.队列的抽象数据类型定义3.队列的顺序表示4.循环顺序队列5. 循环顺序队列基本操作的实现 总结 前言 T_T此专栏用于记录数据结构及算法的(痛苦&#…

CSS常用属性之(列表、表格、鼠标)属性,(如果想知道CSS的列表、表格、鼠标相关的属性知识点,那么只看这一篇就足够了!)

前言:在学习CSS的时候,必不可少的就要学习选择器和常见的属性,而本篇文章讲解的是CSS中的列表、表格、背景、鼠标属性。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 大致了解一下本篇文章…

协议的定制之序列化与反序列化 | 守护进程

目录 一、再谈协议 二、序列化与反序列化 三、网络计算器的简单实现 四、网络计算器完整代码 五、代码改进 六、守护进程 七、Json序列化与反序列化 八、netstat 一、再谈协议 是对数据格式和计算机之间交换数据时必须遵守的规则的正式描述。简单的说了,网络…

SpringCloud 之 服务消费者

前提 便于理解请我修改了本地域名》这里!!! 127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com 127.0.0.1 eureka7003.comRest学习实例之消费者 创建一个消费者去消费 消费者模块展示 1、导入依赖 <!-- 实体类api自己创建的模块 Web 部分依赖展示--><de…

jmeter之跨线程关联

1&#xff09;_setproperty函数&#xff1a;将值保存成jmeter属性 2&#xff09;_property函数&#xff1a;在其他线程组中使用property函数读取属性 一、跨线程接口引用变量 1. 法一&#xff1a;jmeter自带函数_setProperty和_property 1. 1线程组 01 创建登录的【HTTP请求】…

OriginPro作图之箱线图

前言 箱线图(Box-plot) 又称为盒须图、盒式图或箱线图&#xff0c;是一种用作显示一组数据分散情况资料的统计图。因型状如箱子而得名。 本文将结合实例阐述其意义和绘图过程。 箱线图简介 箱线图(Boxplot) 也称箱须图( Box-whisker Plot)&#xff0c;是利用数据中的五个统计量…

FANUC机器人socket通讯硬件配置

一、添加机器人选配包 Fanuc机器人要进行socket通讯&#xff0c;需要有机器人通讯的选配包&#xff0c;1A05B-2600-R648 User Socket Msg&#xff0c;1A05B-2600-R632 KAREL&#xff0c;1A05B-2600-R566 KAREL Diagnostic&#xff0c;1A05B-2600-J971 KAREL Use Sprt FCTN。 二…

有没有一本从电路开始讲然后汇编再到C语言的书?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 没有一本书或者几本书能把这整…

【NPU】A800-9000服务器8*Ascend 910 B的HCCS测试

HCCS集合通信带宽数据 HCCS集合通信带宽数据timeline信息在msprof_*.json文件的HCCS层级展示 summary信息在hccs_*.csv文件汇总。 支持的型号 Atlas 训练系列产品 Atlas A2训练系列产品 测试命令 npu-smi info -t topo结果展示 NPU0 NPU1 NPU2 NPU3 …

微信小程序实现美食检索功能

1、打开浏览器搜索&#xff1a;腾讯位置服务 2、注册一个账号&#xff0c;有账号的直接登陆就行 3、注册登陆成功后&#xff0c;点击控制台 4、进入控制台后点击我的应用——>创建应用 5、添加key,注意看注释 6、key添加成功后&#xff0c;开始分配额度&#xff08;配额&…

人工智能(AI)与地理信息技术(GIS)的融合:开启智能地理信息时代

随着科技的不断发展&#xff0c;人工智能&#xff08;AI&#xff09;和地理信息技术&#xff08;GIS&#xff09;的应用越来越广泛&#xff0c;两者的结合更是为许多行业带来了前所未有的变革。本文将以“人工智能&#xff08;AI&#xff09;地理信息技术&#xff08;GIS&#…

【C++】5.C语言/C++内存管理

一、C/C内存分布 栈中存储的是局部变量&#xff0c;函数参数&#xff0c;返回值等 堆主要用于动态内存分配 数据段用以存储全局数据和静态数据 代码段存储可执行代码和常量 二、C语言和C中的内存管理方式 在C语言中&#xff0c;我们使用 malloc、calloc、realloc、free来进…

理解与使用Linux设备树编译器(DTC)

这里写目录标题 设备树简介设备树编译器&#xff08;DTC&#xff09;安装DTC使用DTC实例&#xff1a;编辑设备树小结参考资料 Linux设备树编译器&#xff08;DTC&#xff09;是一个关键工具&#xff0c;用于处理嵌入式Linux系统中的设备树文件。本文将介绍设备树的概念、DTC的基…