Home C / C++ C++23 Stacktrace and Source Location: Debugging Like a Pro
Advanced 3 min · July 13, 2026

C++23 Stacktrace and Source Location: Debugging Like a Pro

Master C++23's std::stacktrace and std::source_location for production debugging.

N
Naren Founder & Principal Engineer

20+ years shipping performance-critical C and C++ systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 13, 2026
last updated
2,073
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic knowledge of C++ (functions, classes, exceptions)
  • Familiarity with C++17/20 features (e.g., structured bindings, if constexpr)
  • A compiler that supports C++23 (GCC 12+, Clang 14+, MSVC 2022 17.5+)
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • std::stacktrace captures the current call stack as a sequence of stacktrace_entry objects.
  • std::source_location provides file, line, column, and function name at compile time.
  • Both are part of C++23 and require #include and #include .
  • Use stacktrace for runtime debugging; use source_location for logging and assertions.
  • They are zero-overhead when not used, but capturing a stacktrace has runtime cost.
✦ Definition~90s read
What is C++23 Stacktrace and Source Location?

std::stacktrace and std::source_location are C++23 features that let you capture the call stack at runtime and embed source file/line information at compile time, respectively.

Imagine you're a detective investigating a crime.
Plain-English First

Imagine you're a detective investigating a crime. std::stacktrace is like a list of all the places the suspect visited before the crime. std::source_location is like a GPS tag that tells you exactly where you are at any moment. Together, they help you trace the path of your program's execution and pinpoint where things went wrong.

Debugging complex C++ applications can be like finding a needle in a haystack. When a crash occurs deep in a library call, you often have no idea how you got there. C++23 introduces two powerful tools: std::stacktrace and std::source_location. These features bring stack trace and source location capabilities directly into the standard library, making debugging and logging more efficient and portable.

In this tutorial, you'll learn how to capture and analyze stack traces at runtime, and how to embed source location information into your logs and assertions. We'll cover practical examples, common pitfalls, and production-ready patterns. By the end, you'll be able to add robust debugging capabilities to your C++ applications with minimal overhead.

Whether you're maintaining a legacy codebase or building a new system, these tools will save you hours of debugging time. Let's dive in!

What is std::source_location?

std::source_location is a class that represents source location information (file name, line number, column number, and function name). It is obtained via the static method current(), which returns a source_location object populated with the call site's information. This is evaluated at compile time, so there is no runtime overhead when not used.

source_location_demo.cppCPP
1
2
3
4
5
6
7
8
9
10
11
#include <source_location>
#include <iostream>

void log(const std::string& msg, const std::source_location& loc = std::source_location::current()) {
    std::cout << loc.file_name() << ":" << loc.line() << " " << loc.function_name() << " - " << msg << std::endl;
}

int main() {
    log("Hello, world!");
    return 0;
}
Output
example.cpp:8 main - Hello, world!
💡Default Argument Trick
📊 Production Insight
In production, avoid passing source_location objects by value in hot paths; use const references or default arguments.
🎯 Key Takeaway
std::source_location provides compile-time source location info with zero overhead when not used.

What is std::stacktrace?

std::stacktrace is a container of std::stacktrace_entry objects, each representing a frame in the call stack. You can capture the current stack trace with std::stacktrace::current(). The stacktrace can be printed or iterated. It is similar to boost::stacktrace but standard.

stacktrace_demo.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stacktrace>
#include <iostream>

void func3() {
    auto st = std::stacktrace::current();
    std::cout << st << std::endl;
}

void func2() { func3(); }
void func1() { func2(); }

int main() {
    func1();
    return 0;
}
Output
0# func3() at /path/to/stacktrace_demo.cpp:5
1# func2() at /path/to/stacktrace_demo.cpp:9
2# func1() at /path/to/stacktrace_demo.cpp:10
3# main at /path/to/stacktrace_demo.cpp:13
4# ...
🔥Performance Note
📊 Production Insight
In production, consider capturing stack traces only when a certain log level is enabled or when an error occurs.
🎯 Key Takeaway
std::stacktrace captures the entire call stack at runtime, useful for debugging crashes and unexpected behavior.

Using source_location in Logging Macros

One of the most practical uses of std::source_location is in logging macros. Instead of manually writing __FILE__ and __LINE__, you can define a macro that automatically passes the source location. This reduces boilerplate and ensures consistency.

