FreeRtos堆栈检测应用

        Free rtos每个任务都有自己的栈空间,每个任务需要的栈大小也是不同的。如果堆栈过小就会造成栈溢出,有时候栈溢出发生在某种特定顺序的任务切换中,比较难检测出。所以前期测试和监控任务栈用量就显得尤其重要。

  • FreeRTOSConfig.h文件中配置宏定义:
#define configCHECK_FOR_STACK_OVERFLOW	2   
#define INCLUDE_uxTaskGetStackHighWaterMark     1
  • 在任务切换时检测任务栈指针是否过界了,如果过界了,在任务切换的时候会触发栈溢出钩子函数。
void vApplicationStackOverflowHook( TaskHandle_t xTask,signed char *pcTaskName );
  • 用户可以在钩子函数里面做一些处理。
  • 任务创建的时候将任务栈所有数据初始化为0xa5,任务切换时进行任务栈检测的时候会检测末尾的16个字节是否都是0xa5,通过这种方式来检测任务栈是否溢出了。相比方法一,这种方法的速度稍慢些,但是这样就有效地避免了方法一里面的部分情况。不过依然不能保证所有的栈溢出都能检测到,比如任务栈末尾的16个字节没有用到,即没有被修改,但是任务栈已经溢出了,这种情况是检测不到的。另外任务栈溢出后,任务栈末尾的16个字节没有修改,但是溢出部分的栈区的数据修改了,这部分栈区的数据不重要或者暂时没有用到还不会有什么问题,但如果是重要数据被修改将直接导致系统进入硬件异常,这种情况下,栈溢出检测功能也是检测不到的。
  • 溢出检测的实现原理:
    #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) )
    
        #define taskCHECK_FOR_STACK_OVERFLOW()                                                                \
        {                                                                                                    \
            const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack;                            \
            const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5;                                            \
                                                                                                            \
            if( ( pulStack[ 0 ] != ulCheckValue ) ||                                                \
                ( pulStack[ 1 ] != ulCheckValue ) ||                                                \
                ( pulStack[ 2 ] != ulCheckValue ) ||                                                \
                ( pulStack[ 3 ] != ulCheckValue ) )                                                \
            {                                                                                                \
                vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName );    \
            }                                                                                                \
        }
    
    #endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
  • 实现用户的钩子函数:
/**
  * @brief  if task overflow, it will run here.
  * @param  [IN]task handle. 
  * @param  [IN]task name string pointer.
  * @retval None
  */
void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
     while(1)
     {
         printf("task %s is stack overflow. \r\n", pcTaskName);
         vTaskDelay(500);
     }
}
  • 测试每个任务的堆栈大小:
/**
  * @brief  check all task stack and get the remain stack size.
  * @param  None. 
  * @retval None
  */
void CheckTaskStack(void)
{
    uint8_t i = 0;
    float stack_usage = 0;
    
    DEBUG_PRINT("\r\n------------------------------------\r\n");
    for(i=0; i<APP_TASK_NUMS; i++) 
    {
      TaskConfigStruct[i].stack_remain = uxTaskGetStackHighWaterMark( TaskConfigStruct[i].handle );
      stack_usage = 100.0f*((float)TaskConfigStruct[i].stack_max - 
(float)TaskConfigStruct[i].stack_remain )/ (float)TaskConfigStruct[i].stack_max; DEBUG_PRINT("> id=%d, name=%8s, stack usage=%5.2f%%, free/all=%4d/%4d.\r\n",
TaskConfigStruct[i].id, TaskConfigStruct[i].name, stack_usage,
TaskConfigStruct[i].stack_remain, TaskConfigStruct[i].stack_max ); } DEBUG_PRINT(
"------------------------------------\r\n\r\n"); }
  • 测试结果,还剩余4个u32空间,溢出检测的方法2刚好是检查最后的4个数据,此时恰好不会触发overflow。

/**
  * @brief  check all task stack and get the remain stack size.
  * @param  None. 
  * @retval None
  */
void CheckTaskStack(void)
{
    uint8_t i = 0;
    uint8_t i2 = 0; // test stack overflow
    float stack_usage = 0;
    
    DEBUG_PRINT("\r\n------------------------------------\r\n");
    for(i=0; i<APP_TASK_NUMS; i++) 
    {
      TaskConfigStruct[i].stack_remain = uxTaskGetStackHighWaterMark( TaskConfigStruct[i].handle );
      stack_usage = 100.0f*((float)TaskConfigStruct[i].stack_max - 
(float)TaskConfigStruct[i].stack_remain )/ (float)TaskConfigStruct[i].stack_max; DEBUG_PRINT("> id=%d, name=%8s, stack usage=%5.2f%%, free/all=%4d/%4d.\r\n",
TaskConfigStruct[i].id, TaskConfigStruct[i].name, stack_usage,
TaskConfigStruct[i].stack_remain, TaskConfigStruct[i].stack_max ); } DEBUG_PRINT(
"------------------------------------\r\n\r\n"); }
  • 测试结果:再次定义局部变量uint8_t i2, 任务切换时候压栈会把i2入栈,此时会触发溢出检测。测试发现,任务在第2次切换时候就发生了堆栈溢出。

posted on 2017-08-29 15:13  啊哈彭  阅读(821)  评论(0编辑  收藏

导航