What is stack unwinding? Searched through but couldn't find enlightening answer!
-
85If he doesn't know what it is, how can you expect him to know they are not the same for C and for C++?– dreamlaxCommented Feb 25, 2010 at 3:17
-
@dreamlax: So, how concept of "stack unwinding" is different in C & C++?– DestructorCommented Jan 14, 2016 at 5:57
-
7@PravasiMeet: C has no exception handling, so stack unwinding is very straightfoward, however, in C++, if an exception is thrown or a function exits, stack unwinding involves destructing any C++ objects with automatic storage duration.– dreamlaxCommented Jan 14, 2016 at 6:04
11 Answers
Stack unwinding is usually talked about in connection with exception handling. Here's an example:
void func( int x )
{
char* pleak = new char[1024]; // might be lost => memory leak
std::string s( "hello world" ); // will be properly destructed
if ( x ) throw std::runtime_error( "boom" );
delete [] pleak; // will only get here if x == 0. if x!=0, throw exception
}
int main()
{
try
{
func( 10 );
}
catch ( const std::exception& e )
{
return 1;
}
return 0;
}
Here memory allocated for pleak
will be lost if an exception is thrown, while memory allocated to s
will be properly released by std::string
destructor in any case. The objects allocated on the stack are "unwound" when the scope is exited (here the scope is of the function func
.) This is done by the compiler inserting calls to destructors of automatic (stack) variables.
Now this is a very powerful concept leading to the technique called RAII, that is Resource Acquisition Is Initialization, that helps us manage resources like memory, database connections, open file descriptors, etc. in C++.
Now that allows us to provide exception safety guarantees.
-
2Hmm, crash is a little bit different, though related, animal (signals, core dumps, etc.) What I'm talking about are C++ tools/facilities to safely manage resources in the presence of exceptions/errors, that might lead to a crash if not handled. Commented Feb 25, 2010 at 4:03
-
25If the program "crashes" (i.e. terminates due to an error), then any memory leakage or heap corruption is irrelevant since the memory is released at termination. Commented Feb 25, 2010 at 16:20
-
14@TylerMcHenry: The standard does not guarantee that resources or memory are released at termination. Most OS's happen to do so however. Commented May 8, 2012 at 19:15
-
4
-
3@TylerMcHenry, Not completely. The process may leak resources which are system/session global (e.g. ATOMs on Windows). Commented Mar 18, 2020 at 9:15
All this relates to C++:
Definition: As you create objects statically (on the stack as opposed to allocating them in the heap memory) and perform function calls, they are "stacked up".
When a scope (anything delimited by {
and }
) is exited (by using return XXX;
, reaching the end of the scope or throwing an exception) everything within that scope is destroyed (destructors are called for everything). This process of destroying local objects and calling destructors is called stack unwinding.
You have the following issues related to stack unwinding:
avoiding memory leaks (anything dynamically allocated that is not managed by a local object and cleaned up in the destructor will be leaked) - see RAII referred to by Nikolai, and the documentation for boost::scoped_ptr or this example of using boost::mutex::scoped_lock.
program consistency: the C++ specifications state that you should never throw an exception before any existing exception has been handled. This means that the stack unwinding process should never throw an exception (either use only code guaranteed not to throw in destructors, or surround everything in destructors with
try {
and} catch(...) {}
).
If any destructor throws an exception during stack unwinding you end up in the land of undefined behavior which could cause your program to terminate unexpectedly (most common behavior) or the universe to end (theoretically possible but has not been observed in practice yet).
-
3On the contrary. While gotos should not be abused, they do cause stack unwinding in MSVC (not in GCC, so it's probably an extension). setjmp and longjmp do this in a cross platform way, with somewhat less flexibility. Commented Sep 7, 2010 at 12:04
-
12I've just tested this with gcc and it does correctly call the destructors when you goto out of a code block. See stackoverflow.com/questions/334780/… - as mentioned in that link, this is part of the standard as well.– DamyanCommented Dec 14, 2010 at 12:54
-
1reading Nikolai's, jrista's and your answer in this order, now it makes sense!– n611x007Commented Aug 10, 2012 at 13:46
-
1@sashoalm Do you really think it's necessary to edit a post seven years later? Commented Jan 19, 2018 at 17:59
-
@DavidHoelzer I agree, David!! I was thinking that too when I saw the edit date and the posting date. Commented Jun 28, 2021 at 23:03
In a general sense, a stack "unwind" is pretty much synonymous with the end of a function call and the subsequent popping of the stack.
However, specifically in the case of C++, stack unwinding has to do with how C++ calls the destructors for the objects allocated since the started of any code block. Objects that were created within the block are deallocated in reverse order of their allocation.
-
4There is nothing special about
try
blocks. Stack objects allocated in any block (whethertry
or not) is subject to unwinding when the block exits. Commented Feb 25, 2010 at 3:11 -
Its been a while since I have done much C++ coding. I had to dig that answer out of the rusty depths. ;P– jristaCommented Feb 25, 2010 at 3:23
-
I don't know if you read this yet, but Wikipedia's article on the call stack has a decent explanation.
Unwinding:
Returning from the called function will pop the top frame off of the stack, perhaps leaving a return value. The more general act of popping one or more frames off the stack to resume execution elsewhere in the program is called stack unwinding and must be performed when non-local control structures are used, such as those used for exception handling. In this case, the stack frame of a function contains one or more entries specifying exception handlers. When an exception is thrown, the stack is unwound until a handler is found that is prepared to handle (catch) the type of the thrown exception.
Some languages have other control structures that require general unwinding. Pascal allows a global goto statement to transfer control out of a nested function and into a previously invoked outer function. This operation requires the stack to be unwound, removing as many stack frames as necessary to restore the proper context to transfer control to the target statement within the enclosing outer function. Similarly, C has the setjmp and longjmp functions that act as non-local gotos. Common Lisp allows control of what happens when the stack is unwound by using the unwind-protect special operator.
When applying a continuation, the stack is (logically) unwound and then rewound with the stack of the continuation. This is not the only way to implement continuations; for example, using multiple, explicit stacks, application of a continuation can simply activate its stack and wind a value to be passed. The Scheme programming language allows arbitrary thunks to be executed in specified points on "unwinding" or "rewinding" of the control stack when a continuation is invoked.
Inspection[edit]
Stack unwinding is a mostly C++ concept, dealing with how stack-allocated objects are destroyed when its scope is exited (either normally, or through an exception).
Say you have this fragment of code:
void hw() {
string hello("Hello, ");
string world("world!\n");
cout << hello << world;
} // at this point, "world" is destroyed, followed by "hello"
-
Does this apply to any block? I mean if there is only { // some local objects } Commented Feb 25, 2010 at 3:52
-
@Rajendra: Yes, an anonymous block defines an area of scope, so it counts too.– Michael Myers ♦Commented Feb 25, 2010 at 22:59
IMO, the given below diagram in this article beautifully explains the effect of stack unwinding on the route of next instruction (to be executed once an exception is thrown which is uncaught):
In the pic:
- Top one is a normal call execution (with no exception thrown).
- Bottom one when an exception is thrown.
In the second case, when an exception occurs, the function call stack is linearly searched for the exception handler. The search ends at the function with exception handler i.e. main()
with enclosing try-catch
block, but not before removing all the entries before it from the function call stack.
-
Diagrams are good but explanation is bit confusing viz. ...with enclosing try-catch block, but not before removing all the entries before it from the function call stack...– AtulCommented Jun 22, 2019 at 11:10
I read a blog post that helped me understand.
What is stack unwinding?
In any language that supports recursive functions (ie. pretty much everything except Fortran 77 and Brainf*ck) the language runtime keeps a stack of what functions are currently executing. Stack unwinding is a way of inspecting, and possibly modifying, that stack.
Why would you want to do that?
The answer may seem obvious, but there are several related, yet subtly different, situations where unwinding is useful or necessary:
- As a runtime control-flow mechanism (C++ exceptions, C longjmp(), etc).
- In a debugger, to show the user the stack.
- In a profiler, to take a sample of the stack.
- From the program itself (like from a crash handler to show the stack).
These have subtly different requirements. Some of these are performance-critical, some are not. Some require the ability to reconstruct registers from outer frame, some do not. But we'll get into all that in a second.
You can find the full post here.
Everyone has talked about the exception handling in C++. But,I think there is another connotation for stack unwinding and that is related to debugging. A debugger has to do stack unwinding whenever it is supposed to go to a frame previous to the current frame. However, this is sort of virtual unwinding as it needs to rewind when it comes back to current frame. The example for this could be up/down/bt commands in gdb.
-
7The debugger action is typically called "Stack Walking" which is simply parsing the stack. "Stack Unwinding" implies not only "Stack Walking" but also calling the destructors of objects that exist on the stack.– AdisakCommented Sep 9, 2013 at 19:10
-
@Adisak I didn't know it is also called "stack walking". I have always been seeing "stack unwinding" in the context of all debugger articles and also even inside gdb code. I felt "stack unwinding" more appropriate as it is not just about peeking into stack information for every function, but involves unwinding of frame information (c.f. CFI in dwarf).This is processed in-order one function by one.– bbvCommented Aug 4, 2014 at 12:10
-
I guess the "stack walking" is made more famous by Windows. Also, I found as an example code.google.com/p/google-breakpad/wiki/StackWalking apart from dwarf standard's doc itself uses term unwinding few times. Though agree, it is virtual unwinding. Moreover, the question seems to be asking for every possible meaning "stack unwinding" can suggest.– bbvCommented Aug 4, 2014 at 12:19
C++ runtime destructs all automatic variables created between between throw & catch. In this simple example below f1() throws and main() catches, in between objects of type B and A are created on the stack in that order. When f1() throws, B and A's destructors are called.
#include <iostream>
using namespace std;
class A
{
public:
~A() { cout << "A's dtor" << endl; }
};
class B
{
public:
~B() { cout << "B's dtor" << endl; }
};
void f1()
{
B b;
throw (100);
}
void f()
{
A a;
f1();
}
int main()
{
try
{
f();
}
catch (int num)
{
cout << "Caught exception: " << num << endl;
}
return 0;
}
The output of this program will be
B's dtor
A's dtor
This is because the program's callstack when f1() throws looks like
f1()
f()
main()
So, when f1() is popped, automatic variable b gets destroyed, and then when f() is popped automatic variable a gets destroyed.
Hope this helps, happy coding!
Stack unwinding is the process of removing function entries from the function call stack at runtime. It generally related to exception handling. In C++ when an exception occurs , the function call stack is linearly searched for the exception handler all the entries before the function with exception handlers are removed from the function call stack .
In Java stack unwiding or unwounding isn't very important (with garbage collector). In many exception handling papers I saw this concept (stack unwinding), in special those writters deals with exception handling in C or C++. with try catch
blocks we shouln't forget: free stack from all objects after local blocks.