logging_macro.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <source_location>
#include <iostream>

#define LOG(msg) log(msg, std::source_location::current())

void log(const std::string& msg, const std::source_location& loc) {
    std::cout << "[" << loc.file_name() << ":" << loc.line() << "] " << msg << std::endl;
}

int main() {
    LOG("Starting process");
    // ...
    LOG("Process completed");
    return 0;
}
Output
[example.cpp:12] Starting process
[example.cpp:14] Process completed
⚠ Macro Pitfall
📊 Production Insight
In production, consider using a logging library that already supports source_location (e.g., spdlog).
🎯 Key Takeaway
Logging macros with source_location automate file/line capture, making logs more informative.

Capturing Stack Traces in Exception Handlers

When an exception is thrown, the stack is unwound. To capture the stack at the throw point, you need to capture it before the exception is caught. One common pattern is to capture the stack trace in the catch block, but that gives the catch site's stack, not the throw site's. To capture the throw site, you can use a custom exception class that stores a stack trace.

exception_stacktrace.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stacktrace>
#include <iostream>
#include <stdexcept>

class traced_exception : public std::runtime_error {
public:
    traced_exception(const std::string& msg)
        : std::runtime_error(msg), trace_(std::stacktrace::current()) {}
    const std::stacktrace& trace() const { return trace_; }
private:
    std::stacktrace trace_;
};

void func() {
    throw traced_exception("Something went wrong");
}

int main() {
    try {
        func();
    } catch (const traced_exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
        std::cout << "Stack trace:\n" << e.trace() << std::endl;
    }
    return 0;
}
Output
Exception: Something went wrong
Stack trace:
0# traced_exception::traced_exception(...) at ...
1# func() at ...
2# main at ...
💡Stack Trace at Throw
📊 Production Insight
Be mindful of memory: stack traces can be large. Consider limiting the depth with std::stacktrace::current(max_depth).
🎯 Key Takeaway
Store stack traces in custom exception objects to capture the throw site's call stack.

Comparing Stacktrace Entries

std::stacktrace_entry provides methods to get the source file, line number, and function name. You can compare entries for equality. This is useful for deduplication or filtering.

compare_entries.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stacktrace>
#include <iostream>

int main() {
    auto st1 = std::stacktrace::current();
    auto st2 = std::stacktrace::current();
    if (st1 == st2) {
        std::cout << "Same stack trace" << std::endl;
    } else {
        std::cout << "Different" << std::endl;
    }
    // Compare individual entries
    if (st1[0] == st2[0]) {
        std::cout << "Top frame same" << std::endl;
    }
    return 0;
}
Output
Same stack trace
Top frame same
🔥Equality Semantics
📊 Production Insight
Comparing stack traces can be expensive; use it judiciously.
🎯 Key Takeaway
You can compare stacktrace entries for equality, useful for filtering or grouping.

Performance Considerations and Best Practices

