当前位置: 首页 > 编程日记 > 正文

内存问题分析的利器——valgrind的memcheck

在《内存、性能问题分析的利器——valgrind》一文中我们简单介绍了下valgrind工具集,本文将使用memcheck工具分析各种内存问题。(转载请指明出于breaksoftware的csdn博客)

本文所有的代码都是使用g++ -O0 -g mem_error.c -o mem_erro编译;分析都是使用valgrind --tool=memcheck ./mem_error指定(除非特殊说明)。

写违例

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

上述代码只分配了4个int型大小的空间,但是第6行要往该空间之后的空间写入数据,这就造成了写违例。使用valgrind分析会显示

==18100== Invalid write of size 4
==18100==    at 0x400658: main (mem_error.c:6)
==18100==  Address 0x51e0050 is 0 bytes after a block of size 16 alloc'd
==18100==    at 0x4C27BC3: malloc (vg_replace_malloc.c:299)
==18100==    by 0x40063F: main (mem_error.c:5)

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

读违例

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

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

==31461== Invalid read of size 4
==31461==    at 0x400658: main (mem_error.c:6)
==31461==  Address 0x51e0050 is 0 bytes after a block of size 16 alloc'd
==31461==    at 0x4C27BC3: malloc (vg_replace_malloc.c:299)
==31461==    by 0x40063F: main (mem_error.c:5)

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

使用未初始化变量

这是初学C/C++编程的人非常容易犯的错误。

#include <stdlib.h>
#include <stdio.h>int main() {const int array_count = 4;int* p = 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分析显示

==24104== Conditional jump or move depends on uninitialised value(s)
==24104==    at 0x4E79F7F: vfprintf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==24104==    by 0x4E837A8: printf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==24104==    by 0x4006BA: main (mem_error.c:7)
==24104== 
==24104== Conditional jump or move depends on uninitialised value(s)
==24104==    at 0x4E79E37: vfprintf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==24104==    by 0x4E837A8: printf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==24104==    by 0x4006DA: main (mem_error.c:11)
==24104== 

虽然这个报告已经非常详细,但是我们还可以给valgrind增加--track-origins=yes,以打印问题出现在哪个结构上。当然这也会导致valgrind分析的比较慢

==29911== Conditional jump or move depends on uninitialised value(s)
==29911==    at 0x4E79F7F: vfprintf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==29911==    by 0x4E837A8: printf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==29911==    by 0x4006BA: main (mem_error.c:7)
==29911==  Uninitialised value was created by a heap allocation
==29911==    at 0x4C27BC3: malloc (vg_replace_malloc.c:299)
==29911==    by 0x40068F: main (mem_error.c:6)
==29911== 
==29911== Conditional jump or move depends on uninitialised value(s)
==29911==    at 0x4E79E37: vfprintf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==29911==    by 0x4E837A8: printf (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==29911==    by 0x4006DA: main (mem_error.c:11)
==29911==  Uninitialised value was created by a stack allocation
==29911==    at 0x400670: main (mem_error.c:4)

在系统函数中使用未初始化变量

我们看一个稍微复杂点的例子。下例中,test函数操作的是一个未初始化的变量,所以其结果是不可预知的。

#include <stdlib.h>
#include <stdio.h>void test(int n) {n = n + 1;
}int main() {const int array_count = 4;int* p = malloc(array_count * sizeof(int));test(p[array_count - 1]);free(p);return 0;
}

valgrind并不知道上述代码的作者想表达什么,所以它并没有报错

==28259== Command: ./mem_error
==28259== 
==28259== 
==28259== HEAP SUMMARY:
==28259==     in use at exit: 0 bytes in 0 blocks
==28259==   total heap usage: 1 allocs, 1 frees, 16 bytes allocated
==28259== 
==28259== All heap blocks were freed -- no leaks are possible
==28259== 
==28259== For counts of detected and suppressed errors, rerun with: -v
==28259== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

但是如果错误调用是针对系统函数。valgrind是知道系统函数的输入要求的,于是就可以判定这种行为违例。我们稍微改下代码

#include <stdlib.h>
#include <stdio.h>void test(int n) {n = n + 1;write(stdout, "xxx", n); 
}int main() {const int array_count = 4;int* p = malloc(array_count * sizeof(int));test(p[array_count - 1]);free(p);return 0;
}

