调查线程堆栈溢出

问题描述

我在运行多线程嵌入式应用程序时遇到分段错误。 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 捕获故障时碰巧超出边界多远,这很可能是在它访问外部分配的堆栈空间,在页面大小的分辨率内。它只是告诉你你的筹码不够大——你已经知道了。

堆栈分析的“动态”技术是“超大”堆栈,用单字节值填充它,然后在通过测试序列运行代码后,测试序列旨在执行所有可能的调用路径,您检查堆栈区域以查看位置“高潮标记”是相对于该区域的开始,然后相应地“调整大小”您的堆栈。这是一种常见的技术,但取决于练习所有可能的路径。通常会省略错误和异常处理路径,因此您最终可能会遇到堆栈溢出,就在您的代码尝试处理其他错误时 - 这有风险。