简介:
嵌入式开发中我们通常对任务栈的大小设置通常根据经验值来设定,设置的小了会造成栈溢出,设置打了会造成资源的浪费。如果能够计算出栈在任务过程中使用的最大深度的话对就可以根据实际情况来设置避免资源的浪费。计算任务栈的使用大小通常使用水印法,初始化时将栈设置为特定的magic ,栈内未使用的部分会时连续的magic,连续magic 的大小和栈的总深度的比较即可计算出栈的最大使用率。
最近在学习RISC-V架构的知识,本地使用的开发板为GD32VF103的芯片,在上面运行的FreeRtos 系统,我们在上面实现任务栈的使用最大深度计算的功能。
实现方式:
本次实现基于freertos来实现,上述的实现方式主要需要实现如下几个部分:
1.创建任务的时候将任务栈初始化为特定的magic(0xa5)
2.将任务tcb信息中的栈的起始地址和栈深度缓存下来
3.通过tcb 中的栈分配信息计算出栈连续的magic0xa5 长度得出未使用区域长度从而计算出使用率信息。
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 的配置文件中定义该宏即可,为了不影响内核原有配置,我们只是想初始化栈的内容为特定的magic 值,我们添加新的配置项目开启tskSET_NEW_STACKS_TO_KNOWN_VALUE 。
FreeRTOS 中记录栈地址的最高地址默认在TCB 中是关闭的,我们需要开启configRECORD_STACK_HIGH_ADDRESS配置宏添在TCB中添加该成员变量。
2.将任务tcb信息中的栈的起始地址和栈深度缓存下来
我们使用Freertos 的hook 函数在创建任务时将任务的TCB添加到本地list 中后续计算时可以从中获取栈的起始及结束地址信息。
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
至此就完成了任务栈使用的最大深度检测功能。