当需要了解应用中,预留的STACK空间是否合理,或者是否足够时,可以按如下 步骤,来实现STACK使用率检测。 实现原理: (1)代码运行前,对STACK空间,除栈顶小部分外,其它全部初始化为全00。 (2)通过Map文件,核对Stack栈底和栈底符号。 据此可以计算实现栈区大小。 (3)栈使用是从栈顶(高地址)往低地址方向增长。被使用过的栈区数据大概率被修改为非0。 因此,定期(如4秒)从栈底Bottom往栈顶方向,读内存数据,碰到任何一个非0数据,就代表当前栈已经触及此处,非0处即为当前栈使用的最大值。通过此值可以计算使用率。 (4)优化方向:当前使用全测的方法,好处时准确度高。 劣势为占用时间稍长。不适合频繁调用。 可以按2分法方向优化,读取部分连续数据。当前例程基于6626 Keil工程。其它项目可以类同参考。 以上是步骤 步骤一: 确认栈底栈顶的符号 #define __STACK_SIZE 0x6000 步骤二: 在sysstemInit() 函数中,添加下面if 1 部分代码,用于初始化栈区为全0。(除栈顶0x200) void SystemInit(void) { #if defined(CONFIG_XIP_FLASH_ALL) drv_icache_enable(); #endif #if defined(__VTOR_PRESENT) && (__VTOR_PRESENT == 1U) SCB->VTOR = (uint32_t)&(__VECTOR_TABLE[0]); #endif #if defined(__FPU_USED) && (__FPU_USED == 1U) SCB->CPACR |= ((3U << 10U * 2U) | /* enable CP10 Full Access */ (3U << 11U * 2U)); /* enable CP11 Full Access */ #endif #ifdef UNALIGNED_SUPPORT_DISABLE SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk; #endif #if 1 //初始化栈空间 // 声明外部链接器定义的栈顶和栈底符号 // Image$$ARM_LIB_STACK$$ZI$$Limit表示栈顶地址(栈空间的最高地址) // Image$$ARM_LIB_STACK$$ZI$$Base表示栈底地址(栈空间的最低地址) extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Limit; extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base; // 定义栈顶和栈底指针变量 uint32_t *pStack_Top; uint32_t *pStack_Bottom; // 注意:这里"Buttom"应该是"Bottom"的拼写错误 // 将链接器定义的符号地址赋值给指针变量 pStack_Top = (uint32_t *)&Image$$ARM_LIB_STACK$$ZI$$Limit; pStack_Bottom = (uint32_t *)&Image$$ARM_LIB_STACK$$ZI$$Base; // 清空栈空间内容,但保留顶部0x200字节不清理 // 计算需要清理的栈空间大小:栈顶地址 - 栈底地址 - 0x200 // 这通常是为了保留栈顶的一些关键数据或防止完全清空栈导致的问题 memset((uint8_t *)pStack_Bottom, 0x00, (uint32_t)pStack_Top - (uint32_t)pStack_Bottom-0x200); #endif #if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) TZ_SAU_Setup(); #endif drv_pmu_init(); } 步骤3: 实现栈查询功能函数 // 栈使用情况检测函数,用于计算当前栈空间的使用百分比 static uint8_t stack_usage_get(void) { // 定义栈顶和栈底指针以及相关计算变量 uint32_t *pStack_Top; // 栈顶指针(栈空间的最高地址) uint32_t *pStack_Bottom; // 栈底指针(栈空间的最低地址) uint32_t stack_size; // 栈总大小(字节数) uint32_t used_words = 0; // 已使用的栈空间大小(按字计算) uint32_t total_words; // 栈总大小(按字计算) uint8_t stack_usage; // 栈使用百分比 #if 1 // for Keil arm clang compiler // 声明外部链接器定义的栈顶和栈底符号 extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Limit; // 栈顶地址符号 extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base; // 栈底地址符号 // 获取栈顶和栈底地址 pStack_Top = (uint32_t *)&Image$$ARM_LIB_STACK$$ZI$$Limit; pStack_Bottom = (uint32_t *)&Image$$ARM_LIB_STACK$$ZI$$Base; // 计算栈总大小和总字数 stack_size = (uint32_t)pStack_Top - (uint32_t)pStack_Bottom; // 栈大小(字节) total_words = stack_size / sizeof(uint32_t); // 栈大小(字) // 打印调试信息:栈顶地址、栈底地址、栈大小等 OM_LOG_DEBUG("\n\npStack_Top=0x%x,pStack_Bottom=0x%x", pStack_Top, pStack_Bottom); OM_LOG_DEBUG("\nstack_size=0x%x,total_words=0x%x\n\n", (uint32_t)stack_size, (uint32_t)total_words); #else #endif // 从栈底开始检查栈使用情况 uint32_t *stack_ptr = pStack_Bottom; // 从栈底开始检查,找到第一个非0x00000000的值 // 栈通常初始化为0x00000000(在使用前清空栈空间) while (stack_ptr < pStack_Top) { // 检查当前位置是否为0x00000000 if (*stack_ptr != 0x00000000) { // 计算已使用的栈空间大小(从栈顶到第一个非0值的位置) used_words = (uint32_t)pStack_Top - (uint32_t)stack_ptr; break; } stack_ptr++; } // 如果整个栈都是0x00000000,则几乎没有使用栈空间 if (stack_ptr == pStack_Top) { used_words = 0; } // 计算栈使用百分比:已使用字数 / 总字数 * 100% stack_usage = (uint8_t)((used_words * 100) / total_words); // 打印调试信息:已使用字数和栈使用百分比 OM_LOG_DEBUG("\nused_words=0x%x\/0x%x", used_words,total_words); OM_LOG_DEBUG("\nstack_usage = %d%% \n", stack_usage); // 返回栈使用百分比 return stack_usage; } 步骤4: 在某个定时器或者其它任何指令触发处调用即可。 static void simpler_timer1_cb(evt_timer_t *timer, void *param) { auto_connect_test(); // 自动连接测试 stack_usage_get(); } 测试Log输出参考(红色部分) used_words=0x200/0x1800 stack_usage = 8% [dbg]uart_state_update|222 : Uart ON_0 uart_dma_noblock_init[dbg]spi_dma_init|122 : spi_dma_init [dbg]get_slp_uart_state|167 : slp=0 pStack_Top=0x200136a0,pStack_Buttom=0x2000d6a0 stack_size=0x6000,total_words=0x1800 used_words=0x390/0x1800 stack_usage = 14% cid=0,at_len=19,left=0,sof=0[inf]cmd_CMD_CONN_handle|615 : addr type: public connect to peer add ->CE8B9499DDFB [dbg]user_scan_set|502 : 00:00:00:00:00:00 [dbg]user_scan_set|503 : scan type=1 [inf]user_scan_start|535 : scanning 1 start scan go..[inf]cmd_CMD_CONN_handle|647 : user_scan_star used_words=0x430/0x1800 stack_usage = 17% cid=1,at_len=19,left=0,sof=0[inf]cmd_CMD_CONN_handle|615 : addr type: public connect to peer add ->CE8B9499DDFB [dbg]user_scan_set|502 : 00:00:00:00:00:00 [dbg]user_scan_set|503 : scan type=1 [inf]user_scan_start|535 : scanning 1 start scan go..[inf]cmd_CMD_CONN_handle|647 : user_scan_start [dbg]app_scan_event_cb|468 : Timeout scan finished======================0,1 [inf]mw_SCAN_Result|497 : scan_dev_count:0 pStack_Top=0x200136a0,pStack_Buttom=0x2000d6a0 stack_size=0x6000,total_words=0x1800 used_words=0x430/0x1800 stack_usage = 17% cid=2,at_len=19,left=0,sof=0[inf]cmd_CMD_CONN_handle|615 : addr type: public connect to peer add ->CE8B9499DDFB [dbg]user_scan_set|502 : 00:00:00:00:00:00 [dbg]user_scan_set|503 : scan type=1 [inf]user_scan_start|535 : scanning 1 start scan go..[inf]cmd_CMD_CONN_handle|647 : user_scan_start [dbg]app_scan_event_cb|468 : Timeout scan finished======================0,2 [inf]mw_SCAN_Result|497 : scan_dev_count:0 pStack_Top=0x200136a0,pStack_Buttom=0x2000d6a0 stack_size=0x6000,total_words=0x1800 used_words=0x430/0x1800 stack_usage = 17% 按最高负荷使用一段时间后,观察最终使用率,即为最高使用率。 一般最高使用率,最好不要超过90%。 |