valgrind就会分析出第6行系统方法write的第三个参数未初始化。

==4344== Syscall param write(count) contains uninitialised byte(s)
==4344==    at 0x4F0BED0: __write_nocancel (in /home/opt/gcc-4.8.2.bpkg-r4/gcc-4.8.2.bpkg-r4/lib64/libc-2.18.so)
==4344==    by 0x4006CA: test (mem_error.c:6)
==4344==    by 0x40070D: main (mem_error.c:12)

释放空间出错

我们可能重复释放同一段空间,或者给释放函数传入不是堆上的地址,或者使用了不对称的方法申请释放函数。这类错误发生在free,delete,delete[]和realloc上。

反复free同一段空间

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

使用valgrind分析,报告显示第7行释放了第6行已经释放了的空间,这个空间是在第5行申请的。

==6537== Invalid free() / delete / delete[] / realloc()
==6537==    at 0x4C28CBD: free (vg_replace_malloc.c:530)
==6537==    by 0x40065B: main (mem_error.c:7)
==6537==  Address 0x51e0040 is 0 bytes inside a block of size 16 free'd
==6537==    at 0x4C28CBD: free (vg_replace_malloc.c:530)
==6537==    by 0x40064F: main (mem_error.c:6)
==6537==  Block was alloc'd at
==6537==    at 0x4C27BC3: malloc (vg_replace_malloc.c:299)
==6537==    by 0x40063F: main (mem_error.c:5)

释放一个不是堆的空间

#include <stdlib.h>int main() {int n = 1;int* p = &n;free(p);return 0;
}

valgrind会报告错误的释放栈上空间

==32411== Invalid free() / delete / delete[] / realloc()
==32411==    at 0x4C28CBD: free (vg_replace_malloc.c:530)
==32411==    by 0x4005F2: main (mem_error.c:6)
==32411==  Address 0x1fff000234 is on thread 1's stack
==32411==  in frame #1, created by main (mem_error.c:3)

申请释放方法不对称

对称的方法是指:

  • new使用delete释放
  • new[]使用delete[]释放
  • alloc类函数,如malloc,realloc等使用free释放
#include <stdlib.h>int main() {int* p = new int(1);free(p);return 0;
}

valgrind可以分析出这种不对称调用——new申请空间,free释放空间。

==5666== Mismatched free() / delete / delete []
==5666==    at 0x4C28CBD: free (vg_replace_malloc.c:530)
==5666==    by 0x400737: main (mem_error.c:5)
==5666==  Address 0x59fc040 is 0 bytes inside a block of size 4 alloc'd
==5666==    at 0x4C281E3: operator new(unsigned long) (vg_replace_malloc.c:334)
==5666==    by 0x400721: main (mem_error.c:4)

空间覆盖

当我们操作内存时,可能会发生内存覆盖。

#include <stdlib.h>
#include <string.h>                                                                                                                                                        int main() {const int array_size = 8;char p[array_size] = {0};memcpy(p + 1, p, sizeof(char) * array_size);return 0;
}

这段代码的目的空间覆盖了源空间

valgrind分析的报告也说明了这个错误

==25991== Source and destination overlap in memcpy(0x1fff000231, 0x1fff000230, 8)
==25991==    at 0x4C2BFEC: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1022)
==25991==    by 0x4006E2: main (mem_error.c:7)

可疑的参数

在C/C++中,有符号数的负数的二进制最高位是0x1。如果把一个负数看成一个无符号类型的数,则可以表达出极大数,比如0xFFFFFFFF(无符号值4294967295,有符号值-1),因为它们的底层二进制值是一致的。

有事我们在调用内存分配时,不小心将空间大小设置为一个负数,就要求申请一个极大的空间,这明显是有问题的。

#include <stdlib.h>int main() {const int array_size = -1;void* p = malloc(array_size);free(p);return 0;
}

这个时候valgrind就会检测出参数可疑

==3364== Argument 'size' of function malloc has a fishy (possibly negative) value: -1
==3364==    at 0x4C27BC3: malloc (vg_replace_malloc.c:299)
==3364==    by 0x40070A: main (mem_error.c:5)

内存泄露

内存泄露是比较常见的问题,往往也是非常难以排查的问题。

