Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:52:31

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