测量峰值堆栈指针值及其 PC 位置

Posted

技术标签:

【中文标题】测量峰值堆栈指针值及其 PC 位置【英文标题】:Measure the peak stackpointer value and its PC location 【发布时间】:2017-11-05 17:50:03 【问题描述】:

为了分析不同的二进制文件,我需要测量实际堆栈内存使用的峰值(不仅仅是保留的堆栈页面,而是实际使用的内存)。我正在使用 gdb 尝试以下操作

watch $sp
commands
silent
if $sp < $spnow
  set $spnow=$sp
  set $pcnow=$pc
  print $spnow
  print $pcnow
  end
c

当应用于ls时,这似乎“有效”,除了即使对于像ls这样的短期运行程序,它实际上似乎并没有进展,但它卡在了函数中比如“in strcoll_l() from /usr/lib/libc.so.6”。这种方法可能太慢了。

我还研究了 valgrind massif 工具。它可以分析堆栈使用情况,但不幸的是似乎无法报告在程序的哪个部分遇到了峰值使用情况。

【问题讨论】:

如果massif 对您来说足够快,您应该考虑修补它以在每次超过最大值时捕获堆栈跟踪。这是 valgrind,构建块应该都在那里。 【参考方案1】:

为了分析不同的二进制文件,我需要测量实际堆栈内存使用的峰值

您的 GDB 方法

仅适用于单线程程序 太慢了,不实用(watch $sp 命令强制 GDB 单步执行您的程序)。

如果您只关心页面粒度上的堆栈使用情况(我认为您应该——真的程序使用 1024 字节还是 2000 字节的堆栈?),那么一个更快的方法就是循环运行程序,在程序成功运行时减少它的ulimit -s(你也可以二分查找,例如从默认8MB开始,然后尝试4、2、1、512K等直到失败,然后增加堆栈限制以找到确切的值)。

对于/bin/ls

bash -c 'x=4096; while /bin/ls > /dev/null; do
         echo $x; x=$(($x/2)); ulimit -s $x || break; done'
4096
2048
1024
512
256
128
64
32
bash: line 1: 109951 Segmentation fault      (core dumped) /bin/ls > /dev/null

然后您可以通过查看core 转储找到$PC

我需要精确的限制,因为我想弄清楚哪些编译器优化会导致堆栈使用发生哪些细微变化(即使在字节范围内。以及 .data 和 .text 大小)。

我认为尝试这样做是愚蠢的。

根据我的经验,堆栈使用受编译器内联决策的影响最大。这些反过来又受精确的编译器版本和调整、运行时信息的存在(用于配置文件引导的优化)以及被优化程序的精确来源的影响最大。

对内联决策进行是/否更改会使递归程序中的堆栈使用量增加 100 KB,而对上述任何因素的微小更改都可以改变该决策。

【讨论】:

感谢您的想法。我需要精确的限制,因为我想弄清楚哪些编译器优化会导致堆栈使用发生哪些微变化(即使在字节范围内。以及 .data 和 .text 大小)。我想知道.. 使用perf 并检查页面错误(或触发新堆栈内存映射的任何事件)是否有效,以便在堆栈向下增长一页时得到通知?这应该会产生类似的结果 我同意您的编辑。我想仅测量一组特定程序(部分)的数字,以便将一些数字放入此表中,然后辩称由于您所说的原因,这不是一个真正有用的数字。但我认为努力并没有真正得到回报,所以我会放弃我的计划。 @JohannesSchaub-litb 如果您只记录 sp 的值,x86-64 上的红色区域也会使您的测量结果失效。函数最多可以使用超出 sp 的 128 个字节用于本地存储。因此,为了彻底,您需要查看访问该范围内内存的机器代码,可能使用来自 rsp 的负偏移量,也可能使用来自其他寄存器的偏移量。 @mark 啊我明白了。所以我想我会回退到定性地讨论效果(如果我们阻止内联、持续传播等会发生什么变化),而不仅仅是测量。我想这将更加科学真实。我正在尝试做的实际事情是研究它如何与影响更严重的高级综合后端结果进行互操作/比较。

以上是关于测量峰值堆栈指针值及其 PC 位置的主要内容,如果未能解决你的问题,请参考以下文章

如何知道堆栈指针是不是已到达 SRAM 中的最后一个位置?

java数据的5种存储位置(转)

数据压入堆栈时,ESP寄存器的指向?

FFT 是不是需要在音频文件上找到峰值和凹点

Keil C51 中堆栈指针的问题

JProfiler 可以测量堆栈深度吗?