The std::stacktrace library was introduced in C++23, providing a standard, cross-platform way to generate and inspect the call stack at runtime. Before C++23, developers had to rely on platform-specific APIs or third-party libraries, which led to non-portable and inconsistent code.
What is std::stacktrace?
A stack trace is a record of the function calls that led to a particular point in a program’s execution. The <stacktrace> library defines:
std::basic_stacktrace: A class template that stores a sequence ofstd::stacktrace_entryobjects. The common aliasstd::stacktraceuses the standard allocator.std::stacktrace_entry: A class that represents a single frame in the call stack. If debug information is available, it can provide details such as the function name, source file, and line number.std::stacktrace::current(): A static member function that captures the current call stack.
How to use std::stacktrace
You can capture and print a stack trace with minimal code. For example:
1#include <iostream>
2#include <stacktrace>
3
4void foo() {
5 std::cout << std::stacktrace::current() << '\n';
6}
7
8void bar() {
9 foo();
10}
11
12int main() {
13 bar();
14}
If compiled with debug symbols, this program will output a formatted stack trace showing the sequence of calls from main to bar to foo.
Platforms and compiler support
While part of the C++23 standard, support for std::stacktrace depends on the specific compiler and platform. The library relies on underlying operating system APIs or a helper library to gather stack information.
- Windows:
std::stacktraceis supported on recent versions of Microsoft Visual C++ (MSVC). - Linux: G++ provides support, but often requires linking with an external library, such as
libbacktrace. For example, with GCC 12.1 or newer, you may need to link with-lstdc++_libbacktrace. - macOS: A functional implementation is available with recent Clang and libc++ versions.
- Embedded and stripped binaries: The level of detail available (like function names and line numbers) depends on the presence of debug symbols. In release builds or on platforms that lack the necessary debug information, the stack trace may show only memory addresses.
Limitations and design considerations
- No local variables:
std::stacktracecaptures the function call sequence but cannot inspect the values of local variables within the stack frames. This is a deliberate choice to keep it separate from the more invasive functionality of a debugger. - Not exception-integrated (in C++23): In C++23, exceptions do not automatically carry stack traces. Developers must explicitly capture a stack trace within a
catchblock. - Implementation-defined details: The precise format and level of detail for the string representation of a stack trace can vary between implementations.
- Async-signal-safety: Capturing a stack trace is generally not async-signal-safe, meaning it should not be done inside a signal handler for crashes like
SIGSEGV. Doing so can lead to deadlocks or other undefined behavior. Although this is true, there are manybacktrace()solutions similar to thestd::stacktracelibrary being used production systems without any deadlocks in this signal handle
Previous to std::stacktrace
Linux and POSIX systems On Linux and other POSIX-compliant systems (like macOS), developers used the GNU libbacktrace library and the functions it provides.
backtrace(): This function captures the current stack trace and returns an array of program counter addresses for each frame.backtrace_symbols(): This function converts the array of addresses frombacktrace()into a human-readable array of strings. The strings contain function names, but these names are often “mangled” (i.e., encoded by the compiler).__cxa_demangle(): A function from the GNU libstdc++ that is used to “demangle” the function names returned by backtrace_symbols(), making them readable.