查看: 596|回复: 3

[原创] 【LPC845-BRK板卡试用申请】(九)任务使用栈深度计算

[复制链接]
  • TA的每日心情
    奋斗
    前天 13:25
  • 签到天数: 594 天

    [LV.9]以坛为家II

    51

    主题

    2222

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7061
    最后登录
    2024-4-27
    发表于 2023-8-31 13:54:15 | 显示全部楼层 |阅读模式
    本帖最后由 andeyqi 于 2023-12-8 13:30 编辑

    简介:

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

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


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

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

    stack_init.png

    只要配置 tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1就会初始化栈为特定的魔数(0xa5),  通过如下代码可知配置configUSE_STACK_MAX_USAGE  = 1 会定义tskSET_NEW_STACKS_TO_KNOWN_VALUE = 1,我们在freertos 的配置文件中定义该宏即可。
    1. /* If any of the following are set then task stacks are filled with a known
    2. * value so the high water mark can be determined.  If none of the following are
    3. * set then don't fill the stack so there is no unnecessary dependency on memset. */
    4. #if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) || ( configUSE_STACK_MAX_USAGE == 1) )
    5.     #define tskSET_NEW_STACKS_TO_KNOWN_VALUE    1
    6. #else
    7.     #define tskSET_NEW_STACKS_TO_KNOWN_VALUE    0
    8. #endif
    复制代码
    对应配置代码如下:
    freertosconfig.png


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

    freertos 有很多hook 函数时可以供用户使用来劫持操作系统的信息,我们可以通过如下的回调函数(traceTASK_CREATE)截取系统的栈信息。
    task_create.png
    task_create1.png


    3.计算栈的非踩踏区域的长度和栈深度计算栈的最大使用率

    添加stack 命令计算显示当前的栈信息,代码如下。

    1. #include <stdint.h>
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>
    5. #include "FreeRTOS.h"
    6. #if (configUSE_PERF_CNT == 1)
    7. #include "perf_counter.h"
    8. #endif
    9. #include "littleshell.h"
    10. #include "trace_dump.h"
    11. #include "fsl_debug_console.h"

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


    15. #if ( configUSE_STACK_MAX_USAGE == 1 )

    16. struct task_stack_info
    17. {
    18.     char task_name[configMAX_TASK_NAME_LEN];
    19.     uint32_t stack_deep;
    20.     uint32_t*  stack_tail;
    21. };

    22. #define OS_NUM_TASKS 4

    23. static struct task_stack_info stack_all[OS_NUM_TASKS+2]; //add timer & idle


    24. void task_create_hook_stack(char * name ,unsigned int deep,unsigned int * stack_tail)
    25. {
    26.     static uint8_t i_loop = 0;
    27.     uint8_t len,i;

    28.     if(i_loop < OS_NUM_TASKS+2)
    29.     {
    30.         strcpy(stack_all[i_loop].task_name,name);
    31.         if((len = strlen(stack_all[i_loop].task_name)) != (configMAX_TASK_NAME_LEN-1))
    32.         {
    33.             for(i = len;i < configMAX_TASK_NAME_LEN-1;i++)
    34.                 stack_all[i_loop].task_name[i] = '\0';
    35.         }
    36.         stack_all[i_loop].task_name[configMAX_TASK_NAME_LEN-1] = '\0';
    37.         stack_all[i_loop].stack_deep = deep*4;
    38.         stack_all[i_loop].stack_tail = stack_tail;
    39.         /* if use pref to set perf default value */
    40. #if ( configUSE_PERF_CNT == 1 )
    41.         memset((void*)stack_all[i_loop].stack_tail,0,sizeof(task_cycle_info_t)+8);
    42. #endif
    43.         i_loop++;
    44.     }
    45.     else
    46.     {
    47.         PRINTF("array is overflow \r\n");
    48.     }
    49. }

    50. #if 0
    51. void get_cur_thread_stack_info(uint32_t sp, uint32_t *start_addr, size_t *size)
    52. {
    53.     uint8_t i_loop = 0;
    54.     extern __no_init volatile char switchintaskname[configMAX_TASK_NAME_LEN];

    55.     for(i_loop = 0;i_loop< OS_NUM_TASKS+2;i_loop++)
    56.     {
    57.         if(strcmp(stack_all[i_loop].task_name,(const char *)switchintaskname) == 0)
    58.             break;
    59.         if((sp > (uint32_t)stack_all[i_loop].stack_tail) && ( sp < ((uint32_t)stack_all[i_loop].stack_tail)+stack_all[i_loop].stack_deep))
    60.             break;
    61.     }
    62.     /* find stack info update */
    63.     if(i_loop != OS_NUM_TASKS+2)
    64.     {
    65.         *start_addr = (uint32_t)stack_all[i_loop].stack_tail;
    66.         *size = stack_all[i_loop].stack_deep;
    67.     }
    68.     else
    69.     {
    70.          *size = 0;
    71.     }
    72. }
    73. #endif

    74. static int buff_continuous_numbers(uint8_t * buff,uint8_t data)
    75. {
    76.     int l = 0;

    77.     if(NULL == buff)
    78.         return 0;

    79.     while(data == buff[l++]);

    80.     return  --l;
    81. }

    82. extern size_t xPortGetFreeHeapSize( void );

    83. unsigned int stack(char argc,char ** argv)
    84. {
    85.     uint8_t i,j;
    86.     int len,name_len;
    87.     char task_name[configMAX_TASK_NAME_LEN];

    88.     if(argc == 1)
    89.     {
    90.         //logctrl_instance()->is_sync = 1;
    91.         PRINTF("taskname\tdeep\tused\taddress\t\t\tusage\r\n");
    92.         for(i = 0;i < OS_NUM_TASKS+2;i++)
    93.         {
    94. #if ( configUSE_PERF_CNT != 1 )
    95.             len = buff_continuous_numbers((uint8_t *)stack_all[i].stack_tail,0xa5);
    96. #else
    97.             len = buff_continuous_numbers((uint8_t *)&stack_all[i].stack_tail[WORD_OF_CYCEL_INFO],0xa5);
    98.             if(len)
    99.                 len += BYTE_OF_CYCEL_INFO;
    100. #endif
    101.         strcpy(task_name,stack_all[i].task_name);
    102.         task_name[configMAX_TASK_NAME_LEN - 1] = '\0';
    103.         if((name_len = strlen(stack_all[i].task_name)) != (configMAX_TASK_NAME_LEN-1))
    104.         {
    105.             for(j = name_len;j < configMAX_TASK_NAME_LEN-1;j++)
    106.                 task_name[j] = ' ';
    107.         }
    108.             PRINTF("%s\t%d\t%d\t0x%p~0x%p\t%d%%\r\n",task_name,stack_all[i].stack_deep,stack_all[i].stack_deep-len,stack_all[i].stack_tail,stack_all[i].stack_tail+(stack_all[i].stack_deep/4),(100-(len*100)/stack_all[i].stack_deep));
    109.         }
    110.         PRINTF("\r\nHeap Total:%d\tFree:%d\r\n",configTOTAL_HEAP_SIZE,xPortGetFreeHeapSize());
    111.         //logctrl_instance()->is_sync = 0;
    112.     }
    113.     if(argc == 2)
    114.     {
    115.         uint8_t index = atoi(argv[1]);
    116.         if(index >=  OS_NUM_TASKS+2)
    117.             goto out;
    118.         PRINTF("task name [%s] stack deep 0x%x\r\n",stack_all[index].task_name,stack_all[index].stack_deep);
    119.         //logctrl_instance()->is_sync = 1;
    120.         trace_byte_stream((uint8_t *)stack_all[index].stack_tail,stack_all[index].stack_deep);
    121.         //logctrl_instance()->is_sync = 0;
    122.     }
    123. out:
    124.     return 0;
    125. }

    126. LTSH_FUNCTION_EXPORT(stack,"show task statck info");
    127. #endif /* end of configUSE_STACK_MAX_USAGE */
    复制代码

    运行效果:



    stack.png


    可以检测堆栈最大使用率,我们来对比下不同的编译选项对堆栈的使用率的数据,我们使用的是gcc的编译器。


    o0 编译选项的资源及堆栈使用情况:
    O0.png

    o0s.png

    o1 编译选项的资源及堆栈使用情况:
    o1.png

    o1s.png


    o2 编译选项的资源及堆栈使用情况:
    O2.png

    O2S.png


    o3 编译选项的资源及堆栈使用情况:

    O3.png

    O3S.png


    oS 编译选项的资源及堆栈使用情况:

    os.png

    os1.png



    oG 编译选项的资源及堆栈使用情况:

    OG.png

    OGS.png




    =================代码如下=================



    该会员没有填写今日想说内容.
    回复

    使用道具 举报

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

    [LV.10]以坛为家III

    88

    主题

    4292

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9049
    最后登录
    2024-4-13
    发表于 2023-8-31 14:33:39 | 显示全部楼层
    这个功能很早就知道。可是一直没有使用过。
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    前天 13:25
  • 签到天数: 594 天

    [LV.9]以坛为家II

    51

    主题

    2222

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    7061
    最后登录
    2024-4-27
     楼主| 发表于 2023-8-31 14:49:24 | 显示全部楼层
    jobszheng5 发表于 2023-8-31 14:33
    这个功能很早就知道。可是一直没有使用过。

    功能不复杂实现起来也用不了多少代码量,集成在系统里作为个小工具使用
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

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

    [LV.10]以坛为家III

    88

    主题

    4292

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9049
    最后登录
    2024-4-13
    发表于 2023-8-31 17:49:06 | 显示全部楼层
    坦白讲,现在项目开发的,能在时间内完成,通过验证就够累的,没有时间进行二次优化重构了。
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-27 21:16 , Processed in 0.117651 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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