• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright Antony Polukhin, 2016-2020.
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 
bar(int i)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 
foo(int i)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 
my_signal_handler(int signum)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 
setup_handlers()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 
my_signal_handler2(int signum)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 
copy_and_run(const char * exec_name,char param,bool not_null)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 
run_1(const char * [])95 int run_1(const char* /*argv*/[]) {
96     setup_handlers();
97     foo(5);
98     return 11;
99 }
100 
run_2(const char * argv[])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 
run_3(const char * [])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 
run_4(const char * argv[])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 
test_inplace()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 
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 
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 
main(int argc,const char * argv[])301 int main(int argc, const char* argv[]) {
302     if (argc < 2) {
303 #ifndef BOOST_WINDOWS
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 
308         // There are some issues with async-safety of shared memory writes on Windows.
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 
327