【问题标题】:Is there a way to measure how much memory a specific function consumes from stack?有没有办法测量特定函数从堆栈中消耗了多少内存?
【发布时间】:2015-10-16 16:02:31
【问题描述】:

有没有办法测量一个特定的 c++ 函数从调用到返回时从程序堆栈中消耗了多少内存?

【问题讨论】:

  • @Ben:你为什么不自己检查一下?
  • @KarolyHorvath 我做了......它是......
  • 这是用于 PC 还是嵌入式/微控制器环境?
  • 适用于 PC Windows 或 Linux

标签: c++ stack-overflow


【解决方案1】:

假设有一个函数“testfunc”,我们想知道这个函数使用了多少堆栈空间.....

#include <stdio.h>
#include <stddef.h>
ptrdiff_t testfunc (int arg1, int arg2, char *stackbase); 

int main()
{
char *stackbase;
printf("\nThe amount of stack space used by \"testfunc\" is : %ul       bytes\n",testfunc(10, 5, stackbase));

return 0;
}

ptrdiff_t testfunc (int arg1, int arg2, char *stackbase)
{
 //.
 //all function processing goes here
 //.
 //.
 char temp;
 return stackbase - &temp;
 }

看这里 http://cboard.cprogramming.com/c-programming/90572-determine-functions-stack-size.html

【讨论】:

  • 我阅读了您提供链接的论坛帖子,有人提到“您声明局部变量的顺序与它们在堆栈中的相对位置之间没有关系。”
  • 您可以尝试gcc-S 编译您的代码,然后查找链接中提到的%esp 的大小调整。
  • 如果有帮助请告诉我。
【解决方案2】:

显然没有可移植的方式,因为允许编译器对函数做很多事情:从内联到尾调用优化。

但严格来说,没有什么可衡量的,因为编译器完全知道这个数字。好吧,除非您使用非常量大小的堆栈数组(在 C99 中允许,但在 C++ 中不允许)。

一种愚蠢的方法是查看汇编代码:

例如这个函数:

int f(int x, int y)
{
  int z = x + y;
  int h = z - 2;
  return h;
}

在amd64上编译为:

f:
.LFB0:
.cfi_startproc
pushq   %rbp              ; save the pointer to the caller's frame
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp        ; new frame starts from %rsp (current stack ptr)
                          ; starting from this place look how %rbp is used
                          ; the maximum offset is what you're looking for
.cfi_def_cfa_register 6
movl    %edi, -20(%rbp) 
movl    %esi, -24(%rbp)
movl    -20(%rbp), %edx
movl    -24(%rbp), %eax
addl    %edx, %eax
movl    %eax, -8(%rbp)
movl    -8(%rbp), %eax
subl    $2, %eax
movl    %eax, -4(%rbp)
movl    -4(%rbp), %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret

因此,在此示例中,函数 f 将 8 字节 %rbp(旧帧指针)推送到堆栈,然后它使用 %rbp-20、%rbp-24、%rbp-8 和 %rbp-4 处的内存。最大偏移量为 -24。 使用的总字节数是 24 字节加上 8 字节用于 %rbp 和 8 字节在这里不可见的返回指针,如果我没有忘记任何东西,总共 40 字节。

我不确定这是不是你问的。

【讨论】:

    【解决方案3】:

    首先要查看的是 C 编译器为函数输出的汇编输出,并计算所有调用、推送和堆栈帧。然后你需要对调用树中的所有函数重复,找到最长的路径,以及它的总和。

    如果您可以在调试器中运行该函数,只需在调用该函数之前记下堆栈指针的值,然后进入该函数,直到您处于最深点并记下堆栈指针的值。这会为您提供一个真实的数字。

    如果您可以重新编译整个函数树,请添加一个简单的序言和尾声宏,用于在全局变量中记录堆栈指针的低水位标记。重新编译整个项目并运行它。这为您提供了多次迭代的真实数字。

    如果您的函数调用您无法查看的第三方代码,问题就会变得更加棘手。为此,您可以简单地 memset 堆栈内存,从当前堆栈点以下 40(左右)字节开始,调用函数,然后查找自 memset 以来未触及的内存。

    类似的东西(未经测试!):

    编辑:哎呀,忘了堆栈增长了...

    int StackTest() {
      //marker is on the stack.
      //"volatile" prevents it from optimized into a register.
      volatile unsigned int marker= 0xDEADBEEF;
    
      //The current stack pointer should be just below marker.
      //I add 10*4 to move well below the current stack frame.
      //If the program crashes at this point, try increasing
      //the size of the buffer zone.
      char *pStack= (char*)&marker[-10];
    
      //I zap the unused stack space to a recognizable value.
      //The fill bytes will be overwritten as the stack is used.
      //The 4096 number may need to be adjusted; it needs to be
      //larger than the stack bytes used but less than the total
      //stack space remaining.
      memset(pStack-4096,0xCD,4096);
    
      //Now I call my target function.
      function();
    
      //Now I search for the first 8 fill bytes in a row.
      //This number may need to be increased to rule out
      //false positives, such as buffers (arrays) allocated
      //on the stack but not completely filled, which leaves
      //fill bytes untouched inside the buffer.
      for(n1=0,matchCt=0;n1<4096 && matchCt<8;n1++) {
        if(*(pStack-n1)==0xCD)
          matchCt++;
        else
          matchCt= 0;
      }
    
      int stackUsed= n1-matchCt;
      printf("Stack used: %d bytes.\n",stackUsed);
      return(stackUsed);
    }
    

    请记住,硬件中断也可以随机消耗堆栈。 编辑:这可能不是用户进程的问题。

    【讨论】:

    • 我认为您可能使用的任何操作系统都不会在硬件中断时消耗用户堆栈。通用操作系统不这样做有两个原因:1)如果用户堆栈没有足够的空间,就会发生可怕的事情。 2) 除非中断代码对堆栈进行消毒,这是棘手且昂贵的,否则敏感信息将可用于被中断的进程。
    • 这是否也适用于定时器等“软”中断?
    猜你喜欢
    • 1970-01-01
    • 2015-01-28
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 2020-09-11
    • 2010-10-11
    相关资源
    最近更新 更多