1 // Copyright Antony Polukhin, 2016-2018.
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)
7 #include <boost/array.hpp>
8 BOOST_NOINLINE
void foo(int i
);
9 BOOST_NOINLINE
void bar(int i
);
11 BOOST_NOINLINE
void bar(int i
) {
12 boost::array
<int, 5> a
= {{-1, -231, -123, -23, -32}};
20 BOOST_NOINLINE
void foo(int i
) {
24 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
26 //[getting_started_terminate_handlers
28 #include <signal.h> // ::signal, ::raise
29 #include <boost/stacktrace.hpp>
31 void my_signal_handler(int signum
) {
32 ::signal(signum
, SIG_DFL
);
33 boost::stacktrace::safe_dump_to("./backtrace.dump");
38 void setup_handlers() {
39 //[getting_started_setup_handlers
40 ::signal(SIGSEGV
, &my_signal_handler
);
41 ::signal(SIGABRT
, &my_signal_handler
);
45 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
47 BOOST_CONSTEXPR_OR_CONST
std::size_t shared_memory_size
= 4096 * 8;
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>
54 boost::interprocess::shared_memory_object g_shm
; // inited at program start
55 boost::interprocess::mapped_region g_region
; // inited at program start
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*));
68 #include <iostream> // std::cerr
69 #include <fstream> // std::ifstream
70 #include <boost/filesystem/path.hpp>
71 #include <boost/filesystem/operations.hpp>
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
);
80 boost::filesystem::path command_args
= command
;
82 command_args
+= param
;
83 const int ret
= std::system(command_args
.string().c_str());
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
) {
90 } else if (!not_null
&& ret
) {
95 int run_1(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
) {
107 boost::stacktrace::stacktrace st
= boost::stacktrace::stacktrace::from_dump(std::cin
);
114 //[getting_started_on_program_restart
115 if (boost::filesystem::exists("./backtrace.dump")) {
116 // there is a backtrace
117 std::ifstream
ifs("./backtrace.dump");
119 boost::stacktrace::stacktrace st
= boost::stacktrace::stacktrace::from_dump(ifs
);
120 std::cout
<< "Previous run crashed:\n" << st
<< std::endl
; /*<-*/
128 boost::filesystem::remove("./backtrace.dump");
136 int run_3(const char* /*argv*/[]) {
137 using namespace boost::interprocess
;
139 shared_memory_object
shm_obj(open_or_create
, "shared_memory", read_write
);
142 g_shm
.truncate(shared_memory_size
);
145 mapped_region
m(g_shm
, read_write
, 0, shared_memory_size
);
148 void** f
= static_cast<void**>(g_region
.get_address());
151 ::signal(SIGSEGV
, &my_signal_handler2
);
152 ::signal(SIGABRT
, &my_signal_handler2
);
157 int run_4(const char* argv
[]) {
158 using namespace boost::interprocess
;
160 shared_memory_object
shm_obj(open_only
, "shared_memory", read_write
);
165 mapped_region
m(g_shm
, read_write
, 0, shared_memory_size
);
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));
175 std::cout
<< "Previous run crashed and left trace in shared memory:\n" << st
<< std::endl
;
177 shared_memory_object::remove("shared_memory");
178 if (std::string(argv
[0]).find("noop") == std::string::npos
) {
199 const bool is_noop
= !boost::stacktrace::stacktrace();
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
);
210 boost::filesystem::remove("./backtrace2.dump");
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';
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.
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
));
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';
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";
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";
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();
255 if (!is_noop
&& (size_1_skipped
+ 1 != size_0_skipped
)) {
256 std::cerr
<< "failed to skip 1 frame!\n";
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
);
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
);
272 boost::filesystem::remove("./backtrace3.dump");
275 // `ss2` could be empty on some combinations of Windows+MSVC.
281 if (ss1
.size() != ss2
.size()) {
282 std::cerr
<< "Stacktraces differ:\n" << ss1
<< "\n vs \n" << ss2
<< '\n';
286 if (!is_noop
&& ss1
.size() != 1) {
287 std::cerr
<< "Stacktraces does not have size 1:\n" << ss1
<< '\n';
291 if (ss1
&& ss1
[0].address() == ss2
[0].address()) {
292 std::cerr
<< "Stacktraces must differ:\n" << ss1
<< "\n vs \n" << ss2
<< '\n';
301 int main(int argc
, const char* argv
[]) {
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);
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);
313 return test_inplace();
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
);