]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/stacktrace/example/terminate_handler.cpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / libs / stacktrace / example / terminate_handler.cpp
CommitLineData
f67539c2 1// Copyright Antony Polukhin, 2016-2020.
b32b8144
FG
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/array.hpp>
8BOOST_NOINLINE void foo(int i);
9BOOST_NOINLINE void bar(int i);
10
11BOOST_NOINLINE void bar(int i) {
12 boost::array<int, 5> a = {{-1, -231, -123, -23, -32}};
13 if (i >= 0) {
14 foo(a[i]);
15 } else {
16 std::terminate();
17 }
18}
19
20BOOST_NOINLINE void foo(int i) {
21 bar(--i);
22}
23
24////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
25
26//[getting_started_terminate_handlers
27
28#include <signal.h> // ::signal, ::raise
29#include <boost/stacktrace.hpp>
30
31void my_signal_handler(int signum) {
32 ::signal(signum, SIG_DFL);
33 boost::stacktrace::safe_dump_to("./backtrace.dump");
34 ::raise(SIGABRT);
35}
36//]
37
38void setup_handlers() {
39//[getting_started_setup_handlers
40 ::signal(SIGSEGV, &my_signal_handler);
41 ::signal(SIGABRT, &my_signal_handler);
42//]
43}
44
45////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46
47BOOST_CONSTEXPR_OR_CONST std::size_t shared_memory_size = 4096 * 8;
48
49//[getting_started_terminate_handlers_shmem
50#include <boost/stacktrace.hpp>
51#include <boost/interprocess/shared_memory_object.hpp>
52#include <boost/interprocess/mapped_region.hpp>
53
54boost::interprocess::shared_memory_object g_shm; // inited at program start
55boost::interprocess::mapped_region g_region; // inited at program start
56
57
58void my_signal_handler2(int signum) {
59 ::signal(signum, SIG_DFL);
60 void** f = static_cast<void**>(g_region.get_address());
61 *f = reinterpret_cast<void*>(1); // Setting flag that shared memory now constains stacktrace.
62 boost::stacktrace::safe_dump_to(f + 1, g_region.get_size() - sizeof(void*));
63
64 ::raise(SIGABRT);
65}
66//]
67
68#include <iostream> // std::cerr
69#include <fstream> // std::ifstream
70#include <boost/filesystem/path.hpp>
71#include <boost/filesystem/operations.hpp>
72
73
74inline void copy_and_run(const char* exec_name, char param, bool not_null) {
75 std::cout << "Running with param " << param << std::endl;
76 boost::filesystem::path command = exec_name;
77 command = command.parent_path() / (command.stem().string() + param + command.extension().string());
78 boost::filesystem::copy_file(exec_name, command, boost::filesystem::copy_option::overwrite_if_exists);
79
80 boost::filesystem::path command_args = command;
81 command_args += ' ';
82 command_args += param;
83 const int ret = std::system(command_args.string().c_str());
84
85 std::cout << "End Running with param " << param << "; ret code is " << ret << std::endl;
86 boost::system::error_code ignore;
87 boost::filesystem::remove(command, ignore);
88 if (not_null && !ret) {
89 std::exit(97);
90 } else if (!not_null && ret) {
91 std::exit(ret);
92 }
93}
94
95int run_1(const char* /*argv*/[]) {
96 setup_handlers();
97 foo(5);
98 return 11;
99}
100
101int run_2(const char* argv[]) {
102 if (!boost::filesystem::exists("./backtrace.dump")) {
103 if (std::string(argv[0]).find("noop") == std::string::npos) {
104 return 21;
105 }
106
107 boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump(std::cin);
108 if (st) {
109 return 22;
110 }
111 return 0;
112 }
113
114//[getting_started_on_program_restart
115 if (boost::filesystem::exists("./backtrace.dump")) {
116 // there is a backtrace
117 std::ifstream ifs("./backtrace.dump");
118
119 boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump(ifs);
120 std::cout << "Previous run crashed:\n" << st << std::endl; /*<-*/
121
122 if (!st) {
123 return 23;
124 } /*->*/
125
126 // cleaning up
127 ifs.close();
128 boost::filesystem::remove("./backtrace.dump");
129 }
130//]
131
132 return 0;
133}
134
135
136int run_3(const char* /*argv*/[]) {
137 using namespace boost::interprocess;
138 {
139 shared_memory_object shm_obj(open_or_create, "shared_memory", read_write);
140 shm_obj.swap(g_shm);
141 }
142 g_shm.truncate(shared_memory_size);
143
144 {
145 mapped_region m(g_shm, read_write, 0, shared_memory_size);
146 m.swap(g_region);
147 }
148 void** f = static_cast<void**>(g_region.get_address());
149 *f = 0;
150
151 ::signal(SIGSEGV, &my_signal_handler2);
152 ::signal(SIGABRT, &my_signal_handler2);
153 foo(5);
154 return 31;
155}
156
157int run_4(const char* argv[]) {
158 using namespace boost::interprocess;
159 {
160 shared_memory_object shm_obj(open_only, "shared_memory", read_write);
161 shm_obj.swap(g_shm);
162 }
163
164 {
165 mapped_region m(g_shm, read_write, 0, shared_memory_size);
166 m.swap(g_region);
167 }
168
169//[getting_started_on_program_restart_shmem
170 void** f = static_cast<void**>(g_region.get_address());
171 if (*f) { // Checking if memory constains stacktrace.
172 boost::stacktrace::stacktrace st
173 = boost::stacktrace::stacktrace::from_dump(f + 1, g_region.get_size() - sizeof(bool));
174
175 std::cout << "Previous run crashed and left trace in shared memory:\n" << st << std::endl;
176 *f = 0; /*<-*/
177 shared_memory_object::remove("shared_memory");
178 if (std::string(argv[0]).find("noop") == std::string::npos) {
179 if (!st) {
180 return 43;
181 }
182 } else {
183 if (st) {
184 return 44;
185 }
186 }
187 } else {
188 return 42; /*->*/
189 }
190//]
191
192
193 return 0;
194}
195
196#include <sstream>
197
198int test_inplace() {
199 const bool is_noop = !boost::stacktrace::stacktrace();
200
201 {
202 // This is very dependent on compiler and link flags. No sane way to make it work, because:
203 // * BOOST_NOINLINE could be ignored by MSVC compiler if link-time optimization is enabled.
204 // * BOOST_FORCEINLINE could be ignored by GCC depending on the std::vector default constructor length.
205 const std::size_t frames_ss1 = boost::stacktrace::safe_dump_to("./backtrace2.dump");
206 boost::stacktrace::stacktrace ss2;
207 std::ifstream ifs("./backtrace2.dump");
208 boost::stacktrace::stacktrace ss1 = boost::stacktrace::stacktrace::from_dump(ifs);
209 ifs.close();
210 boost::filesystem::remove("./backtrace2.dump");
211
212 if (ss1.size() + 1 != frames_ss1 || ss2.size() != ss1.size()) {
213 std::cerr << "51: Stacktraces differ. Dumped size == " << frames_ss1 << ".\n" << ss1 << "\n vs \n" << ss2 << '\n';
214 } else if (ss1.size() > 1 && ss1[1].name() != ss2[1].name()) {
215 std::cerr << "52: Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
216 }
217 }
218
219 {
220 // This is very dependent on compiler and link flags. No sane way to make it work, because:
221 // * BOOST_NOINLINE could be ignored by MSVC compiler if link-time optimization is enabled.
222 // * BOOST_FORCEINLINE could be ignored by GCC depending on the std::vector default constructor length.
223 void* data[1024];
224 const std::size_t frames_ss1 = boost::stacktrace::safe_dump_to(data, sizeof(data));
225 boost::stacktrace::stacktrace ss2;
226 boost::stacktrace::stacktrace ss1 = boost::stacktrace::stacktrace::from_dump(data, sizeof(data));
227
228 if (ss1.size() + 1 != frames_ss1 || ss1.size() != ss2.size()) {
229 std::cerr << "53: Stacktraces differ. Dumped size == " << frames_ss1 << ".\n" << ss1 << "\n vs \n" << ss2 << '\n';
230 } else if (ss1.size() > 1 && ss1[1].name() != ss2[1].name()) {
231 std::cerr << "54: Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
232 }
233 }
234
235 {
236 void* data[1024];
237 boost::stacktrace::safe_dump_to(1024, data, sizeof(data));
238 if (boost::stacktrace::stacktrace::from_dump(data, sizeof(data))) {
239 std::cerr << "Stacktrace not empty!\n";
240 return 55;
241 }
242 }
243
244 {
245 void* data[1024];
246 boost::stacktrace::safe_dump_to(1, data, sizeof(data));
247 if (!is_noop && !boost::stacktrace::stacktrace::from_dump(data, sizeof(data))) {
248 std::cerr << "Stacktrace empty!\n";
249 return 56;
250 }
251 const std::size_t size_1_skipped = boost::stacktrace::stacktrace::from_dump(data, sizeof(data)).size();
252 boost::stacktrace::safe_dump_to(0, data, sizeof(data));
253 const std::size_t size_0_skipped = boost::stacktrace::stacktrace::from_dump(data, sizeof(data)).size();
254
255 if (!is_noop && (size_1_skipped + 1 != size_0_skipped)) {
256 std::cerr << "failed to skip 1 frame!\n";
257 return 57;
258 }
259 }
260
261 {
262 boost::stacktrace::safe_dump_to(0, 1, "./backtrace3.dump");
263 std::ifstream ifs("./backtrace3.dump");
264 boost::stacktrace::stacktrace ss1 = boost::stacktrace::stacktrace::from_dump(ifs);
265 ifs.close();
266
267 boost::stacktrace::safe_dump_to(1, 1, "./backtrace3.dump");
268 ifs.open("./backtrace3.dump");
269 boost::stacktrace::stacktrace ss2 = boost::stacktrace::stacktrace::from_dump(ifs);
270 ifs.close();
271
272 boost::filesystem::remove("./backtrace3.dump");
273
11fdf7f2
TL
274#ifdef BOOST_WINDOWS
275 // `ss2` could be empty on some combinations of Windows+MSVC.
276 if (!ss2) {
277 return 0;
278 }
279#endif
280
b32b8144
FG
281 if (ss1.size() != ss2.size()) {
282 std::cerr << "Stacktraces differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
283 return 58;
284 }
285
286 if (!is_noop && ss1.size() != 1) {
287 std::cerr << "Stacktraces does not have size 1:\n" << ss1 << '\n';
288 return 59;
289 }
290
291 if (ss1 && ss1[0].address() == ss2[0].address()) {
292 std::cerr << "Stacktraces must differ:\n" << ss1 << "\n vs \n" << ss2 << '\n';
293 return 60;
294 }
295 }
296
297 return 0;
298}
299
300
301int main(int argc, const char* argv[]) {
302 if (argc < 2) {
11fdf7f2 303#ifndef BOOST_WINDOWS
b32b8144
FG
304 // We are copying files to make sure that stacktrace printing works independently from executable name
305 copy_and_run(argv[0], '1', true);
306 copy_and_run(argv[0], '2', false);
307
11fdf7f2 308 // There are some issues with async-safety of shared memory writes on Windows.
b32b8144
FG
309 copy_and_run(argv[0], '3', true);
310 copy_and_run(argv[0], '4', false);
311#endif
312
313 return test_inplace();
314 }
315
316 switch (argv[1][0]) {
317 case '1': return run_1(argv);
318 case '2': return run_2(argv);
319 case '3': return run_3(argv);
320 case '4': return run_4(argv);
321 }
322
323 return 404;
324}
325
326