Capturing a stack trace involves walking the stack and resolving symbols, which can be slow (microseconds to milliseconds). In production, you should: - Capture stack traces only on error paths. - Limit the depth using std::stacktrace::current(max_depth). - Use conditional compilation (e.g., #ifdef DEBUG) to disable stack tracing in release builds. - For source_location, there is no runtime cost when not used, but passing it by value copies the object (though it's small).

conditional_stacktrace.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stacktrace>
#include <iostream>

void on_error() {
#ifdef DEBUG
    auto st = std::stacktrace::current(10); // limit to 10 frames
    std::cerr << "Error: stack trace:\n" << st << std::endl;
#else
    std::cerr << "Error occurred" << std::endl;
#endif
}

int main() {
    on_error();
    return 0;
}
Output
Error: stack trace:
0# on_error() at ...
1# main at ...
⚠ Release Builds
📊 Production Insight
Consider using a ring buffer of stack traces to limit memory usage in production.
🎯 Key Takeaway
Use stack traces sparingly and conditionally to avoid performance degradation.
● Production incidentPOST-MORTEMseverity: high

The Mysterious Crash in the Payment Gateway

Symptom
Users reported payment failures with a generic 'Internal Server Error'. Logs showed only an exception message: 'std::bad_alloc'.
Assumption
The developer assumed it was a memory leak in the transaction processing module.
Root cause
A third-party library threw std::bad_alloc due to an invalid input, but the exception handler only logged the message without any context. The actual call stack was lost because the handler didn't capture a stack trace.
Fix
Added std::stacktrace capture in the exception handler and logged the entire stack trace. Also added std::source_location to log the exact file and line where the exception was caught.
Key lesson
  • Always capture stack traces in exception handlers for production debugging.
  • Use std::source_location to automatically log file and line numbers.
  • Don't assume the location of an error; let the tools tell you.
  • Test your logging infrastructure with actual stack traces in staging.
  • Consider performance impact: capturing stack traces is expensive; do it only when needed.
Production debug guideSymptom to Action4 entries
Symptom · 01
Crash with no stack trace in logs
Fix
Add std::stacktrace capture in catch blocks and log the stack trace.
Symptom · 02
Log messages without file/line info
Fix
Use std::source_location::current() as default argument in logging macros.
Symptom · 03
Performance degradation when stack tracing is enabled
Fix
Conditionally enable stack tracing only in debug builds or via a runtime flag.
Symptom · 04
Stack trace symbols are mangled
Fix
Use tools like addr2line or boost::stacktrace to demangle symbols.
★ Quick Debug Cheat SheetCommon debugging scenarios and immediate actions.
Need to log current function name
Immediate action
Use std::source_location::current().function_name()
Commands
#include <source_location>
auto loc = std::source_location::current();
Fix now
std::cout << loc.function_name();
Need to capture stack trace on exception+
Immediate action
Use std::stacktrace::current() in catch block
Commands
#include <stacktrace>
auto st = std::stacktrace::current();
Fix now
std::cout << st;
Need to log file and line in a macro+
Immediate action
Define a macro that uses std::source_location
Commands
#define LOG(msg) log(msg, std::source_location::current())
void log(const std::string& msg, const std::source_location& loc = std::source_location::current());
Fix now
LOG("Something happened");
Featurestd::source_locationstd::stacktrace
PurposeCompile-time source infoRuntime call stack capture
OverheadZero when not usedSignificant when captured
UsageLogging, assertionsError handling, crash analysis
Header<source_location><stacktrace>
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
source_location_demo.cppvoid log(const std::string& msg, const std::source_location& loc = std::source_l...What is std
stacktrace_demo.cppvoid func3() {What is std
logging_macro.cppvoid log(const std::string& msg, const std::source_location& loc) {Using source_location in Logging Macros
exception_stacktrace.cppclass traced_exception : public std::runtime_error {Capturing Stack Traces in Exception Handlers
compare_entries.cppint main() {Comparing Stacktrace Entries
conditional_stacktrace.cppvoid on_error() {Performance Considerations and Best Practices

Key takeaways

1
std::source_location provides compile-time source info with zero overhead when not used.
2
std::stacktrace captures the runtime call stack; use it sparingly due to performance cost.
3
Combine both in custom exception classes for powerful debugging.
4
Use macros or default arguments to automate source location capture.
5
Always consider the performance impact in production environments.

Common mistakes to avoid

3 patterns
×

Capturing stack trace in catch block instead of throw point

×

Using std::source_location::current() outside of a default argument

×

Assuming stack traces are always available in release builds

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
What is std::source_location and how do you use it in logging?
Q02SENIOR
How would you capture a stack trace at the point an exception is thrown?
Q03SENIOR
What are the performance implications of using std::stacktrace?
Q01 of 03SENIOR

What is std::source_location and how do you use it in logging?

ANSWER
std::source_location provides file name, line number, column, and function name at compile time. You can use it as a default argument in logging functions to automatically capture the caller's location without macros.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
Do I need C++23 to use std::stacktrace and std::source_location?
02
Can I use std::stacktrace with exceptions?
03
Is std::stacktrace portable across platforms?
N
Naren Founder & Principal Engineer

20+ years shipping performance-critical C and C++ systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 13, 2026
last updated
2,073
articles · all by Naren
🔥

That's C++ Advanced. Mark it forged?

3 min read · try the examples if you haven't

Previous
GoogleTest, Catch2, and doctest in C++
36 / 41 · C++ Advanced
Next
C++23 std::generator and Coroutine Patterns