是否有一种方法可以测量特定函数从堆栈中消耗了多少内存?

Is there a way to measure how much memory a specific function consumes from stack?

本文关键字:堆栈 函数 内存 多少 一种 测量 方法 是否      更新时间:2023-10-16

是否有一种方法可以测量一个特定的c++函数从被调用到返回时从程序堆栈中消耗了多少内存?

假设有一个函数"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       bytesn",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

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

但严格来说,没有什么可测量的,因为编译器知道这个数字。好吧,除非你使用非常量大小的堆栈数组(在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字节加上%rbp的8字节和这里不可见的返回指针的8字节,如果我没有忘记任何东西,总共40字节。

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

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

如果可以在调试器中运行该函数,只需在调用该函数之前记录堆栈指针的值,逐步进入该函数,直到到达最深处,并记录堆栈指针的值。这给你一个真实世界的数字。

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

如果您的函数调用到您不可见的第三方代码,则问题变得更加棘手。为此,您可以简单地从当前堆栈点以下40(或左右)字节开始memset堆栈内存,调用该函数,然后查找自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);
}

请记住,硬件中断也可以在随机时间消耗堆栈。编辑:这可能不是用户进程所关心的。