Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-13 08:51:48

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