写点什么

鸿蒙轻内核的得力助手:带你掌握 4 种内存调试方法

发布于: 3 小时前

​​​​​​摘要:内存调测方法旨在辅助定位动态内存相关问题,提供了内存池信息统计、内存泄漏检测和踩内存检测三种调测手段。


本文分享自华为云社区《鸿蒙轻内核-内存调测-内存信息统计》,作者:zhushy 。

 

内存调测方法旨在辅助定位动态内存相关问题,提供了基础的动态内存池信息统计手段,向用户呈现内存池水线、碎片率等信息;提供了内存泄漏检测手段,方便用户准确定位存在内存泄漏的代码行,也可以辅助分析系统各个模块内存的使用情况;提供了踩内存检测手段,可以辅助定位越界踩内存的场景。

内存信息统计

1、基础概念


内存信息包括内存池大小、内存使用量、剩余内存大小、最大空闲内存、内存水线、内存节点数统计、碎片率等。


  • 内存水线:即内存池的最大使用量,每次申请和释放时,都会更新水线值,实际业务可根据该值,优化内存池大小;


  • 碎片率:衡量内存池的碎片化程度,碎片率高表现为内存池剩余内存很多,但是最大空闲内存块很小,可以用公式(fragment=100-最大空闲内存块大小/剩余内存大小)来度量;


  • 其他参数:通过内存管理模块的调用接口,扫描内存池的节点信息,统计出相关信息。

2、功能配置


LOSCFG_MEM_WATERLINE:开关宏,默认打开;若关闭这个功能,在 target_config.h 中将这个宏定义为 0。如需获取内存水线,需要打开该配置。

3、开发指导


关键结构体介绍:



  • 内存水线获取

调用 LOS_MemInfoGet 接口,第 1 个参数是内存池首地址,第 2 个参数是 LOS_MEM_POOL_STATUS 类型的句柄,其中字段 usageWaterLine 即水线值。


  • 内存碎片率计算

同样调用 LOS_MemInfoGet 接口,可以获取内存池的剩余内存大小和最大空闲内存块大小,然后根据公式(fragment=100-最大空闲内存块大小/剩余内存大小)得出此时的动态内存池碎片率。

4、编程实例


本实例实现如下功能:


1.创建一个监控线程,用于获取内存池的信息;

2.调用 LOS_MemInfoGet 接口,获取内存池的基础信息;

3.利用公式算出使用率及碎片率。


代码实现如下:



编译运行输出的结果如下:



内存泄漏检测机制

1、基础概念


内存泄漏检测机制作为内核的可选功能,用于辅助定位动态内存泄漏问题。开启该功能,动态内存机制会自动记录申请内存时的函数调用关系(下文简称 LR)。如果出现泄漏,就可以利用这些记录的信息,找到内存申请的地方,方便进一步确认。

2、功能配置


  • LOSCFG_MEM_LEAKCHECK:开关宏,默认关闭;若打开这个功能,在 target_config.h 中将这个宏定义为 1。


  • LOSCFG_MEM_RECORD_LR_CNT:记录的 LR 层数,默认 3 层;每层 LR 消耗 sizeof(void *)字节数的内存。


  • LOSCFG_MEM_OMIT_LR_CNT:忽略的 LR 层数,默认 4 层,即从调用 LOS_MemAlloc 的函数开始记录,可根据实际情况调整。为啥需要这个配置?有 3 点原因如下:

- LOS_MemAlloc 接口内部也有函数调用;

- 外部可能对 LOS_MemAlloc 接口有封装;

- LOSCFG_MEM_RECORD_LR_CNT 配置的 LR 层数有限;

正确配置这个宏,将无效的 LR 层数忽略,就可以记录有效的 LR 层数,节省内存消耗。

3、开发指导

3.1 开发流程


该调测功能可以分析关键的代码逻辑中是否存在内存泄漏。开启这个功能,每次申请内存时,会记录 LR 信息。在需要检测的代码段前后,调用 LOS_MemUsedNodeShow 接口,每次都会打印指定内存池已使用的全部节点信息,对比前后两次的节点信息,新增的节点信息就是疑似泄漏的内存节点。通过 LR,可以找到具体申请的代码位置,进一步确认是否泄漏。


调用 LOS_MemUsedNodeShow 接口输出的节点信息格式如下:每 1 行为一个节点信息;第 1 列为节点地址,可以根据这个地址,使用 GDB 等手段查看节点完整信息;第 2 列为节点的大小,等于节点头大小+数据域大小;第 3~5 列为函数调用关系 LR 地址,可以根据这个值,结合汇编文件,查看该节点具体申请的位置。



注意: 开启内存检测会影响内存申请的性能,且每个内存节点都会记录 LR 地址,内存开销也加大。

3.2 编程实例


本实例实现如下功能:构建内存泄漏代码段。


1.     调用 LOS_MemUsedNodeShow 接口,输出全部节点信息打印;

2.     申请内存,但没有释放,模拟内存泄漏;

3.     再次调用 LOS_MemUsedNodeShow 接口,输出全部节点信息打印;

4.     将两次 log 进行对比,得出泄漏的节点信息;

5.     通过 LR 地址,找出泄漏的代码位置;

3.3 示例代码


代码实现如下:



3.4 结果验证


编译运行输出 log 如下:



对比两次 log,差异如下,这些内存节点就是疑似泄漏的内存块:



部分汇编文件如下:



其中,通过查找 0x080041ee,就可以发现该内存节点是在 MemLeakTest 接口里申请的且是没有释放的。

踩内存检测机制


踩内存检测机制作为内核的可选功能,用于检测动态内存池的完整性。通过该机制,可以及时发现内存池是否发生了踩内存问题,并给出错误信息,便于及时发现系统问题,提高问题解决效率,降低问题定位成本。

功能配置


LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK:开关宏,默认关闭;若打开这个功能,在 target_config.h 中将这个宏定义为 1。


1.     开启这个功能,每次申请内存,会实时检测内存池的完整性。

2.     如果不开启该功能,也可以调用 LOS_MemIntegrityCheck 接口检测,但是每次申请内存时,不会实时检测内存完整性,而且由于节点头没有魔鬼数字(开启时才有,省内存),检测的准确性也会相应降低,但对于系统的性能没有影响,故根据实际情况开关该功能。


由于该功能只会检测出哪个内存节点被破坏了,并给出前节点信息(因为内存分布是连续的,当前节点最有可能被前节点破坏)。如果要进一步确认前节点在哪里申请的,需开启内存泄漏检测功能,通过 LR 记录,辅助定位。


注意:

开启该功能,节点头多了魔鬼数字字段,会增大节点头大小。由于实时检测完整性,故性能影响较大;若性能敏感的场景,可以不开启该功能,使用 LOS_MemIntegrityCheck 接口检测。

开发指导

开发流程


通过调用 LOS_MemIntegrityCheck 接口检测内存池是否发生了踩内存,如果没有踩内存问题,那么接口返回 0 且没有 log 输出;如果存在踩内存问题,那么会输出相关 log,详见下文编程实例的结果输出。

编程实例


本实例实现如下功能:

1.     申请两个物理上连续的内存块;

2.     通过 memset 构造越界访问,踩到下个节点的头 4 个字节;

3.     调用 LOS_MemIntegrityCheck 检测是否发生踩内存。

示例代码


代码实现如下:


结果验证


编译运行输出 log 如下:



点击关注,第一时间了解华为云新鲜技术~ 

发布于: 3 小时前阅读数: 4
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
鸿蒙轻内核的得力助手:带你掌握4种内存调试方法