1// Copyright Antony Polukhin, 2023-2024.
2//
3// Distributed under the Boost Software License, Version 1.0. (See
4// accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6
7#include <boost/stacktrace.hpp>
8
9#include <iostream>
10#include <thread>
11
12#include <boost/core/lightweight_test.hpp>
13
14namespace boost { namespace stacktrace { namespace impl {
15 void assert_no_pending_traces() noexcept;
16}}}
17
18using boost::stacktrace::stacktrace;
19
20struct test_no_pending_on_finish {
21 ~test_no_pending_on_finish() {
22 boost::stacktrace::impl::assert_no_pending_traces();
23 }
24};
25
26
27BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_1(const char* msg) {
28 std::string new_msg{msg};
29 throw std::runtime_error(new_msg);
30}
31
32BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_2(const char* msg) {
33 std::string new_msg{msg};
34 throw std::runtime_error(new_msg);
35}
36
37BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_no_exception() {
38 auto trace = stacktrace::from_current_exception();
39 BOOST_TEST(!trace);
40}
41
42BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_trace_from_exception() {
43 // const test_no_pending_on_finish guard{}; // something strange
44 try {
45 in_test_throw_1(msg: "testing basic");
46 } catch (const std::exception&) {
47 auto trace = stacktrace::from_current_exception();
48 BOOST_TEST(trace);
49 std::cout << "Tarce in test_trace_from_exception(): " << trace << '\n';
50 BOOST_TEST(to_string(trace).find("in_test_throw_1") != std::string::npos);
51 }
52}
53
54BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_after_other_exception() {
55 try {
56 in_test_throw_1(msg: "test_other_exception_active");
57 } catch (const std::exception&) {
58 try {
59 in_test_throw_2(msg: "test_other_exception_active 2");
60 } catch (const std::exception&) {}
61
62 auto trace = stacktrace::from_current_exception();
63 BOOST_TEST(trace);
64 std::cout << "Tarce in test_after_other_exception(): " << trace;
65 BOOST_TEST(to_string(trace).find("in_test_throw_1") != std::string::npos);
66 BOOST_TEST(to_string(trace).find("in_test_throw_2") == std::string::npos);
67 }
68}
69
70BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_nested() {
71 try {
72 in_test_throw_1(msg: "test_other_exception_active");
73 } catch (const std::exception&) {
74 try {
75 in_test_throw_2(msg: "test_other_exception_active 2");
76 } catch (const std::exception&) {
77 auto trace = stacktrace::from_current_exception();
78 BOOST_TEST(trace);
79 std::cout << "Tarce in test_nested(): " << trace << '\n';
80 BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
81 BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
82 }
83 }
84}
85
86BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_rethrow_nested() {
87 std::exception_ptr ptr;
88
89 try {
90 in_test_throw_1(msg: "test_other_exception_active");
91 } catch (const std::exception&) {
92 try {
93 in_test_throw_2(msg: "test_other_exception_active 2");
94 } catch (const std::exception&) {
95 ptr = std::current_exception();
96 }
97 }
98
99 try {
100 std::rethrow_exception(ptr);
101 } catch (...) {
102 auto trace = stacktrace::from_current_exception();
103 BOOST_TEST(trace);
104 std::cout << "Tarce in test_rethrow_nested(): " << trace << '\n';
105 BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
106 BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
107 }
108}
109
110BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_from_other_thread() {
111
112// MinGW error: 'thread' is not a member of 'std'
113#ifndef __MINGW32__
114 std::exception_ptr ptr;
115
116 std::thread t([&ptr]{
117 try {
118 in_test_throw_1(msg: "test_other_exception_active");
119 } catch (const std::exception&) {
120 try {
121 in_test_throw_2(msg: "test_other_exception_active 2");
122 } catch (const std::exception&) {
123 ptr = std::current_exception();
124 }
125 }
126 });
127 t.join();
128
129 try {
130 std::rethrow_exception(ptr);
131 } catch (...) {
132 auto trace = stacktrace::from_current_exception();
133 BOOST_TEST(trace);
134 std::cout << "Tarce in test_rethrow_nested(): " << trace << '\n';
135 BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
136 BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
137 }
138#endif
139}
140
141int main() {
142 const test_no_pending_on_finish guard{};
143
144 BOOST_TEST(boost::stacktrace::this_thread::get_capture_stacktraces_at_throw());
145
146 test_no_exception();
147 test_trace_from_exception();
148 test_after_other_exception();
149 test_nested();
150 test_rethrow_nested();
151 test_from_other_thread();
152
153 return boost::report_errors();
154}
155

source code of boost/libs/stacktrace/test/test_from_exception.cpp