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