andeyqi 发表于 2023-10-26 12:31:08

【Telink-泰凌微电子 B91通用开发套件】(四)FreeRtos任务栈统计

本帖最后由 andeyqi 于 2023-10-26 14:24 编辑

简介:

       嵌入式开发中我们通常对任务栈的大小设置通常根据经验值来设定,设置的小了会造成栈溢出,设置大了会造成资源的浪费。如果能够计算出栈在任务过程中使用的最大深度的话对就可以根据实际情况来设置避免资源的浪费。计算任务栈的使用大小通常使用水印法,初始化时将栈设置为特定的magic ,栈内未使用的部分会时连续的magic,连续magic 的大小和栈的总深度的比较即可计算出栈的最大使用率。

实现方式:
本次实现基于freertos来实现,上述的实现方式主要需要实现如下几个部分:
1.创建任务的时候将任务栈初始化为特定的magic(0xa5)
2.将任务tcb信息中的栈的起始地址和栈深度缓存下来
3.计算栈的非踩踏区域的长度和栈深度计算栈的最大使用率


1.创建任务的时候将任务栈初始化为特定的magic(0xa5)

FreeRTOS 创建任务的时候会根据配置项目,决定是否将栈初始化特定的magic,代码如下:




只要配置 tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1就会初始化栈为特定的魔数(0xa5),通过如下代码可知配置configUSE_STACK_MAX_USAGE= 1 会定义tskSET_NEW_STACKS_TO_KNOWN_VALUE = 1,我们在freertos 的配置文件中定义该宏即可
/* If any of the following are set then task stacks are filled with a known
* value so the high water mark can be determined.If none of the following are
* set then don't fill the stack so there is no unnecessary dependency on memset. */
#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) || (configUSE_STACK_MAX_USAGE == 1))
    #define tskSET_NEW_STACKS_TO_KNOWN_VALUE    1
#else
    #define tskSET_NEW_STACKS_TO_KNOWN_VALUE    0
#endif对应FreeRtos配置追加如下宏配置:
修改如下配置将在栈创建深度信息通知给hook函数处理,修改如下:




2.将任务tcb信息中的栈的起始地址和栈深度缓存下来

freertos 有很多hook 函数时可以供用户使用来劫持操作系统的信息,我们可以通过如下的回调函数(traceTASK_CREATE)截取系统的栈信息。
3.计算栈的非踩踏区域的长度和栈深度计算栈的最大使用率

添加stack 命令计算显示当前的栈信息,代码如下。
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "FreeRTOS.h"
#if (configUSE_PERF_CNT == 1)
#include "perf_counter.h"
#endif
#include "littleshell.h"
#include "trace_dump.h"

#if ((configUSE_STACK_MAX_USAGE == 0 )&& (configUSE_PERF_CNT == 1))
    #error perf is depend on configUSE_STACK_MAX_USAGE = 1
#endif


#if ( configUSE_STACK_MAX_USAGE == 1 )

struct task_stack_info
{
    char task_name;
    uint32_t stack_deep;
    uint32_t*stack_tail;
};

struct task_stack_info_tbl
{
    uint8_t num;
    struct task_stack_info * tasks;
};

#define OS_NUM_TASKS_SUPPORT 10

static struct task_stack_info stack_all;

static struct task_stack_info_tbl task_stacks =
{
    .num = 0,
    .tasks = stack_all,
};


#define TASK_STACK_NAME(i) task_stacks.tasks.task_name
#define TASK_STACK_DEEP(i) task_stacks.tasks.stack_deep
#define TASK_STACK_TAIL(i) task_stacks.tasks.stack_tail
#define TASK_STACK_TAIL_OFF_ADDR(i,off) &task_stacks.tasks.stack_tail



void task_create_hook_stack(char * name ,unsigned int deep,unsigned int * stack_tail)
{
    uint8_t i_loop = task_stacks.num;
    uint8_t len,i;
    char * p_task_name;

    if(i_loop < OS_NUM_TASKS_SUPPORT)
    {
      p_task_name = TASK_STACK_NAME(i_loop);
      strcpy(TASK_STACK_NAME(i_loop),name);
      if((len = strlen(TASK_STACK_NAME(i_loop))) != (configMAX_TASK_NAME_LEN-1))
      {
            for(i = len;i < configMAX_TASK_NAME_LEN-1;i++)
                p_task_name = '\0';
      }
      p_task_name = '\0';
      TASK_STACK_DEEP(i_loop) = deep*4;
      TASK_STACK_TAIL(i_loop) = stack_tail;
      /* if use pref to set perf default value */
#if ( configUSE_PERF_CNT == 1 )
      memset((void*)TASK_STACK_TAIL(i_loop),0,sizeof(task_cycle_info_t)+8);
#endif
      task_stacks.num++;
    }
    else
    {
      printf("task num is max than OS_NUM_TASKS_SUPPORT.\r\n");
    }
}


