How much stack and heap (in bytes) is required by

2019-09-21 03:58发布

The implementation of a function sum in “C” is as follows:

int sum(int b[], int c)
{
    int s,i;

    if (c<0)
    {
            printf("ERROR\n");
    }
    s = 0;

    for(i=0; i<c; ++i)
    {
        s = s + b[i];
    }
    return s;
}

I'd like to know, How much stack and heap in bytes is required by the function sum in X86 Linux platform? How to know this?

Is invocation of the function from within an interrupt-handler likely to be problematic or successful?

3条回答
The star\"
2楼-- · 2019-09-21 04:10

To build upon what other users have already pointed out, I will try to address both questions of the OP.

OP's first Question:

I'd like to know, How much stack and heap in bytes is required by the function sum in X86 Linux platform? How to know this?

We can break this first question down into 2 parts. One is about stack size and the other is about heap size.

Stack Size:

To find out how much stack your function is using, you can use one of the GCC diagnostic pragmas, i.e. the -Wframe-larger-than=<X> pragma. Here is an example, on how to use it. First we add the pragma to the code and save the file.

main.cpp

#include <stdio.h>
#pragma GCC diagnostic error "-Wframe-larger-than=1"

int sum(int b[], int c) {
    int s,i;
    if (c<0) {
            printf("ERROR\n");
    }
    s = 0;
    for(i=0; i<c; ++i) {
        s = s + b[i];
    }
    return s;
}

We can now try to compile the code:

junglefox@ubuntu:~$ gcc -c main.cpp
main.cpp: In function ‘int sum(int*, int)’:
main.cpp:20:1: error: the frame size of 32 bytes is larger than 1 bytes [-Werror=frame-larger-than=]
 }
 ^
cc1plus: some warnings being treated as errors
junglefox@ubuntu:~$

which reports a size of 32 bytes.

  • An alternative method to measuring the stack size is to use the stack-usage compiler flag in GCC. So, we remove or comment out the // #pragma GCC diagnostic error "-Wframe-larger-than=1" line and try to compile the file again, as shown below.

junglefox@ubuntu:~$ gcc -c main.cpp -fstack-usage

This will generate a file main.su.

junglefox@ubuntu:~$ cat main.su 
main.cpp:5:5:int sum(int*, int) 48  static

which shows that apparently, we are using 48 bytes of stack.


Heap Size

To find out how much heap size our program is using, we will use the valgrind tool Massif. For that we first need to add a main() function to our code (without which we cannot create a binary. And a binary is what we would need to be run with valgrind). So the main.cpp, looks like this now,

#include <stdio.h>
// #pragma GCC diagnostic error "-Wframe-larger-than=1"

int sum(int b[], int c) {
    int s,i;
    if (c<0) {
            printf("ERROR\n");
    }
    s = 0;
    for(i=0; i<c; ++i) {
        s = s + b[i];
    }
    return s;
}

int main() {
    // As Peter pointed, uncomment one of the following lines,
    // for it to be a valid test. Also, compiler optimizations,
    // when turned on, can give different results.
    // sum(NULL,0);
    // sum(NULL,-1);
    return 0;
}

And now we will compile, build and run the binary with the help of valgrind, as shown here:

junglefox@ubuntu:~$ gcc -o main main.cpp
junglefox@ubuntu:~$ valgrind ./main --tool=massif

This will generate a bunch of info, which looks something like below:

    ==8179== Memcheck, a memory error detector
==8179== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8179== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==8179== Command: ./main --tool=massif
==8179== 
==8179== 
==8179== HEAP SUMMARY:
==8179==     in use at exit: 0 bytes in 0 blocks
==8179==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==8179== 
==8179== All heap blocks were freed -- no leaks are possible
==8179== 
==8179== For counts of detected and suppressed errors, rerun with: -v
==8179== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

which reports a total heap usage of 0 Kilobytes.

