Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-06 08:31:38

0001 // Copyright Antony Polukhin, 2016-2024.
0002 //
0003 // Distributed under the Boost Software License, Version 1.0. (See
0004 // accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
0008 #define BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
0009 
0010 #include <boost/config.hpp>
0011 #ifdef BOOST_HAS_PRAGMA_ONCE
0012 #   pragma once
0013 #endif
0014 
0015 #include <boost/stacktrace/frame.hpp>
0016 
0017 #include <boost/core/demangle.hpp>
0018 #include <boost/core/noncopyable.hpp>
0019 #include <boost/stacktrace/detail/to_dec_array.hpp>
0020 #include <boost/stacktrace/detail/to_hex_array.hpp>
0021 
0022 #ifdef WIN32_LEAN_AND_MEAN
0023 #include <windows.h>
0024 #else
0025 // Prevent inclusion of extra Windows SDK headers which can cause conflict
0026 // with other code using Windows SDK
0027 #define WIN32_LEAN_AND_MEAN
0028 #include <windows.h>
0029 #undef WIN32_LEAN_AND_MEAN
0030 #endif
0031 
0032 #include "dbgeng.h"
0033 
0034 #include <mutex>
0035 
0036 #ifdef BOOST_MSVC
0037 #   pragma comment(lib, "ole32.lib")
0038 #   pragma comment(lib, "Dbgeng.lib")
0039 #endif
0040 
0041 
0042 #ifdef __CRT_UUID_DECL // for __MINGW32__
0043 #if !defined(__MINGW32__) || \
0044     (!defined(__clang__) && __GNUC__ < 12) || \
0045     (defined(__clang__) && __clang_major__ < 16)
0046     __CRT_UUID_DECL(IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8)
0047     __CRT_UUID_DECL(IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba)
0048     __CRT_UUID_DECL(IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50)
0049 #endif
0050 #elif defined(DEFINE_GUID) && !defined(BOOST_MSVC)
0051     DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8);
0052     DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba);
0053     DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50);
0054 #endif
0055 
0056 
0057 
0058 // Testing. Remove later
0059 //#   define __uuidof(x) ::IID_ ## x
0060 
0061 namespace boost { namespace stacktrace { namespace detail {
0062 
0063 template <class T>
0064 class com_holder: boost::noncopyable {
0065     T* holder_;
0066 
0067 public:
0068     com_holder() noexcept
0069         : holder_(0)
0070     {}
0071 
0072     T* operator->() const noexcept {
0073         return holder_;
0074     }
0075 
0076     void** to_void_ptr_ptr() noexcept {
0077         return reinterpret_cast<void**>(&holder_);
0078     }
0079 
0080     bool is_inited() const noexcept {
0081         return !!holder_;
0082     }
0083 
0084     ~com_holder() noexcept {
0085         if (holder_) {
0086             holder_->Release();
0087         }
0088     }
0089 };
0090 
0091 
0092 inline std::string mingw_demangling_workaround(const std::string& s) {
0093 #ifdef BOOST_GCC
0094     if (s.empty()) {
0095         return s;
0096     }
0097 
0098     if (s[0] != '_') {
0099         return boost::core::demangle(('_' + s).c_str());
0100     }
0101 
0102     return boost::core::demangle(s.c_str());
0103 #else
0104     return s;
0105 #endif
0106 }
0107 
0108 inline void trim_right_zeroes(std::string& s) {
0109     // MSVC-9 does not have back() and pop_back() functions in std::string
0110     while (!s.empty()) {
0111         const std::size_t last = static_cast<std::size_t>(s.size() - 1);
0112         if (s[last] != '\0') {
0113             break;
0114         }
0115         s.resize(last);
0116     }
0117 }
0118 
0119 class debugging_symbols: boost::noncopyable {
0120     static void try_init_com(com_holder< ::IDebugSymbols>& idebug) noexcept {
0121         com_holder< ::IDebugClient> iclient;
0122         if (S_OK != ::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr())) {
0123             return;
0124         }
0125 
0126         com_holder< ::IDebugControl> icontrol;
0127         const bool res0 = (S_OK == iclient->QueryInterface(
0128             __uuidof(IDebugControl),
0129             icontrol.to_void_ptr_ptr()
0130         ));
0131         if (!res0) {
0132             return;
0133         }
0134 
0135         const bool res1 = (S_OK == iclient->AttachProcess(
0136             0,
0137             ::GetCurrentProcessId(),
0138             DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND
0139         ));
0140         if (!res1) {
0141             return;
0142         }
0143 
0144         if (S_OK != icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) {
0145             return;
0146         }
0147 
0148         // No checking: QueryInterface sets the output parameter to NULL in case of error.
0149         iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr());
0150     }
0151 
0152 #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
0153     static std::mutex& get_mutex_inst() noexcept {
0154         static std::mutex m;
0155         return m;
0156     }
0157 
0158     static com_holder< ::IDebugSymbols>& get_static_debug_inst() noexcept {
0159         // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
0160         // or not the member function is inline.
0161         static com_holder< ::IDebugSymbols> idebug;
0162 
0163         if (!idebug.is_inited()) {
0164             try_init_com(idebug);
0165         }
0166 
0167         return idebug;
0168     }
0169 
0170     std::lock_guard<std::mutex> guard_;
0171     com_holder< ::IDebugSymbols>& idebug_;
0172 public:
0173     debugging_symbols() noexcept
0174         : guard_( get_mutex_inst() )
0175         , idebug_( get_static_debug_inst() )
0176     {}
0177 #else
0178 
0179 #ifdef BOOST_NO_CXX11_THREAD_LOCAL
0180 #   error Your compiler does not support C++11 thread_local storage. It`s impossible to build with BOOST_STACKTRACE_USE_WINDBG_CACHED.
0181 #endif
0182 
0183     static com_holder< ::IDebugSymbols>& get_thread_local_debug_inst() noexcept {
0184         // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
0185         // or not the member function is inline.
0186         static thread_local com_holder< ::IDebugSymbols> idebug;
0187 
0188         if (!idebug.is_inited()) {
0189             try_init_com(idebug);
0190         }
0191 
0192         return idebug;
0193     }
0194 
0195     com_holder< ::IDebugSymbols>& idebug_;
0196 public:
0197     debugging_symbols() noexcept
0198         : idebug_( get_thread_local_debug_inst() )
0199     {}
0200 
0201 #endif // #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
0202 
0203     bool is_inited() const noexcept {
0204         return idebug_.is_inited();
0205     }
0206 
0207     std::string get_name_impl(const void* addr, std::string* module_name = 0) const {
0208         std::string result;
0209         if (!is_inited()) {
0210             return result;
0211         }
0212         const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
0213 
0214         char name[256];
0215         name[0] = '\0';
0216         ULONG size = 0;
0217         bool res = (S_OK == idebug_->GetNameByOffset(
0218             offset,
0219             name,
0220             sizeof(name),
0221             &size,
0222             0
0223         ));
0224 
0225         if (!res && size != 0) {
0226             result.resize(size);
0227             res = (S_OK == idebug_->GetNameByOffset(
0228                 offset,
0229                 &result[0],
0230                 static_cast<ULONG>(result.size()),
0231                 &size,
0232                 0
0233             ));
0234 
0235             // According to https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/dbgeng/nf-dbgeng-idebugsymbols-getnamebyoffset
0236             // "This size includes the space for the '\0' terminating character."
0237             result.resize(size - 1);
0238         } else if (res) {
0239             result.assign(name, size - 1);
0240         }
0241 
0242         if (!res) {
0243             result.clear();
0244             return result;
0245         }
0246 
0247         const std::size_t delimiter = result.find_first_of('!');
0248         if (module_name) {
0249             *module_name = result.substr(0, delimiter);
0250         }
0251 
0252         if (delimiter == std::string::npos) {
0253             // If 'delimiter' is equal to 'std::string::npos' then we have only module name.
0254             result.clear();
0255             return result;
0256         }
0257 
0258         result = mingw_demangling_workaround(
0259             result.substr(delimiter + 1)
0260         );
0261 
0262         return result;
0263     }
0264 
0265     std::size_t get_line_impl(const void* addr) const noexcept {
0266         ULONG result = 0;
0267         if (!is_inited()) {
0268             return result;
0269         }
0270 
0271         const bool is_ok = (S_OK == idebug_->GetLineByOffset(
0272             reinterpret_cast<ULONG64>(addr),
0273             &result,
0274             0,
0275             0,
0276             0,
0277             0
0278         ));
0279 
0280         return (is_ok ? result : 0);
0281     }
0282 
0283     std::pair<std::string, std::size_t> get_source_file_line_impl(const void* addr) const {
0284         std::pair<std::string, std::size_t> result;
0285         if (!is_inited()) {
0286             return result;
0287         }
0288         const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
0289 
0290         char name[256];
0291         name[0] = 0;
0292         ULONG size = 0;
0293         ULONG line_num = 0;
0294         bool res = (S_OK == idebug_->GetLineByOffset(
0295             offset,
0296             &line_num,
0297             name,
0298             sizeof(name),
0299             &size,
0300             0
0301         ));
0302 
0303         if (res) {
0304             result.first = name;
0305             result.second = line_num;
0306             return result;
0307         }
0308 
0309         if (!res && size == 0) {
0310             return result;
0311         }
0312 
0313         result.first.resize(size);
0314         res = (S_OK == idebug_->GetLineByOffset(
0315             offset,
0316             &line_num,
0317             &result.first[0],
0318             static_cast<ULONG>(result.first.size()),
0319             &size,
0320             0
0321         ));
0322         trim_right_zeroes(result.first);
0323         result.second = line_num;
0324 
0325         if (!res) {
0326             result.first.clear();
0327             result.second = 0;
0328         }
0329 
0330         return result;
0331     }
0332 
0333     void to_string_impl(const void* addr, std::string& res) const {
0334         if (!is_inited()) {
0335             return;
0336         }
0337 
0338         std::string module_name;
0339         std::string name = this->get_name_impl(addr, &module_name);
0340         if (!name.empty()) {
0341             res += name;
0342         } else {
0343             res += to_hex_array(addr).data();
0344         }
0345 
0346         std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr);
0347         if (!source_line.first.empty() && source_line.second) {
0348             res += " at ";
0349             res += source_line.first;
0350             res += ':';
0351             res += boost::stacktrace::detail::to_dec_array(source_line.second).data();
0352         } else if (!module_name.empty()) {
0353             res += " in ";
0354             res += module_name;
0355         }
0356     }
0357 };
0358 
0359 std::string to_string(const frame* frames, std::size_t size) {
0360     boost::stacktrace::detail::debugging_symbols idebug;
0361     if (!idebug.is_inited()) {
0362         return std::string();
0363     }
0364 
0365     std::string res;
0366     res.reserve(64 * size);
0367     for (std::size_t i = 0; i < size; ++i) {
0368         if (i < 10) {
0369             res += ' ';
0370         }
0371         res += boost::stacktrace::detail::to_dec_array(i).data();
0372         res += '#';
0373         res += ' ';
0374         idebug.to_string_impl(frames[i].address(), res);
0375         res += '\n';
0376     }
0377 
0378     return res;
0379 }
0380 
0381 } // namespace detail
0382 
0383 std::string frame::name() const {
0384     boost::stacktrace::detail::debugging_symbols idebug;
0385     return idebug.get_name_impl(addr_);
0386 }
0387 
0388 
0389 std::string frame::source_file() const {
0390     boost::stacktrace::detail::debugging_symbols idebug;
0391     return idebug.get_source_file_line_impl(addr_).first;
0392 }
0393 
0394 std::size_t frame::source_line() const {
0395     boost::stacktrace::detail::debugging_symbols idebug;
0396     return idebug.get_line_impl(addr_);
0397 }
0398 
0399 std::string to_string(const frame& f) {
0400     std::string res;
0401 
0402     boost::stacktrace::detail::debugging_symbols idebug;
0403     idebug.to_string_impl(f.address(), res);
0404     return res;
0405 }
0406 
0407 }} // namespace boost::stacktrace
0408 
0409 #endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP