Skip to content

Commit 1cd0279

Browse files
Add RtlVirtualUnwind back-end (#317)
1 parent d77f5e8 commit 1cd0279

File tree

8 files changed

+121
-5
lines changed

8 files changed

+121
-5
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ target_sources(
142142
src/symbols/symbols_with_libdwarf.cpp
143143
src/symbols/symbols_with_nothing.cpp
144144
src/unwind/unwind_with_dbghelp.cpp
145+
src/unwind/unwind_with_rtlvirtualunwind.cpp
145146
src/unwind/unwind_with_execinfo.cpp
146147
src/unwind/unwind_with_libunwind.cpp
147148
src/unwind/unwind_with_nothing.cpp
@@ -499,6 +500,10 @@ if(CPPTRACE_UNWIND_WITH_DBGHELP)
499500
target_link_libraries(${target_name} PRIVATE dbghelp)
500501
endif()
501502

503+
if(CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND)
504+
target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND)
505+
endif()
506+
502507
if(CPPTRACE_UNWIND_WITH_NOTHING)
503508
target_compile_definitions(${target_name} PRIVATE CPPTRACE_UNWIND_WITH_NOTHING)
504509
endif()

ci/build-in-all-remaining-configs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"CPPTRACE_UNWIND_WITH_EXECINFO",
1818
"CPPTRACE_UNWIND_WITH_WINAPI",
1919
"CPPTRACE_UNWIND_WITH_DBGHELP",
20+
"CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND",
2021
"CPPTRACE_UNWIND_WITH_NOTHING",
2122
]
2223
ALL_SYMBOL_OPTIONS = [

ci/test-all-configs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def get_c_compiler_counterpart(compiler: str) -> str:
2424
"CPPTRACE_UNWIND_WITH_EXECINFO",
2525
"CPPTRACE_UNWIND_WITH_WINAPI",
2626
"CPPTRACE_UNWIND_WITH_DBGHELP",
27+
"CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND",
2728
"CPPTRACE_UNWIND_WITH_NOTHING",
2829
]
2930
ALL_SYMBOL_OPTIONS = [
@@ -433,6 +434,7 @@ def run_windows_matrix(compilers: list, shared: bool):
433434
"unwind": [
434435
"CPPTRACE_UNWIND_WITH_WINAPI",
435436
"CPPTRACE_UNWIND_WITH_DBGHELP",
437+
"CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND",
436438
"CPPTRACE_UNWIND_WITH_UNWIND", # Broken on github actions for some reason
437439
#"CPPTRACE_UNWIND_WITH_NOTHING",
438440
],

cmake/Autoconfig.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ if(
8080
CPPTRACE_UNWIND_WITH_EXECINFO OR
8181
CPPTRACE_UNWIND_WITH_WINAPI OR
8282
CPPTRACE_UNWIND_WITH_DBGHELP OR
83+
CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND OR
8384
CPPTRACE_UNWIND_WITH_NOTHING
8485
)
8586
)

cmake/OptionVariables.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ option(CPPTRACE_UNWIND_WITH_LIBUNWIND "" OFF)
132132
option(CPPTRACE_UNWIND_WITH_EXECINFO "" OFF)
133133
option(CPPTRACE_UNWIND_WITH_WINAPI "" OFF)
134134
option(CPPTRACE_UNWIND_WITH_DBGHELP "" OFF)
135+
option(CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND "" OFF)
135136
option(CPPTRACE_UNWIND_WITH_NOTHING "" OFF)
136137

137138
# ---- Demangling Options ----

src/from_current.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ namespace detail {
3737
}
3838
}
3939

