内存泄漏定位工具

1、原理

在编写复杂代码的时候,有时一大意就会忘了释放申请的内存;或是调试前人代码时,发现有内存泄漏,这些情况排查起来相当麻烦。这里基于RT-Thread写了一个内存泄漏定位工具(实际和RTT无关,什么系统都可以用,要适当修改),原理非常简单:申请内存时,记录申请的内存地址、大小,以及申请内存这行代码所在的文件名和行号,当释放内存时,根据内存地址找到之前的记录并删除,最后留下的记录就极有可能是发生内存的代码(当然有些内存是常驻的,需要使用者自己辨别)。
linux下有mtrace库,它的原理也是类似,利用钩子函数(hook)在内存申请的地方插入一段代码,这段代码记录了内存申请的信息,包含了申请时的PC指针等,在回溯时可以根据这些地址反推出申请的代码位置;而本文的实现则是靠__func__和__LINE__宏直接记录调用的位置信息,虽然方便,但也有一定的局限。

2、使用方法

demo程序如下,使用时,需要做两件事:

  1. 将原先使用的内存申请和释放接口分别复制给MEM_USR_MALLOC和MEM_USR_FREE,demo里使用的是rt_malloc,根据情况而定,并包含头文件memtrace.h。
  2. 将要待排查的内存接口换成MEM_TRACE_MALLOC和MEM_TRACE_FREE。
#include <rtthread.h>#define MEM_USR_MALLOC  rt_malloc
#define MEM_USR_FREE    rt_free
#include "memtrace.h"int main(void)
{void *p = RT_NULL;p = MEM_TRACE_MALLOC(1024);MEM_TRACE_FREE(p);p = MEM_TRACE_MALLOC(568);rt_thread_mdelay(100);p = MEM_TRACE_MALLOC(8);return 0;
}

demo里申请了三段内存,第一段内存及时释放了,第二、三段内存申请后没有释放,造成了内存泄漏。此时用memtrace命令可以看到泄漏的位置:

msh />memtrace--------------------Memory Trace Info-------------------------file          line            addr            size  main.c           14         0x80d264a8         568main.c           18         0x80b6d158         8

3、源码

memtrace.c,这里有两个可调的变量MEM_TRACE_MAX和MEM_TRACE_FILE_NAME_LEN,MEM_TRACE_MAX是最大可记录的内存记录数目,默认值100,记录所用的空间是编译时预先分配的静态内存,所以这个组件只适合出问题的时候调试用。申请内存时会记录代码所在的文件名,MEM_TRACE_FILE_NAME_LEN就是文件名的最大长度,最终记录的只有最后级的文件名,不包含路径,所以16字节应该是够用的。

#include "rtthread.h"
#include "memtrace.h"#define MEM_TRACE_MAX               100
#define MEM_TRACE_FILE_NAME_LEN     16#ifndef RT_USING_MUTEX
#error "please define RT_USING_MUTEX"
#endiftypedef struct
{rt_list_t           node;char                file[MEM_TRACE_FILE_NAME_LEN];rt_uint16_t         line;rt_uint8_t          res[2];rt_uint32_t         size;void                *addr;
}mem_record_t;typedef struct
{rt_uint32_t         init;rt_uint32_t         used;rt_uint32_t         total;struct rt_mutex     lock;
}mem_info_t;static mem_info_t mem_info;
static mem_record_t mem_node[MEM_TRACE_MAX];
static rt_list_t free_list;
static rt_list_t used_list;rt_err_t mem_trace_add(char *file, rt_uint16_t line, rt_uint32_t size, void *addr)
{mem_record_t *record = RT_NULL;rt_uint32_t index = 0, len = 0;rt_mutex_take(&mem_info.lock, RT_WAITING_FOREVER);if(rt_list_isempty(&free_list)){rt_kprintf("please increase MEM_TRACE_MAX\n");rt_mutex_release(&mem_info.lock);return -RT_EEMPTY;}record = (mem_record_t*)free_list.next;rt_list_remove((rt_list_t*)record);/* find last '/' */len = rt_strlen(file);for(index = len - 1; index != 0; index--){if(file[index] == '/'){index++;break;}}rt_strncpy(record->file, &file[index], len - index);record->line = line;record->size = size;record->addr = addr;rt_list_insert_before(&used_list, (rt_list_t*)record);rt_mutex_release(&mem_info.lock);return RT_EOK;
}rt_err_t mem_trace_del(void *addr)
{rt_list_t *list_node = RT_NULL;mem_record_t *record = RT_NULL;int find = 0;if(rt_list_isempty(&used_list)){rt_kprintf("no memory in use!\n");return -RT_EEMPTY;}rt_mutex_take(&mem_info.lock, RT_WAITING_FOREVER); rt_list_for_each(list_node, (&used_list)){record = (mem_record_t*)list_node;if(record->addr == addr){find = 1;break;} }if(find){rt_list_remove(&(record->node));rt_list_insert_before(&free_list, &(record->node));}else{rt_kprintf("mem_trace_del err,can't find addr:0x%p\n", addr);}rt_mutex_release(&mem_info.lock);return find ? RT_EOK : -RT_ERROR;
}static int cmd_print_memtrace(int argc, char **argv)
{   rt_list_t *list_node = RT_NULL;mem_record_t *record = RT_NULL;if(rt_list_isempty(&used_list)){rt_kprintf("no memory in use!\n");return 0;}rt_kprintf("   --------------------Memory Trace Info-------------------------\n");rt_kprintf("           file          line            addr            size  \n");rt_mutex_take(&mem_info.lock, RT_WAITING_FOREVER);rt_list_for_each(list_node, (&used_list)){record = (mem_record_t*)list_node;rt_kprintf("%16.16s        %5d         0x%08p         %d\n", record->file, record->line, record->addr, record->size);}rt_mutex_release(&mem_info.lock);return 0;
}
MSH_CMD_EXPORT_ALIAS(cmd_print_memtrace, memtrace, print memory trace info);static int mem_trace_init(void)
{rt_uint32_t i = 0;rt_err_t err = 0;rt_memset(mem_node, 0 , sizeof(mem_node));rt_memset(&mem_info, 0 , sizeof(mem_info));err = rt_mutex_init(&mem_info.lock, "mem_trace", RT_IPC_FLAG_PRIO);if(err){rt_kprintf("mem_trace_init fail!\n");return -1;}mem_info.init = 1;mem_info.used = 0;mem_info.total = MEM_TRACE_MAX;rt_list_init(&free_list);rt_list_init(&used_list);for(i = 0; i < MEM_TRACE_MAX; i++){rt_list_insert_before(&free_list, &mem_node[i].node);}return 0;
}
INIT_ENV_EXPORT(mem_trace_init);

memtrace.h,没错,这个头文件没有用“#ifndef …… #define”这样的写法,这样有个好处是MEM_TRACE_MALLOC宏可以随着MEM_USR_MALLOC宏变化,即这个组件能用同时监控不同的内存接口,例如test_a.c里用的是malloc,test_b.c里用的是rt_malloc.c,都可以添加到内存监控中去。

#define MEM_TRACE_MALLOC(size)                          \
({                                                      \void *addr = RT_NULL;                               \addr = MEM_USR_MALLOC(size);                        \if(addr != RT_NULL)                                 \mem_trace_add(__FILE__, __LINE__, size, addr);  \addr;                                               \
})#define MEM_TRACE_FREE(addr)                            \
({                                                      \MEM_USR_FREE(addr);                                 \mem_trace_del(addr);                                \
})extern rt_err_t mem_trace_add(char *file, rt_uint16_t line, rt_uint32_t size, void *addr);
extern rt_err_t mem_trace_del(void *addr);

4、局限性

有些模块可能会进一步封装内存申请的接口,而memtrace只支持void* malloc(size_t size)这样的形式,如果是这种情况,可能需要使用者将待排查的内存接口转成宏定义,会比较麻烦。

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

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

相关文章

四种常见的数据模型

为什么要进行数仓建模&#xff1f; 性能&#xff1a;良好的数据模型能帮助我们快速查询需要的数据&#xff0c;减少数据I/O吞吐 成本&#xff1a;减少数据冗余、计算结果复用、从而减低存储和计算成本 效率&#xff1a;改善用户使用数据的体验&#xff0c;提高使用数据的效率 改…

移动APP、WEB端和PC端测试的区别

移动APP、WEB端和PC端的测试区别主要体现在以下几个方面&#xff1a; 1. 平台和环境 移动APP是在移动设备上运行的应用程序&#xff0c;测试需要考虑不同的操作系统&#xff08;如iOS、Android等&#xff09;、不同的设备型号和屏幕分辨率。WEB端是在浏览器上运行的应用程序&am…

ssm+vue中国咖啡文化宣传网站源码和论文

ssmvue中国咖啡文化宣传网站源码和论文078 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 课题背景 随着时代的发展和人们生活理念的进一步改变&#xff0c;咖啡业已经成为了全球经济中发展最迅猛的产业之一。…

专访 Hyper Oracle:可编程的 zkOracle 打造未来世界的超算

许多 Web3 应用在实现的过程中&#xff0c;常常会遇到基础设施方面的限制&#xff0c;包括去中心化自动化、预言机、链上信息搜索等问题。绝大部分区块链的中间件网络都是依赖于节点质押来保证节点执行的诚实性&#xff0c;这样的模式会产生诸多衍生问题&#xff0c;例如安全性…

几个nlp的小任务(多选问答)

@TOC 安装库 多选问答介绍 定义参数、导入加载函数 缓存数据集 随机选择一些数据展示 进行数据预处理部分(tokenizer) 调用t

探究代理技术在网络安全、爬虫与HTTP通信中的多重应用

在当今高度互联的世界中&#xff0c;代理技术在网络安全、爬虫开发以及HTTP通信中扮演着举足轻重的角色。本文将深入探讨Socks5代理、IP代理以及HTTP代理在这些领域中的多重应用&#xff0c;探索其如何为我们创造更安全、高效的网络环境。 1. Socks5代理&#xff1a;构建安全通…

启动程序结束程序打开指定网页

import subprocess subprocess.Popen(r"C:\\Program Files\\5EClient\\5EClient.exe") # 打开指定程序 import os os.system(TASKKILL /F /IM notepad.exe) # 结束指定程序 import webbrowser webbrowser.open_new_tab(https://www.baidu.com) # 打开指定网页

MySQL对小数进行四舍五入等操作

数学函数是MySQL中常用的一类函数。其主要用于处理数字&#xff0c;包括整型和浮点数等等。 MySQL常用的四舍五入函数&#xff1a; 函数说明 FLOOR(X) 返回不大于X的最大整数。CEIL(X)、CEILING(X) 返回不小于X的最小整数。 ROUND(X) 返回离X最近的整数&#xff0c;截断时要进行…

8月28日,每日信息差

1、欧拉汽车第40万台整车下线。据介绍品牌与用户共创的最新成果2023款好猫&好猫GT木兰版尊荣型也在同一时间上市&#xff0c;限时12.98万起 2、马克古尔曼&#xff1a;M3款苹果MacBook最早今年10月发布 3、大麦成立“艺展鸿图”展览厂牌。专注于高品质艺术展览、授权等业…

事务特性 - 达梦数据库

达梦数据库事务特性 1 事务特性1.1 原子性1.2 一致性1.3 隔离性1.4 持久性 1 事务特性 事务必须具备什么属性才是一个有效的事务呢&#xff1f;一个逻辑工作单元必须表现出四种属性&#xff0c;即原子性、一致性、隔离性和持久性&#xff0c;这样才能成为一个有效的事务。DM 数…

基于SSM+vue框架的个人博客网站源码和论文

基于SSMvue框架的个人博客网站源码和论文061 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm &#xff08;设计&#xff09;研究背景与意义 关于博客的未来&#xff1a;在创办了博客中国(blogchina)、被誉为“…

2023最新任务悬赏平台源码uniapp+Thinkphp新款悬赏任务地推拉新充场游戏试玩源码众人帮威客兼职任务帮任务发布分销机

新款悬赏任务地推拉新充场游戏试玩源码众人帮威客兼职任务帮任务发布分销机制 后端是&#xff1a;thinkphpFastAdmin 前端是&#xff1a;uniapp 1.优化首页推荐店铺模块如有则会显示此模块没有则隐藏。 2修复首页公告&#xff0c;更改首页公告逻辑。&#xff08;后台添加有公…

C# 学习笔记--个人学习使用 <1>

C# 学习笔记 Chapter 1 C# 比较软的基础部分Section 1 类与命名空间Part 1 命名空间 NameSpacePart 2 类 Class Section 2 基本元素Section 3 数据类型Part 1 什么是类型&#xff1f;Part 2 类型在 C Sharp 中的作用Part 3 C Sharp 中的数据类型 Section 4 变量、对象与内存Par…

软件工程(十) 需求工程之需求开发与管理

前面我们学习到了需求工程的概念与分类,我们知道了需求工程主要分为需求开发和需求管理,但是没有说明到底该如何开发需求,有哪些方法去开发需求。到底该如何进行需求管理,又有哪些进行需求管理的方式。具体是如何去做的。下面我们将会详细进行描述。 1、需求开发 1.1、需…

URI和URL和URN区别

URI、URL 和 URN 是一系列从不同角度来看待资源标识和定位的概念。虽然它们有一些重叠&#xff0c;但每个概念都强调了不同的方面。 URI&#xff08;Uniform Resource Identifier&#xff09;&#xff1a;URI 是一个通用的术语&#xff0c;用于标识和定位资源。它是一个抽象的概…

多线程(二)

一.关于线程的常用操作 1.启动线程 run(): 对于run方法的覆写只是指定线程要做的任务清单&#xff0c;而不是真正的启动线程 start()&#xff1a; start()方法才是真正的在底层创建出一个线程&#xff0c;并且启动 2.中断线程 1.通过共享的标记来中断 package demo; impor…

UML四大关系

文章目录 引言UML的定义和作用UML四大关系的重要性和应用场景关联关系继承关系聚合关系组合关系 UML四大关系的进一步讨论UML四大关系的实际应用软件开发中的应用其他领域的应用 总结 引言 在软件开发中&#xff0c;统一建模语言&#xff08;Unified Modeling Language&#x…

Go 自学:文件的写入和读取

首先&#xff0c;使用os.Create()函数建立一个文件。 接着&#xff0c;使用io.WriteString()函数将内容写入文件。 最后&#xff0c;使用os.ReadFile()函数读取文件内容。 注意&#xff0c;这里读取的文件内容是data byte&#xff0c;我们需要使用string()函数将其转换为字符串…

谈谈对 GMP 的简单认识

犹记得最开始学习 golang 的时候&#xff0c;大佬们分享 GMP 模型的时候&#xff0c;总感觉云里雾里&#xff0c;听了半天&#xff0c;并没有一个很清晰的概念&#xff0c;不知 xmd 是否会有这样的体会 虽然 golang 入门很简单&#xff0c;但是对于理解 golang 的设计思想和原…

Python遥感图像处理应用篇038 GDAL 遥感图像特征提取(统计特征图)

1.图像统计特征 遥感图像的统计特征是对图像中像素值的统计分布进行定量化描述的过程。这些统计特征可以提供关于图像内容和特性的有用信息。下面是一些常用的遥感图像统计特征描述方法: 平均值(Mean):计算图像中所有像素值的平均值,可以反映整个图像的亮度水平。 方差(…