3

How can I calculate the minimum Stack Size required for my program in UNIX, so that my program never get crashed.

Suppose my program is

int main()
{
         int number;
         number++;
         return 0;
}

1) What can be the Stack Size requried to run for this program? How it is calculated ?

2) My Unix system gives ulimit -s 512000. Is this value 512MB really required for my small program?

3) And what if I have a big program having Multithreads, some 500 functions, including some libraries, Macros, Dynamically allocated memory etc. How much Stack Size is required for that ?

CodeCodeCode
  • 459
  • 1
  • 12
  • 21
  • 3
    Since your program doesn't do *anything*, it really doesn't need any stack at all... – Kerrek SB Aug 28 '13 at 09:18
  • Additional threads get their own stack, and in pthreads you can specify where that stack is and how large it is. It's perfectly sensible to give small threads a small stack to conserve resources. – Kerrek SB Aug 28 '13 at 09:19
  • The C and C++ standard do not dictate things like stack depth, those are generally an environmental issue. If you think you might be limited by stack space, maybe you are doing something bad. Read this: http://msdn.microsoft.com/en-us/library/ms686774.aspx – jfly Aug 28 '13 at 09:21
  • @KerrekSB: `Since your program doesn't do anything` My program is 1) Creating Memory of integer size in hard disc. 2) Incrementing the value 3) Returning value `0` to anonyms variable of main function which is used as return code. – CodeCodeCode Aug 28 '13 at 09:24
  • stack is used to put the argument of a function when you call it and stacks contains also locale variables – jambono Aug 28 '13 at 09:26
  • 1
    @CodeCodeCode You're not creating a *memory of integer size in hard disc*, the stack is in the RAM not on the hard disk. And yes your program doesn't do anything, since the compiler has optimized it. Look at the ASM translation of your code with [godbolt](http://gcc.godbolt.org/). – nouney Aug 28 '13 at 09:28
  • 1
    @KerrekSB The function `main` must be called, and it must allocate space for `number` on the stack. In practice, it's not rare to have used several hundred bytes of stack even before entering `main`. (I don't know the current practice, but I have used systems in the past which put the entire environment on the stack before calling `main`.) – James Kanze Aug 28 '13 at 09:28
  • @nouney How is this godbold used? I pasted the code in LHS Code Editor, but it does not show anything in Assembly Output. There is no value in dropdown Sources, Name or Compiler. – CodeCodeCode Aug 28 '13 at 09:38
  • 1
    @CodeCodeCode Here is a [permalink](http://gcc.godbolt.org/#{%22version%22%3A3%2C%22filterAsm%22%3A{%22labels%22%3Atrue%2C%22directives%22%3Atrue%2C%22commentOnly%22%3Atrue}%2C%22compilers%22%3A[{%22source%22%3A%22int%20main%28%29\n{\n%20%20%20%20%20%20%20%20%20int%20number%3B\n%20%20%20%20%20%20%20%20%20number%2B%2B%3B\n%20%20%20%20%20%20%20%20%20return%200%3B\n}%22%2C%22compiler%22%3A%22%2Fusr%2Fbin%2Fg%2B%2B-4.8%22%2C%22options%22%3A%22-O2%20-W%20-Wall%22}]}). – nouney Aug 28 '13 at 09:39
  • @CodeCodeCode: If your code does nothing, the asm output will be essentially nothing (probably just a `ret` instruction). – R.. GitHub STOP HELPING ICE Aug 28 '13 at 10:06
  • Your code from above is most likely optimized to `xor eax, eax; ret;` so yes it really does nothing. If you add a `printf("%d\n", number);` call, then you will have a small amount of stackspace used (minimum 12 bytes on a 32bit system). The local variable number doesn't need a space, because the optimizer probably recognizes that is nowhere used beyond that, and therefore will not create space for it. – Devolus Aug 28 '13 at 10:45
  • Many OS shovel quite a bit of stuff into the memory allocation that is to be used as the stack for a new thread - the SP is not at TOS when the first instruction of the new thread is executed, (@JamesKanze comment). – Martin James Aug 28 '13 at 10:49
  • @Devolus That depends on the options passed to the compiler. If I compile with optimization turned off, I would expect to be able to put a breakpoint on the return, in the debugger, and see what the value of `number` is. (Of course, the code contains undefined behavior, so a compiler could even refuse to compile it.) – James Kanze Aug 28 '13 at 11:10
  • 1
    This is a valid question. There are times when it is important to know how much stack space is used, as when writing code for a kernel with limited stack or writing a complex recursive algorithm. Compilers ought to assist with calculating stack space by providing the amount used in routines or blocks with static usage (no VLAs etc.), and linkers ought to provide totals for non-recursive call trees. – Eric Postpischil Aug 28 '13 at 11:21
  • Completely useless, yet more or less true answer: "Enough to enable reasonably efficient coding of the particular problem at hand, but not enough to enable stack abuse". Which, roughly interpreted, means "it depends". – twalberg Aug 28 '13 at 21:22

3 Answers3

3
  1. Your program in itself uses a few bytes - 1 int, but there is of course the part of the runtime that comes BEFORE main as well to take into account. But it's unlikely to be more than a few dozen bytes, maybe a couple of hundred bytes at a stretch. Since the minimum stack size in any modern OS is "one page" = 4KB, this should easily fit in that.
  2. 51200 = 51.2MB, but that seems quite high. On my Linux Fedora 16 x86-64 machine, it is 8192.
  3. Threads don't really matter, as each thread has its own stack. The number of functions are in themselves not a huge contributor to stack usage. Running out of stack is nearly always caused by large local variables and/or deep recursion. For any program that is more than a little bit complex, calculating precise stack usage can be quite tricky. Typically, it involves running the program a lot and seeing if the stack "explodes". If not, you have enough stack. Library functions, generally speaking, tends to not use huge amounts of stack, but there are always exceptions.

To examplify:

void func()
{
   int x, y, z;
   float w;
   ...
}

This function takes up approximately 16 bytes of stack, plus the general overhead of calling a function, typically 1-3 "machine words" (4-12 bytes on a 32-bit machine, 8-24 bytes for a 64-bit machine).

void func2()
{
    int x[10000];
    ...
}

This function will take 40000 bytes of stack-space. Obviously, you don't need many recursive calls to this function to run out of stack.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • The OP's problem is that, with such a massive default stack, his/her 32-bit virtual memory space runs out after creating very few threads. – Martin James Aug 28 '13 at 10:53
  • @MartinJames - is the maximum stack size really pre-allocated per thread? It's hard to tell without knowing the specific OS. LINUX NPTL does use it as the default size for new thread stacks (if it isn't _unlimited_), but even that doesn't it's all faulted in. – Useless Aug 28 '13 at 12:15
  • @Useless: Correct, stacks are not pre-allocated. But if you use 50+MB per thread of VIRTUAL space, you will soon run out of the 2-3GB that the user-code can use (bearing in mind that there are other "lumps" to fit in there, such as shared libraries, application code itself, etc) – Mats Petersson Aug 28 '13 at 12:21
  • Doh, reading comprehension fail. Thanks. – Useless Aug 28 '13 at 12:22
2

There is no magic way to tell how much space will your program require on the stack. It'd depend on what the code is actually doing. Infinite (or very deep) recursion would result in stack overflow even if the program doesn't seem to do anything.

As an example, see the following:

$ ulimit
unlimited
$ echo "foo(){foo();} main(){foo();}" | gcc -x c -
$ ./a.out 
Segmentation fault (core dumped)
devnull
  • 118,548
  • 33
  • 236
  • 227
1

Most people rely on the stack being “large” and their programs not using all of it, simply because the size has been set so large that programs rarely fail because they run out of stack space unless they use very large arrays with automatic storage duration.

This is an engineering failure, in the sense that it is not engineering: A known and largely preventable source of complete failure is uncontrolled.

In general, it can be difficult to compute the actual stack needs of a program. Especially when there is recursion, a compiler cannot generally predict how many times a routine will be called recursively, so it cannot know how many times that routine will need stack space. Another complication is calls to addresses prepared at run-time, such as calls to virtual functions or through other pointers-to-functions.

However, compilers and linkers could provide some assistance. For any routine that uses a fixed amount of stack space, a compiler, in theory, could provide that information. A routine may include blocks that are or are not executed, and each block might have different stack space requirements. This would interfere with a compiler providing a fixed number for the routine, but a compiler might provide information about each block individually and/or a maximum for the routine.

Linkers could, in theory, examine the call tree and, if it is static and is not recursive, provide a maximum stack use for the linked program. They could also provide the stack use along a particular call subchain (e.g., from one routine through the chain of calls that leads to the same routine being called recursively) so that a human could then apply knowledge of the algorithm to multiple the stack use of the subchain by the maximum number of times it might be called recursively).

I have not seen compilers or linkers with these features. This suggests there is little economic incentive for developing these features.

There are times when stack use information is important. Operating system kernels may have a stack that is much more limited than user processes, so the maximum stack use of the kernel code ought (as a good engineering practice) to be calculated so that the stack size can be set appropriately (or the code redesigned to use less stack).

If you have a critical need for calculating stack space requirements, you can examine the assembly code generated by the compiler. In many routines on many computing platforms, a fixed number is subtracted from the stack pointer at the beginning of the routine. In the absence of additional subtractions or “push” instructions, this is the stack use of the routine, excluding further stack used by subroutines it calls. However, routines may contain blocks of code that contain additional stack allocations, so you must be careful about examining the generated assembly code to ensure you have found all stack adjustments.

Routines may also contain stack allocations computed at run-time. In a situation where calculating stack space is critical, you might avoid writing code that causes such allocations (e.g., avoid using C’s variable-length array feature).

Once you have determined the stack use of each routine, you can determine the total stack use of the program by adding the stack use of each routine along various routine-call paths (including the stack use of the start routine that runs before main is called).

This sort of calculation of the stack use of a complete program is generally difficult and is rarely performed.

You can generally estimate the stack use of a program by knowing how much data it “needs” to do its work. Each routine generally needs stack space for the objects it uses with automatic storage duration plus some overhead for saving processor registers, passing parameters to subroutines, some scratch work, and so on. Many things can alter stack use, so only an estimate can be obtained this way. For example, your sample program does not need any space for number. Since no result of declaring or using number is ever printed, the optimizer in your compiler can eliminate it. Your program only needs stack space for the start routine; the main routine does not need to do anything except return zero.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thanks Eric for the detailed explanation. It really helped me. – CodeCodeCode Aug 28 '13 at 12:56
  • _This is an engineering failure, in the sense that it is not engineering: A known and largely preventable source of complete failure is uncontrolled._ Not an engineering failure: a known and largely preventable source of complete failure **with about 0 chance of ever happening** on systems with MMU programmed by competent people. I've only seen programs running out of stack space on non-MMU systems (including DOS), and on Java programs (but typical Java programmers are another issue altogether). – ninjalj Aug 28 '13 at 15:20
  • @ninjalj: (1) Claiming that product failures are rare does not disprove that there is a failure to perform proper engineering. (2) You limited your counterexamples to systems with MMUs programmed by competent people. This answer is not restricted to that subset; it addresses programming generally. (2) Your lack of observation of programs running out of stack space is not strong evidence about its occurrence. (4) My answer gave real-world examples of where the stack limit is a significant factor (such as kernel programming). – Eric Postpischil Aug 28 '13 at 15:58