summaryrefslogtreecommitdiff
path: root/boost/stacktrace/detail/frame_msvc.ipp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/stacktrace/detail/frame_msvc.ipp')
-rw-r--r--boost/stacktrace/detail/frame_msvc.ipp392
1 files changed, 392 insertions, 0 deletions
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