#include <stdlib.h>int main() {const int array_size = 32; void* p = malloc(array_size);return 0;
}

这次我们给valgrind增加选项--leak-check=full以显示出详细信息

valgrind --tool=memcheck --leak-check=full ./mem_error

valgrind分析出第5行分配的空间没有释放。其中definitely lost是指“确认泄露”,

==17393== HEAP SUMMARY:
==17393==     in use at exit: 32 bytes in 1 blocks
==17393==   total heap usage: 1 allocs, 0 frees, 32 bytes allocated
==17393== 
==17393== 32 bytes in 1 blocks are definitely lost in loss record 1 of 1
==17393==    at 0x4C27BC3: malloc (vg_replace_malloc.c:299)
==17393==    by 0x4006B8: main (mem_error.c:5)
==17393== 
==17393== LEAK SUMMARY:
==17393==    definitely lost: 32 bytes in 1 blocks
==17393==    indirectly lost: 0 bytes in 0 blocks
==17393==      possibly lost: 0 bytes in 0 blocks
==17393==    still reachable: 0 bytes in 0 blocks
==17393==         suppressed: 0 bytes in 0 blocks

相关文章:

类项目中的配置文件app.config在打包安装后的信息获取的问题

在一个项目中碰到这样的一个问题&#xff0c;做一个WORD插件&#xff0c;功能在类库项目中实现了&#xff0c;配置信息存在类库项目的配置文件app.config中&#xff0c;在进行打包后&#xff0c;获取的配置文件中的DocType节点信息时&#xff0c;使用以下方法 ConfigurationMa…

AAAI 2020论文解读:商汤科技提出新弱监督目标检测框架

来源 | Object Instance Mining for WeaklySupervised Object Detection编辑 | Carol出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;商汤科技视频大数据团队发表论文《Object Instance Mining forWeakly Supervised Object Detection》&#xff0c;该论文…

20135306黄韧 信息安全系统设计基础期中学习总结

信息安全系统设计基础第八周学习总结 知识点总结 第1章 计算机系统漫游 △计算机系统是由硬件和系统软件组成的&#xff0c;它们共同协作以运行应用程序。计算机内部的信息被表示为一组组的位&#xff0e;它们依据上下文有不同的解释方式。程序被其他程序翻译成不同的形式&…

使用SQL Server维护计划实现数据库定时自动备份

在SQL Server中出于数据安全的考虑&#xff0c;所以需要定期的备份数据库。而备份数据库一般又是在凌晨时间基本没有数据库操作的时候进行&#xff0c;所以我们不可能要求管理员每天守到晚上1点去备份数据库。要实现数据库的定时自动备份&#xff0c;最常用的方式就是使用SQL S…

AI 医疗公司“战疫”在前线

作者 | Just来源 | CSDN&#xff08;CSDNnews&#xff09;紧急驰援疫区&#xff0c;涉足AI医疗领域的公司也出动了。截止到2月6日&#xff0c;随着新冠病毒肺炎疫情的不断发展&#xff0c;全国累计已有31161例确诊病例&#xff0c;26359例疑似病例。不过&#xff0c;由于医疗资…

动态执行流程分析和性能瓶颈分析的利器——valgrind的callgrind

在《内存、性能问题分析的利器——valgrind》一文中我们简单介绍了下valgrind工具集&#xff0c;本文将使用callgrind工具进行动态执行流程分析和性能瓶颈分析。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 之前的《利器》系列中&#xff0c;我们介绍了两种…

CentOS6.3编译安装Nginx1.4.7 + MySQL5.5.25a + PHP5.3.28

2019独角兽企业重金招聘Python工程师标准>>> 【准备工作】 01 #在编译安装lnmp之前&#xff0c;首先先卸载已存在的rpm包。 02 rpm -e httpd 03 rpm -e mysql 04 rpm -e php 05 06 yum -y remove httpd 07 yum -y remove mysql-server mysql 08 yum -y remove php 0…

GitHub标星14000+,阿里开源的SEATA如何应用到极致?

作者简介&#xff1a;袁鸣凯&#xff0c;家乐福技术总监&#xff0c; 高知特有限技术公司中国区架构师&#xff0c;HP上海研发技术专家&#xff0c;夸客金融首席架构师&#xff0c;现任家乐福中国区技术总监。多年互联网、企业级SOA、微服务、全渠道中台方面的架构设计实战经验…

