简介:
嵌入式开发中我们通常对任务栈的大小设置通常根据经验值来设定,设置的小了会造成栈溢出,设置打了会造成资源的浪费。如果能够计算出栈在任务过程中使用的最大深度的话对就可以根据实际情况来设置避免资源的浪费。计算任务栈的使用大小通常使用水印法,初始化时将栈设置为特定的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 的配置文件中定义该宏即可。
FreeRTOS 中记录栈地址的最高地址默认在TCB 中是关闭的,我们需要开启configRECORD_STACK_HIGH_ADDRESS配置宏添在TCB中添加该成员变量。
修改代码在freertos配置文件中添加上诉配置选项
2.获取tcb信息中的栈的起始地址和栈深度信息
我们使用Freertos 的hook 函数在创建任务时将任务的TCB添加到本地list 中后续计算时可以从中获取栈的起始及结束地址信息。对应hook 函数代码如下
# define traceTASK_CREATE( pxNewTCB ) \ __freertos_evr_on_create_task((void*)pxNewTCB) rt_list_t tasklist = RT_LIST_OBJECT_INIT(tasklist); void __freertos_evr_on_create_task(void * pxNewTCB){ tskTCBList * node; /* malloc a node */ node = pvPortMalloc(sizeof(tskTCBList)); if(node != NULL) { rt_list_init(&node->list); node->tcb = (TCB_t *)pxNewTCB; node->cycle = &(((struct __task_cycle_info_t *)node->tcb->pxStack)->tInfo); init_task_cycle_counter1((void *)node->cycle); rt_list_insert_before(&tasklist,&node->list); } }
3.计算栈的非踩踏区域的长度和栈深度计算栈的最大使用率
根据上述的任务栈的信息我们就可以runtime 过程中查看任务栈使用的最大深度信息,添加如下stack 命令查看任务栈的信息。
#if (1 == configUSE_PERF_STACK) 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; } unsigned int stack(char argc,char ** argv) { /* Thread list */ rt_list_t * pos; tskTCBList * node; int stackfree = 0; printf("%-16s %-8s %-8s %-8s %-10s~%-10s\r\n","name","deep","used","usage(%)","stack_s","stack_e"); rt_list_for_each(pos,&tasklist) { node = rt_list_entry(pos,tskTCBList,list); stackfree = buff_continuous_numbers((uint8_t *)((uint32_t)(node->tcb->pxStack) + sizeof(struct __task_cycle_info_t)),0xa5); stackfree += sizeof(struct __task_cycle_info_t); printf("%-16s %-8d %-8d %-8d 0x%08x~0x%08x\r\n",node->tcb->pcTaskName, (uint32_t)node->tcb->pxEndOfStack - (uint32_t)(node->tcb->pxStack) + 1, (uint32_t)node->tcb->pxEndOfStack - (uint32_t)(node->tcb->pxStack) + 1 - stackfree, ((uint32_t)node->tcb->pxEndOfStack - (uint32_t)(node->tcb->pxStack) + 1 - stackfree)*100/((uint32_t)node->tcb->pxEndOfStack - (uint32_t)(node->tcb->pxStack) + 1), (uint32_t)(node->tcb->pxStack),(uint32_t)node->tcb->pxEndOfStack); } printf("\r\nHeap Total:%d\tFree:%d\r\n",configTOTAL_HEAP_SIZE,xPortGetFreeHeapSize()); return 1; } LTSH_FUNCTION_EXPORT(stack,"show stack usage"); #endif
运行验证:
串口输入stack 命令即可打印出任务栈的最大使用深度及栈free 空间大小。