]>
Commit | Line | Data |
---|---|---|
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> | |
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 | ||
26 | //[getting_started_terminate_handlers | |
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); | |
33 | boost::stacktrace::safe_dump_to("./backtrace.dump"); | |
34 | ::raise(SIGABRT); | |
35 | } | |
36 | //] | |
37 | ||
38 | void setup_handlers() { | |
39 | //[getting_started_setup_handlers | |
40 | ::signal(SIGSEGV, &my_signal_handler); | |
41 | ::signal(SIGABRT, &my_signal_handler); | |
42 | //] | |
43 | } | |
44 | ||
45 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
46 | ||
47 | BOOST_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 | ||
54 | boost::interprocess::shared_memory_object g_shm; // inited at program start | |
55 | boost::interprocess::mapped_region g_region; // inited at program start | |
56 | ||
57 | ||
58 | void 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 | ||
74 | inline 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 | ||
95 | int run_1(const char* /*argv*/[]) { | |
96 | setup_handlers(); | |
97 | foo(5); | |
98 | return 11; | |
99 | } | |
100 | ||
101 | int 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 | ||
136 | int 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 | ||
157 | int 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 | ||
198 | int 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 | ||
301 | int 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 |