frame_msvc.ipp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. // Copyright Antony Polukhin, 2016-2019.
  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. class com_global_initer: boost::noncopyable {
  36. bool ok_;
  37. public:
  38. com_global_initer() BOOST_NOEXCEPT
  39. : ok_(false)
  40. {
  41. // COINIT_MULTITHREADED means that we must serialize access to the objects manually.
  42. // This is the fastest way to work. If user calls CoInitializeEx before us - we
  43. // can end up with other mode (which is OK for us).
  44. //
  45. // If we call CoInitializeEx befire user - user may end up with different mode, which is a problem.
  46. // So we need to call that initialization function as late as possible.
  47. const DWORD res = ::CoInitializeEx(0, COINIT_MULTITHREADED);
  48. ok_ = (res == S_OK || res == S_FALSE);
  49. }
  50. ~com_global_initer() BOOST_NOEXCEPT {
  51. if (ok_) {
  52. ::CoUninitialize();
  53. }
  54. }
  55. };
  56. template <class T>
  57. class com_holder: boost::noncopyable {
  58. T* holder_;
  59. public:
  60. com_holder(const com_global_initer&) BOOST_NOEXCEPT
  61. : holder_(0)
  62. {}
  63. T* operator->() const BOOST_NOEXCEPT {
  64. return holder_;
  65. }
  66. void** to_void_ptr_ptr() BOOST_NOEXCEPT {
  67. return reinterpret_cast<void**>(&holder_);
  68. }
  69. bool is_inited() const BOOST_NOEXCEPT {
  70. return !!holder_;
  71. }
  72. ~com_holder() BOOST_NOEXCEPT {
  73. if (holder_) {
  74. holder_->Release();
  75. }
  76. }
  77. };
  78. static std::string mingw_demangling_workaround(const std::string& s) {
  79. #ifdef BOOST_GCC
  80. if (s.empty()) {
  81. return s;
  82. }
  83. if (s[0] != '_') {
  84. return boost::core::demangle(('_' + s).c_str());
  85. }
  86. return boost::core::demangle(s.c_str());
  87. #else
  88. return s;
  89. #endif
  90. }
  91. class debugging_symbols: boost::noncopyable {
  92. static void try_init_com(com_holder< ::IDebugSymbols>& idebug, const com_global_initer& com) BOOST_NOEXCEPT {
  93. com_holder< ::IDebugClient> iclient(com);
  94. if (S_OK != ::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr())) {
  95. return;
  96. }
  97. com_holder< ::IDebugControl> icontrol(com);
  98. const bool res0 = (S_OK == iclient->QueryInterface(
  99. __uuidof(IDebugControl),
  100. icontrol.to_void_ptr_ptr()
  101. ));
  102. if (!res0) {
  103. return;
  104. }
  105. const bool res1 = (S_OK == iclient->AttachProcess(
  106. 0,
  107. ::GetCurrentProcessId(),
  108. DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND
  109. ));
  110. if (!res1) {
  111. return;
  112. }
  113. if (S_OK != icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) {
  114. return;
  115. }
  116. // No cheking: QueryInterface sets the output parameter to NULL in case of error.
  117. iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr());
  118. }
  119. #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
  120. boost::stacktrace::detail::com_global_initer com_;
  121. com_holder< ::IDebugSymbols> idebug_;
  122. public:
  123. debugging_symbols() BOOST_NOEXCEPT
  124. : com_()
  125. , idebug_(com_)
  126. {
  127. try_init_com(idebug_, com_);
  128. }
  129. #else
  130. #ifdef BOOST_NO_CXX11_THREAD_LOCAL
  131. # error Your compiler does not support C++11 thread_local storage. It`s impossible to build with BOOST_STACKTRACE_USE_WINDBG_CACHED.
  132. #endif
  133. static com_holder< ::IDebugSymbols>& get_thread_local_debug_inst() BOOST_NOEXCEPT {
  134. // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
  135. // or not the member function is inline.
  136. static thread_local boost::stacktrace::detail::com_global_initer com;
  137. static thread_local com_holder< ::IDebugSymbols> idebug(com);
  138. if (!idebug.is_inited()) {
  139. try_init_com(idebug, com);
  140. }
  141. return idebug;
  142. }
  143. com_holder< ::IDebugSymbols>& idebug_;
  144. public:
  145. debugging_symbols() BOOST_NOEXCEPT
  146. : idebug_( get_thread_local_debug_inst() )
  147. {}
  148. #endif // #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
  149. bool is_inited() const BOOST_NOEXCEPT {
  150. return idebug_.is_inited();
  151. }
  152. std::string get_name_impl(const void* addr, std::string* module_name = 0) const {
  153. std::string result;
  154. if (!is_inited()) {
  155. return result;
  156. }
  157. const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
  158. char name[256];
  159. name[0] = '\0';
  160. ULONG size = 0;
  161. bool res = (S_OK == idebug_->GetNameByOffset(
  162. offset,
  163. name,
  164. sizeof(name),
  165. &size,
  166. 0
  167. ));
  168. if (!res && size != 0) {
  169. result.resize(size);
  170. res = (S_OK == idebug_->GetNameByOffset(
  171. offset,
  172. &result[0],
  173. static_cast<ULONG>(result.size()),
  174. &size,
  175. 0
  176. ));
  177. } else if (res) {
  178. result = name;
  179. }
  180. if (!res) {
  181. result.clear();
  182. return result;
  183. }
  184. const std::size_t delimiter = result.find_first_of('!');
  185. if (module_name) {
  186. *module_name = result.substr(0, delimiter);
  187. }
  188. if (delimiter == std::string::npos) {
  189. // If 'delimiter' is equal to 'std::string::npos' then we have only module name.
  190. result.clear();
  191. return result;
  192. }
  193. result = mingw_demangling_workaround(
  194. result.substr(delimiter + 1)
  195. );
  196. return result;
  197. }
  198. std::size_t get_line_impl(const void* addr) const BOOST_NOEXCEPT {
  199. ULONG result = 0;
  200. if (!is_inited()) {
  201. return result;
  202. }
  203. const bool is_ok = (S_OK == idebug_->GetLineByOffset(
  204. reinterpret_cast<ULONG64>(addr),
  205. &result,
  206. 0,
  207. 0,
  208. 0,
  209. 0
  210. ));
  211. return (is_ok ? result : 0);
  212. }
  213. std::pair<std::string, std::size_t> get_source_file_line_impl(const void* addr) const {
  214. std::pair<std::string, std::size_t> result;
  215. if (!is_inited()) {
  216. return result;
  217. }
  218. const ULONG64 offset = reinterpret_cast<ULONG64>(addr);
  219. char name[256];
  220. name[0] = 0;
  221. ULONG size = 0;
  222. ULONG line_num = 0;
  223. bool res = (S_OK == idebug_->GetLineByOffset(
  224. offset,
  225. &line_num,
  226. name,
  227. sizeof(name),
  228. &size,
  229. 0
  230. ));
  231. if (res) {
  232. result.first = name;
  233. result.second = line_num;
  234. return result;
  235. }
  236. if (!res && size == 0) {
  237. return result;
  238. }
  239. result.first.resize(size);
  240. res = (S_OK == idebug_->GetLineByOffset(
  241. offset,
  242. &line_num,
  243. &result.first[0],
  244. static_cast<ULONG>(result.first.size()),
  245. &size,
  246. 0
  247. ));
  248. result.second = line_num;
  249. if (!res) {
  250. result.first.clear();
  251. result.second = 0;
  252. }
  253. return result;
  254. }
  255. void to_string_impl(const void* addr, std::string& res) const {
  256. if (!is_inited()) {
  257. return;
  258. }
  259. std::string module_name;
  260. std::string name = this->get_name_impl(addr, &module_name);
  261. if (!name.empty()) {
  262. res += name;
  263. } else {
  264. res += to_hex_array(addr).data();
  265. }
  266. std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr);
  267. if (!source_line.first.empty() && source_line.second) {
  268. res += " at ";
  269. res += source_line.first;
  270. res += ':';
  271. res += boost::stacktrace::detail::to_dec_array(source_line.second).data();
  272. } else if (!module_name.empty()) {
  273. res += " in ";
  274. res += module_name;
  275. }
  276. }
  277. };
  278. std::string to_string(const frame* frames, std::size_t size) {
  279. boost::stacktrace::detail::debugging_symbols idebug;
  280. if (!idebug.is_inited()) {
  281. return std::string();
  282. }
  283. std::string res;
  284. res.reserve(64 * size);
  285. for (std::size_t i = 0; i < size; ++i) {
  286. if (i < 10) {
  287. res += ' ';
  288. }
  289. res += boost::stacktrace::detail::to_dec_array(i).data();
  290. res += '#';
  291. res += ' ';
  292. idebug.to_string_impl(frames[i].address(), res);
  293. res += '\n';
  294. }
  295. return res;
  296. }
  297. } // namespace detail
  298. std::string frame::name() const {
  299. boost::stacktrace::detail::debugging_symbols idebug;
  300. return idebug.get_name_impl(addr_);
  301. }
  302. std::string frame::source_file() const {
  303. boost::stacktrace::detail::debugging_symbols idebug;
  304. return idebug.get_source_file_line_impl(addr_).first;
  305. }
  306. std::size_t frame::source_line() const {
  307. boost::stacktrace::detail::debugging_symbols idebug;
  308. return idebug.get_line_impl(addr_);
  309. }
  310. std::string to_string(const frame& f) {
  311. std::string res;
  312. boost::stacktrace::detail::debugging_symbols idebug;
  313. idebug.to_string_impl(f.address(), res);
  314. return res;
  315. }
  316. }} // namespace boost::stacktrace
  317. #endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP