5 ms·
> Unless you do alloca() or dynamically sized local arrays, you can measure your stack usage in the deepest call stack. How does a normal working programmer ca
by chrisseaton 5y ago
> Unless you do alloca() or dynamically sized local arrays, you can measure your stack usage in the deepest call stack.
How does a normal working programmer calculate the size of each of their stack frames? I'm a compiler researcher and I'd struggle to do that. How are application developers going to do it?
And how do you design a program to have a deterministic maximum call stack depth?
I don't think these things are as easy as you're making out.
- mjw1007 5y agoNot easy at all. I know that in the small-embedded world, people do work on such things. Eg https://github.com/japaric/cargo-call-stack https://github.com/japaric/cargo-call-stack
- viraptor 5y agoYou have 2 options: either your functions are recursive and you can hope and pray, or they're not and you can figure out which of your functions are the bottom of the call graph. In those leaf functions you can check &local_var and compare it to pthread_attr_getstack(pthread_getattr_np()). (Of course that's not precise for many reasons.) > And how do you design a program to have a deterministic maximum call stack depth? If you're running only your code - don't use recursion, or alloca. If you use external libraries, you have to research what they do and add some extra in case of updates. Bounded stack size is also a common issue if you're targeting small microprocessors. For non-critical apps it should be pretty easy to figure out the needed stack size. For cases when you want to guarantee it... that gets more tricky. Edit: just learned that clang has the option -fstack-usage which should help a lot.
- CodesInChaos 5y agoDynamic dispatch (e.g. function pointers/delegates, virtual methods) is another case that makes figuring out the call graph and thus the maximum stack size difficult (via whole-program data-flow analysis) to impossible (function pointers come from outside your codebase or are constructed in ways analysis can't handle).
- creata 5y ago> You have 2 options: either your functions are recursive and you can hope and pray Or you can try to figure out the maximum number of times it'll recurse: for example, the height of a red-black tree with less than 2^64 nodes is less than 128, iirc.
- lanstin 5y agoOne rather quickly runs into halting-problem type issues, especially in the function dispatch method. Imagine a DSL that does stuff, and is implemented by function pointers in the parser/interpreter, and then the question becomes one of program inputs. In any case, having such a small limit is crazy, and defending it with references to correctness smells of Ulrich Drepper and the memmove issue. The whole "sucks less" movement is a little too focused on purity for my taste. The only time I'm sad when I look at my memory usage on my personal laptop is when I have unused memory. Please, pre-fetch some news.ycombinator, cache some more inodes for my next ncdu or find command; I have already paid for the memory, not using it is silly. Sure, we software engineers get lazy, but, except in AWS, use all your memory, all your processors upto throttling, all the time. Why not? I remember one day in the 90s counting out like max address len and max zip code len and so and trying to figure out how long to make my target stack allocated buffer, and i was like fuck it, I have more important things to do, all my stack buffers are hence forth 65536 bytes long.
- MaxBarraclough 5y agoIf we forbid things like recursion (including mutual recursion), function pointers, dynamic dispatch, and unbounded use of alloca, doesn't it then follow from the call graph and the per-function worst-case stack-usage numbers (which the compiler presumably knows)? Is that mistaken, or is the difficulty in generalising this approach to where those restrictions are lifted? I tried googling for how SPARK Ada provides assurances against exceeding stack-size limits, but I couldn't find a decent answer. I presume it does so, though. edit: forgot about alloca edit 2: Turns out the AdaCore folks have a tool specifically for static analysis of stack-space requirements of Ada/C/C++ code: https://www.adacore.com/gnatpro/toolsuite/gnatstack https://www.adacore.com/gnatpro/toolsuite/gnatstack
- megous 5y agoYou ask a compiler, since it knows the max stack requirements of every function it compiled, if it's fixed. If it's not fixed it may give you at least the minimum. For total depth, keeping you program simple and predictable helps. People certainly manage to do it even for large programs like Linux itself, where stack size is like 16KiB or so. https://elixir.bootlin.com/linux/v5.2/source/arch/x86/include/asm/page_64_types.h#L16 https://elixir.bootlin.com/linux/v5.2/source/arch/x86/includ... and less on other archs. 8 KiB on arm https://elixir.bootlin.com/linux/v5.13-rc7/source/arch/arm/include/asm/thread_info.h#L25 https://elixir.bootlin.com/linux/v5.13-rc7/source/arch/arm/i...
- chrisseaton 5y agoBut the compiler may not compile simple 'functions' as the user understands them - it may compile loop bodies, functions with other functions compiled in them, individual branches of functions, multiple versions of functions based on where they're called from... If I tell you as a compiler writer that this loop body from this function, but with this branch and this branch outlined, but only when called from this context, takes n bytes... I don't get what most working programmers are going to usefully do with that information.
- megous 5y agoWhere's the problem? Compiler will tell you what amount of stack a function will use, if it's inlined it may not tell you for that function, but it will tell you for the function the function was inlined to, which is what matters. If the language is complicated and has generics or whatever, the programmer will have to do more work to understand it. It's not a huge issue in C.
- chrisseaton 5y ago> Compiler will tell you what amount of stack a function will use If you ask a compiler how much stack a function will use the answer for a non-trivial compiler for a complicated language is always going to be 'it depends...'