C++拾趣——有趣的操作符重载

操作符重载是C语言中一个非常有用的特性。它可以让我们比较优雅的简化代码&#xff0c;从而更加方便的编写逻辑。 为什么要使用操作符重载一种常见的用法是重载<<运算符&#xff0c;让标准输出可以输出自定义的类型。比如 #include <iostream>class Sample {friend…

urlparse模块(专门用来解析URL格式)

# -*- coding: utf-8 -*- #python 27 #xiaodeng #urlparse模块&#xff08;专门用来解析URL格式&#xff09;#URL格式&#xff1a; #protocol ://hostname[:port] / path / [;parameters][?query]#fragment #parameters&#xff1a;特殊参数&#xff0c;一般用的很少。#1、url…

使用Boost的Serialization库序列化STL标准容器

使用Boost做对象序列化是非常方便的&#xff0c;本文将介绍一种序列化STL标准容器的方法。这是之前设计的异步框架的一个子功能&#xff1a;过程A将标准容器数据序列化成二进制流&#xff0c;然后将该二进制数据发送到过程B&#xff0c;过程B将数据反序列化为标准容器。&#x…

连登GitHub TOP榜,中国开发者在行动!

作者 | 唐小引数据 | 于瑞洋出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;中国开发者正在走向世界中文开源项目正在不断登上 GitHub TOP 榜不久前&#xff0c;一个名叫「wuhan2020」的开源项目进入了 GitHub Trending TOP 榜&#xff0c;截至到现在&#xff0c;已经…

Merge into的使用

用途 merge 命令可以用来用一个表中的数据来修改或者插入到另一个表。插入或者修改的操作取决于on子句的条件。该语句可以在同一语句中执行两步操作&#xff0c;可以减少执行多条insert 和update语句。merge是一个确定性的语句&#xff0c;即不会在同一条merge语句中去对同一条…

PHP和MySQL Web开发从新手到高手,第8天-创建categories管理页面

