diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-09-13 11:24:46 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-09-13 11:25:39 +0900 |
commit | 4fadd968fa12130524c8380f33fcfe25d4de79e5 (patch) | |
tree | fd26a490cd15388d42fc6652b3c5c13012e7f93e /boost/stacktrace | |
parent | b5c87084afaef42b2d058f68091be31988a6a874 (diff) | |
download | boost-4fadd968fa12130524c8380f33fcfe25d4de79e5.tar.gz boost-4fadd968fa12130524c8380f33fcfe25d4de79e5.tar.bz2 boost-4fadd968fa12130524c8380f33fcfe25d4de79e5.zip |
Imported Upstream version 1.65.0upstream/1.65.0
Change-Id: Icf8400b375482cb11bcf77440a6934ba360d6ba4
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'boost/stacktrace')
22 files changed, 2373 insertions, 0 deletions
diff --git a/boost/stacktrace/detail/addr2line_impls.hpp b/boost/stacktrace/detail/addr2line_impls.hpp new file mode 100644 index 0000000000..e314fe9f31 --- /dev/null +++ b/boost/stacktrace/detail/addr2line_impls.hpp @@ -0,0 +1,225 @@ +// 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_ADDR2LINE_IMPLS_HPP +#define BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/detail/to_hex_array.hpp> +#include <boost/core/demangle.hpp> +#include <boost/lexical_cast.hpp> +#include <cstdio> + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> + + +namespace boost { namespace stacktrace { namespace detail { + + +#if defined(BOOST_STACKTRACE_ADDR2LINE_LOCATION) && !defined(BOOST_NO_CXX11_CONSTEXPR) + +constexpr bool is_abs_path(const char* path) BOOST_NOEXCEPT { + return *path != '\0' && ( + *path == ':' || *path == '/' || is_abs_path(path + 1) + ); +} + +#endif + +class addr2line_pipe { + ::FILE* p; + ::pid_t pid; + +public: + explicit addr2line_pipe(const char *flag, const char* exec_path, const char* addr) BOOST_NOEXCEPT + : p(0) + , pid(0) + { + int pdes[2]; + #ifdef BOOST_STACKTRACE_ADDR2LINE_LOCATION + char prog_name[] = BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION ); + #if !defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CXX11_STATIC_ASSERT) + static_assert( + boost::stacktrace::detail::is_abs_path( BOOST_STRINGIZE( BOOST_STACKTRACE_ADDR2LINE_LOCATION ) ), + "BOOST_STACKTRACE_ADDR2LINE_LOCATION must be an absolute path" + ); + #endif + + #else + char prog_name[] = "/usr/bin/addr2line"; + #endif + + char* argp[] = { + prog_name, + const_cast<char*>(flag), + const_cast<char*>(exec_path), + const_cast<char*>(addr), + 0 + }; + + if (::pipe(pdes) < 0) { + return; + } + + pid = ::fork(); + switch (pid) { + case -1: + // Failed... + ::close(pdes[0]); + ::close(pdes[1]); + return; + + case 0: + // We are the child. + ::close(STDERR_FILENO); + ::close(pdes[0]); + if (pdes[1] != STDOUT_FILENO) { + ::dup2(pdes[1], STDOUT_FILENO); + } + + // Do not use `execlp()`, `execvp()`, and `execvpe()` here! + // `exec*p*` functions are vulnerable to PATH variable evaluation attacks. + ::execv(prog_name, argp); + ::_exit(127); + } + + p = ::fdopen(pdes[0], "r"); + ::close(pdes[1]); + } + + operator ::FILE*() const BOOST_NOEXCEPT { + return p; + } + + ~addr2line_pipe() BOOST_NOEXCEPT { + if (p) { + ::fclose(p); + int pstat = 0; + ::kill(pid, SIGKILL); + ::waitpid(pid, &pstat, 0); + } + } +}; + +inline std::string addr2line(const char* flag, const void* addr) { + std::string res; + + boost::stacktrace::detail::location_from_symbol loc(addr); + if (!loc.empty()) { + res = loc.name(); + } else { + res.resize(16); + int rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); + while (rlin_size == static_cast<int>(res.size() - 1)) { + res.resize(res.size() * 4); + rlin_size = ::readlink("/proc/self/exe", &res[0], res.size() - 1); + } + if (rlin_size == -1) { + res.clear(); + return res; + } + res.resize(rlin_size); + } + + addr2line_pipe p(flag, res.c_str(), to_hex_array(addr).data()); + res.clear(); + + if (!p) { + return res; + } + + char data[32]; + while (!::feof(p)) { + if (::fgets(data, sizeof(data), p)) { + res += data; + } else { + break; + } + } + + // Trimming + while (!res.empty() && (res[res.size() - 1] == '\n' || res[res.size() - 1] == '\r')) { + res.erase(res.size() - 1); + } + + return res; +} + + +struct to_string_using_addr2line { + std::string res; + void prepare_function_name(const void* addr) { + res = boost::stacktrace::frame(addr).name(); + } + + bool prepare_source_location(const void* addr) { + //return addr2line("-Cfipe", addr); // Does not seem to work in all cases + std::string source_line = boost::stacktrace::detail::addr2line("-Cpe", addr); + if (!source_line.empty() && source_line[0] != '?') { + res += " at "; + res += source_line; + return true; + } + + return false; + } +}; + +template <class Base> class to_string_impl_base; +typedef to_string_impl_base<to_string_using_addr2line> to_string_impl; + +inline std::string name_impl(const void* addr) { + std::string res = boost::stacktrace::detail::addr2line("-fe", addr); + res = res.substr(0, res.find_last_of('\n')); + res = boost::core::demangle(res.c_str()); + + if (res == "??") { + res.clear(); + } + + return res; +} + +} // namespace detail + +std::string frame::source_file() const { + std::string res; + res = boost::stacktrace::detail::addr2line("-e", addr_); + res = res.substr(0, res.find_last_of(':')); + if (res == "??") { + res.clear(); + } + + return res; +} + + +std::size_t frame::source_line() const { + std::size_t line_num = 0; + std::string res = boost::stacktrace::detail::addr2line("-e", addr_); + const std::size_t last = res.find_last_of(':'); + if (last == std::string::npos) { + return 0; + } + res = res.substr(last + 1); + + if (!boost::conversion::try_lexical_convert(res, line_num)) { + return 0; + } + + return line_num; +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_ADDR2LINE_IMPLS_HPP diff --git a/boost/stacktrace/detail/collect_msvc.ipp b/boost/stacktrace/detail/collect_msvc.ipp new file mode 100644 index 0000000000..d75df558f3 --- /dev/null +++ b/boost/stacktrace/detail/collect_msvc.ipp @@ -0,0 +1,33 @@ +// 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_COLLECT_MSVC_IPP +#define BOOST_STACKTRACE_DETAIL_COLLECT_MSVC_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/safe_dump_to.hpp> + +#include <boost/detail/winapi/stack_backtrace.hpp> + +namespace boost { namespace stacktrace { namespace detail { + +std::size_t this_thread_frames::collect(native_frame_ptr_t* out_frames, std::size_t max_frames_count, std::size_t skip) BOOST_NOEXCEPT { + return boost::detail::winapi::RtlCaptureStackBackTrace( + static_cast<boost::detail::winapi::ULONG_>(skip), + static_cast<boost::detail::winapi::ULONG_>(max_frames_count), + const_cast<boost::detail::winapi::PVOID_*>(out_frames), + 0 + ); +} + + +}}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_COLLECT_MSVC_IPP diff --git a/boost/stacktrace/detail/collect_noop.ipp b/boost/stacktrace/detail/collect_noop.ipp new file mode 100644 index 0000000000..bcfae4c400 --- /dev/null +++ b/boost/stacktrace/detail/collect_noop.ipp @@ -0,0 +1,25 @@ +// 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_COLLECT_NOOP_IPP +#define BOOST_STACKTRACE_DETAIL_COLLECT_NOOP_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/safe_dump_to.hpp> + +namespace boost { namespace stacktrace { namespace detail { + +std::size_t this_thread_frames::collect(native_frame_ptr_t* /*out_frames*/, std::size_t /*max_frames_count*/, std::size_t /*skip*/) BOOST_NOEXCEPT { + return 0; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_COLLECT_NOOP_IPP diff --git a/boost/stacktrace/detail/collect_unwind.ipp b/boost/stacktrace/detail/collect_unwind.ipp new file mode 100644 index 0000000000..3ee4e8f61e --- /dev/null +++ b/boost/stacktrace/detail/collect_unwind.ipp @@ -0,0 +1,68 @@ +// 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_COLLECT_UNWIND_IPP +#define BOOST_STACKTRACE_DETAIL_COLLECT_UNWIND_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/safe_dump_to.hpp> + +#include <unwind.h> +#include <cstdio> + +namespace boost { namespace stacktrace { namespace detail { + +struct unwind_state { + std::size_t frames_to_skip; + native_frame_ptr_t* current; + native_frame_ptr_t* end; +}; + +inline _Unwind_Reason_Code unwind_callback(::_Unwind_Context* context, void* arg) { + // Note: do not write `::_Unwind_GetIP` because it is a macro on some platforms. + // Use `_Unwind_GetIP` instead! + unwind_state* const state = static_cast<unwind_state*>(arg); + if (state->frames_to_skip) { + --state->frames_to_skip; + return _Unwind_GetIP(context) ? ::_URC_NO_REASON : ::_URC_END_OF_STACK; + } + + *state->current = reinterpret_cast<native_frame_ptr_t>( + _Unwind_GetIP(context) + ); + + ++state->current; + if (!*(state->current - 1) || state->current == state->end) { + return ::_URC_END_OF_STACK; + } + return ::_URC_NO_REASON; +} + +std::size_t this_thread_frames::collect(native_frame_ptr_t* out_frames, std::size_t max_frames_count, std::size_t skip) BOOST_NOEXCEPT { + std::size_t frames_count = 0; + if (!max_frames_count) { + return frames_count; + } + + boost::stacktrace::detail::unwind_state state = { skip + 1, out_frames, out_frames + max_frames_count }; + ::_Unwind_Backtrace(&boost::stacktrace::detail::unwind_callback, &state); + frames_count = state.current - out_frames; + + if (frames_count && out_frames[frames_count - 1] == 0) { + -- frames_count; + } + + return frames_count; +} + + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_COLLECT_UNWIND_IPP diff --git a/boost/stacktrace/detail/frame_decl.hpp b/boost/stacktrace/detail/frame_decl.hpp new file mode 100644 index 0000000000..10b912113f --- /dev/null +++ b/boost/stacktrace/detail/frame_decl.hpp @@ -0,0 +1,159 @@ +// 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_FRAME_DECL_HPP +#define BOOST_STACKTRACE_DETAIL_FRAME_DECL_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <iosfwd> +#include <string> + +#include <boost/core/explicit_operator_bool.hpp> + +#include <boost/stacktrace/safe_dump_to.hpp> // boost::stacktrace::detail::native_frame_ptr_t +#include <boost/stacktrace/detail/void_ptr_cast.hpp> + +#include <boost/stacktrace/detail/push_options.pp> + +/// @file boost/stacktrace/detail/frame_decl.hpp +/// Use <boost/stacktrace/frame.hpp> header instead of this one! + +namespace boost { namespace stacktrace { + +/// @class boost::stacktrace::frame boost/stacktrace/detail/frame_decl.hpp <boost/stacktrace/frame.hpp> +/// @brief Class that stores frame/function address and can get information about it at runtime. +class frame { +public: + typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t; + +private: + /// @cond + native_frame_ptr_t addr_; + /// @endcond + +public: + /// @brief Constructs frame that references NULL address. + /// Calls to source_file() and source_line() will return empty string. + /// Calls to source_line() will return 0. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + /// @throws Nothing. + BOOST_CONSTEXPR frame() BOOST_NOEXCEPT + : addr_(0) + {} + +#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED + /// @brief Copy constructs frame. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + /// @throws Nothing. + constexpr frame(const frame&) = default; + + /// @brief Copy assigns frame. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + /// @throws Nothing. + constexpr frame& operator=(const frame&) = default; +#endif + + /// @brief Constructs frame that references addr and could later generate information about that address using platform specific features. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + /// @throws Nothing. + BOOST_CONSTEXPR explicit frame(native_frame_ptr_t addr) BOOST_NOEXCEPT + : addr_(addr) + {} + + /// @brief Constructs frame that references function_addr and could later generate information about that function using platform specific features. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + /// @throws Nothing. + template <class T> + explicit frame(T* function_addr) BOOST_NOEXCEPT + : addr_(boost::stacktrace::detail::void_ptr_cast<native_frame_ptr_t>(function_addr)) + {} + + /// @returns Name of the frame (function name in a human readable form). + /// + /// @b Complexity: unknown (lots of platform specific work). + /// + /// @b Async-Handler-Safety: Unsafe. + /// @throws std::bad_alloc if not enough memory to construct resulting string. + BOOST_STACKTRACE_FUNCTION std::string name() const; + + /// @returns Address of the frame function. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + /// @throws Nothing. + BOOST_CONSTEXPR native_frame_ptr_t address() const BOOST_NOEXCEPT { + return addr_; + } + + /// @returns Path to the source file, were the function of the frame is defined. Returns empty string + /// if this->source_line() == 0. + /// @throws std::bad_alloc if not enough memory to construct resulting string. + /// + /// @b Complexity: unknown (lots of platform specific work). + /// + /// @b Async-Handler-Safety: Unsafe. + BOOST_STACKTRACE_FUNCTION std::string source_file() const; + + /// @returns Code line in the source file, were the function of the frame is defined. + /// @throws std::bad_alloc if not enough memory to construct string for internal needs. + /// + /// @b Complexity: unknown (lots of platform specific work). + /// + /// @b Async-Handler-Safety: Unsafe. + BOOST_STACKTRACE_FUNCTION std::size_t source_line() const; + + /// @brief Checks that frame is not references NULL address. + /// @returns `true` if `this->address() != 0` + /// + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + BOOST_EXPLICIT_OPERATOR_BOOL() + + /// @brief Checks that frame references NULL address. + /// @returns `true` if `this->address() == 0` + /// + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + BOOST_CONSTEXPR bool empty() const BOOST_NOEXCEPT { return !address(); } + + /// @cond + BOOST_CONSTEXPR bool operator!() const BOOST_NOEXCEPT { return !address(); } + /// @endcond +}; + + +namespace detail { + BOOST_STACKTRACE_FUNCTION std::string to_string(const frame* frames, std::size_t size); +} // namespace detail + +}} // namespace boost::stacktrace + + +#include <boost/stacktrace/detail/pop_options.pp> + +#endif // BOOST_STACKTRACE_DETAIL_FRAME_DECL_HPP diff --git a/boost/stacktrace/detail/frame_msvc.ipp b/boost/stacktrace/detail/frame_msvc.ipp new file mode 100644 index 0000000000..6719598b33 --- /dev/null +++ b/boost/stacktrace/detail/frame_msvc.ipp @@ -0,0 +1,392 @@ +// 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_FRAME_MSVC_IPP +#define BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/frame.hpp> + +#include <boost/core/demangle.hpp> +#include <boost/core/noncopyable.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/stacktrace/detail/to_hex_array.hpp> +#include <windows.h> +#include "dbgeng.h" + +#include <boost/detail/winapi/get_current_process.hpp> + +#ifdef BOOST_MSVC +# pragma comment(lib, "ole32.lib") +# pragma comment(lib, "Dbgeng.lib") +#endif + + +#ifdef __CRT_UUID_DECL // for __MINGW32__ + __CRT_UUID_DECL(IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8) + __CRT_UUID_DECL(IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba) + __CRT_UUID_DECL(IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50) +#elif defined(DEFINE_GUID) && !defined(BOOST_MSVC) + DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8); + DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba); + DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50); +#endif + + + +// Testing. Remove later +//# define __uuidof(x) ::IID_ ## x + +namespace boost { namespace stacktrace { namespace detail { + +class com_global_initer: boost::noncopyable { + bool ok_; + +public: + com_global_initer() BOOST_NOEXCEPT + : ok_(false) + { + // COINIT_MULTITHREADED means that we must serialize access to the objects manually. + // This is the fastest way to work. If user calls CoInitializeEx before us - we + // can end up with other mode (which is OK for us). + // + // If we call CoInitializeEx befire user - user may end up with different mode, which is a problem. + // So we need to call that initialization function as late as possible. + const boost::detail::winapi::DWORD_ res = ::CoInitializeEx(0, COINIT_MULTITHREADED); + ok_ = (res == S_OK || res == S_FALSE); + } + + ~com_global_initer() BOOST_NOEXCEPT { + if (ok_) { + ::CoUninitialize(); + } + } +}; + + +template <class T> +class com_holder: boost::noncopyable { + T* holder_; + +public: + com_holder(const com_global_initer&) BOOST_NOEXCEPT + : holder_(0) + {} + + T* operator->() const BOOST_NOEXCEPT { + return holder_; + } + + void** to_void_ptr_ptr() BOOST_NOEXCEPT { + return reinterpret_cast<void**>(&holder_); + } + + bool is_inited() const BOOST_NOEXCEPT { + return !!holder_; + } + + ~com_holder() BOOST_NOEXCEPT { + if (holder_) { + holder_->Release(); + } + } +}; + + +static std::string minwg_demangling_workaround(const std::string& s) { +#ifdef BOOST_GCC + if (s.empty()) { + return s; + } + + if (s[0] != '_') { + return boost::core::demangle(('_' + s).c_str()); + } + + return boost::core::demangle(s.c_str()); +#else + return s; +#endif +} + +class debugging_symbols: boost::noncopyable { + static void try_init_com(com_holder< ::IDebugSymbols>& idebug, const com_global_initer& com) BOOST_NOEXCEPT { + com_holder< ::IDebugClient> iclient(com); + if (S_OK != ::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr())) { + return; + } + + com_holder< ::IDebugControl> icontrol(com); + const bool res0 = (S_OK == iclient->QueryInterface( + __uuidof(IDebugControl), + icontrol.to_void_ptr_ptr() + )); + if (!res0) { + return; + } + + const bool res1 = (S_OK == iclient->AttachProcess( + 0, + ::GetCurrentProcessId(), + DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND + )); + if (!res1) { + return; + } + + if (S_OK != icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) { + return; + } + + // No cheking: QueryInterface sets the output parameter to NULL in case of error. + iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr()); + } + +#ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED + + boost::stacktrace::detail::com_global_initer com_; + com_holder< ::IDebugSymbols> idebug_; +public: + debugging_symbols() BOOST_NOEXCEPT + : com_() + , idebug_(com_) + { + try_init_com(idebug_, com_); + } + +#else + +#ifdef BOOST_NO_CXX11_THREAD_LOCAL +# error Your compiler does not support C++11 thread_local storage. It's impossible to build with BOOST_STACKTRACE_USE_WINDBG_CACHED. +#endif + + static com_holder< ::IDebugSymbols>& get_thread_local_debug_inst() BOOST_NOEXCEPT { + // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether + // or not the member function is inline. + static thread_local boost::stacktrace::detail::com_global_initer com; + static thread_local com_holder< ::IDebugSymbols> idebug(com); + + if (!idebug.is_inited()) { + try_init_com(idebug, com); + } + + return idebug; + } + + com_holder< ::IDebugSymbols>& idebug_; +public: + debugging_symbols() BOOST_NOEXCEPT + : idebug_( get_thread_local_debug_inst() ) + {} + +#endif // #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED + + bool is_inited() const BOOST_NOEXCEPT { + return idebug_.is_inited(); + } + + std::string get_name_impl(const void* addr, std::string* module_name = 0) const { + std::string result; + if (!is_inited()) { + return result; + } + const ULONG64 offset = reinterpret_cast<ULONG64>(addr); + + char name[256]; + name[0] = '\0'; + ULONG size = 0; + bool res = (S_OK == idebug_->GetNameByOffset( + offset, + name, + sizeof(name), + &size, + 0 + )); + + if (!res && size != 0) { + result.resize(size); + res = (S_OK == idebug_->GetNameByOffset( + offset, + &result[0], + static_cast<ULONG>(result.size()), + &size, + 0 + )); + } else if (res) { + result = name; + } + + if (!res) { + result.clear(); + return result; + } + + const std::size_t delimiter = result.find_first_of('!'); + if (module_name) { + *module_name = result.substr(0, delimiter); + } + + if (delimiter == std::string::npos) { + // If 'delimiter' is equal to 'std::string::npos' then we have only module name. + result.clear(); + return result; + } + + result = minwg_demangling_workaround( + result.substr(delimiter + 1) + ); + + return result; + } + + std::size_t get_line_impl(const void* addr) const BOOST_NOEXCEPT { + ULONG result = 0; + if (!is_inited()) { + return result; + } + + const bool is_ok = (S_OK == idebug_->GetLineByOffset( + reinterpret_cast<ULONG64>(addr), + &result, + 0, + 0, + 0, + 0 + )); + + return (is_ok ? result : 0); + } + + std::pair<std::string, std::size_t> get_source_file_line_impl(const void* addr) const { + std::pair<std::string, std::size_t> result; + if (!is_inited()) { + return result; + } + const ULONG64 offset = reinterpret_cast<ULONG64>(addr); + + char name[256]; + name[0] = 0; + ULONG size = 0; + ULONG line_num = 0; + bool res = (S_OK == idebug_->GetLineByOffset( + offset, + &line_num, + name, + sizeof(name), + &size, + 0 + )); + + if (res) { + result.first = name; + result.second = line_num; + return result; + } + + if (!res && size == 0) { + return result; + } + + result.first.resize(size); + res = (S_OK == idebug_->GetLineByOffset( + offset, + &line_num, + &result.first[0], + static_cast<ULONG>(result.first.size()), + &size, + 0 + )); + result.second = line_num; + + if (!res) { + result.first.clear(); + result.second = 0; + } + + return result; + } + + void to_string_impl(const void* addr, std::string& res) const { + if (!is_inited()) { + return; + } + + std::string module_name; + std::string name = this->get_name_impl(addr, &module_name); + if (!name.empty()) { + res += name; + } else { + res += to_hex_array(addr).data(); + } + + std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr); + if (!source_line.first.empty() && source_line.second) { + res += " at "; + res += source_line.first; + res += ':'; + res += boost::lexical_cast<boost::array<char, 40> >(source_line.second).data(); + } else if (!module_name.empty()) { + res += " in "; + res += module_name; + } + } +}; + +std::string to_string(const frame* frames, std::size_t size) { + boost::stacktrace::detail::debugging_symbols idebug; + if (!idebug.is_inited()) { + return std::string(); + } + + std::string res; + res.reserve(64 * size); + for (std::size_t i = 0; i < size; ++i) { + if (i < 10) { + res += ' '; + } + res += boost::lexical_cast<boost::array<char, 40> >(i).data(); + res += '#'; + res += ' '; + idebug.to_string_impl(frames[i].address(), res); + res += '\n'; + } + + return res; +} + +} // namespace detail + +std::string frame::name() const { + boost::stacktrace::detail::debugging_symbols idebug; + return idebug.get_name_impl(addr_); +} + + +std::string frame::source_file() const { + boost::stacktrace::detail::debugging_symbols idebug; + return idebug.get_source_file_line_impl(addr_).first; +} + +std::size_t frame::source_line() const { + boost::stacktrace::detail::debugging_symbols idebug; + return idebug.get_line_impl(addr_); +} + +std::string to_string(const frame& f) { + std::string res; + + boost::stacktrace::detail::debugging_symbols idebug; + idebug.to_string_impl(f.address(), res); + return res; +} + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP diff --git a/boost/stacktrace/detail/frame_noop.ipp b/boost/stacktrace/detail/frame_noop.ipp new file mode 100644 index 0000000000..b9b1b98800 --- /dev/null +++ b/boost/stacktrace/detail/frame_noop.ipp @@ -0,0 +1,44 @@ +// 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_FRAME_NOOP_IPP +#define BOOST_STACKTRACE_DETAIL_FRAME_NOOP_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/frame.hpp> + +namespace boost { namespace stacktrace { namespace detail { + +std::string to_string(const frame* /*frames*/, std::size_t /*count*/) { + return std::string(); +} + +} // namespace detail + +std::string frame::name() const { + return std::string(); +} + +std::string frame::source_file() const { + return std::string(); +} + +std::size_t frame::source_line() const { + return 0; +} + +std::string to_string(const frame& /*f*/) { + return std::string(); +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_FRAME_NOOP_IPP diff --git a/boost/stacktrace/detail/frame_unwind.ipp b/boost/stacktrace/detail/frame_unwind.ipp new file mode 100644 index 0000000000..d4e7973968 --- /dev/null +++ b/boost/stacktrace/detail/frame_unwind.ipp @@ -0,0 +1,103 @@ +// 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_FRAME_UNWIND_IPP +#define BOOST_STACKTRACE_DETAIL_FRAME_UNWIND_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/frame.hpp> + +#include <boost/stacktrace/detail/to_hex_array.hpp> +#include <boost/stacktrace/detail/location_from_symbol.hpp> +#include <boost/core/demangle.hpp> +#include <boost/lexical_cast.hpp> + +#include <cstdio> + +#ifdef BOOST_STACKTRACE_USE_BACKTRACE +# include <boost/stacktrace/detail/libbacktrace_impls.hpp> +#elif defined(BOOST_STACKTRACE_USE_ADDR2LINE) +# include <boost/stacktrace/detail/addr2line_impls.hpp> +#else +# include <boost/stacktrace/detail/unwind_base_impls.hpp> +#endif + +namespace boost { namespace stacktrace { namespace detail { + +template <class Base> +class to_string_impl_base: private Base { +public: + std::string operator()(boost::stacktrace::detail::native_frame_ptr_t addr) { + Base::res.clear(); + Base::prepare_function_name(addr); + if (!Base::res.empty()) { + Base::res = boost::core::demangle(Base::res.c_str()); + } else { + Base::res = to_hex_array(addr).data(); + } + + if (Base::prepare_source_location(addr)) { + return Base::res; + } + + boost::stacktrace::detail::location_from_symbol loc(addr); + if (!loc.empty()) { + Base::res += " in "; + Base::res += loc.name(); + } + + return Base::res; + } +}; + +std::string to_string(const frame* frames, std::size_t size) { + std::string res; + res.reserve(64 * size); + + to_string_impl impl; + + for (std::size_t i = 0; i < size; ++i) { + if (i < 10) { + res += ' '; + } + res += boost::lexical_cast<boost::array<char, 40> >(i).data(); + res += '#'; + res += ' '; + res += impl(frames[i].address()); + res += '\n'; + } + + return res; +} + + +} // namespace detail + + +std::string frame::name() const { +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + ::Dl_info dli; + const bool dl_ok = !!::dladdr(addr_, &dli); + if (dl_ok && dli.dli_sname) { + return boost::core::demangle(dli.dli_sname); + } +#endif + return boost::stacktrace::detail::name_impl(addr_); +} + +std::string to_string(const frame& f) { + boost::stacktrace::detail::to_string_impl impl; + return impl(f.address()); +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_FRAME_UNWIND_IPP diff --git a/boost/stacktrace/detail/libbacktrace_impls.hpp b/boost/stacktrace/detail/libbacktrace_impls.hpp new file mode 100644 index 0000000000..a0c2d2a5aa --- /dev/null +++ b/boost/stacktrace/detail/libbacktrace_impls.hpp @@ -0,0 +1,173 @@ +// 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_LIBBACKTRACE_IMPLS_HPP +#define BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/detail/to_hex_array.hpp> +#include <boost/core/demangle.hpp> +#include <boost/lexical_cast.hpp> + +#include <backtrace.h> + +namespace boost { namespace stacktrace { namespace detail { + + +struct pc_data { + std::string* function; + std::string* filename; + std::size_t line; +}; + +inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) { + pc_data& d = *static_cast<pc_data*>(data); + if (d.filename && filename) { + *d.filename = filename; + } + if (d.function && function) { + *d.function = function; + } + d.line = lineno; + return 0; +} + +inline void libbacktrace_error_callback(void* /*data*/, const char* /*msg*/, int /*errnum*/) BOOST_NOEXCEPT { + // Do nothing, just return. +} + + +extern inline ::backtrace_state* construct_state() BOOST_NOEXCEPT { + return ::backtrace_create_state( + 0, 0 /*thread-safe*/, boost::stacktrace::detail::libbacktrace_error_callback, 0 + ); + + // TODO: this does not seem to work well when this function is in .so: + // Not async-signal-safe, so this method is not called from async-safe functions. + // + // This function is not async signal safe because: + // * Dynamic initialization of a block-scope variable with static storage duration could lock a mutex + // * No guarantees on `backtrace_create_state` function. + + // [dcl.inline]: A static local variable in an inline function with external linkage always refers to the same object. + + /* + static ::backtrace_state* state = ::backtrace_create_state( + 0, 1 , boost::stacktrace::detail::libbacktrace_error_callback, 0 + ); + + return state; + */ +} + +struct to_string_using_backtrace { + std::string res; + ::backtrace_state* state; + std::string filename; + std::size_t line; + + void prepare_function_name(const void* addr) { + boost::stacktrace::detail::pc_data data = {&res, &filename, 0}; + if (state) { + ::backtrace_pcinfo( + state, + reinterpret_cast<uintptr_t>(addr), + boost::stacktrace::detail::libbacktrace_full_callback, + boost::stacktrace::detail::libbacktrace_error_callback, + &data + ); + } + line = data.line; + } + + bool prepare_source_location(const void* /*addr*/) { + if (filename.empty() || !line) { + return false; + } + + res += " at "; + res += filename; + res += ':'; + res += boost::lexical_cast<boost::array<char, 40> >(line).data(); + return true; + } + + to_string_using_backtrace() BOOST_NOEXCEPT { + state = boost::stacktrace::detail::construct_state(); + } +}; + +template <class Base> class to_string_impl_base; +typedef to_string_impl_base<to_string_using_backtrace> to_string_impl; + +inline std::string name_impl(const void* addr) { + std::string res; + + ::backtrace_state* state = boost::stacktrace::detail::construct_state(); + + boost::stacktrace::detail::pc_data data = {&res, 0, 0}; + if (state) { + ::backtrace_pcinfo( + state, + reinterpret_cast<uintptr_t>(addr), + boost::stacktrace::detail::libbacktrace_full_callback, + boost::stacktrace::detail::libbacktrace_error_callback, + &data + ); + } + if (!res.empty()) { + res = boost::core::demangle(res.c_str()); + } + + return res; +} + +} // namespace detail + +std::string frame::source_file() const { + std::string res; + + ::backtrace_state* state = boost::stacktrace::detail::construct_state(); + + boost::stacktrace::detail::pc_data data = {0, &res, 0}; + if (state) { + ::backtrace_pcinfo( + state, + reinterpret_cast<uintptr_t>(addr_), + boost::stacktrace::detail::libbacktrace_full_callback, + boost::stacktrace::detail::libbacktrace_error_callback, + &data + ); + } + + return res; +} + +std::size_t frame::source_line() const { + ::backtrace_state* state = boost::stacktrace::detail::construct_state(); + + boost::stacktrace::detail::pc_data data = {0, 0, 0}; + if (state) { + ::backtrace_pcinfo( + state, + reinterpret_cast<uintptr_t>(addr_), + boost::stacktrace::detail::libbacktrace_full_callback, + boost::stacktrace::detail::libbacktrace_error_callback, + &data + ); + } + + return data.line; +} + + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP diff --git a/boost/stacktrace/detail/location_from_symbol.hpp b/boost/stacktrace/detail/location_from_symbol.hpp new file mode 100644 index 0000000000..d20b0d6707 --- /dev/null +++ b/boost/stacktrace/detail/location_from_symbol.hpp @@ -0,0 +1,76 @@ +// 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_LOCATION_FROM_SYMBOL_HPP +#define BOOST_STACKTRACE_DETAIL_LOCATION_FROM_SYMBOL_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +# include <dlfcn.h> +#else +# include <boost/detail/winapi/dll.hpp> +#endif + +namespace boost { namespace stacktrace { namespace detail { + +class location_from_symbol { +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + ::Dl_info dli_; + +public: + explicit location_from_symbol(const void* addr) BOOST_NOEXCEPT + : dli_() + { + if (!::dladdr(addr, &dli_)) { + dli_.dli_fname = 0; + } + } + + bool empty() const BOOST_NOEXCEPT { + return !dli_.dli_fname; + } + + const char* name() const BOOST_NOEXCEPT { + return dli_.dli_fname; + } +#else + BOOST_STATIC_CONSTEXPR boost::detail::winapi::DWORD_ DEFAULT_PATH_SIZE_ = 260; + + char file_name_[DEFAULT_PATH_SIZE_]; + +public: + explicit location_from_symbol(const void* addr) BOOST_NOEXCEPT { + file_name_[0] = '\0'; + + boost::detail::winapi::MEMORY_BASIC_INFORMATION_ mbi; + if (!boost::detail::winapi::VirtualQuery(addr, &mbi, sizeof(mbi))) { + return; + } + + boost::detail::winapi::HMODULE_ handle = reinterpret_cast<boost::detail::winapi::HMODULE_>(mbi.AllocationBase); + if (!boost::detail::winapi::GetModuleFileNameA(handle, file_name_, DEFAULT_PATH_SIZE_)) { + file_name_[0] = '\0'; + return; + } + } + + bool empty() const BOOST_NOEXCEPT { + return file_name_[0] == '\0'; + } + + const char* name() const BOOST_NOEXCEPT { + return file_name_; + } +#endif +}; + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_LOCATION_FROM_SYMBOL_HPP diff --git a/boost/stacktrace/detail/pop_options.pp b/boost/stacktrace/detail/pop_options.pp new file mode 100644 index 0000000000..8995b00ada --- /dev/null +++ b/boost/stacktrace/detail/pop_options.pp @@ -0,0 +1,12 @@ +// 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) + +// No include guards! Intentionally. + +#ifdef BOOST_STACKTRACE_FUNCTION +# undef BOOST_STACKTRACE_FUNCTION +#endif + diff --git a/boost/stacktrace/detail/push_options.pp b/boost/stacktrace/detail/push_options.pp new file mode 100644 index 0000000000..3adb626121 --- /dev/null +++ b/boost/stacktrace/detail/push_options.pp @@ -0,0 +1,31 @@ +// 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) + +// No include guards! Intentionally. + +// Link or header only +#if !defined(BOOST_STACKTRACE_LINK) && defined(BOOST_STACKTRACE_DYN_LINK) +# define BOOST_STACKTRACE_LINK +#endif + +#if defined(BOOST_STACKTRACE_LINK) && !defined(BOOST_STACKTRACE_DYN_LINK) && defined(BOOST_ALL_DYN_LINK) +# define BOOST_STACKTRACE_DYN_LINK +#endif + +#ifdef BOOST_STACKTRACE_LINK +# if defined(BOOST_STACKTRACE_DYN_LINK) +# ifdef BOOST_STACKTRACE_INTERNAL_BUILD_LIBS +# define BOOST_STACKTRACE_FUNCTION BOOST_SYMBOL_EXPORT +# else +# define BOOST_STACKTRACE_FUNCTION BOOST_SYMBOL_IMPORT +# endif +# else +# define BOOST_STACKTRACE_FUNCTION +# endif +#elif !defined(BOOST_STACKTRACE_DOXYGEN_INVOKED) +# define BOOST_STACKTRACE_FUNCTION inline +#endif + diff --git a/boost/stacktrace/detail/safe_dump_noop.ipp b/boost/stacktrace/detail/safe_dump_noop.ipp new file mode 100644 index 0000000000..78fdc3a8ae --- /dev/null +++ b/boost/stacktrace/detail/safe_dump_noop.ipp @@ -0,0 +1,37 @@ +// 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_SAFE_DUMP_NOOP_IPP +#define BOOST_STACKTRACE_DETAIL_SAFE_DUMP_NOOP_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/safe_dump_to.hpp> + +namespace boost { namespace stacktrace { namespace detail { + + +#if defined(BOOST_WINDOWS) +std::size_t dump(void* /*fd*/, const native_frame_ptr_t* /*frames*/, std::size_t /*frames_count*/) BOOST_NOEXCEPT { + return 0; +} +#else +std::size_t dump(int /*fd*/, const native_frame_ptr_t* /*frames*/, std::size_t /*frames_count*/) BOOST_NOEXCEPT { + return 0; +} +#endif + + +std::size_t dump(const char* /*file*/, const native_frame_ptr_t* /*frames*/, std::size_t /*frames_count*/) BOOST_NOEXCEPT { + return 0; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_SAFE_DUMP_NOOP_IPP diff --git a/boost/stacktrace/detail/safe_dump_posix.ipp b/boost/stacktrace/detail/safe_dump_posix.ipp new file mode 100644 index 0000000000..37eef5ac7c --- /dev/null +++ b/boost/stacktrace/detail/safe_dump_posix.ipp @@ -0,0 +1,58 @@ +// 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_SAFE_DUMP_POSIX_IPP +#define BOOST_STACKTRACE_DETAIL_SAFE_DUMP_POSIX_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/safe_dump_to.hpp> + +#include <unistd.h> // ::write +#include <fcntl.h> // ::open + + +namespace boost { namespace stacktrace { namespace detail { + +std::size_t dump(int fd, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT { + // We do not retry, because 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, frames, sizeof(native_frame_ptr_t) * frames_count) == -1) { + return 0; + } + + return frames_count; +} + +std::size_t dump(const char* file, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT { + const int fd = ::open( + file, + O_CREAT | O_WRONLY | O_TRUNC, +#if defined(S_IWUSR) && defined(S_IRUSR) // Workarounds for some Android OSes + S_IWUSR | S_IRUSR +#elif defined(S_IWRITE) && defined(S_IREAD) + S_IWRITE | S_IREAD +#else + 0 +#endif + ); + if (fd == -1) { + return 0; + } + + const std::size_t size = boost::stacktrace::detail::dump(fd, frames, frames_count); + ::close(fd); + return size; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_SAFE_DUMP_POSIX_IPP diff --git a/boost/stacktrace/detail/safe_dump_win.ipp b/boost/stacktrace/detail/safe_dump_win.ipp new file mode 100644 index 0000000000..26f0516c01 --- /dev/null +++ b/boost/stacktrace/detail/safe_dump_win.ipp @@ -0,0 +1,60 @@ +// 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_SAFE_DUMP_WIN_IPP +#define BOOST_STACKTRACE_DETAIL_SAFE_DUMP_WIN_IPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/safe_dump_to.hpp> + +#include <boost/core/noncopyable.hpp> + +#include <boost/detail/winapi/get_current_process.hpp> +#include <boost/detail/winapi/file_management.hpp> +#include <boost/detail/winapi/handles.hpp> +#include <boost/detail/winapi/access_rights.hpp> + +namespace boost { namespace stacktrace { namespace detail { + +std::size_t dump(void* fd, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT { + boost::detail::winapi::DWORD_ written; + const boost::detail::winapi::DWORD_ bytes_to_write = static_cast<boost::detail::winapi::DWORD_>( + sizeof(native_frame_ptr_t) * frames_count + ); + if (!boost::detail::winapi::WriteFile(fd, frames, bytes_to_write, &written, 0)) { + return 0; + } + + return frames_count; +} + +std::size_t dump(const char* file, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT { + void* const fd = boost::detail::winapi::CreateFileA( + file, + boost::detail::winapi::GENERIC_WRITE_, + 0, + 0, + boost::detail::winapi::CREATE_ALWAYS_, + boost::detail::winapi::FILE_ATTRIBUTE_NORMAL_, + 0 + ); + + if (fd == boost::detail::winapi::invalid_handle_value) { + return 0; + } + + const std::size_t size = boost::stacktrace::detail::dump(fd, frames, frames_count); + boost::detail::winapi::CloseHandle(fd); + return size; +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_SAFE_DUMP_WIN_IPP diff --git a/boost/stacktrace/detail/to_hex_array.hpp b/boost/stacktrace/detail/to_hex_array.hpp new file mode 100644 index 0000000000..dd55ded8b6 --- /dev/null +++ b/boost/stacktrace/detail/to_hex_array.hpp @@ -0,0 +1,54 @@ +// 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_TO_HEX_ARRAY_HPP +#define BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/array.hpp> +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_pointer.hpp> +#include <boost/type_traits/make_unsigned.hpp> + +namespace boost { namespace stacktrace { namespace detail { + +BOOST_STATIC_CONSTEXPR char to_hex_array_bytes[] = "0123456789ABCDEF"; + +template <class T> +inline boost::array<char, 2 + sizeof(void*) * 2 + 1> to_hex_array(T addr) BOOST_NOEXCEPT { + boost::array<char, 2 + sizeof(void*) * 2 + 1> ret = {"0x"}; + ret.back() = '\0'; + BOOST_STATIC_ASSERT_MSG(!boost::is_pointer<T>::value, ""); + + const std::size_t s = sizeof(T); + + char* out = ret.data() + s * 2 + 1; + + for (std::size_t i = 0; i < s; ++i) { + const unsigned char tmp_addr = (addr & 0xFFu); + *out = to_hex_array_bytes[tmp_addr & 0xF]; + -- out; + *out = to_hex_array_bytes[tmp_addr >> 4]; + -- out; + addr >>= 8; + } + + return ret; +} + +inline boost::array<char, 2 + sizeof(void*) * 2 + 1> to_hex_array(const void* addr) BOOST_NOEXCEPT { + return to_hex_array( + reinterpret_cast< boost::make_unsigned<std::ptrdiff_t>::type >(addr) + ); +} + +}}} // namespace boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_TO_HEX_ARRAY_HPP diff --git a/boost/stacktrace/detail/unwind_base_impls.hpp b/boost/stacktrace/detail/unwind_base_impls.hpp new file mode 100644 index 0000000000..18a2378d25 --- /dev/null +++ b/boost/stacktrace/detail/unwind_base_impls.hpp @@ -0,0 +1,50 @@ +// 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_UNWIND_BASE_IMPLS_HPP +#define BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/frame.hpp> + +namespace boost { namespace stacktrace { namespace detail { + +struct to_string_using_nothing { + std::string res; + + void prepare_function_name(const void* addr) { + res = boost::stacktrace::frame(addr).name(); + } + + bool prepare_source_location(const void* /*addr*/) const BOOST_NOEXCEPT { + return false; + } +}; + +template <class Base> class to_string_impl_base; +typedef to_string_impl_base<to_string_using_nothing> to_string_impl; + +inline std::string name_impl(const void* /*addr*/) { + return std::string(); +} + +} // namespace detail + +std::string frame::source_file() const { + return std::string(); +} + +std::size_t frame::source_line() const { + return 0; +} + +}} // namespace boost::stacktrace + +#endif // BOOST_STACKTRACE_DETAIL_UNWIND_BASE_IMPLS_HPP diff --git a/boost/stacktrace/detail/void_ptr_cast.hpp b/boost/stacktrace/detail/void_ptr_cast.hpp new file mode 100644 index 0000000000..f956f4789f --- /dev/null +++ b/boost/stacktrace/detail/void_ptr_cast.hpp @@ -0,0 +1,46 @@ +// Copyright 2014 Renato Tegon Forti, Antony Polukhin. +// Copyright 2015-2017 Antony Polukhin. +// +// 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_VOID_PTR_CAST_HPP +#define BOOST_STACKTRACE_DETAIL_VOID_PTR_CAST_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_pointer.hpp> + +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && (__GNUC__ * 100 + __GNUC_MINOR__ > 301) +# pragma GCC system_header +#endif + +namespace boost { namespace stacktrace { namespace detail { + +// GCC warns when reinterpret_cast between function pointer and object pointer occur. +// This functionsuppress the warnings and ensures that such casts are safe. +template <class To, class From> +To void_ptr_cast(From* v) BOOST_NOEXCEPT { + BOOST_STATIC_ASSERT_MSG( + boost::is_pointer<To>::value, + "`void_ptr_cast` function must be used only for casting to or from void pointers." + ); + + BOOST_STATIC_ASSERT_MSG( + sizeof(From*) == sizeof(To), + "Pointer to function and pointer to object differ in size on your platform." + ); + + return reinterpret_cast<To>(v); +} + + +}}} // boost::stacktrace::detail + +#endif // BOOST_STACKTRACE_DETAIL_VOID_PTR_CAST_HPP + diff --git a/boost/stacktrace/frame.hpp b/boost/stacktrace/frame.hpp new file mode 100644 index 0000000000..4ad3c507f3 --- /dev/null +++ b/boost/stacktrace/frame.hpp @@ -0,0 +1,67 @@ +// 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_FRAME_HPP +#define BOOST_STACKTRACE_FRAME_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <iosfwd> +#include <string> + +#include <boost/core/explicit_operator_bool.hpp> + +#include <boost/stacktrace/safe_dump_to.hpp> // boost::stacktrace::detail::native_frame_ptr_t + +#include <boost/stacktrace/detail/frame_decl.hpp> +#include <boost/stacktrace/detail/push_options.pp> + +namespace boost { namespace stacktrace { + +/// Comparison operators that provide platform dependant ordering and have O(1) complexity; are Async-Handler-Safe. +BOOST_CONSTEXPR inline bool operator< (const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() < rhs.address(); } +BOOST_CONSTEXPR inline bool operator> (const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return rhs < lhs; } +BOOST_CONSTEXPR inline bool operator<=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs > rhs); } +BOOST_CONSTEXPR inline bool operator>=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs < rhs); } +BOOST_CONSTEXPR inline bool operator==(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return lhs.address() == rhs.address(); } +BOOST_CONSTEXPR inline bool operator!=(const frame& lhs, const frame& rhs) BOOST_NOEXCEPT { return !(lhs == rhs); } + +/// Fast hashing support, O(1) complexity; Async-Handler-Safe. +inline std::size_t hash_value(const frame& f) BOOST_NOEXCEPT { + return reinterpret_cast<std::size_t>(f.address()); +} + +/// Outputs stacktrace::frame in a human readable format to string; unsafe to use in async handlers. +BOOST_STACKTRACE_FUNCTION std::string to_string(const frame& f); + +/// Outputs stacktrace::frame in a human readable format to output stream; unsafe to use in async handlers. +template <class CharT, class TraitsT> +std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const frame& f) { + return os << boost::stacktrace::to_string(f); +} + +}} // namespace boost::stacktrace + +/// @cond + +#include <boost/stacktrace/detail/pop_options.pp> + +#ifndef BOOST_STACKTRACE_LINK +# if defined(BOOST_STACKTRACE_USE_NOOP) +# include <boost/stacktrace/detail/frame_noop.ipp> +# elif defined(BOOST_MSVC) || defined(BOOST_STACKTRACE_USE_WINDBG) || defined(BOOST_STACKTRACE_USE_WINDBG_CACHED) +# include <boost/stacktrace/detail/frame_msvc.ipp> +# else +# include <boost/stacktrace/detail/frame_unwind.ipp> +# endif +#endif +/// @endcond + + +#endif // BOOST_STACKTRACE_FRAME_HPP diff --git a/boost/stacktrace/safe_dump_to.hpp b/boost/stacktrace/safe_dump_to.hpp new file mode 100644 index 0000000000..79981dabea --- /dev/null +++ b/boost/stacktrace/safe_dump_to.hpp @@ -0,0 +1,218 @@ +// 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_SAFE_DUMP_TO_HPP +#define BOOST_STACKTRACE_SAFE_DUMP_TO_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/stacktrace/detail/push_options.pp> + +#ifdef BOOST_INTEL +# pragma warning(push) +# pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline" +#endif + +/// @file safe_dump_to.hpp This header contains low-level async-signal-safe functions for dumping call stacks. Dumps are binary serialized arrays of `void*`, +/// so you could read them by using 'od -tx8 -An stacktrace_dump_failename' Linux command or using boost::stacktrace::stacktrace::from_dump functions. + +namespace boost { namespace stacktrace { + +/// @cond +namespace detail { + + typedef const void* native_frame_ptr_t; // TODO: change to `typedef void(*native_frame_ptr_t)();` + enum helper{ max_frames_dump = 128 }; + + BOOST_STACKTRACE_FUNCTION std::size_t from_dump(const char* filename, native_frame_ptr_t* out_frames); + BOOST_STACKTRACE_FUNCTION std::size_t dump(const char* file, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT; +#if defined(BOOST_WINDOWS) + BOOST_STACKTRACE_FUNCTION std::size_t dump(void* fd, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT; +#else + // POSIX + BOOST_STACKTRACE_FUNCTION std::size_t dump(int fd, const native_frame_ptr_t* frames, std::size_t frames_count) BOOST_NOEXCEPT; +#endif + + +struct this_thread_frames { // struct is required to avoid warning about usage of inline+BOOST_NOINLINE + BOOST_NOINLINE BOOST_STACKTRACE_FUNCTION static std::size_t collect(native_frame_ptr_t* out_frames, std::size_t max_frames_count, std::size_t skip) BOOST_NOEXCEPT; + + BOOST_NOINLINE static std::size_t safe_dump_to_impl(void* memory, std::size_t size, std::size_t skip) BOOST_NOEXCEPT { + typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t; + + if (size < sizeof(native_frame_ptr_t)) { + return 0; + } + + native_frame_ptr_t* mem = static_cast<native_frame_ptr_t*>(memory); + const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(mem, size / sizeof(native_frame_ptr_t) - 1, skip + 1); + mem[frames_count] = 0; + return frames_count + 1; + } + + template <class T> + BOOST_NOINLINE static std::size_t safe_dump_to_impl(T file, std::size_t skip, std::size_t max_depth) BOOST_NOEXCEPT { + typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t; + + native_frame_ptr_t buffer[boost::stacktrace::detail::max_frames_dump + 1]; + if (max_depth > boost::stacktrace::detail::max_frames_dump) { + max_depth = boost::stacktrace::detail::max_frames_dump; + } + + const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, max_depth, skip + 1); + buffer[frames_count] = 0; + return boost::stacktrace::detail::dump(file, buffer, frames_count + 1); + } +}; + +} // namespace detail +/// @endcond + +/// @brief Stores current function call sequence into the memory. +/// +/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. +/// +/// @b Async-Handler-Safety: Safe. +/// +/// @returns Stored call sequence depth including terminating zero frame. +/// +/// @param memory Preallocated buffer to store current function call sequence into. +/// +/// @param size Size of the preallocated buffer. +BOOST_FORCEINLINE std::size_t safe_dump_to(void* memory, std::size_t size) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(memory, size, 0); +} + +/// @brief Stores current function call sequence into the memory. +/// +/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. +/// +/// @b Async-Handler-Safety: Safe. +/// +/// @returns Stored call sequence depth including terminating zero frame. +/// +/// @param skip How many top calls to skip and do not store. +/// +/// @param memory Preallocated buffer to store current function call sequence into. +/// +/// @param size Size of the preallocated buffer. +BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, void* memory, std::size_t size) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(memory, size, skip); +} + + +/// @brief Opens a file and rewrites its content with current function call sequence. +/// +/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. +/// +/// @b Async-Handler-Safety: Safe. +/// +/// @returns Stored call sequence depth including terminating zero frame. +/// +/// @param file File to store current function call sequence. +BOOST_FORCEINLINE std::size_t safe_dump_to(const char* file) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(file, 0, boost::stacktrace::detail::max_frames_dump); +} + +/// @brief Opens a file and rewrites its content with current function call sequence. +/// +/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. +/// +/// @b Async-Handler-Safety: Safe. +/// +/// @returns Stored call sequence depth including terminating zero frame. +/// +/// @param skip How many top calls to skip and do not store. +/// +/// @param max_depth Max call sequence depth to collect. +/// +/// @param file File to store current function call sequence. +BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, const char* file) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(file, skip, max_depth); +} + +#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED + +/// @brief Writes into the provided file descriptor the current function call sequence. +/// +/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. +/// +/// @b Async-Handler-Safety: Safe. +/// +/// @returns Stored call sequence depth including terminating zero frame. +/// +/// @param file File to store current function call sequence. +BOOST_FORCEINLINE std::size_t safe_dump_to(platform_specific_descriptor fd) BOOST_NOEXCEPT; + +/// @brief Writes into the provided file descriptor the current function call sequence. +/// +/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. +/// +/// @b Async-Handler-Safety: Safe. +/// +/// @returns Stored call sequence depth including terminating zero frame. +/// +/// @param skip How many top calls to skip and do not store. +/// +/// @param max_depth Max call sequence depth to collect. +/// +/// @param file File to store current function call sequence. +BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, platform_specific_descriptor fd) BOOST_NOEXCEPT; + +#elif defined(BOOST_WINDOWS) + +BOOST_FORCEINLINE std::size_t safe_dump_to(void* fd) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, 0, boost::stacktrace::detail::max_frames_dump); +} + +BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, void* fd) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, skip, max_depth); +} + +#else + +// POSIX +BOOST_FORCEINLINE std::size_t safe_dump_to(int fd) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, 0, boost::stacktrace::detail::max_frames_dump); +} + +BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_depth, int fd) BOOST_NOEXCEPT { + return boost::stacktrace::detail::this_thread_frames::safe_dump_to_impl(fd, skip, max_depth); +} + +#endif + + +}} // namespace boost::stacktrace + +#ifdef BOOST_INTEL +# pragma warning(pop) +#endif + +#include <boost/stacktrace/detail/pop_options.pp> + +#if !defined(BOOST_STACKTRACE_LINK) || defined(BOOST_STACKTRACE_INTERNAL_BUILD_LIBS) +# if defined(BOOST_STACKTRACE_USE_NOOP) +# include <boost/stacktrace/detail/safe_dump_noop.ipp> +# include <boost/stacktrace/detail/collect_noop.ipp> +# else +# if defined(BOOST_WINDOWS) +# include <boost/stacktrace/detail/safe_dump_win.ipp> +# else +# include <boost/stacktrace/detail/safe_dump_posix.ipp> +# endif +# if defined(BOOST_WINDOWS) && !defined(BOOST_GCC) +# include <boost/stacktrace/detail/collect_msvc.ipp> +# else +# include <boost/stacktrace/detail/collect_unwind.ipp> +# endif +# endif +#endif + +#endif // BOOST_STACKTRACE_SAFE_DUMP_TO_HPP diff --git a/boost/stacktrace/stacktrace.hpp b/boost/stacktrace/stacktrace.hpp new file mode 100644 index 0000000000..3c72488767 --- /dev/null +++ b/boost/stacktrace/stacktrace.hpp @@ -0,0 +1,414 @@ +// 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_STACKTRACE_HPP +#define BOOST_STACKTRACE_STACKTRACE_HPP + +#include <boost/config.hpp> +#ifdef BOOST_HAS_PRAGMA_ONCE +# pragma once +#endif + +#include <boost/core/explicit_operator_bool.hpp> + +#include <iosfwd> +#include <string> +#include <vector> + +#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS +# include <type_traits> +#endif + +#include <boost/stacktrace/stacktrace_fwd.hpp> +#include <boost/stacktrace/safe_dump_to.hpp> +#include <boost/stacktrace/detail/frame_decl.hpp> + +#ifdef BOOST_INTEL +# pragma warning(push) +# pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline" +#endif + +/// @cond +namespace boost { + // Forward declaration + template <class It> std::size_t hash_range(It, It); +} +/// @endcond + +namespace boost { namespace stacktrace { + +/// Class that on construction copies minimal information about call stack into its internals and provides access to that information. +/// @tparam Allocator Allocator to use during stack capture. +template <class Allocator> +class basic_stacktrace { + std::vector<boost::stacktrace::frame, Allocator> impl_; + typedef boost::stacktrace::detail::native_frame_ptr_t native_frame_ptr_t; + + /// @cond + void fill(native_frame_ptr_t* begin, std::size_t size) { + if (!size) { + return; + } + + impl_.reserve(static_cast<std::size_t>(size)); + for (std::size_t i = 0; i < size; ++i) { + if (!begin[i]) { + return; + } + impl_.push_back( + frame(begin[i]) + ); + } + } + + static std::size_t frames_count_from_buffer_size(std::size_t buffer_size) BOOST_NOEXCEPT { + const std::size_t ret = (buffer_size > sizeof(native_frame_ptr_t) ? buffer_size / sizeof(native_frame_ptr_t) : 0); + return (ret > 1024 ? 1024 : ret); // Dealing with suspiciously big sizes + } + + BOOST_NOINLINE void init(std::size_t frames_to_skip, std::size_t max_depth) { + BOOST_CONSTEXPR_OR_CONST std::size_t buffer_size = 128; + if (!max_depth) { + return; + } + + try { + { // Fast path without additional allocations + native_frame_ptr_t buffer[buffer_size]; + const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(buffer, buffer_size, frames_to_skip + 1); + if (buffer_size > frames_count || frames_count >= max_depth) { + const std::size_t size = (max_depth < frames_count ? max_depth : frames_count); + fill(buffer, size); + return; + } + } + + // Failed to fit in `buffer_size`. Allocating memory: + typedef typename Allocator::template rebind<native_frame_ptr_t>::other allocator_void_t; + std::vector<native_frame_ptr_t, allocator_void_t> buf(buffer_size * 2, 0, impl_.get_allocator()); + do { + const std::size_t frames_count = boost::stacktrace::detail::this_thread_frames::collect(&buf[0], buf.size(), frames_to_skip + 1); + if (buf.size() > frames_count || frames_count >= max_depth) { + const std::size_t size = (max_depth < frames_count ? max_depth : frames_count); + fill(&buf[0], size); + return; + } + + buf.resize(buf.size() * 2); + } while (buf.size() < buf.max_size()); // close to `true`, but suppresses `C4127: conditional expression is constant`. + } catch (...) { + // ignore exception + } + } + /// @endcond + +public: + typedef typename std::vector<boost::stacktrace::frame, Allocator>::value_type value_type; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::allocator_type allocator_type; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer pointer; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_pointer const_pointer; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference reference; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reference const_reference; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::size_type size_type; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::difference_type difference_type; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator iterator; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_iterator const_iterator; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator reverse_iterator; + typedef typename std::vector<boost::stacktrace::frame, Allocator>::const_reverse_iterator const_reverse_iterator; + + /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations. + /// + /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. + /// + /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe. + BOOST_FORCEINLINE basic_stacktrace() BOOST_NOEXCEPT + : impl_() + { + init(0 , static_cast<std::size_t>(-1)); + } + + /// @brief Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations. + /// + /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. + /// + /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe. + /// + /// @param a Allocator that would be passed to underlying storeage. + BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a) BOOST_NOEXCEPT + : impl_(a) + { + init(0 , static_cast<std::size_t>(-1)); + } + + /// @brief Stores [skip, skip + max_depth) of the current function call sequence inside *this without any decoding or any other heavy platform specific operations. + /// + /// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined. + /// + /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe. + /// + /// @param skip How many top calls to skip and do not store in *this. + /// + /// @param max_depth Max call sequence depth to collect. + /// + /// @param a Allocator that would be passed to underlying storeage. + /// + /// @throws Nothing. Note that default construction of allocator may throw, however it is + /// performed outside the constructor and exception in `allocator_type()` would not result in calling `std::terminate`. + BOOST_FORCEINLINE basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type& a = allocator_type()) BOOST_NOEXCEPT + : impl_(a) + { + init(skip , max_depth); + } + + /// @b Complexity: O(st.size()) + /// + /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe. + basic_stacktrace(const basic_stacktrace& st) + : impl_(st.impl_) + {} + + /// @b Complexity: O(st.size()) + /// + /// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe. + basic_stacktrace& operator=(const basic_stacktrace& st) { + impl_ = st.impl_; + return *this; + } + +#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe if Allocator::deallocate is async signal safe. + ~basic_stacktrace() BOOST_NOEXCEPT = default; +#endif + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe. + basic_stacktrace(basic_stacktrace&& st) BOOST_NOEXCEPT + : impl_(std::move(st.impl_)) + {} + + /// @b Complexity: O(st.size()) + /// + /// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe. + basic_stacktrace& operator=(basic_stacktrace&& st) +#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS + BOOST_NOEXCEPT_IF(( std::is_nothrow_move_assignable< std::vector<boost::stacktrace::frame, Allocator> >::value )) +#else + BOOST_NOEXCEPT +#endif + { + impl_ = std::move(st.impl_); + return *this; + } +#endif + + /// @returns Number of function names stored inside the class. + /// + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + size_type size() const BOOST_NOEXCEPT { + return impl_.size(); + } + + /// @param frame_no Zero based index of frame to return. 0 + /// is the function index where stacktrace was constructed and + /// index close to this->size() contains function `main()`. + /// @returns frame that references the actual frame info, stored inside *this. + /// + /// @b Complexity: O(1). + /// + /// @b Async-Handler-Safety: Safe. + const_reference operator[](std::size_t frame_no) const BOOST_NOEXCEPT { + return impl_[frame_no]; + } + + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_iterator begin() const BOOST_NOEXCEPT { return impl_.begin(); } + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_iterator cbegin() const BOOST_NOEXCEPT { return impl_.begin(); } + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_iterator end() const BOOST_NOEXCEPT { return impl_.end(); } + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_iterator cend() const BOOST_NOEXCEPT { return impl_.end(); } + + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return impl_.rbegin(); } + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_reverse_iterator crbegin() const BOOST_NOEXCEPT { return impl_.rbegin(); } + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_reverse_iterator rend() const BOOST_NOEXCEPT { return impl_.rend(); } + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + const_reverse_iterator crend() const BOOST_NOEXCEPT { return impl_.rend(); } + + + /// @brief Allows to check that stack trace capturing was successful. + /// @returns `true` if `this->size() != 0` + /// + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT() + + /// @brief Allows to check that stack trace failed. + /// @returns `true` if `this->size() == 0` + /// + /// @b Complexity: O(1) + /// + /// @b Async-Handler-Safety: Safe. + bool empty() const BOOST_NOEXCEPT { return !size(); } + + /// @cond + bool operator!() const BOOST_NOEXCEPT { return !size(); } + /// @endcond + + const std::vector<boost::stacktrace::frame, Allocator>& as_vector() const BOOST_NOEXCEPT { + return impl_; + } + + /// Constructs stacktrace from basic_istreamable that references the dumped stacktrace. Terminating zero frame is discarded. + /// + /// @b Complexity: O(N) + template <class Char, class Trait> + static basic_stacktrace from_dump(std::basic_istream<Char, Trait>& in, const allocator_type& a = allocator_type()) { + typedef typename std::basic_istream<Char, Trait>::pos_type pos_type; + basic_stacktrace ret(0, 0, a); + + // reserving space + const pos_type pos = in.tellg(); + in.seekg(0, in.end); + const std::size_t frames_count = frames_count_from_buffer_size(static_cast<std::size_t>(in.tellg())); + in.seekg(pos); + + if (!frames_count) { + return ret; + } + + native_frame_ptr_t ptr = 0; + ret.impl_.reserve(frames_count); + while (in.read(reinterpret_cast<Char*>(&ptr), sizeof(ptr))) { + if (!ptr) { + break; + } + + ret.impl_.push_back(frame(ptr)); + } + + return ret; + } + + /// Constructs stacktrace from raw memory dump. Terminating zero frame is discarded. + /// + /// @b Complexity: O(size) in worst case + static basic_stacktrace from_dump(const void* begin, std::size_t buffer_size_in_bytes, const allocator_type& a = allocator_type()) { + basic_stacktrace ret(0, 0, a); + const native_frame_ptr_t* first = static_cast<const native_frame_ptr_t*>(begin); + const std::size_t frames_count = frames_count_from_buffer_size(buffer_size_in_bytes); + if (!frames_count) { + return ret; + } + + const native_frame_ptr_t* const last = first + frames_count; + ret.impl_.reserve(frames_count); + for (; first != last; ++first) { + if (!*first) { + break; + } + + ret.impl_.push_back(frame(*first)); + } + + return ret; + } +}; + +/// @brief Compares stacktraces for less, order is platform dependent. +/// +/// @b Complexity: Amortized O(1); worst case O(size()) +/// +/// @b Async-Handler-Safety: Safe. +template <class Allocator1, class Allocator2> +bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT { + return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs.as_vector() < rhs.as_vector()); +} + +/// @brief Compares stacktraces for equality. +/// +/// @b Complexity: Amortized O(1); worst case O(size()) +/// +/// @b Async-Handler-Safety: Safe. +template <class Allocator1, class Allocator2> +bool operator==(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT { + return lhs.as_vector() == rhs.as_vector(); +} + + +/// Comparison operators that provide platform dependant ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe. +template <class Allocator1, class Allocator2> +bool operator> (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT { + return rhs < lhs; +} + +template <class Allocator1, class Allocator2> +bool operator<=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT { + return !(lhs > rhs); +} + +template <class Allocator1, class Allocator2> +bool operator>=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT { + return !(lhs < rhs); +} + +template <class Allocator1, class Allocator2> +bool operator!=(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) BOOST_NOEXCEPT { + return !(lhs == rhs); +} + +/// Fast hashing support, O(st.size()) complexity; Async-Handler-Safe. +template <class Allocator> +std::size_t hash_value(const basic_stacktrace<Allocator>& st) BOOST_NOEXCEPT { + return boost::hash_range(st.as_vector().begin(), st.as_vector().end()); +} + +/// Outputs stacktrace in a human readable format to output stream; unsafe to use in async handlers. +template <class CharT, class TraitsT, class Allocator> +std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) { + if (bt) { + os << boost::stacktrace::detail::to_string(&bt.as_vector()[0], bt.size()); + } + + return os; +} + +/// This is the typedef to use unless you'd like to provide a specific allocator to boost::stacktrace::basic_stacktrace. +typedef basic_stacktrace<> stacktrace; + +}} // namespace boost::stacktrace + +#ifdef BOOST_INTEL +# pragma warning(pop) +#endif + +#endif // BOOST_STACKTRACE_STACKTRACE_HPP diff --git a/boost/stacktrace/stacktrace_fwd.hpp b/boost/stacktrace/stacktrace_fwd.hpp new file mode 100644 index 0000000000..ec131564c0 --- /dev/null +++ b/boost/stacktrace/stacktrace_fwd.hpp @@ -0,0 +1,28 @@ +// 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_STACKTRACE_FWD_HPP +#define BOOST_STACKTRACE_STACKTRACE_FWD_HPP + +#include <cstddef> +#include <memory> + +/// @file stacktrace_fwd.hpp This header contains only forward declarations of +/// boost::stacktrace::frame, boost::stacktrace::basic_stacktrace, boost::stacktrace::stacktrace +/// and does not include any other Boost headers. + +/// @cond +namespace boost { namespace stacktrace { + +class frame; +template <class Allocator = std::allocator<frame> > class basic_stacktrace; +typedef basic_stacktrace<> stacktrace; + +}} // namespace boost::stacktrace +/// @endcond + + +#endif // BOOST_STACKTRACE_STACKTRACE_FWD_HPP |