Compare commits

..

4 Commits

Author SHA1 Message Date
Peter Dimov
07fd4c1e98 Add macos jobs to ci.yml 2024-05-25 14:39:45 +03:00
Marat Abrarov
39afcefb64 Avoid inclusion of rarely used Windows SDK headers which can cause conflict with other code using Windows SDK. (#157)
Signed-off-by: Marat Abrarov <abrarov@gmail.com>
2024-04-16 11:47:28 +03:00
huangqinjin
d1b7a61353 Implement stacktrace from current exception for MSVC (#159)
std::current_exception() makes a copy of current exception object
into returned std::exception_ptr. So the tracking of the original
exception object and its stacktrace are lost.
2024-04-16 11:39:07 +03:00
Antony Polukhin
351b03d522 Fix typo in documentation (fixes #158) 2024-04-01 12:17:55 +03:00
9 changed files with 266 additions and 9 deletions

View File

@@ -30,6 +30,15 @@ jobs:
- toolset: clang-15
cxxstd: "03,11,14,17,2a"
os: ubuntu-22.04
- toolset: clang
cxxstd: "03,11,14,17,20,2b"
os: macos-12
- toolset: clang
cxxstd: "03,11,14,17,20,2b"
os: macos-13
- toolset: clang
cxxstd: "03,11,14,17,20,2b"
os: macos-14
# TODO: fix and uncomment
#- toolset: clang
# cxxstd: "03,11,14,17,2a"
@@ -118,10 +127,10 @@ jobs:
cxxstd: "14,17,latest"
addrmd: 64
os: windows-2019
#- toolset: gcc
# cxxstd: "03,11,14,17,2a"
# addrmd: 64
# os: windows-2019
- toolset: gcc
cxxstd: "03,11,14,17,2a"
addrmd: 64
os: windows-2019
runs-on: ${{matrix.os}}

View File

@@ -140,13 +140,12 @@ lib boost_stacktrace_from_exception
: # requirements
<warnings>all
<target-os>linux:<library>dl
<target-os>windows:<build>no # not supported at the moment
# Command line option to disable build
<boost.stacktrace.from_exception>off:<build>no
# Require usable libbacktrace on other platforms
[ check-target-builds ../build//libbacktrace : : <build>no ]
#[ check-target-builds ../build//libbacktrace : : <build>no ]
: # default build
: # usage-requirements
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1

View File

@@ -137,7 +137,7 @@ See [link stacktrace.theoretical_async_signal_safety "Theoretical async signal s
[section Stacktrace from arbitrary exception]
[warning At the moment the functionality is only available for some of the
popular C++ runtimes for POSIX systems and requires *libbacktrace*.
popular C++ runtimes for POSIX systems with *libbacktrace* and for Windows.
Make sure that your platform is supported by running some tests.
]
@@ -163,7 +163,7 @@ int main() {
foo("test1");
bar("test2");
} catch (const std::exception& exc) {
boost::stacktrace::stacktrace trace = std::stacktrace::from_current_exception(); // <---
boost::stacktrace::stacktrace trace = boost::stacktrace::from_current_exception(); // <---
std::cerr << "Caught exception: " << exc.what() << ", trace:\n" << trace;
}
}

View File

@@ -18,7 +18,17 @@
#include <boost/core/noncopyable.hpp>
#include <boost/stacktrace/detail/to_dec_array.hpp>
#include <boost/stacktrace/detail/to_hex_array.hpp>
#ifdef WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
// Prevent inclusion of extra Windows SDK headers which can cause conflict
// with other code using Windows SDK
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#endif
#include "dbgeng.h"
#include <mutex>

View File

@@ -33,6 +33,26 @@
# pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline"
#endif
#if defined(BOOST_MSVC)
extern "C" {
BOOST_SYMBOL_EXPORT inline void* boost_stacktrace_impl_return_nullptr() { return nullptr; }
const char* boost_stacktrace_impl_current_exception_stacktrace();
bool* boost_stacktrace_impl_ref_capture_stacktraces_at_throw();
}
#ifdef _M_IX86
# pragma comment(linker, "/ALTERNATENAME:_boost_stacktrace_impl_current_exception_stacktrace=_boost_stacktrace_impl_return_nullptr")
# pragma comment(linker, "/ALTERNATENAME:_boost_stacktrace_impl_ref_capture_stacktraces_at_throw=_boost_stacktrace_impl_return_nullptr")
#else
# pragma comment(linker, "/ALTERNATENAME:boost_stacktrace_impl_current_exception_stacktrace=boost_stacktrace_impl_return_nullptr")
# pragma comment(linker, "/ALTERNATENAME:boost_stacktrace_impl_ref_capture_stacktraces_at_throw=boost_stacktrace_impl_return_nullptr")
#endif
#endif
namespace boost { namespace stacktrace {
namespace impl {
@@ -381,6 +401,8 @@ public:
if (impl::current_exception_stacktrace) {
trace = impl::current_exception_stacktrace();
}
#elif defined(BOOST_MSVC)
trace = boost_stacktrace_impl_current_exception_stacktrace();
#endif
if (trace) {

View File

@@ -27,6 +27,10 @@ inline void set_capture_stacktraces_at_throw(bool enable = true) noexcept {
if (impl::ref_capture_stacktraces_at_throw) {
impl::ref_capture_stacktraces_at_throw() = enable;
}
#elif defined(BOOST_MSVC)
if (bool* p = boost_stacktrace_impl_ref_capture_stacktraces_at_throw()) {
*p = enable;
}
#endif
(void)enable;
}
@@ -45,6 +49,10 @@ inline bool get_capture_stacktraces_at_throw() noexcept {
if (impl::ref_capture_stacktraces_at_throw) {
return impl::ref_capture_stacktraces_at_throw();
}
#elif defined(BOOST_MSVC)
if (bool* p = boost_stacktrace_impl_ref_capture_stacktraces_at_throw()) {
return *p;
}
#endif
return false;
}

View File

@@ -4,6 +4,147 @@
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#if defined(_MSC_VER)
#include <boost/stacktrace/safe_dump_to.hpp>
#include <windows.h>
extern "C" void** __cdecl __current_exception(); // exported from vcruntime.dll
#define _pCurrentException static_cast<PEXCEPTION_RECORD>(*__current_exception())
namespace {
constexpr std::size_t kStacktraceDumpSize = 4096;
struct thrown_info {
ULONG_PTR object;
char* dump;
};
struct exception_data {
bool capture_stacktraces_at_throw = true;
unsigned count = 0;
thrown_info* info = nullptr;
~exception_data() noexcept {
HANDLE hHeap = GetProcessHeap();
for (unsigned i = 0; i < count; ++i) {
HeapFree(hHeap, 0, info[i].dump);
}
HeapFree(hHeap, 0, info);
}
};
thread_local exception_data data;
inline bool PER_IS_MSVC_EH(PEXCEPTION_RECORD p) noexcept {
const DWORD EH_EXCEPTION_NUMBER = 0xE06D7363;
const ULONG_PTR EH_MAGIC_NUMBER1 = 0x19930520;
return p->ExceptionCode == EH_EXCEPTION_NUMBER &&
(p->NumberParameters == 3 || p->NumberParameters == 4) &&
p->ExceptionInformation[0] == EH_MAGIC_NUMBER1;
}
inline ULONG_PTR PER_PEXCEPTOBJ(PEXCEPTION_RECORD p) noexcept {
return p->ExceptionInformation[1];
}
unsigned current_cxx_exception_index() noexcept {
if (PEXCEPTION_RECORD current_cxx_exception = _pCurrentException) {
for (unsigned i = data.count; i > 0;) {
--i;
if (data.info[i].object == PER_PEXCEPTOBJ(current_cxx_exception)) {
return i;
}
}
}
return data.count;
}
bool is_processing_rethrow(PEXCEPTION_RECORD p) noexcept {
// Processing flow:
// 0. throw;
// 1. _CxxThrowException(pExceptionObject = nullptr)
// 2. VEH & SEH (may throw new c++ exceptions!)
// 3. __RethrowException(_pCurrentException)
// 4. VEH
if (PER_PEXCEPTOBJ(p) == 0) return true;
PEXCEPTION_RECORD current_cxx_exception = _pCurrentException;
if (current_cxx_exception == nullptr) return false;
return PER_PEXCEPTOBJ(p) == PER_PEXCEPTOBJ(current_cxx_exception);
}
LONG NTAPI veh(PEXCEPTION_POINTERS p) {
if (data.capture_stacktraces_at_throw &&
PER_IS_MSVC_EH(p->ExceptionRecord) &&
!is_processing_rethrow(p->ExceptionRecord)) {
HANDLE hHeap = GetProcessHeap();
unsigned index = current_cxx_exception_index();
unsigned new_count = 1 + (index < data.count ? index + 1 : 0);
for (unsigned i = new_count; i < data.count; ++i) {
HeapFree(hHeap, 0, data.info[i].dump);
data.info[i].dump = nullptr;
}
void* new_info;
if (data.info) {
new_info = HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, data.info, sizeof(thrown_info) * new_count);
} else {
new_info = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(thrown_info) * new_count);
}
if (new_info) {
data.count = new_count;
data.info = static_cast<thrown_info*>(new_info);
data.info[data.count - 1].object = PER_PEXCEPTOBJ(p->ExceptionRecord);
char*& dump_ptr = data.info[data.count - 1].dump;
if (dump_ptr == nullptr) {
dump_ptr = static_cast<char*>(HeapAlloc(hHeap, 0, kStacktraceDumpSize));
}
if (dump_ptr != nullptr) {
const std::size_t kSkip = 4;
boost::stacktrace::safe_dump_to(kSkip, dump_ptr, kStacktraceDumpSize);
}
} else if (new_count <= data.count) {
data.count = new_count - 1;
HeapFree(hHeap, 0, data.info[data.count - 1].dump);
data.info[data.count - 1].dump = nullptr;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
struct veh_installer {
PVOID h;
veh_installer() noexcept : h(AddVectoredExceptionHandler(1, veh)) {}
~veh_installer() noexcept { RemoveVectoredExceptionHandler(h); }
} installer;
}
extern "C" {
BOOST_SYMBOL_EXPORT const char* boost_stacktrace_impl_current_exception_stacktrace() {
unsigned index = current_cxx_exception_index();
return index < data.count ? data.info[index].dump : nullptr;
}
BOOST_SYMBOL_EXPORT bool* boost_stacktrace_impl_ref_capture_stacktraces_at_throw() {
return &data.capture_stacktraces_at_throw;
}
}
namespace boost { namespace stacktrace { namespace impl {
BOOST_SYMBOL_EXPORT void assert_no_pending_traces() noexcept {
}
}}} // namespace boost::stacktrace::impl
#else
#include "exception_headers.h"
// At the moment the file is used only on POSIX. _Unwind_Backtrace may be
@@ -222,3 +363,4 @@ BOOST_SYMBOL_EXPORT void assert_no_pending_traces() noexcept {
}}} // namespace boost::stacktrace::impl
#endif

View File

@@ -183,17 +183,29 @@ test-suite stacktrace_tests
[ run test_from_exception_none.cpp : : : $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_none_basic_ho ]
[ run test_from_exception_none.cpp : : : $(LINKSHARED_BT) <debug-symbols>on : from_exception_none_bt ]
[ run test_from_exception_none.cpp : : : <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) <debug-symbols>on : from_exception_none_bt_ho ]
[ run test_from_exception_none.cpp : : : $(LINKSHARED_WIND) <debug-symbols>on : from_exception_none_windbg ]
[ run test_from_exception_none.cpp : : : <define>BOOST_STACKTRACE_USE_WINDBG $(WIND_DEPS) <debug-symbols>on : from_exception_none_windbg_ho ]
[ run test_from_exception_none.cpp : : : $(LINKSHARED_WIND_CACHED) <debug-symbols>on : from_exception_none_windbg_cached ]
[ run test_from_exception_none.cpp : : : <define>BOOST_STACKTRACE_USE_WINDBG_CACHED $(WICA_DEPS) <debug-symbols>on : from_exception_none_windbg_cached_ho ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_disabled_basic ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_disabled_basic_ho ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BT) <debug-symbols>on : from_exception_disabled_bt ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) : from_exception_disabled_bt_ho ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_WIND) <debug-symbols>on : from_exception_disabled_windbg ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_WINDBG $(WIND_DEPS) <debug-symbols>on : from_exception_disabled_windbg_ho ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_WIND_CACHED) <debug-symbols>on : from_exception_disabled_windbg_cached ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_WINDBG_CACHED $(WICA_DEPS) <debug-symbols>on : from_exception_disabled_windbg_cached_ho ]
[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_basic ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BT) <debug-symbols>on : from_exception_bt ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_WIND) <debug-symbols>on : from_exception_windbg ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_WIND_CACHED) <debug-symbols>on : from_exception_windbg_cached ]
[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_basic_ho ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) <debug-symbols>on : from_exception_bt_ho ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_WINDBG $(WIND_DEPS) <debug-symbols>on : from_exception_windbg_ho ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_WINDBG_CACHED $(WICA_DEPS) <debug-symbols>on : from_exception_windbg_cached_ho ]
;
# Assuring that examples compile and run. Adding sources from `examples` directory to the `type_index` test suite.

View File

@@ -31,7 +31,27 @@ BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_1(const char* msg) {
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_2(const char* msg) {
std::string new_msg{msg};
throw std::runtime_error(new_msg);
throw std::logic_error(new_msg);
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_rethrow_1(const char* msg) {
try {
in_test_throw_1(msg);
} catch (const std::exception&) {
throw;
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_rethrow_2(const char* msg) {
try {
in_test_throw_2(msg);
} catch (const std::exception&) {
try {
in_test_throw_1(msg);
} catch (const std::exception&) {}
throw;
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_no_exception() {
@@ -67,6 +87,31 @@ BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_after_other_exception() {
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_rethrow() {
try {
in_test_rethrow_1("test rethrow");
} catch (const std::exception&) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_rethrow(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") != std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_rethrow_1") != std::string::npos);
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_rethrow_after_other_exception() {
try {
in_test_rethrow_2("test_rethrow_after_other_exception");
} catch (const std::exception&) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_rethrow_after_other_exception(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_rethrow_2") != std::string::npos);
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_nested() {
try {
in_test_throw_1("test_other_exception_active");
@@ -103,7 +148,11 @@ BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_rethrow_nested() {
BOOST_TEST(trace);
std::cout << "Tarce in test_rethrow_nested(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
#if defined(BOOST_MSVC)
BOOST_TEST(to_string(trace).find("in_test_throw_2") == std::string::npos);
#else
BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
#endif
}
}
@@ -133,7 +182,11 @@ BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_from_other_thread() {
BOOST_TEST(trace);
std::cout << "Tarce in test_rethrow_nested(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
#if defined(BOOST_MSVC)
BOOST_TEST(to_string(trace).find("in_test_throw_2") == std::string::npos);
#else
BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
#endif
}
#endif
}
@@ -146,6 +199,8 @@ int main() {
test_no_exception();
test_trace_from_exception();
test_after_other_exception();
test_rethrow();
test_rethrow_after_other_exception();
test_nested();
test_rethrow_nested();
test_from_other_thread();