123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- // Copyright Antony Polukhin, 2016-2021.
- //
- // 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/stacktrace/detail/to_dec_array.hpp>
- #include <boost/stacktrace/detail/to_hex_array.hpp>
- #include <windows.h>
- #include "dbgeng.h"
- #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 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();
- }
- }
- };
- inline std::string mingw_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
- }
- inline void trim_right_zeroes(std::string& s) {
- // MSVC-9 does not have back() and pop_back() functions in std::string
- while (!s.empty()) {
- const std::size_t last = static_cast<std::size_t>(s.size() - 1);
- if (s[last] != '\0') {
- break;
- }
- s.resize(last);
- }
- }
- 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
- ));
- trim_right_zeroes(result);
- } 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 = mingw_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
- ));
- trim_right_zeroes(result.first);
- 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::stacktrace::detail::to_dec_array(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::stacktrace::detail::to_dec_array(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
|