1. 创建categories管理页面 主要包含以下几个页面: A. index.php, 准备各种变量数据.供展示页面使用. B. categories.html.php, 显示categories. C. form.html.php, 用于编缉或添加作者的页面. 页面郊果: 2. categories页面的主要流程 2.1 是否已登录 if (!user_is_login()){in…

堆状态分析的利器——valgrind的DHAT

在《堆问题分析的利器——valgrind的massif》一文中&#xff0c;我们介绍了如何使用massif查看和分析堆分配/释放的问题。但是除了申请和释放&#xff0c;堆空间还有其他问题&#xff0c;比如堆空间的使用率、使用周期等。通过分析这些问题&#xff0c;我们可以对程序代码进行优…

cisco2950交换机密码恢复

在实际工作中可能会忘记交换机密码&#xff0c;导致不能对交换机进行配置的情况。cisco提供了密码恢复的方法。以下是关于交换机密码恢复说明&#xff1a;如果忘记密码&#xff0c;这时我们如果要配置交换机就必须在启动时绕过config.text的配置【密码保存在config.text中】然后…

程序员SQL都不会?能干啥?资深研发:别再瞎努力了!

国外有人曾做过调查显示&#xff1a;“SQL的使用人数仅次于JavaScript”。更有统计&#xff0c;世界上一流的互联网公司中&#xff0c;排名前 20 的有 80% 都是 MySQL 的忠实用户。为什么这项技术仍有这么多人在用&#xff1f;又为什么值得我们学习&#xff1f;1、无论你是前端…

OC管理文件方法

1、常见的NSFileManager文件方法 -(NSData *)contentsAtPath:path  //从一个文件读取数据 -(BOOL)createFileAtPath: path contents:(NSData *)data attributes:attr  //向一个文件写入数据 -(BOOL)removeItemAtPath:path error:err  //删除一个文件 -(BOOL)moveItemAtPa…

堆状态分析的利器——gperftools的Heap Profiler

在《内存泄漏分析的利器——gperftools的Heap Checker》一文中&#xff0c;我们介绍了如何使用gperftools分析内存泄漏。本文将介绍其另一个强大的工具——Heap Profiler去分析堆的变化过程。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 我们使用类似于《堆…

亲戚称呼关系表

直系血亲父系曾曾祖父--曾祖父--祖父--父亲曾曾祖母--曾祖母--祖母--父亲母系曾曾外祖父--曾外祖父--外祖父--母亲曾曾外祖母--曾外祖母--外祖母--母亲儿子&#xff1a;夫妻间男性的第一子代。女儿&#xff1a;夫妻间女性的第一子代。孙&#xff1a;夫妻间的第二子代&#xff0…

技术驰援抗疫一线, Python 线上峰会免费学!

截至截止2月9号24时&#xff0c;新型冠状病毒在全国已确诊42714例&#xff0c;疑似病例已达21675例。而专家所说的“拐点”始终未至&#xff0c;受疫的影响&#xff0c;各大公司开启远程办公模式&#xff0c;将返回工作场所办公的时间一延再延。在抗疫前线&#xff0c;中国医疗…

ZeroMq实现跨线程通信

ZeroMq实现跨线程通信 之前在技术崇拜的技术经理指导下阅读了ZeroMq的基础代码&#xff0c;现在就将阅读的心得与成果记录一下&#xff0c;并重新模仿实现了一下经理的异步队列。 1、对外接口 //主要接口&#xff08;1&#xff09;void *ymq_attach (void *ctx_, int oid, voi…

动态执行流程分析和性能瓶颈分析的利器——gperftools的Cpu Profiler

在《动态执行流程分析和性能瓶颈分析的利器——valgrind的callgrind》中&#xff0c;我们领略了valgrind对流程和性能瓶颈分析的强大能力。本文将介绍拥有相似能力的gperftools的Cpu Profiler。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 我们依然以callg…

C语言内存管理内幕(二)----半自动内存管理策略

2019独角兽企业重金招聘Python工程师标准>>> C语言内存管理内幕(二&#xff09;----半自动内存管理策略 转载于:https://my.oschina.net/hengcai001/blog/466

无人机巡逻喊话、疫情排查、送药消毒,抗疫战中机器人化身钢铁战士!

整理 | 夕颜责编 | Carol出品 | CSDN&#xff08;ID:CSDNnews&#xff09;这场抗疫战争似乎格外漫长&#xff0c;但回头细数一下才发现&#xff0c;自疫情爆发以来&#xff0c;也不过半月之久。在接下来的几个半月中&#xff0c;抗疫战仍将继续&#xff0c;各方力量也要绷紧神经…

jQuery EasyUI 表单插件 - Datebox 日期框

为什么80%的码农都做不了架构师&#xff1f;>>> 扩展自 $.fn.combo.defaults。通过 $.fn.datebox.defaults 重写默认的 defaults。 日期框&#xff08;datebox&#xff09;把可编辑的文本框和下拉日历面板结合起来&#xff0c;用户可以从下拉日历面板中选择日期。在…

互斥量、读写锁长占时分析的利器——valgrind的DRD

在进行多线程编程时&#xff0c;我们可能会存在同时操作&#xff08;读、写&#xff09;同一份内存的可能性。为了保证数据的正确性&#xff0c;我们往往会使用互斥量、读写锁等同步方法。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 互斥量的用法如下 pth…

一次性同步修改多台linux服务器的密码

如何一次性修改多台linux服务器的密码&#xff0c;这是个问题&#xff0c;我给大家提供一个脚本&#xff0c;是前一段我刚刚写的&#xff0c;希望能对大家有所帮助一 , 需求:linux环境下运行&#xff0c;需要tcl和expect支持原理说明&#xff1a;利用expect的摸拟交互的功能&…

麻省理工学院的新系统TextFooler, 可以欺骗Google的自然语言处理系统及Google Home的音频...

来源 | news.mit编译 | 武明利责编 | Carol出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;两年前&#xff0c;Google的AI还不太成熟。一段时间以来&#xff0c;有一部分计算机科学研究一直致力于更好地理解机器学习模型如何处理这些“对抗性”攻击&#xff0c;这些攻…

Oracle VS DB2 数据类型

Oracle VS DB2 本文转自&#xff1a;http://www.bitscn.com/oracle/install/200604/16541.html首先&#xff0c;通过下表介绍ORACLE与DB2/400数据类型之间的对应关系&#xff0c;是一对多的关系&#xff0c;具体采用哪种对应关系&#xff0c;应具体问题具体分析。 OracleDB2/40…