1 // Copyright Antony Polukhin, 2016-2022.
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 #ifndef BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
8 #define BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
10 #include <boost/config.hpp>
11 #ifdef BOOST_HAS_PRAGMA_ONCE
15 #include <boost/stacktrace/frame.hpp>
17 #include <boost/core/demangle.hpp>
18 #include <boost/core/noncopyable.hpp>
19 #include <boost/stacktrace/detail/to_dec_array.hpp>
20 #include <boost/stacktrace/detail/to_hex_array.hpp>
25 # pragma comment(lib, "ole32.lib")
26 # pragma comment(lib, "Dbgeng.lib")
30 #ifdef __CRT_UUID_DECL // for __MINGW32__
31 __CRT_UUID_DECL(IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8)
32 __CRT_UUID_DECL(IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba)
33 __CRT_UUID_DECL(IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50)
34 #elif defined(DEFINE_GUID) && !defined(BOOST_MSVC)
35 DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8);
36 DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba);
37 DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50);
42 // Testing. Remove later
43 //# define __uuidof(x) ::IID_ ## x
45 namespace boost { namespace stacktrace { namespace detail {
47 class com_global_initer: boost::noncopyable {
51 com_global_initer() BOOST_NOEXCEPT
54 // COINIT_MULTITHREADED means that we must serialize access to the objects manually.
55 // This is the fastest way to work. If user calls CoInitializeEx before us - we
56 // can end up with other mode (which is OK for us).
58 // If we call CoInitializeEx befire user - user may end up with different mode, which is a problem.
59 // So we need to call that initialization function as late as possible.
60 const DWORD res = ::CoInitializeEx(0, COINIT_MULTITHREADED);
61 ok_ = (res == S_OK || res == S_FALSE);
64 ~com_global_initer() BOOST_NOEXCEPT {
73 class com_holder: boost::noncopyable {
77 com_holder(const com_global_initer&) BOOST_NOEXCEPT
81 T* operator->() const BOOST_NOEXCEPT {
85 void** to_void_ptr_ptr() BOOST_NOEXCEPT {
86 return reinterpret_cast<void**>(&holder_);
89 bool is_inited() const BOOST_NOEXCEPT {
93 ~com_holder() BOOST_NOEXCEPT {
101 inline std::string mingw_demangling_workaround(const std::string& s) {
108 return boost::core::demangle(('_' + s).c_str());
111 return boost::core::demangle(s.c_str());
117 inline void trim_right_zeroes(std::string& s) {
118 // MSVC-9 does not have back() and pop_back() functions in std::string
120 const std::size_t last = static_cast<std::size_t>(s.size() - 1);
121 if (s[last] != '\0') {
128 class debugging_symbols: boost::noncopyable {
129 static void try_init_com(com_holder< ::IDebugSymbols>& idebug, const com_global_initer& com) BOOST_NOEXCEPT {
130 com_holder< ::IDebugClient> iclient(com);
131 if (S_OK != ::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr())) {
135 com_holder< ::IDebugControl> icontrol(com);
136 const bool res0 = (S_OK == iclient->QueryInterface(
137 __uuidof(IDebugControl),
138 icontrol.to_void_ptr_ptr()
144 const bool res1 = (S_OK == iclient->AttachProcess(
146 ::GetCurrentProcessId(),
147 DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND
153 if (S_OK != icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) {
157 // No cheking: QueryInterface sets the output parameter to NULL in case of error.
158 iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr());
161 #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
163 boost::stacktrace::detail::com_global_initer com_;
164 com_holder< ::IDebugSymbols> idebug_;
166 debugging_symbols() BOOST_NOEXCEPT
170 try_init_com(idebug_, com_);
175 #ifdef BOOST_NO_CXX11_THREAD_LOCAL
176 # error Your compiler does not support C++11 thread_local storage. It`s impossible to build with BOOST_STACKTRACE_USE_WINDBG_CACHED.
179 static com_holder< ::IDebugSymbols>& get_thread_local_debug_inst() BOOST_NOEXCEPT {
180 // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
181 // or not the member function is inline.
182 static thread_local boost::stacktrace::detail::com_global_initer com;
183 static thread_local com_holder< ::IDebugSymbols> idebug(com);
185 if (!idebug.is_inited()) {
186 try_init_com(idebug, com);
192 com_holder< ::IDebugSymbols>& idebug_;
194 debugging_symbols() BOOST_NOEXCEPT
195 : idebug_( get_thread_local_debug_inst() )
198 #endif // #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
200 bool is_inited() const BOOST_NOEXCEPT {
201 return idebug_.is_inited();
204 std::string get_name_impl(const void* addr, std::string* module_name = 0) const {
209 const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
214 bool res = (S_OK == idebug_->GetNameByOffset(
222 if (!res && size != 0) {
224 res = (S_OK == idebug_->GetNameByOffset(
227 static_cast<ULONG>(result.size()),
231 trim_right_zeroes(result);
241 const std::size_t delimiter = result.find_first_of('!');
243 *module_name = result.substr(0, delimiter);
246 if (delimiter == std::string::npos) {
247 // If 'delimiter' is equal to 'std::string::npos' then we have only module name.
252 result = mingw_demangling_workaround(
253 result.substr(delimiter + 1)
259 std::size_t get_line_impl(const void* addr) const BOOST_NOEXCEPT {
265 const bool is_ok = (S_OK == idebug_->GetLineByOffset(
266 reinterpret_cast<ULONG64>(addr),
274 return (is_ok ? result : 0);
277 std::pair<std::string, std::size_t> get_source_file_line_impl(const void* addr) const {
278 std::pair<std::string, std::size_t> result;
282 const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
288 bool res = (S_OK == idebug_->GetLineByOffset(
299 result.second = line_num;
303 if (!res && size == 0) {
307 result.first.resize(size);
308 res = (S_OK == idebug_->GetLineByOffset(
312 static_cast<ULONG>(result.first.size()),
316 trim_right_zeroes(result.first);
317 result.second = line_num;
320 result.first.clear();
327 void to_string_impl(const void* addr, std::string& res) const {
332 std::string module_name;
333 std::string name = this->get_name_impl(addr, &module_name);
337 res += to_hex_array(addr).data();
340 std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr);
341 if (!source_line.first.empty() && source_line.second) {
343 res += source_line.first;
345 res += boost::stacktrace::detail::to_dec_array(source_line.second).data();
346 } else if (!module_name.empty()) {
353 std::string to_string(const frame* frames, std::size_t size) {
354 boost::stacktrace::detail::debugging_symbols idebug;
355 if (!idebug.is_inited()) {
356 return std::string();
360 res.reserve(64 * size);
361 for (std::size_t i = 0; i < size; ++i) {
365 res += boost::stacktrace::detail::to_dec_array(i).data();
368 idebug.to_string_impl(frames[i].address(), res);
375 } // namespace detail
377 std::string frame::name() const {
378 boost::stacktrace::detail::debugging_symbols idebug;
379 return idebug.get_name_impl(addr_);
383 std::string frame::source_file() const {
384 boost::stacktrace::detail::debugging_symbols idebug;
385 return idebug.get_source_file_line_impl(addr_).first;
388 std::size_t frame::source_line() const {
389 boost::stacktrace::detail::debugging_symbols idebug;
390 return idebug.get_line_impl(addr_);
393 std::string to_string(const frame& f) {
396 boost::stacktrace::detail::debugging_symbols idebug;
397 idebug.to_string_impl(f.address(), res);
401 }} // namespace boost::stacktrace
403 #endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP