查看: 2430|回复: 2

三分钟搞定内存溢出

[复制链接]

该用户从未签到

2

主题

3

帖子

0

新手上路

Rank: 1

积分
25
最后登录
2020-1-3
发表于 2022-2-22 14:28:27 | 显示全部楼层 |阅读模式
今天我来讲一讲MCU开发中的一个棘手问题——内存溢出,希望能帮到遇到该问题的同学们。

开发环境
SDK版本:SDK_2_6_13_FRDM-KW38
SDK下载地址:https://mcuxpresso.nxp.com
开发板:FRDM-KW38
IDE:IAR EmbeddedWorkbench for Arm version 8.50
演示代码:https://github.com/N40E116/SDK_2_6_13_FRDM-KW38.git

本文总结了如下三类RAM使用情况的分析:
FreeRTOS RAM
CSTACK
动态内存分配

FreeRTOS RAM分析
因为我们使用的是带RTOS的工程,所以这里先介绍一下FreeRTOS里stack和heap的管理和分析。


Task Stack分析
每个task的stack是独立分配的,我们使用IAR的FreeRTOS分析插件对stack进行分析,打开和使能方式如下:
14.png
以上方式针对的是在线debug时的分析查看,该方式查看信息较全面,可以在开发阶段根据多数场景分配合适的stack,但是对于debugger离线后的溢出检测则需要使用FreeRTOS自带的stack异常检测工具,打开方式如下,详细信息请参考FreeRTOS- stacks and stack overflow checking
  1. #define configCHECK_FOR_STACK_OVERFLOW 2

  2. #if (configCHECK_FOR_STACK_OVERFLOW !=  0)
  3. void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
  4. {
  5.     panic(0,(uint32_t)vApplicationStackOverflowHook,0,0);
  6. }
  7. #endif
复制代码
FreeRTOS Heap分析

FreeRTOS使用的heap通过如下宏定义,对于该Heap的溢出检测可以使用FreeRTOS自带的内存分配失败钩子函数进行检测。
  1. #define gTotalHeapSize_c        9000

  2. #define configUSE_MALLOC_FAILED_HOOK 1
复制代码
CSTACK分析
上面章节我们讲了FreeRTOS中task占用stack的检测方法,但是对于RTOS初始化前和中断处理函数中用到的CSTACK该如何检测呢?


IAR本身集成了CSTACK检测功能,会显示当前栈的使用情况和最大栈深度,开发阶段连接debugger,按如下方式设置后即可查看CSTACK信息。
15.png
16.png
如果系统产生了CSTACK溢出,我们该如何检测哪里产生了这个溢出呢?这时我们可以使用IAR的数据断点功能,将栈底位置写入数据断点的break位置,Access type改为Write,这样只要栈底被修改了,即可产生断点,根据代码break的位置,即可知道是哪里产生了CSTACK溢出。
17.png
一个快速获得栈底位置的方法,如下图所示,将鼠标放到IAR的CSTACK的进度条处即可显示stack的使用范围。
18.png
对于debugger离线后的CSTACK溢出检测,我们可以通过初始化栈空间为一个固定值,例如在线分析时为0xcd,定时检测栈底上的该值是否有被修改来检测。
如下所示为在idle任务中进入低功耗前增加栈底数据的检测。
  1. void check_overflow_cstack()
  2. {
  3.     extern uint32_t CHECK_OVERFLOW_CSTACK_SIZE[];
  4.     uint32_t CHECK_OVERFLOW_CSTACK_END = *((uint32_t*)0UL) - (uint32_t)CHECK_OVERFLOW_CSTACK_SIZE;
  5.     if(*(uint32_t*)CHECK_OVERFLOW_CSTACK_END != 0xcdcdcdcd)
  6.     {
  7.         panic(0,(uint32_t)check_overflow_cstack,0,0);
  8.     }
  9. }

  10. void BOARD_EnterLowPowerCb(void)
  11. {
  12.     check_overflow_cstack();

  13. }
复制代码
另外链接文件MKW38A512xxx4_PD_connectivity_lp.icf需要增加如下定义:
  1. define exported symbol  CHECK_OVERFLOW_CSTACK_SIZE = __size_cstack__;
复制代码
动态内存分配
SDK没有使用标准库的malloc函数,定义__heap_size__为0,所以用户不能使用malloc和free函数。但如果需要动态申请内存该如何操作呢?SDK的Framework里定义了一套简化的内存管理函数MEM_BufferAlloc()和MEM_BufferFree()。


配置文件中需要预先定义需要的数据块大小和数量,内存申请单元会从这些内存块中选取满足大小要求的最小的数据块作为MEM_BufferAlloc()的返回结果。
  1. #define AppPoolsDetails_c \
  2.          _block_size_  80  _number_of_blocks_    7 _eol_  \
  3.          _block_size_ 248   _number_of_blocks_   2 _eol_  \
  4.          _block_size_ 312  _number_of_blocks_    1 _eol_  \
  5.          _block_size_ 392  _number_of_blocks_    1 _eol_
复制代码
当然如果用户使用了该内存分配方法,则需要根据应用情况,对应地增加内存池中的系数。另外可以使能如下宏定义,查看分析内存分配是否合理,具体用法请参考应用笔记:
MemoryPool Optimizer on MKW3xA/KW3xZ (nxp.com.cn)。
  1. MEM_DEBUG,MEM_TRACKING,MEM_DEBUG_OUT_OF_MEMORY
复制代码
以上是我总结的一些overflow的应对策略,强烈建议大家在开发阶段加上这些检测措施,因为内存溢出会导致各种意想不到的结果,如果只跟着看到的异常现象分析,往往会浪费很多不必要的时间和精力,如果大家有其它应对内存溢出的方法,欢迎一起讨论学习。


回复

使用道具 举报

  • TA的每日心情
    开心
    2024-4-10 22:38
  • 签到天数: 1335 天

    [LV.10]以坛为家III

    88

    主题

    4292

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9049
    最后登录
    2024-4-13
    发表于 2022-2-22 15:55:01 | 显示全部楼层
    谢谢楼主分享了。
    方法还是非常值得借鉴的
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-4-9 17:01
  • 签到天数: 1478 天

    [LV.10]以坛为家III

    203

    主题

    2万

    帖子

    64

    超级版主

    Rank: 8Rank: 8

    积分
    92609
    最后登录
    2024-4-9
    发表于 2022-2-22 19:33:43 | 显示全部楼层
    感谢分享~~
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-20 14:35 , Processed in 0.128126 second(s), 23 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表