40-
#if defined(_MSC_VER) && defined(CPPTRACE_UNWIND_WITH_DBGHELP)
40+
#if defined(_MSC_VER) && defined(CPPTRACE_UNWIND_WITH_EXCEPTION_POINTERS)
4141
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip, EXCEPTION_POINTERS* exception_ptrs) {
4242
try {
4343
#if defined(_M_IX86) || defined(__i386__)
@@ -376,7 +376,7 @@ CPPTRACE_BEGIN_NAMESPACE
376376
CPPTRACE_FORCE_NO_INLINE
377377
int maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, int filter_result) {
378378
if(filter_result == EXCEPTION_EXECUTE_HANDLER) {
379-
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
379+
#ifdef CPPTRACE_UNWIND_WITH_EXCEPTION_POINTERS
380380
collect_current_trace(1, exception_ptrs);
381381
#else
382382
collect_current_trace(1);
@@ -388,7 +388,7 @@ CPPTRACE_BEGIN_NAMESPACE
388388
CPPTRACE_FORCE_NO_INLINE
389389
void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info) {
390390
if(matches_exception(exception_ptrs, type_info)) {
391-
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
391+
#ifdef CPPTRACE_UNWIND_WITH_EXCEPTION_POINTERS
392392
collect_current_trace(2, exception_ptrs);
393393
#else
394394
collect_current_trace(2);

src/unwind/unwind.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#include <cstddef>
77
#include <vector>
88

9-
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
9+
#if defined(CPPTRACE_UNWIND_WITH_DBGHELP) || defined(CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND)
10+
#define CPPTRACE_UNWIND_WITH_EXCEPTION_POINTERS
1011
#ifndef WIN32_LEAN_AND_MEAN
1112
#define WIN32_LEAN_AND_MEAN
1213
#endif
@@ -22,7 +23,7 @@ namespace detail {
2223
constexpr std::size_t hard_max_frames = 400;
2324
#endif
2425

25-
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
26+
#ifdef CPPTRACE_UNWIND_WITH_EXCEPTION_POINTERS
2627
CPPTRACE_FORCE_NO_INLINE
2728
std::vector<frame_ptr> capture_frames(
2829
std::size_t skip,
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#ifdef CPPTRACE_UNWIND_WITH_RTLVIRTUALUNWIND
2+
3+
#include <cpptrace/basic.hpp>
4+
#include "unwind/unwind.hpp"
5+
#include "utils/common.hpp"
6+
#include "utils/utils.hpp"
7+
8+
#include <cstddef>
9+
#include <vector>
10+
11+
#ifndef WIN32_LEAN_AND_MEAN
12+
#define WIN32_LEAN_AND_MEAN
13+
#endif
14+
#include <windows.h>
15+
16+
#if !defined(_M_X64) && !defined(__x86_64__) && !defined(_M_ARM64) && !defined(__aarch64__)
17+
#error "Cpptrace: RtlVirtualUnwind is only supported on 64-bit Windows"
18+
#endif
19+
20+
namespace {
21+
DWORD64 context_pc(CONTEXT& context) {
22+
#if defined(_M_X64) || defined(__x86_64__)
23+
return context.Rip;
24+
#elif defined(_M_ARM64) || defined(__aarch64__)
25+
return context.Pc;
26+
#endif
27+
}
28+
}
29+
30+
CPPTRACE_BEGIN_NAMESPACE
31+
namespace detail {
32+
CPPTRACE_FORCE_NO_INLINE
33+
std::vector<frame_ptr> capture_frames(
34+
std::size_t skip,
35+
std::size_t max_depth,
36+
EXCEPTION_POINTERS* exception_pointers
37+
) {
38+
CONTEXT context;
39+
ZeroMemory(&context, sizeof(CONTEXT));
40+
if(exception_pointers) {
41+
context = *exception_pointers->ContextRecord;
42+
} else {
43+
skip++; // we're unwinding from the capture_frames frame, skip it
44+
RtlCaptureContext(&context);
45+
}
46+
47+
std::vector<frame_ptr> trace;
48+
while(trace.size() < max_depth) {
49+
DWORD64 image_base;
50+
PRUNTIME_FUNCTION function_entry = RtlLookupFunctionEntry(context_pc(context), &image_base, NULL);
51+
if(!function_entry) {
52+
break;
53+
}
54+
if(skip) {
55+
skip--;
56+
} else {
57+
// Same adjustment as StackWalk64
58+
trace.push_back(to_frame_ptr(context_pc(context)) - 1);
59+
}
60+
#if defined(_M_X64) || defined(__x86_64__)
61+
PVOID handler_data;
62+
DWORD64 establisher_frame;
63+
RtlVirtualUnwind(
64+
UNW_FLAG_NHANDLER,
65+
image_base,
66+
context_pc(context),
67+
function_entry,
68+
&context,
69+
&handler_data,
70+
&establisher_frame,
71+
NULL
72+
);
73+
#elif defined(_M_ARM64) || defined(__aarch64__)
74+
BOOLEAN in_function;
75+
FRAME_POINTERS establisher_frame;
76+
RtlVirtualUnwind(
77+
image_base,
78+
context_pc(context),
79+
function_entry,
80+
&context,
81+
&in_function,
82+
&establisher_frame,
83+
NULL
84+
);
85+
#endif
86+
if(context_pc(context) == 0) {
87+
break;
88+
}
89+
}
90+
return trace;
91+
}
92+
93+
CPPTRACE_FORCE_NO_INLINE
94+
std::size_t safe_capture_frames(frame_ptr*, std::size_t, std::size_t, std::size_t) {
95+
// Can't safe trace with RtlVirtualUnwind
96+
return 0;
97+
}
98+
99+
bool has_safe_unwind() {
100+
return false;
101+
}
102+
}
103+
CPPTRACE_END_NAMESPACE
104+
105+
#endif

0 commit comments

Comments
 (0)