【FreeRTOS 三类RAM分析方法】

三分钟搞定内存溢出

来源:恩智浦MCU加油站 发布时间:2022-01-20

今天我来讲一讲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进行分析,打开和使能方式如下:

【FreeRTOS 三类RAM分析方法】_第1张图片

【FreeRTOS 三类RAM分析方法】_第2张图片

以上方式针对的是在线debug时的分析查看,该方式查看信息较全面,可以在开发阶段根据多数场景分配合适的stack,但是对于debugger离线后的溢出检测则需要使用FreeRTOS自带的stack异常检测工具,打开方式如下,详细信息请参考FreeRTOS- stacks and stack overflow checking

#define configCHECK_FOR_STACK_OVERFLOW 2

#if (configCHECK_FOR_STACK_OVERFLOW !=  0)
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
{
panic(0,(uint32_t)vApplicationStackOverflowHook,0,0);
}
#endif

FreeRTOS Heap分析

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

#define gTotalHeapSize_c        9000

#define configUSE_MALLOC_FAILED_HOOK 1

CSTACK分析

上面章节我们讲了FreeRTOS中task占用stack的检测方法,但是对于RTOS初始化前和中断处理函数中用到的CSTACK该如何检测呢?

IAR本身集成了CSTACK检测功能,会显示当前栈的使用情况和最大栈深度,开发阶段连接debugger,按如下方式设置后即可查看CSTACK信息。

【FreeRTOS 三类RAM分析方法】_第3张图片

【FreeRTOS 三类RAM分析方法】_第4张图片

如果系统产生了CSTACK溢出,我们该如何检测哪里产生了这个溢出呢?这时我们可以使用IAR的数据断点功能,将栈底位置写入数据断点的break位置,Access type改为Write,这样只要栈底被修改了,即可产生断点,根据代码break的位置,即可知道是哪里产生了CSTACK溢出。

【FreeRTOS 三类RAM分析方法】_第5张图片

一个快速获得栈底位置的方法,如下图所示,将鼠标放到IAR的CSTACK的进度条处即可显示stack的使用范围。

【FreeRTOS 三类RAM分析方法】_第6张图片

对于debugger离线后的CSTACK溢出检测,我们可以通过初始化栈空间为一个固定值,例如在线分析时为0xcd,定时检测栈底上的该值是否有被修改来检测。

如下所示为在idle任务中进入低功耗前增加栈底数据的检测。

void check_overflow_cstack()
{
extern uint32_t CHECK_OVERFLOW_CSTACK_SIZE[];
uint32_t CHECK_OVERFLOW_CSTACK_END = *((uint32_t*)0UL) - (uint32_t)CHECK_OVERFLOW_CSTACK_SIZE;
if(*(uint32_t*)CHECK_OVERFLOW_CSTACK_END != 0xcdcdcdcd)
{
panic(0,(uint32_t)check_overflow_cstack,0,0);
}
}

void BOARD_EnterLowPowerCb(void)
{
check_overflow_cstack();
…
}

另外链接文件MKW38A512xxx4_PD_connectivity_lp.icf需要增加如下定义:

define exported symbol  CHECK_OVERFLOW_CSTACK_SIZE = __size_cstack__;

动态内存分配

SDK没有使用标准库的malloc函数,定义__heap_size__为0,所以用户不能使用malloc和free函数。但如果需要动态申请内存该如何操作呢?SDK的Framework里定义了一套简化的内存管理函数MEM_BufferAlloc()和MEM_BufferFree()。

配置文件中需要预先定义需要的数据块大小和数量,内存申请单元会从这些内存块中选取满足大小要求的最小的数据块作为MEM_BufferAlloc()的返回结果。

#define AppPoolsDetails_c \
_block_size_  80  _number_of_blocks_    7 _eol_  \
_block_size_ 248   _number_of_blocks_   2 _eol_  \
_block_size_ 312  _number_of_blocks_    1 _eol_  \
_block_size_ 392  _number_of_blocks_    1 _eol_

当然如果用户使用了该内存分配方法,则需要根据应用情况,对应地增加内存池中的系数。另外可以使能如下宏定义,查看分析内存分配是否合理,具体用法请参考应用笔记:

MemoryPool Optimizer on MKW3xA/KW3xZ (nxp.com.cn)。

MEM_DEBUG,MEM_TRACKING,MEM_DEBUG_OUT_OF_MEMORY

以上是我总结的一些overflow的应对策略,强烈建议大家在开发阶段加上这些检测措施,因为内存溢出会导致各种意想不到的结果,如果只跟着看到的异常现象分析,往往会浪费很多不必要的时间和精力,如果大家有其它应对内存溢出的方法,欢迎一起讨论学习。

你可能感兴趣的:(RTOS,c语言)