File indexing completed on 2025-07-06 08:31:38
0001
0002
0003
0004
0005
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
0026
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
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
0059
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
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
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
0160
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
0185
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
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
0236
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
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 }
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 }}
0408
0409 #endif