运行多线程嵌入式应用程序时遇到分段错误。GDB 提示我堆栈可能已损坏,这使我相信堆栈对于有问题的线程来说太小了。增加堆栈大小似乎可以解决问题,但我想进一步确认一下。我在这里有什么选择?是否可以在发生段错误时找出当前堆栈大小?
1 回答
在 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 数据为您解决问题。 下面是一个 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
您的线程的堆栈使用将是其入口点/线程函数的堆栈使用,加上操作系统可能需要的任何线程开销的一些余量。
请注意,通过函数指针和递归调用会破坏此方法,因此您可能需要通过考虑调用函数的堆栈使用情况和可能的递归深度来单独评估它。
如何使用 gcc 确定嵌入式系统中的最大堆栈使用量的答案?也可能有用。
为了帮助在运行时检测堆栈问题,在https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Instrumentation-Options.html#Instrumentation-Options上有各种与堆栈检查和保护相关的检测选项
在遇到段错误时了解“当前堆栈大小”并不是特别有用。它不会告诉你需要多少堆栈,它只会告诉你在 MMU 捕获故障时它碰巧超出了界限,这很可能是在它访问外部时分配的堆栈空间,在页面大小的分辨率范围内。它只是告诉你你的筹码不够大——你已经知道了。
堆栈分析的“动态”技术是“超大”堆栈,用单字节值填充它,然后在运行代码通过测试序列后,该测试序列旨在测试所有可能的调用路径,检查堆栈区域以查看“高-tide mark”相对于区域的开始,然后相应地“调整大小”您的堆栈。这是一种常见的技术,但取决于执行所有可能的路径。通常会省略错误和异常处理路径,因此您最终可能会遇到堆栈溢出,就在您的代码尝试处理其他错误时 - 这是有风险的。