0

Is there a way to measure how much memory a specific c++ function consumes from the program stack from the time it's called until it returns?

WhatIf
  • 653
  • 2
  • 8
  • 18

3 Answers3

0

Suppose there's a function "testfunc", and we wish to find how much stackspace this fuction uses.....

#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;
 }

see here http://cboard.cprogramming.com/c-programming/90572-determine-functions-stack-size.html

wrangler
  • 3,454
  • 1
  • 19
  • 28
  • I read the forum post you provided the link to and some one mentioned that " There is no relationship between the order in which you declare local variables, and the relative positions of them on the stack." – WhatIf Oct 16 '15 at 16:29
  • You can try `gcc-S` to compile you code and then look for the size adjustment for `%esp` as mentioned in the link. – wrangler Oct 16 '15 at 16:37
  • Let me know if it was of any help. – wrangler Oct 16 '15 at 16:39
0

Clearly there is no portable way because compiler is allowed to do a lot with functions: from inlining to tail-call optimizing.

But strictly speaking there is nothing to measure as compiler knows this number exactly. Well, except when you're using stack arrays with non-constant sized (in C99 it's allowed, but not in C++).

One dumb way to find out is to look at the assembly code:

For example, this function:

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

is compiled on amd64 to:

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

So, in this example function f pushes to the stack 8-byte %rbp (old frame pointer), then it uses memory at %rbp-20, %rbp-24, %rbp-8 and %rbp-4. The max offset is -24. Total number of bytes used is 24 bytes plus 8 bytes for %rbp and 8 bytes that are not visible here for the return pointer, 40 bytes in total if I didn't forget anything.

I'm not sure if this is what you asked though.

0

The first thing to look at is the assembly output from the C compiler for the function and count up all the calls, pushes, and stack frames. Then you need to repeat for all the functions in the call tree, find the longest path, and the sum for it.

If you can run the function in a debugger, just note the value of the stack pointer before the function is called, step down into the function until you are at the deepest point and note the value of the stack pointer. This gives you a real-world number.

If you can recompile the entire function tree, add a simple prologue and epilogue macro that records the low-water mark for the stack pointer in a global variable. Recompile the entire project and run it. This gives you a real-world number for many iterations.

The problem becomes trickier if your function calls into third-party code into which you have no visibility. For this you can simply memset the stack memory beginning 40 (or so) bytes below the current stack point, call the function, then look for memory that has been untouched since the memset.

Something like (untested!):

EDIT: Ooops, forgot that the stack grows down...

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);
}

Keep in mind that hardware interrupts can also consume stack at random times. EDIT: This may not be a concern for user processes.

TeasingDart
  • 371
  • 1
  • 6
  • I don't think any OS you're likely to use consumes user stack on a hardware interrupt. General purpose OSes don't for two reasons: 1) If the user stack doesn't have enough room, terrible things would happen. 2) Unless the interrupt code sterilizes the stack, which is tricky and expensive, sensitive information would be available to the process that was interrupted. – David Schwartz Oct 16 '15 at 17:34
  • Does that also apply to "soft" interrupts such as timers? – TeasingDart Oct 16 '15 at 17:36