static int buff_continuous_numbers(uint8_t * buff,uint8_t data)
{
    int l = 0;

    if(NULL == buff)
      return 0;

    while(data == buff);

    return--l;
}

extern size_t xPortGetFreeHeapSize( void );

unsigned int stack(char argc,char ** argv)
{
    uint8_t i,j;
    int len,name_len;
    char task_name;

    if(argc == 1)
    {
      //logctrl_instance()->is_sync = 1;
      printf("taskname\tdeep\tused\taddress\t\t\tusage\r\n");
      for(i = 0;i < task_stacks.num;i++)
      {
#if ( configUSE_PERF_CNT != 1 )
            len = buff_continuous_numbers((uint8_t *)TASK_STACK_TAIL(i),0xa5);
#else
            len = buff_continuous_numbers((uint8_t *)TASK_STACK_TAIL_OFF_ADDR(i,WORD_OF_CYCEL_INFO),0xa5);
            if(len)
                len += BYTE_OF_CYCEL_INFO;
#endif
      strcpy(task_name,TASK_STACK_NAME(i));
      task_name = '\0';
      if((name_len = strlen(TASK_STACK_NAME(i))) != (configMAX_TASK_NAME_LEN-1))
      {
            for(j = name_len;j < configMAX_TASK_NAME_LEN-1;j++)
                task_name = ' ';
      }
            printf("%s\t%d\t%d\t0x%08p~0x%08p\t%d%%\r\n",task_name,TASK_STACK_DEEP(i),
            TASK_STACK_DEEP(i)-len,TASK_STACK_TAIL(i),
            TASK_STACK_TAIL(i)+(TASK_STACK_DEEP(i)/4),(100-(len*100)/TASK_STACK_DEEP(i)));
      }
      printf("\r\nHeap Total:%d\tFree:%d\r\n",configTOTAL_HEAP_SIZE,xPortGetFreeHeapSize());
      //logctrl_instance()->is_sync = 0;
    }
    if(argc == 2)
    {
      uint8_t index = atoi(argv);
      if(index >=task_stacks.num)
            goto out;
      printf("task name [%s] stack deep 0x%x\r\n",TASK_STACK_NAME(index),TASK_STACK_DEEP(index));
      //logctrl_instance()->is_sync = 1;
      trace_byte_stream((uint8_t *)TASK_STACK_TAIL(index),TASK_STACK_DEEP(index),0);
      //logctrl_instance()->is_sync = 0;
    }
out:
    return 0;
}

//LTSH_FUNCTION_EXPORT(stack,"show task statck info");

#if ( configUSE_PERF_CNT == 1 )

unsigned int perf(char argc,char ** argv)
{
    uint8_t i,j,len;
    int64_t count = get_system_ticks();
    char task_name;

    //logctrl_instance()->is_sync = 1;
    if(argc == 1)
    {
      printf("taskname\trecent\t\tactive\tusedmax\t\tusedmin\t\tusedavg\t\tcpuusage\r\n");
      for(i = 0;i < task_stacks.num;i++)
      {
            task_cycle_info_t * pcycle = get_rtos_task_cycle_info_by_stack(TASK_STACK_TAIL(i));
            task_name = '\0';
            strcpy(task_name,TASK_STACK_NAME(i));
            if((len = strlen(TASK_STACK_NAME(i))) != (configMAX_TASK_NAME_LEN-1))
            {
                for(j = len;j < configMAX_TASK_NAME_LEN-1;j++)
                  task_name = ' ';
            }
            printf("%s\t%010d\t%d\t%010d\t%010d\t%010d\t%.2f\r\n",
                   task_name,pcycle->nUsedRecent,pcycle->wActiveCount,
                   pcycle->nUsedMax,pcycle->nUsedMin,pcycle->lUsedTotal/pcycle->wActiveCount,
                   ((double)(pcycle->lUsedTotal))/((double)count)*((double)100.0f));
      }
    }
    //logctrl_instance()->is_sync = 0;
    return 0;
}

LTSH_FUNCTION_EXPORT(perf,"show perf info");
#endif

#endif /* end of configUSE_STACK_MAX_USAGE */



运行效果:
输入stack 命令即可查看当前任务的栈使用深度,根据输出结果来确认栈大小设置是否合理。


==================资料分割线==================代码和官方下载的资料已上传至如下git路径https://gitee.com/andeyqi/telink-b91











页: [1]
查看完整版本: 【Telink-泰凌微电子 B91通用开发套件】(四)FreeRtos任务栈统计