嵌入式程序栈使用率测量问题:填充哑值结果与预期不符
问题描述
我正在测量嵌入式程序的最大栈使用率。通过生成.su文件,估算出最大栈使用率约为35523B。为验证该数值,我采用栈填充哑值的方法:已知估算值约35KB,故定义STACK_SIZE为40KB,在startup.c中调用main前,将STACK_START到STACK_END的栈空间填充为0XDEADBEEF。运行程序后,通过StackCount函数统计栈使用率,但结果与35523B不符,且修改STACK_SIZE为其他值时,结果每次都会变化。
填充栈的代码:
src = STACK_START; dst = STACK_END; while(src < dst){ *(src++) = 0xDEADBEEF;
统计栈使用率的代码:
uint16_t StackCount(void) { volatile unsigned long *start, *end; start = STACK_START; end = STACK_END; uint32_t countBytes = 0; while((*start == 0xDEADBEEF && start < end)) { start++; countBytes++; } printf("stack used:%d bytes \n",STACK_SIZE-countBytes); printf("remaining stack :%d bytes\n",countBytes); return c; }
错误分析与修正方案
1. 栈填充逻辑的核心错误
- 循环未闭合:原填充代码缺少
},会导致编译错误或未定义行为。 - 栈生长方向搞反:多数嵌入式CPU的栈是从高地址向低地址生长的,如果
STACK_START是栈的高地址起始点、STACK_END是低地址边界,原循环src < dst的判断完全错误,应该改为src > dst,从高地址向低地址填充。 - 类型未明确:
src和dst未声明为unsigned long*类型,会导致地址操作的类型不匹配问题。
2. 栈统计的计算与逻辑错误
- 生长方向判断错误:统计时应该从栈的高地址起始点
STACK_START向下(低地址)查找第一个非0xDEADBEEF的位置,而不是向上递增查找。 - 字节数计算错误:
countBytes每次递增1,但unsigned long是4字节(多数架构),剩余栈字节数应该是countWords * sizeof(unsigned long),原代码直接用计数当作字节数,结果会缩小为实际值的1/4。 - 返回值未定义:函数最后返回
c,但c从未声明,属于未定义行为。
3. STACK_SIZE与地址边界不匹配
如果STACK_SIZE是栈的总字节数,必须保证STACK_END = STACK_START - STACK_SIZE(栈高地址起始的场景),否则STACK_SIZE和实际填充的栈空间大小不对应,修改STACK_SIZE时结果必然混乱。
修正后的示例代码
栈填充代码(栈从高地址向低地址生长场景)
// 明确指针类型,避免类型不匹配 volatile unsigned long *src = (volatile unsigned long*)STACK_START; volatile unsigned long *dst = (volatile unsigned long*)STACK_END; // 从高地址向低地址填充哑值 while(src > dst){ *(src--) = 0xDEADBEEF; }
栈统计代码
uint32_t StackCount(void) { volatile unsigned long *start = (volatile unsigned long*)STACK_START; volatile unsigned long *end = (volatile unsigned long*)STACK_END; uint32_t countWords = 0; // 从栈起始高地址向下查找第一个被覆盖的位置 while(start > end && *start == 0xDEADBEEF) { start--; countWords++; } // 转换为字节数计算 uint32_t remainingBytes = countWords * sizeof(unsigned long); uint32_t usedBytes = STACK_SIZE - remainingBytes; printf("stack used:%d bytes \n", usedBytes); printf("remaining stack :%d bytes\n", remainingBytes); return usedBytes; }
额外注意事项
- 确认链接脚本中的栈定义:比如多数场景下,栈的起始地址是
_estack,STACK_END需计算为(uint8_t*)_estack - STACK_SIZE,保证和STACK_SIZE对应。 - 统计时机:必须在程序运行过所有最坏场景(如中断嵌套、最深函数调用)后再调用统计函数,否则得到的不是最大栈使用率。
- 编译器优化:保持
volatile修饰指针,避免填充或统计代码被编译器优化掉。
内容的提问来源于stack exchange,提问作者shakingwindow
相关产品推荐
相关产品推荐

