frame_msvc.ipp 10 KB

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