调查线程堆栈溢出
Investigating thread stack overflow
我在 运行 我的多线程嵌入式应用程序时遇到分段错误。 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
线程的堆栈使用量将是其 entry-point/thread 函数的堆栈使用量,再加上 OS 可能需要的任何线程开销的一些余量。
请注意,通过函数指针和递归调用会破坏此方法,因此您可能需要通过考虑被调用函数的堆栈使用情况和可能的递归深度来单独评估。
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 捕获故障时碰巧超出了边界多远,这很可能是在它访问外部时已分配的堆栈 space,在页面大小的分辨率内。它只是告诉你你的堆栈不够大 - 你已经知道了。
堆栈分析的“动态”技术是“超大”堆栈,用单字节值填充它,然后在 运行 代码之后通过一个测试序列,该测试序列旨在运行您检查的所有可能的调用路径堆栈区域以查看“高潮标记”相对于该区域开始的位置,然后相应地“调整大小”您的堆栈。这是一种常见的技术,但取决于运用所有可能的路径。通常会省略错误和异常处理路径,因此当您的代码正在尝试处理其他错误时,您最终可能会遇到堆栈溢出 - 这是有风险的。
我在 运行 我的多线程嵌入式应用程序时遇到分段错误。 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
线程的堆栈使用量将是其 entry-point/thread 函数的堆栈使用量,再加上 OS 可能需要的任何线程开销的一些余量。
请注意,通过函数指针和递归调用会破坏此方法,因此您可能需要通过考虑被调用函数的堆栈使用情况和可能的递归深度来单独评估。
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 捕获故障时碰巧超出了边界多远,这很可能是在它访问外部时已分配的堆栈 space,在页面大小的分辨率内。它只是告诉你你的堆栈不够大 - 你已经知道了。
堆栈分析的“动态”技术是“超大”堆栈,用单字节值填充它,然后在 运行 代码之后通过一个测试序列,该测试序列旨在运行您检查的所有可能的调用路径堆栈区域以查看“高潮标记”相对于该区域开始的位置,然后相应地“调整大小”您的堆栈。这是一种常见的技术,但取决于运用所有可能的路径。通常会省略错误和异常处理路径,因此当您的代码正在尝试处理其他错误时,您最终可能会遇到堆栈溢出 - 这是有风险的。