File indexing completed on 2025-09-13 08:51:48
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 #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
0030
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
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
0063
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
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
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
0164
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
0189
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
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
0240
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
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
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 }
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 }}
0469
0470 #endif