std::stacktrace (C++23)
One-Liner
Capture the current call stack anywhere in your code and get each frame's function name, source file, and line number — the standard tool for program introspection and crash diagnostics, replacing clumsy approaches like the backtrace() family.
Header
#include <stacktrace>
GCC linking pitfall (tested)
GCC's <stacktrace> symbols are not in the main library; you must link the experimental library separately: GCC 16+ uses -lstdc++exp (note: no underscore), GCC 12–15 uses -lstdc++_exp (with underscore). In CMake, write target_link_libraries(target PRIVATE stdc++exp). Miss the link and you get undefined reference to std::__stacktrace_impl::_S_current.
Core API Cheat Sheet
| Operation | Signature | Description |
|---|---|---|
| Capture current stack | std::stacktrace::current() | Returns a snapshot of the current call stack |
| With skipped frames | stacktrace::current(skip, max_depth) | Skips the first skip frames, takes at most max_depth |
| Stack depth | st.size() | Number of frames |
| Get one frame | st[i] | Yields a stacktrace_entry |
| Frame description | entry.description() | Human-readable string with the function name, address, and more |
| Frame source location | entry.source_file() / source_line() | Source file name and line number (requires debug symbols) |
| Stream output | std::cout << st | Prints the whole stack directly |
Minimal Example
// Standard: C++23
// Build: g++ -std=c++23 demo.cpp -lstdc++exp (GCC 16+)
#include <stacktrace>
#include <iostream>
void inner() {
auto st = std::stacktrace::current();
std::cout << "depth=" << st.size() << "\n";
for (std::size_t i = 0; i < st.size(); ++i)
std::cout << " #" << i << " " << st[i] << "\n";
}
void outer() { inner(); }
int main() { outer(); }Actual output (GCC 16.1.1, -std=c++23 -lstdc++exp):
depth=6
#0 inner() [0x5ca9fa0aa28d]
#1 outer() [0x5ca9fa0aa3d4]
#2 main [0x5ca9fa0aa3e0]
#3 <unknown> [0x7f1709227740]
#4 __libc_start_main [0x7f1709227878]
#5 _start [0x5ca9fa0aa184]Function names for our own code (inner/outer/main) resolved fine; the dynamic-library frames showing <unknown> is normal — those addresses were never symbolized.
Embedded Applicability: Low
- Depends on the symbol table and runtime stack unwinding — usually unavailable on bare metal and RTOS, or requires a dedicated port
- Symbol information bloats the image; after
strip(-s) only addresses remain - Embedded work more commonly relies on hardware breakpoints, ITM, SEGGER RTT, or custom crashdump backtraces
- It does come in handy on the host side: tooling, test stubs, and CI failure diagnostics
Compiler Support
| GCC | Clang | MSVC |
|---|---|---|
| 12 | 16 | 19.34 |
See Also
Part of the content referenced from cppreference.com, licensed under CC-BY-SA 4.0