frame_msvc.ipp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright Antony Polukhin, 2016-2024.
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See
  4. // accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
  7. #define BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP
  8. #include <boost/config.hpp>
  9. #ifdef BOOST_HAS_PRAGMA_ONCE
  10. # pragma once
  11. #endif
  12. #include <boost/stacktrace/frame.hpp>
  13. #include <boost/core/demangle.hpp>
  14. #include <boost/core/noncopyable.hpp>
  15. #include <boost/stacktrace/detail/to_dec_array.hpp>
  16. #include <boost/stacktrace/detail/to_hex_array.hpp>
  17. #ifdef WIN32_LEAN_AND_MEAN
  18. #include <windows.h>
  19. #else
  20. // Prevent inclusion of extra Windows SDK headers which can cause conflict
  21. // with other code using Windows SDK
  22. #define WIN32_LEAN_AND_MEAN
  23. #include <windows.h>
  24. #undef WIN32_LEAN_AND_MEAN
  25. #endif
  26. #include "dbgeng.h"
  27. #include <mutex>
  28. #ifdef BOOST_MSVC
  29. # pragma comment(lib, "ole32.lib")
  30. # pragma comment(lib, "Dbgeng.lib")
  31. #endif
  32. #ifdef __CRT_UUID_DECL // for __MINGW32__
  33. #if !defined(__MINGW32__) || \
  34. (!defined(__clang__) && __GNUC__ < 12) || \
  35. (defined(__clang__) && __clang_major__ < 16)
  36. __CRT_UUID_DECL(IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8)
  37. __CRT_UUID_DECL(IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba)
  38. __CRT_UUID_DECL(IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50)
  39. #endif
  40. #elif defined(DEFINE_GUID) && !defined(BOOST_MSVC)
  41. DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8);
  42. DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba);
  43. DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50);
  44. #endif
  45. // Testing. Remove later
  46. //# define __uuidof(x) ::IID_ ## x
  47. namespace boost { namespace stacktrace { namespace detail {
  48. template <class T>
  49. class com_holder: boost::noncopyable {
  50. T* holder_;
  51. public:
  52. com_holder() noexcept
  53. : holder_(0)
  54. {}
  55. T* operator->() const noexcept {
  56. return holder_;
  57. }
  58. void** to_void_ptr_ptr() noexcept {
  59. return reinterpret_cast<void**>(&holder_);
  60. }
  61. bool is_inited() const noexcept {
  62. return !!holder_;
  63. }
  64. ~com_holder() noexcept {
  65. if (holder_) {
  66. holder_->Release();
  67. }
  68. }
  69. };
  70. inline std::string mingw_demangling_workaround(const std::string& s) {
  71. #ifdef BOOST_GCC
  72. if (s.empty()) {
  73. return s;
  74. }
  75. if (s[0] != '_') {
  76. return boost::core::demangle(('_' + s).c_str());
  77. }
  78. return boost::core::demangle(s.c_str());
  79. #else
  80. return s;
  81. #endif
  82. }
  83. inline void trim_right_zeroes(std::string& s) {
  84. // MSVC-9 does not have back() and pop_back() functions in std::string
  85. while (!s.empty()) {
  86. const std::size_t last = static_cast<std::size_t>(s.size() - 1);
  87. if (s[last] != '\0') {
  88. break;
  89. }
  90. s.resize(last);
  91. }
  92. }
  93. class debugging_symbols: boost::noncopyable {
  94. static void try_init_com(com_holder< ::IDebugSymbols>& idebug) noexcept {
  95. com_holder< ::IDebugClient> iclient;
  96. if (S_OK != ::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr())) {
  97. return;
  98. }
  99. com_holder< ::IDebugControl> icontrol;
  100. const bool res0 = (S_OK == iclient->QueryInterface(
  101. __uuidof(IDebugControl),
  102. icontrol.to_void_ptr_ptr()
  103. ));
  104. if (!res0) {
  105. return;
  106. }
  107. const bool res1 = (S_OK == iclient->AttachProcess(
  108. 0,
  109. ::GetCurrentProcessId(),
  110. DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND
  111. ));
  112. if (!res1) {
  113. return;
  114. }
  115. if (S_OK != icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) {
  116. return;
  117. }
  118. // No checking: QueryInterface sets the output parameter to NULL in case of error.
  119. iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr());
  120. }
  121. #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
  122. static std::mutex& get_mutex_inst() noexcept {
  123. static std::mutex m;
  124. return m;
  125. }
  126. static com_holder< ::IDebugSymbols>& get_static_debug_inst() noexcept {
  127. // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
  128. // or not the member function is inline.
  129. static com_holder< ::IDebugSymbols> idebug;
  130. if (!idebug.is_inited()) {
  131. try_init_com(idebug);
  132. }
  133. return idebug;
  134. }
  135. std::lock_guard<std::mutex> guard_;
  136. com_holder< ::IDebugSymbols>& idebug_;
  137. public:
  138. debugging_symbols() noexcept
  139. : guard_( get_mutex_inst() )
  140. , idebug_( get_static_debug_inst() )
  141. {}
  142. #else
  143. #ifdef BOOST_NO_CXX11_THREAD_LOCAL
  144. # error Your compiler does not support C++11 thread_local storage. It`s impossible to build with BOOST_STACKTRACE_USE_WINDBG_CACHED.
  145. #endif
  146. static com_holder< ::IDebugSymbols>& get_thread_local_debug_inst() noexcept {
  147. // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
  148. // or not the member function is inline.
  149. static thread_local com_holder< ::IDebugSymbols> idebug;
  150. if (!idebug.is_inited()) {
  151. try_init_com(idebug);
  152. }
  153. return idebug;
  154. }
  155. com_holder< ::IDebugSymbols>& idebug_;
  156. public:
  157. debugging_symbols() noexcept
  158. : idebug_( get_thread_local_debug_inst() )
  159. {}
  160. #endif // #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
  161. bool is_inited() const noexcept {
  162. return idebug_.is_inited();
  163. }
  164. std::string get_name_impl(const void* addr, std::string* module_name = 0) const {
  165. std::string result;
  166. if (!is_inited()) {
  167. return result;
  168. }
  169. const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
  170. char name[256];
  171. name[0] = '\0';
  172. ULONG size = 0;
  173. bool res = (S_OK == idebug_->GetNameByOffset(
  174. offset,
  175. name,
  176. sizeof(name),
  177. &size,
  178. 0
  179. ));
  180. if (!res && size != 0) {
  181. result.resize(size);
  182. res = (S_OK == idebug_->GetNameByOffset(
  183. offset,
  184. &result[0],
  185. static_cast<ULONG>(result.size()),
  186. &size,
  187. 0
  188. ));
  189. // According to https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/dbgeng/nf-dbgeng-idebugsymbols-getnamebyoffset
  190. // "This size includes the space for the '\0' terminating character."
  191. result.resize(size - 1);
  192. } else if (res) {
  193. result.assign(name, size - 1);
  194. }
  195. if (!res) {
  196. result.clear();
  197. return result;
  198. }
  199. const std::size_t delimiter = result.find_first_of('!');
  200. if (module_name) {
  201. *module_name = result.substr(0, delimiter);
  202. }
  203. if (delimiter == std::string::npos) {
  204. // If 'delimiter' is equal to 'std::string::npos' then we have only module name.
  205. result.clear();
  206. return result;
  207. }
  208. result = mingw_demangling_workaround(
  209. result.substr(delimiter + 1)
  210. );
  211. return result;
  212. }
  213. std::size_t get_line_impl(const void* addr) const noexcept {
  214. ULONG result = 0;
  215. if (!is_inited()) {
  216. return result;
  217. }
  218. const bool is_ok = (S_OK == idebug_->GetLineByOffset(
  219. reinterpret_cast<ULONG64>(addr),
  220. &result,
  221. 0,
  222. 0,
  223. 0,
  224. 0
  225. ));
  226. return (is_ok ? result : 0);
  227. }
  228. std::pair<std::string, std::size_t> get_source_file_line_impl(const void* addr) const {
  229. std::pair<std::string, std::size_t> result;
  230. if (!is_inited()) {
  231. return result;
  232. }
  233. const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
  234. char name[256];
  235. name[0] = 0;
  236. ULONG size = 0;
  237. ULONG line_num = 0;
  238. bool res = (S_OK == idebug_->GetLineByOffset(
  239. offset,
  240. &line_num,
  241. name,
  242. sizeof(name),
  243. &size,
  244. 0
  245. ));
  246. if (res) {
  247. result.first = name;
  248. result.second = line_num;
  249. return result;
  250. }
  251. if (!res && size == 0) {
  252. return result;
  253. }
  254. result.first.resize(size);
  255. res = (S_OK == idebug_->GetLineByOffset(
  256. offset,
  257. &line_num,
  258. &result.first[0],
  259. static_cast<ULONG>(result.first.size()),
  260. &size,
  261. 0
  262. ));
  263. trim_right_zeroes(result.first);
  264. result.second = line_num;
  265. if (!res) {
  266. result.first.clear();
  267. result.second = 0;
  268. }
  269. return result;
  270. }
  271. void to_string_impl(const void* addr, std::string& res) const {
  272. if (!is_inited()) {
  273. return;
  274. }
  275. std::string module_name;
  276. std::string name = this->get_name_impl(addr, &module_name);
  277. if (!name.empty()) {
  278. res += name;
  279. } else {
  280. res += to_hex_array(addr).data();
  281. }
  282. std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr);
  283. if (!source_line.first.empty() && source_line.second) {
  284. res += " at ";
  285. res += source_line.first;
  286. res += ':';
  287. res += boost::stacktrace::detail::to_dec_array(source_line.second).data();
  288. } else if (!module_name.empty()) {
  289. res += " in ";
  290. res += module_name;
  291. }
  292. }
  293. };
  294. std::string to_string(const frame* frames, std::size_t size) {
  295. boost::stacktrace::detail::debugging_symbols idebug;
  296. if (!idebug.is_inited()) {
  297. return std::string();
  298. }
  299. std::string res;
  300. res.reserve(64 * size);
  301. for (std::size_t i = 0; i < size; ++i) {
  302. if (i < 10) {
  303. res += ' ';
  304. }
  305. res += boost::stacktrace::detail::to_dec_array(i).data();
  306. res += '#';
  307. res += ' ';
  308. idebug.to_string_impl(frames[i].address(), res);
  309. res += '\n';
  310. }
  311. return res;
  312. }
  313. } // namespace detail
  314. std::string frame::name() const {
  315. boost::stacktrace::detail::debugging_symbols idebug;
  316. return idebug.get_name_impl(addr_);
  317. }
  318. std::string frame::source_file() const {
  319. boost::stacktrace::detail::debugging_symbols idebug;
  320. return idebug.get_source_file_line_impl(addr_).first;
  321. }
  322. std::size_t frame::source_line() const {
  323. boost::stacktrace::detail::debugging_symbols idebug;
  324. return idebug.get_line_impl(addr_);
  325. }
  326. std::string to_string(const frame& f) {
  327. std::string res;
  328. boost::stacktrace::detail::debugging_symbols idebug;
  329. idebug.to_string_impl(f.address(), res);
  330. return res;
  331. }
  332. }} // namespace boost::stacktrace
  333. #endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP