Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,28 @@ jobs:
ninja
./unittest_module

unittest-windows-32-bit:
runs-on: windows-2022
needs: unittest-windows
strategy:
fail-fast: false
matrix:
build_type: [Debug]
steps:
- uses: actions/checkout@v4
- name: Enable Developer Command Prompt
uses: ilammy/[email protected]
- name: build and test
run: |
mkdir build
cd build
cmake .. `
-A Win32 `
-DCPPTRACE_WERROR_BUILD=On `
-DCPPTRACE_BUILD_TESTING=On
cmake --build . --config ${{matrix.build_type}}
./${{matrix.build_type}}/unittest

unittest-windows-clangcl:
runs-on: windows-2022
needs: unittest-windows
Expand Down
16 changes: 6 additions & 10 deletions include/cpptrace/from_current.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,21 @@ CPPTRACE_BEGIN_NAMESPACE
using type = void;
};

CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip);

#ifdef _MSC_VER
CPPTRACE_EXPORT bool matches_exception(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info);
CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE
void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info);
template<typename E>
CPPTRACE_FORCE_NO_INLINE inline int exception_filter(EXCEPTION_POINTERS* exception_ptrs) {
if(matches_exception(exception_ptrs, typeid(E))) {
collect_current_trace(1);
}
maybe_collect_trace(exception_ptrs, typeid(E));
return EXCEPTION_CONTINUE_SEARCH;
}
class dont_return_from_try_catch_macros {
public:
explicit dont_return_from_try_catch_macros() = default;
};
#else
CPPTRACE_EXPORT bool check_can_catch(const std::type_info*, const std::type_info*, void**, unsigned);
CPPTRACE_EXPORT CPPTRACE_FORCE_NO_INLINE
void maybe_collect_trace(const std::type_info*, const std::type_info*, void**, unsigned);
template<typename T>
class unwind_interceptor {
public:
Expand All @@ -68,9 +66,7 @@ CPPTRACE_BEGIN_NAMESPACE
void** throw_obj,
unsigned outer
) {
if(check_can_catch(&typeid(T), throw_type, throw_obj, outer)) {
collect_current_trace(1);
}
maybe_collect_trace(&typeid(T), throw_type, throw_obj, outer);
return false;
}
};
Expand Down
57 changes: 49 additions & 8 deletions src/from_current.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "utils/microfmt.hpp"
#include "utils/utils.hpp"
#include "logging.hpp"
#include "unwind/unwind.hpp"

#ifndef _MSC_VER
#include <string.h>
Expand Down Expand Up @@ -44,8 +45,7 @@ namespace detail {
return rethrow_switch;
}

CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) {
auto trace = cpptrace::generate_raw_trace(skip + 1);
void save_current_trace(raw_trace trace) {
if(get_rethrow_switch()) {
saved_rethrow_trace = lazy_trace_holder(std::move(trace));
} else {
Expand All @@ -54,6 +54,33 @@ namespace detail {
}
}

#if defined(_MSC_VER) && defined(CPPTRACE_UNWIND_WITH_DBGHELP)
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip, EXCEPTION_POINTERS* exception_ptrs) {
try {
#if defined(_M_IX86) || defined(__i386__)
// skip one frame, first is CxxThrowException
(void)skip;
auto trace = raw_trace{detail::capture_frames(1, SIZE_MAX, exception_ptrs)};
#else
(void)exception_ptrs;
auto trace = raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)};
#endif
save_current_trace(std::move(trace));
} catch(...) {
detail::log_and_maybe_propagate_exception(std::current_exception());
}
}
#else
CPPTRACE_FORCE_NO_INLINE void collect_current_trace(std::size_t skip) {
try {
auto trace = raw_trace{detail::capture_frames(skip + 1, SIZE_MAX)};
save_current_trace(std::move(trace));
} catch(...) {
detail::log_and_maybe_propagate_exception(std::current_exception());
}
}
#endif

#ifdef _MSC_VER
// https://www.youtube.com/watch?v=COEv2kq_Ht8
// 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
Expand All @@ -66,7 +93,7 @@ namespace detail {
// - https://github.com/ecatmur/stacktrace-from-exception/blob/main/stacktrace-from-exception.cpp
// - https://github.com/catboost/catboost/blob/master/contrib/libs/cxxsupp/libcxx/src/support/runtime/exception_pointer_msvc.ipp
// - https://www.geoffchappell.com/studies/msvc/language/predefined/index.htm
#if defined _WIN64
#ifdef _WIN64
#pragma pack(push, 4)
struct CatchableType {
std::uint32_t properties;
Expand All @@ -85,19 +112,20 @@ namespace detail {
};
#pragma warning(disable:4200)
#if IS_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc99-extensions"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc99-extensions"
#endif
struct CatchableTypeArray {
uint32_t nCatchableTypes;
int32_t arrayOfCatchableTypes[];
};
#if IS_CLANG
#pragma clang diagnostic pop
#pragma clang diagnostic pop
#endif
#pragma warning (pop)
#pragma pack(pop)
#else
using CatchableTypeArray = ::_CatchableTypeArray;
using CatchableType = ::_CatchableType;
using ThrowInfo = ::_ThrowInfo;
#endif
Expand Down Expand Up @@ -583,14 +611,27 @@ CPPTRACE_BEGIN_NAMESPACE
CPPTRACE_POP_EXTENSION_WARNINGS
return false;
}
CPPTRACE_FORCE_NO_INLINE
void maybe_collect_trace(EXCEPTION_POINTERS* exception_ptrs, const std::type_info& type_info) {
if(matches_exception(exception_ptrs, type_info)) {
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
collect_current_trace(2, exception_ptrs);
#else
collect_current_trace(2);
#endif
}
}
#else
bool check_can_catch(
CPPTRACE_FORCE_NO_INLINE
void maybe_collect_trace(
const std::type_info* type,
const std::type_info* throw_type,
void** throw_obj,
unsigned outer
) {
return detail::can_catch(type, throw_type, throw_obj, outer);
if(detail::can_catch(type, throw_type, throw_obj, outer)) {
collect_current_trace(2);
}
}

void do_prepare_unwind_interceptor(const std::type_info& type_info, bool(*can_catch)(const std::type_info*, const std::type_info*, void**, unsigned)) {
Expand Down
20 changes: 18 additions & 2 deletions src/unwind/unwind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
#include <cstddef>
#include <vector>

#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
#define UNWIND_MAY_NEED_CONTEXT_RECORD
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif


CPPTRACE_BEGIN_NAMESPACE
namespace detail {
#ifdef CPPTRACE_HARD_MAX_FRAMES
Expand All @@ -14,8 +21,17 @@ namespace detail {
constexpr std::size_t hard_max_frames = 400;
#endif

CPPTRACE_FORCE_NO_INLINE
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth);
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
CPPTRACE_FORCE_NO_INLINE
std::vector<frame_ptr> capture_frames(
std::size_t skip,
std::size_t max_depth,
EXCEPTION_POINTERS* exception_pointers = nullptr
);
#else
CPPTRACE_FORCE_NO_INLINE
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth);
#endif

CPPTRACE_FORCE_NO_INLINE
std::size_t safe_capture_frames(frame_ptr* buffer, std::size_t size, std::size_t skip, std::size_t max_depth);
Expand Down
58 changes: 33 additions & 25 deletions src/unwind/unwind_with_dbghelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,48 @@ namespace detail {
#pragma warning(disable: 4740) // warning C4740: flow in or out of inline asm code suppresses global optimization
#endif
CPPTRACE_FORCE_NO_INLINE
std::vector<frame_ptr> capture_frames(std::size_t skip, std::size_t max_depth) {
std::vector<frame_ptr> capture_frames(
std::size_t skip,
std::size_t max_depth,
EXCEPTION_POINTERS* exception_pointers
) {
skip++;
// https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/

// Get current thread context
// GetThreadContext cannot be used on the current thread.
// RtlCaptureContext doesn't work on i386
CONTEXT context;
#if defined(_M_IX86) || defined(__i386__)
ZeroMemory(&context, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_CONTROL;
#if IS_MSVC
__asm {
label:
mov [context.Ebp], ebp;
mov [context.Esp], esp;
mov eax, [label];
mov [context.Eip], eax;
if(exception_pointers) {
context = *exception_pointers->ContextRecord;
} else {
#if defined(_M_IX86) || defined(__i386__)
context.ContextFlags = CONTEXT_CONTROL;
#if IS_MSVC
__asm {
label:
mov [context.Ebp], ebp;
mov [context.Esp], esp;
mov eax, [label];
mov [context.Eip], eax;
}
#else
asm(
"label:\n\t"
"mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t"
"mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t"
"mov{l $label, %%eax | eax, OFFSET label};\n\t"
"mov{l %%eax, %[cEip] | %[cEip], eax};\n\t"
: [cEbp] "=r" (context.Ebp),
[cEsp] "=r" (context.Esp),
[cEip] "=r" (context.Eip)
);
#endif
#else
RtlCaptureContext(&context);
#endif
}
#else
asm(
"label:\n\t"
"mov{l %%ebp, %[cEbp] | %[cEbp], ebp};\n\t"
"mov{l %%esp, %[cEsp] | %[cEsp], esp};\n\t"
"mov{l $label, %%eax | eax, OFFSET label};\n\t"
"mov{l %%eax, %[cEip] | %[cEip], eax};\n\t"
: [cEbp] "=r" (context.Ebp),
[cEsp] "=r" (context.Esp),
[cEip] "=r" (context.Eip)
);
#endif
#else
RtlCaptureContext(&context);
#endif
// Setup current frame
STACKFRAME64 frame;
ZeroMemory(&frame, sizeof(STACKFRAME64));
Expand Down
Loading
Loading