From 13fe06063b84e32ac143b5b405c17c408192021f Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 19 Jan 2017 21:59:37 +0300 Subject: [PATCH] Added initial version of safe dumping --- example/terminate_handler.cpp | 59 +++++++++++++------ .../boost/stacktrace/detail/frame_msvc.ipp | 22 +++++++ .../boost/stacktrace/detail/frame_noop.ipp | 20 +++++++ .../boost/stacktrace/detail/frame_unwind.ipp | 33 ++++++++++- include/boost/stacktrace/detail/from_dump.ipp | 35 +++++++++++ include/boost/stacktrace/frame.hpp | 12 ++-- include/boost/stacktrace/stacktrace.hpp | 16 ++++- src/addr2line.cpp | 1 + src/backtrace.cpp | 1 + src/basic.cpp | 1 + src/windbg.cpp | 1 + test/Jamfile.v2 | 17 ++++-- 12 files changed, 189 insertions(+), 29 deletions(-) create mode 100644 include/boost/stacktrace/detail/from_dump.ipp diff --git a/example/terminate_handler.cpp b/example/terminate_handler.cpp index 0b3d26a..68c4dd9 100644 --- a/example/terminate_handler.cpp +++ b/example/terminate_handler.cpp @@ -28,42 +28,65 @@ BOOST_NOINLINE void foo(int i) { //[getting_started_terminate_handlers -#include // std::set_terminate, std::abort #include // ::signal #include // std::_Exit #include -#include // std::cerr - -void my_terminate_handler() { - std::cerr << "Terminate called:\n" << boost::stacktrace::stacktrace() << '\n'; - std::abort(); -} void my_signal_handler(int signum) { ::signal(signum, SIG_DFL); - //boost::stacktrace::this_thread_frames::dump("backtrace_file.txt") - boost::stacktrace::stacktrace bt; - if (bt) { - std::cerr << "Signal " << signum << ", backtrace:\n" << boost::stacktrace::stacktrace() << '\n'; // This code is not async-signal-safe! Examples will be changed soon - } + boost::stacktrace::this_thread_frames::dump("./backtrace.dump"); std::_Exit(-1); } //] void setup_handlers() { //[getting_started_setup_handlers - std::set_terminate(&my_terminate_handler); ::signal(SIGSEGV, &my_signal_handler); ::signal(SIGABRT, &my_signal_handler); //] } -int main() { - setup_handlers(); - foo(5); - - return 2; +#include // std::cerr +#include + +int main(int argc, const char* argv[]) { + if (argc < 2) { + // We are copying files to make sure that stacktrace printing works independently from executable name + { + boost::filesystem::path command_1 = argv[0]; + command_1 = command_1.parent_path() / (command_1.stem().string() + "_1") / command_1.extension(); + boost::filesystem::copy_file(argv[0], command_1, boost::filesystem::copy_option::overwrite_if_exists); + command_1 += " 1"; + if (std::system(command_1.c_str())) { + std::exit(-1); + } + } + + { + boost::filesystem::path command_2 = argv[0]; + command_2 = command_2.parent_path() / (command_2.stem().string() + "_2") / command_2.extension(); + boost::filesystem::copy_file(argv[0], command_2, boost::filesystem::copy_option::overwrite_if_exists); + command_2 += " 2"; + if (std::system(command_2.c_str())) { + std::exit(-2); + } + } + + return 0; + } + + switch(argv[1][0]) { + case '1': + setup_handlers(); + foo(5); + return -11; + case '2': + boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump("./backtrace.dump"); + std::cout << st << std::endl; + return 0; + } + return 3; } diff --git a/include/boost/stacktrace/detail/frame_msvc.ipp b/include/boost/stacktrace/detail/frame_msvc.ipp index 48ac6fe..6fdd007 100644 --- a/include/boost/stacktrace/detail/frame_msvc.ipp +++ b/include/boost/stacktrace/detail/frame_msvc.ipp @@ -301,6 +301,28 @@ std::size_t this_thread_frames::collect(void** memory, std::size_t size) BOOST_N ); } +std::size_t this_thread_frames::dump(void* fd) BOOST_NOEXCEPT { + BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = boost::stacktrace::detail::max_frames_dump; + BOOST_CONSTEXPR_OR_CONST std::size_t frames_to_skip = 1; + void* buf[buf_size]; + const std::size_t size = boost::stacktrace::this_thread_frames::collect(buf, buf_size); + if (!::WriteFile(fd, buf + frames_to_skip, sizeof(void*) * (size - frames_to_skip))) { + return 0; + } + + return size; +} + +std::size_t this_thread_frames::dump(const char* file) BOOST_NOEXCEPT { + const void* fd = ::CreateFile(file, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL); + if (fd == INVALID_HANDLE_VALUE) { + return 0; + } + const std::size_t size = boost::stacktrace::this_thread_frames::dump(fd); + ::CloseHandle(fd); + return size; +} + }} // namespace boost::stacktrace #endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP diff --git a/include/boost/stacktrace/detail/frame_noop.ipp b/include/boost/stacktrace/detail/frame_noop.ipp index 3594359..ec9546e 100644 --- a/include/boost/stacktrace/detail/frame_noop.ipp +++ b/include/boost/stacktrace/detail/frame_noop.ipp @@ -20,6 +20,10 @@ std::string to_string(const frame* /*frames*/, std::size_t /*count*/) { return std::string(); } +std::size_t from_dump(const char* /*filename*/, void** /*frames*/) { + return 0; +} + } // namespace detail std::string frame::name() const { @@ -42,6 +46,22 @@ std::size_t this_thread_frames::collect(void** /*memory*/, std::size_t /*size*/) return 0; } +#if defined(BOOST_WINDOWS) +std::size_t this_thread_frames::dump(void* /*fd*/) BOOST_NOEXCEPT { + return 0; +} +#else +std::size_t this_thread_frames::dump(int /*fd*/) BOOST_NOEXCEPT { + return 0; +} +#endif + + + +std::size_t this_thread_frames::dump(const char* /*file*/) BOOST_NOEXCEPT { + return 0; +} + }} // namespace boost::stacktrace #endif // BOOST_STACKTRACE_DETAIL_FRAME_NOOP_IPP diff --git a/include/boost/stacktrace/detail/frame_unwind.ipp b/include/boost/stacktrace/detail/frame_unwind.ipp index def8f9d..efa4c65 100644 --- a/include/boost/stacktrace/detail/frame_unwind.ipp +++ b/include/boost/stacktrace/detail/frame_unwind.ipp @@ -20,9 +20,11 @@ #include -#include +#include // ::dladdr #include #include +#include // ::write +#include // ::open #ifdef BOOST_STACKTRACE_USE_BACKTRACE # include @@ -134,6 +136,35 @@ std::size_t this_thread_frames::collect(void** memory, std::size_t size) BOOST_N return frames_count; } + +std::size_t this_thread_frames::dump(int fd) BOOST_NOEXCEPT { + BOOST_CONSTEXPR_OR_CONST std::size_t buf_size = boost::stacktrace::detail::max_frames_dump; + BOOST_CONSTEXPR_OR_CONST std::size_t frames_to_skip = 1; + void* buf[buf_size]; + const std::size_t size = boost::stacktrace::this_thread_frames::collect(buf, buf_size); + + // We do not retry, becase this function must be typically called from signal handler so it's: + // * to scary to continue in case of EINTR + // * EAGAIN or EWOULDBLOCK may occur only in case of O_NONBLOCK is set for fd, + // so it seems that user does not want to block + if (::write(fd, buf + frames_to_skip, sizeof(void*) * (size - frames_to_skip)) == -1) { + return 0; + } + + return size; +} + +std::size_t this_thread_frames::dump(const char* file) BOOST_NOEXCEPT { + const int fd = ::open(file, O_CREAT | O_WRONLY | O_TRUNC, S_IFREG | S_IWUSR | S_IRUSR); + if (fd == -1) { + return 0; + } + + const std::size_t size = boost::stacktrace::this_thread_frames::dump(fd); + ::close(fd); + return size; +} + }} // namespace boost::stacktrace #endif // BOOST_STACKTRACE_DETAIL_FRAME_UNWIND_IPP diff --git a/include/boost/stacktrace/detail/from_dump.ipp b/include/boost/stacktrace/detail/from_dump.ipp new file mode 100644 index 0000000..f9cd77a --- /dev/null +++ b/include/boost/stacktrace/detail/from_dump.ipp @@ -0,0 +1,35 @@ +// Copyright Antony Polukhin, 2016-2017. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_STACKTRACE_DETAIL_FROM_DUMP_IPP +#define BOOST_STACKTRACE_DETAIL_FROM_DUMP_IPP + +#include +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include +#include + +namespace boost { namespace stacktrace { namespace detail { + +std::size_t from_dump(const char* filename, void** frames) { + std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary); + std::size_t size = static_cast(ifs.tellg()) / sizeof(void*); + if (size > boost::stacktrace::detail::max_frames_dump) { + size = boost::stacktrace::detail::max_frames_dump; + } + + ifs.seekg(0); + ifs.read(reinterpret_cast(frames), size * sizeof(void*)); + + return size; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_FROM_DUMP_IPP diff --git a/include/boost/stacktrace/frame.hpp b/include/boost/stacktrace/frame.hpp index eaefa8b..41792b5 100644 --- a/include/boost/stacktrace/frame.hpp +++ b/include/boost/stacktrace/frame.hpp @@ -47,6 +47,9 @@ class frame; namespace detail { BOOST_STACKTRACE_FUNCTION std::string to_string(const frame* frames, std::size_t size); + + enum helper{ max_frames_dump = 128 }; + BOOST_STACKTRACE_FUNCTION std::size_t from_dump(const char* filename, void** frames); } // namespace detail /// Non-owning class that references the frame information stored inside the boost::stacktrace::stacktrace class. @@ -168,18 +171,17 @@ std::basic_ostream& operator<<(std::basic_ostream # elif defined(BOOST_MSVC) # include +# include # else # include +# include # endif #endif /// @endcond diff --git a/include/boost/stacktrace/stacktrace.hpp b/include/boost/stacktrace/stacktrace.hpp index f7ea2af..91e19c7 100644 --- a/include/boost/stacktrace/stacktrace.hpp +++ b/include/boost/stacktrace/stacktrace.hpp @@ -78,7 +78,7 @@ public: BOOST_NOINLINE explicit basic_stacktrace(std::size_t max_depth = static_cast(-1), const allocator_type& a = allocator_type()) BOOST_NOEXCEPT : impl_(a) { - const size_t buffer_size = 128; + BOOST_CONSTEXPR_OR_CONST size_t buffer_size = 128; if (!max_depth) { return; } @@ -229,6 +229,20 @@ public: const boost::container::vector& as_vector() const BOOST_NOEXCEPT { return impl_; } + + static basic_stacktrace from_dump(const char* file, const allocator_type& a = allocator_type()) { + basic_stacktrace st(0, a); + void* buf[boost::stacktrace::detail::max_frames_dump]; + const std::size_t size = boost::stacktrace::detail::from_dump(file, buf); + st.impl_.reserve(size); + for (std::size_t i = 0; i < size; ++i) { + st.impl_.push_back( + boost::stacktrace::frame(buf[i]) + ); + } + + return st; + } }; diff --git a/src/addr2line.cpp b/src/addr2line.cpp index 7360d41..558edc2 100644 --- a/src/addr2line.cpp +++ b/src/addr2line.cpp @@ -8,3 +8,4 @@ #define BOOST_STACKTRACE_USE_ADDR2LINE #define BOOST_STACKTRACE_LINK #include +#include diff --git a/src/backtrace.cpp b/src/backtrace.cpp index b9e9dc8..a90db49 100644 --- a/src/backtrace.cpp +++ b/src/backtrace.cpp @@ -8,3 +8,4 @@ #define BOOST_STACKTRACE_USE_BACKTRACE #define BOOST_STACKTRACE_LINK #include +#include diff --git a/src/basic.cpp b/src/basic.cpp index 4d6d928..221f312 100644 --- a/src/basic.cpp +++ b/src/basic.cpp @@ -7,3 +7,4 @@ #define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS #define BOOST_STACKTRACE_LINK #include +#include diff --git a/src/windbg.cpp b/src/windbg.cpp index a6be578..d9d9e84 100644 --- a/src/windbg.cpp +++ b/src/windbg.cpp @@ -7,3 +7,4 @@ #define BOOST_STACKTRACE_INTERNAL_BUILD_LIBS #define BOOST_STACKTRACE_LINK #include +#include diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 50cbaaa..c5599f1 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -73,11 +73,18 @@ test-suite stacktrace_tests # Assuring that examples compile and run. Adding sources from `examples` directory to the `type_index` test suite. for local p in [ glob ../example/*.cpp ] { - stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BT) : backtrace_$(p[1]:B) ] ; - stacktrace_tests += [ run $(p) : : : $(LINKSHARED_UNWD) : addr2line_$(p[1]:B) ] ; - stacktrace_tests += [ run $(p) : : : $(LINKSHARED_WIND) : windbg_$(p[1]:B) ] ; - stacktrace_tests += [ run $(p) : : : $(LINKSHARED_NOOP) : noop_$(p[1]:B) ] ; - stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BASIC) : basic_$(p[1]:B) ] ; + local target_name = $(p[1]:B) ; + local additional_dependency = ; + if $(target_name) = "terminate_handler" + { + additional_dependency = /boost/filesystem//boost_filesystem /boost/system//boost_system ; + } + + stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BT) $(additional_dependency) : backtrace_$(p[1]:B) ] ; + stacktrace_tests += [ run $(p) : : : $(LINKSHARED_UNWD) $(additional_dependency) : addr2line_$(p[1]:B) ] ; + stacktrace_tests += [ run $(p) : : : $(LINKSHARED_WIND) $(additional_dependency) : windbg_$(p[1]:B) ] ; + stacktrace_tests += [ run $(p) : : : $(LINKSHARED_NOOP) $(additional_dependency) : noop_$(p[1]:B) ] ; + stacktrace_tests += [ run $(p) : : : $(LINKSHARED_BASIC) $(additional_dependency) : basic_$(p[1]:B) ] ; }