在线时间1364 小时
UID3338155
注册时间2017-7-26
NXP金币1627
TA的每日心情 | 奋斗 前天 13:25 |
---|
签到天数: 594 天 [LV.9]以坛为家II
金牌会员
- 积分
- 7061
- 最后登录
- 2024-4-27
|
本帖最后由 andeyqi 于 2023-12-8 13:30 编辑
简介:
嵌入式开发中我们通常对任务栈的大小设置通常根据经验值来设定,设置的小了会造成栈溢出,设置打了会造成资源的浪费。如果能够计算出栈在任务过程中使用的最大深度的话对就可以根据实际情况来设置避免资源的浪费。计算任务栈的使用大小通常使用水印法,初始化时将栈设置为特定的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
复制代码 对应配置代码如下:
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"
- #include "fsl_debug_console.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[configMAX_TASK_NAME_LEN];
- uint32_t stack_deep;
- uint32_t* stack_tail;
- };
- #define OS_NUM_TASKS 4
- static struct task_stack_info stack_all[OS_NUM_TASKS+2]; //add timer & idle
- void task_create_hook_stack(char * name ,unsigned int deep,unsigned int * stack_tail)
- {
- static uint8_t i_loop = 0;
- uint8_t len,i;
- if(i_loop < OS_NUM_TASKS+2)
- {
- strcpy(stack_all[i_loop].task_name,name);
- if((len = strlen(stack_all[i_loop].task_name)) != (configMAX_TASK_NAME_LEN-1))
- {
- for(i = len;i < configMAX_TASK_NAME_LEN-1;i++)
- stack_all[i_loop].task_name[i] = '\0';
- }
- stack_all[i_loop].task_name[configMAX_TASK_NAME_LEN-1] = '\0';
- stack_all[i_loop].stack_deep = deep*4;
- stack_all[i_loop].stack_tail = stack_tail;
- /* if use pref to set perf default value */
- #if ( configUSE_PERF_CNT == 1 )
- memset((void*)stack_all[i_loop].stack_tail,0,sizeof(task_cycle_info_t)+8);
- #endif
- i_loop++;
- }
- else
- {
- PRINTF("array is overflow \r\n");
- }
- }
- #if 0
- void get_cur_thread_stack_info(uint32_t sp, uint32_t *start_addr, size_t *size)
- {
- uint8_t i_loop = 0;
- extern __no_init volatile char switchintaskname[configMAX_TASK_NAME_LEN];
- for(i_loop = 0;i_loop< OS_NUM_TASKS+2;i_loop++)
- {
- if(strcmp(stack_all[i_loop].task_name,(const char *)switchintaskname) == 0)
- break;
- 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))
- break;
- }
- /* find stack info update */
- if(i_loop != OS_NUM_TASKS+2)
- {
- *start_addr = (uint32_t)stack_all[i_loop].stack_tail;
- *size = stack_all[i_loop].stack_deep;
- }
- else
- {
- *size = 0;
- }
- }
- #endif
- static int buff_continuous_numbers(uint8_t * buff,uint8_t data)
- {
- int l = 0;
- if(NULL == buff)
- return 0;
- while(data == buff[l++]);
- 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[configMAX_TASK_NAME_LEN];
- if(argc == 1)
- {
- //logctrl_instance()->is_sync = 1;
- PRINTF("taskname\tdeep\tused\taddress\t\t\tusage\r\n");
- for(i = 0;i < OS_NUM_TASKS+2;i++)
- {
- #if ( configUSE_PERF_CNT != 1 )
- len = buff_continuous_numbers((uint8_t *)stack_all[i].stack_tail,0xa5);
- #else
- len = buff_continuous_numbers((uint8_t *)&stack_all[i].stack_tail[WORD_OF_CYCEL_INFO],0xa5);
- if(len)
- len += BYTE_OF_CYCEL_INFO;
- #endif
- strcpy(task_name,stack_all[i].task_name);
- task_name[configMAX_TASK_NAME_LEN - 1] = '\0';
- if((name_len = strlen(stack_all[i].task_name)) != (configMAX_TASK_NAME_LEN-1))
- {
- for(j = name_len;j < configMAX_TASK_NAME_LEN-1;j++)
- task_name[j] = ' ';
- }
- 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));
- }
- 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[1]);
- if(index >= OS_NUM_TASKS+2)
- goto out;
- PRINTF("task name [%s] stack deep 0x%x\r\n",stack_all[index].task_name,stack_all[index].stack_deep);
- //logctrl_instance()->is_sync = 1;
- trace_byte_stream((uint8_t *)stack_all[index].stack_tail,stack_all[index].stack_deep);
- //logctrl_instance()->is_sync = 0;
- }
- out:
- return 0;
- }
- LTSH_FUNCTION_EXPORT(stack,"show task statck info");
- #endif /* end of configUSE_STACK_MAX_USAGE */
复制代码
运行效果:
可以检测堆栈最大使用率,我们来对比下不同的编译选项对堆栈的使用率的数据,我们使用的是gcc的编译器。
o0 编译选项的资源及堆栈使用情况:
o1 编译选项的资源及堆栈使用情况:
o2 编译选项的资源及堆栈使用情况:
o3 编译选项的资源及堆栈使用情况:
oS 编译选项的资源及堆栈使用情况:
oG 编译选项的资源及堆栈使用情况:
=================代码如下=================
|
|