问题描述
我在运行多线程嵌入式应用程序时遇到分段错误。 GDB 提示我堆栈可能已损坏,这让我相信堆栈对于有问题的线程来说太小了。增加堆栈大小似乎消除了这个问题,但我想进一步确认一下。我在这里有哪些选择?是否可以在发生段错误时找出当前堆栈大小?
解决方法
在 gcc 中用 -fstack-usage
编译。这将导致为每个目标文件输出一个 .su 文件,其中包含每个函数的纯文本堆栈报告。喜欢:
main.c:36:6:bar 48 static
main.c:41:5:foo 88 static
main.c:47:5:main 8 static
然而,它只报告函数的堆栈帧,堆栈使用量是从该函数调用的每个函数的堆栈帧的总和。对所有可能的调用路径进行计算以确定任何非平凡应用程序的最坏情况堆栈深度是不切实际的 - 您需要查看可以检查调用图并使用 .su 数据为您解决的问题。 Here 是一个 perl 脚本示例,用于组合 objdump
的输出和 .su 文件以生成完整的堆栈报告,例如:
Func Cost Frame Height
------------------------------------------------------------------------
> main 176 12 4
foo 164 92 3
bar 72 52 2
> INTERRUPT 28 0 2
__vector_I2C1 28 28 1
foobar 20 20 1
R recursiveFunct 20 20 1
__vector_UART0 12 12 1
Peak execution estimate (main + worst-case IV):
main = 176,worst IV = 28,total = 204
线程的堆栈使用量将是其入口点/线程函数的堆栈使用量,加上操作系统可能需要的任何线程开销的一些余量。
请注意,通过函数指针和递归的调用会破坏此方法,因此您可能需要通过考虑调用函数的堆栈使用情况和可能的递归深度来单独评估。
How to determine maximum stack usage in embedded system with gcc? 的答案也可能有用。
为了帮助在运行时检测堆栈问题,https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Instrumentation-Options.html#Instrumentation-Options 提供了与堆栈检查和保护相关的各种检测选项
在您遇到段错误时知道“当前堆栈大小”并不是特别有用。它不会告诉您需要多少堆栈,它只会告诉您在 MMU 捕获故障时碰巧超出边界多远,这很可能是在它访问外部分配的堆栈空间,在页面大小的分辨率内。它只是告诉你你的筹码不够大——你已经知道了。
堆栈分析的“动态”技术是“超大”堆栈,用单字节值填充它,然后在通过测试序列运行代码后,测试序列旨在执行所有可能的调用路径,您检查堆栈区域以查看位置“高潮标记”是相对于该区域的开始,然后相应地“调整大小”您的堆栈。这是一种常见的技术,但取决于练习所有可能的路径。通常会省略错误和异常处理路径,因此您最终可能会遇到堆栈溢出,就在您的代码尝试处理其他错误时 - 这有风险。