Also, as @mevets has tried to explain, you can always look into the the underlying assembly code generated by the compiler. In GCC, you could do,

junglefox@ubuntu:~/gcc -S main.cpp
junglefox@ubuntu:~/cat main.s

which will show you what your function looks like in the underlying assembly output.

NOTE/EDIT: But just to be complete, in C or C++, without dynamic memory allocation using malloc() or new, you as a programmer are NOT using the heap. Also, unless you are declaring an array inside your function, you are not using any significant amount of the stack.


OP's second Question:

Is invocation of the function from within an interrupt-handler likely to be problematic or successful?

As many people have kindly pointed out in the comments, do NOT use printf() in your Interrupt handler.

To quote from this link:

What differentiates interrupt handlers from other kernel functions is that the kernel invokes them in response to interrupts and that they run in a special context called interrupt context. This special context is occasionally called atomic context because code executing in this context is unable to block.

Because an interrupt can occur at any time, an interrupt handler can be executed at any time. It is imperative that the handler runs quickly, to resume execution of the interrupted code as soon as possible.

So, apart from printf(), one thing that could take a long time, is how big an array you are passing to that function, when used as an Interrupt Service Routine. It has a complexity of O(n). If c is too big, your program would be halted for a relatively long time till the ISR finishes going through that for() loop.

查看更多
乱世女痞
3楼-- · 2019-09-21 04:12

How much stack? After removing the printf, 0 bytes of stack are needed; the function is simple enough to work with 3 registers:

.globl _sum
_sum: /* (int *b, int c); */
    mov 4(%esp), %edx
    mov 8(%esp), %ecx
    cmp $0, %ecx
    jl   badcount
    leal (%edx,%ecx,4), %ecx
    xor %eax, %eax
nxt:
    cmp %ecx, %edx
    je    done
    add (%edx), %eax
    add $4, %edx
    jmp nxt
done:
    ret
badcount:
    mov $-1, %eax
    ret

You need to be able to rely on a compiler to not do completely stupid stuff ( no matter what standard c## thinks ).

If you are so far into a corner that you need to count stack bytes, look for a mistake earlier in your path. Even Frank n Furter realized that solving the symptoms was different from the cause.

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-09-21 04:27

C has no concept of "stack"; and when code written in C is compiled you should think of it as being mangled into an unrecognizable form.

For example, if the caller does this:

    myArray[2] = 99;
    result = sum( myArray, 4);

Then the compiler can inline a completely separate copy of the sum() function, and then (using optimizations like constant folding, dead code elimination and loop unrolling) convert that separate copy of the function into (the equivalent of):

    result = myArray[0] + myArray[1] + 99 + myArray[3];

..and then annotate it (or convert to "single static assignment' form) to allow more parallelism, like:

    temp1 = (myArray[0] + myArray[1]); temp2 = (99 + myArray[3]);
    result = temp1 + temp2;

..and then convert into something like:

    mov eax,[myArray]
    mov ebx,[myArray+4]
    lea eax,[eax+ebx]

    mov ecx,99
    mov edx,[myArray+12]
    lea ecx,[ecx+edx]

    lea eax,[eax+ecx]

    mov [result],eax

..and then optimize and reorder the instructions to get:

    mov eax,[myArray]
    mov ebx,[myArray+4]
    mov edx,[myArray+12]
    lea eax,[eax+ebx]
    lea eax,[eax+edx+99]
    mov [result],eax

Notice that this looks nothing like the original code - e.g. there's no printf(), there's no loop and there's no branches.

Of course (given that the function isn't static, and assuming no link time optimization or link time code generation is being done) the compiler will probably also generate an "I know nothing about the caller" version of the function and shove that into the output object file in case the linker wants it; and (if no other object files use the function) the linker might throw that version of the function away. In that case; you can look at the code the compiler generated for the "I know nothing about the caller" version, and then make worthless/incorrect assumptions based on the amount of stack used by code that is discarded and never executed.

查看更多
登录 后发表回答