Skip to content

Commit 95e98c7

Browse files
Test and fix 32-bit windows (#262)
1 parent 2819fb1 commit 95e98c7

File tree

6 files changed

+186
-95
lines changed

6 files changed

+186
-95
lines changed

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,28 @@ jobs:
678678
ninja
679679
./unittest_module
680680
681+
unittest-windows-32-bit:
682+
runs-on: windows-2022
683+
needs: unittest-windows
684+
strategy:
685+
fail-fast: false
686+
matrix:
687+
build_type: [Debug]
688+
steps:
689+
- uses: actions/checkout@v4
690+
- name: Enable Developer Command Prompt
691+
uses: ilammy/msvc-dev-cmd@v1.13.0
692+
- name: build and test
693+
run: |
694+
mkdir build
695+
cd build
696+
cmake .. `
697+
-A Win32 `
698+
-DCPPTRACE_WERROR_BUILD=On `
699+
-DCPPTRACE_BUILD_TESTING=On
700+
cmake --build . --config ${{matrix.build_type}}
701+
./${{matrix.build_type}}/unittest
702+
681703
unittest-windows-clangcl:
682704
runs-on: windows-2022
683705
needs: unittest-windows

include/cpptrace/from_current.hpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,21 @@ CPPTRACE_BEGIN_NAMESPACE
4141
using type = void;
4242
};
4343

44-
CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip);
45-
4644
#ifdef _MSC_VER
47-
CPPTRACE_EXPORT bool matches_exception(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info);
45+
CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE
46+
void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info);
4847
template<typename E>
4948
CPPTRACE_FORCE_NO_INLINE inline int exception_filter(EXCEPTION_POINTERS* exception_ptrs) {
50-
if(matches_exception(exception_ptrs, typeid(E))) {
51-
collect_current_trace(1);
52-
}
49+
maybe_collect_trace(exception_ptrs, typeid(E));
5350
return EXCEPTION_CONTINUE_SEARCH;
5451
}
5552
class dont_return_from_try_catch_macros {
5653
public:
5754
explicit dont_return_from_try_catch_macros() = default;
5855
};
5956
#else
60-
CPPTRACE_EXPORT bool check_can_catch(const std::type_info*, const std::type_info*, void**, unsigned);
57+
CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE
58+
void maybe_collect_trace(const std::type_info*, const std::type_info*, void**, unsigned);
6159
template<typename T>
6260
class unwind_interceptor {
6361
public:
@@ -68,9 +66,7 @@ CPPTRACE_BEGIN_NAMESPACE
6866
void** throw_obj,
6967
unsigned outer
7068
) {
71-
if(check_can_catch(&typeid(T), throw_type, throw_obj, outer)) {
72-
collect_current_trace(1);
73-
}
69+
maybe_collect_trace(&typeid(T), throw_type, throw_obj, outer);
7470
return false;
7571
}
7672
};

src/from_current.cpp

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "utils/microfmt.hpp"
1212
#include "utils/utils.hpp"
1313
#include "logging.hpp"
14+
#include "unwind/unwind.hpp"
1415

1516
#ifndef _MSC_VER
1617
#include <string.h>
@@ -44,8 +45,7 @@ namespace detail {
4445
return rethrow_switch;
4546
}
4647

47-
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) {
48-
auto trace = cpptrace::generate_raw_trace(skip + 1);
48+
void save_current_trace(raw_trace trace) {
4949
if(get_rethrow_switch()) {
5050
saved_rethrow_trace = lazy_trace_holder(std::move(trace));
5151
} else {
@@ -54,6 +54,33 @@ namespace detail {
5454
}
5555
}
5656

57+
#if defined(_MSC_VER) && defined(CPPTRACE_UNWIND_WITH_DBGHELP)
58+
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip, EXCEPTION_POINTERS* exception_ptrs) {
59+
try {
60+
#if defined(_M_IX86) || defined(__i386__)
61+
// skip one frame, first is CxxThrowException
62+
(void)skip;
63+
auto trace = raw_trace{detail::capture_frames(1, SIZE_MAX, exception_ptrs)};
64+
#else
65+
(void)exception_ptrs;
66+
auto trace = raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)};
67+
#endif
68+
save_current_trace(std::move(trace));
69+
} catch(...) {
70+
detail::log_and_maybe_propagate_exception(std::current_exception());
71+
}
72+
}
73+
#else
74+
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) {
75+
try {
76+
auto trace = raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)};
77+
save_current_trace(std::move(trace));
78+
} catch(...) {
79+
detail::log_and_maybe_propagate_exception(std::current_exception());
80+
}
81+
}
82+
#endif
83+
5784
#ifdef _MSC_VER
5885
// https://www.youtube.com/watch?v=COEv2kq_Ht8
5986
// https://github.com/tpn/pdfs/blob/master/2018%20CppCon%20Unwinding%20the%20Stack%20-%20Exploring%20how%20C%2B%2B%20Exceptions%20work%20on%20Windows%20-%20James%20McNellis.pdf
@@ -66,7 +93,7 @@ namespace detail {
6693
// - https://github.com/ecatmur/stacktrace-from-exception/blob/main/stacktrace-from-exception.cpp
6794
// - https://github.com/catboost/catboost/blob/master/contrib/libs/cxxsupp/libcxx/src/support/runtime/exception_pointer_msvc.ipp
6895
// - https://www.geoffchappell.com/studies/msvc/language/predefined/index.htm
69-
#if defined _WIN64
96+
#ifdef _WIN64
7097
#pragma pack(push, 4)
7198
struct CatchableType {
7299
std::uint32_t properties;
@@ -85,19 +112,20 @@ namespace detail {
85112
};
86113
#pragma warning(disable:4200)
87114
#if IS_CLANG
88-
#pragma clang diagnostic push
89-
#pragma clang diagnostic ignored "-Wc99-extensions"
115+
#pragma clang diagnostic push
116+
#pragma clang diagnostic ignored "-Wc99-extensions"
90117
#endif
91118
struct CatchableTypeArray {
92119
uint32_t nCatchableTypes;
93120
int32_t arrayOfCatchableTypes[];
94121
};
95122
#if IS_CLANG
96-
#pragma clang diagnostic pop
123+
#pragma clang diagnostic pop
97124
#endif
98125
#pragma warning (pop)
99126
#pragma pack(pop)
100127
#else
128+
using CatchableTypeArray = ::_CatchableTypeArray;
101129
using CatchableType = ::_CatchableType;
102130
using ThrowInfo = ::_ThrowInfo;
103131
#endif
@@ -583,14 +611,27 @@ CPPTRACE_BEGIN_NAMESPACE
583611
CPPTRACE_POP_EXTENSION_WARNINGS
584612
return false;
585613
}
614+
CPPTRACE_FORCE_NO_INLINE
615+
void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info) {
616+
if(matches_exception(exception_ptrs, type_info)) {
617+
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
618+
collect_current_trace(2, exception_ptrs);
619+
#else
620+
collect_current_trace(2);
621+
#endif
622+
}
623+
}
586624
#else
587-
bool check_can_catch(
625+
CPPTRACE_FORCE_NO_INLINE
626+
void maybe_collect_trace(
588627
const std::type_info* type,
589628
const std::type_info* throw_type,
590629
void** throw_obj,
591630
unsigned outer
592631
) {
593-
return detail::can_catch(type, throw_type, throw_obj, outer);
632+
if(detail::can_catch(type, throw_type, throw_obj, outer)) {
633+
collect_current_trace(2);
634+
}
594635
}
595636

596637
void do_prepare_unwind_interceptor(const std::type_info& type_info, bool(*can_catch)(const std::type_info*, const std::type_info*, void**, unsigned)) {

src/unwind/unwind.hpp

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

9+
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
10+
#define UNWIND_MAY_NEED_CONTEXT_RECORD
11+
#define WIN32_LEAN_AND_MEAN
12+
#include <windows.h>
13+
#endif
14+
15+
916
CPPTRACE_BEGIN_NAMESPACE
1017
namespace detail {
1118
#ifdef CPPTRACE_HARD_MAX_FRAMES
@@ -14,8 +21,17 @@ namespace detail {
1421
constexpr std::size_t hard_max_frames = 400;
1522
#endif
1623

17-
CPPTRACE_FORCE_NO_INLINE
18-
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth);
24+
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
25+
CPPTRACE_FORCE_NO_INLINE
26+
std::vector<frame_ptr> capture_frames(
27+
std::size_t skip,
28+
std::size_t max_depth,
29+
EXCEPTION_POINTERS* exception_pointers = nullptr
30+
);
31+
#else
32+
CPPTRACE_FORCE_NO_INLINE
33+
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth);
34+
#endif
1935

2036
CPPTRACE_FORCE_NO_INLINE
2137
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth);

src/unwind/unwind_with_dbghelp.cpp

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,40 +24,48 @@ namespace detail {
2424
#pragma warning(disable: 4740) // warning C4740: flow in or out of inline asm code suppresses global optimization
2525
#endif
2626
CPPTRACE_FORCE_NO_INLINE
27-
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth) {
27+
std::vector<frame_ptr> capture_frames(
28+
std::size_t skip,
29+
std::size_t max_depth,
30+
EXCEPTION_POINTERS* exception_pointers
31+
) {
2832
skip++;
2933
// https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/
3034

3135
// Get current thread context
3236
// GetThreadContext cannot be used on the current thread.
3337
// RtlCaptureContext doesn't work on i386
3438
CONTEXT context;
35-
#if defined(_M_IX86) || defined(__i386__)
3639
ZeroMemory(&context, sizeof(CONTEXT));
37-
context.ContextFlags = CONTEXT_CONTROL;
38-
#if IS_MSVC
39-
__asm {
40-
label:
41-
mov [context.Ebp], ebp;
42-
mov [context.Esp], esp;
43-
mov eax, [label];
44-
mov [context.Eip], eax;
40+
if(exception_pointers) {
41+
context = *exception_pointers->ContextRecord;
42+
} else {
43+
#if defined(_M_IX86) || defined(__i386__)
44+
context.ContextFlags = CONTEXT_CONTROL;
45+
#if IS_MSVC
46+
__asm {
47+
label:
48+
mov [context.Ebp], ebp;
49+
mov [context.Esp], esp;
50+
mov eax, [label];
51+
mov [context.Eip], eax;
52+
}
53+
#else
54+
asm(
55+
"label:\n\t"
56+
"mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t"
57+
"mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t"
58+
"mov{l $label, %%eax | eax, OFFSET label};\n\t"
59+
"mov{l %%eax, %[cEip] | %[cEip], eax};\n\t"
60+
: [cEbp] "=r" (context.Ebp),
61+
[cEsp] "=r" (context.Esp),
62+
[cEip] "=r" (context.Eip)
63+
);
64+
#endif
65+
#else
66+
RtlCaptureContext(&context);
67+
#endif
4568
}
46-
#else
47-
asm(
48-
"label:\n\t"
49-
"mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t"
50-
"mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t"
51-
"mov{l $label, %%eax | eax, OFFSET label};\n\t"
52-
"mov{l %%eax, %[cEip] | %[cEip], eax};\n\t"
53-
: [cEbp] "=r" (context.Ebp),
54-
[cEsp] "=r" (context.Esp),
55-
[cEip] "=r" (context.Eip)
56-
);
57-
#endif
58-
#else
59-
RtlCaptureContext(&context);
60-
#endif
6169
// Setup current frame
6270
STACKFRAME64 frame;
6371
ZeroMemory(&frame, sizeof(STACKFRAME64));

0 commit comments

Comments
 (0)