]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // Copyright Antony Polukhin, 2016-2017. |
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 | ||
7 | #ifndef BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP | |
8 | #define BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP | |
9 | ||
10 | #include <boost/config.hpp> | |
11 | #ifdef BOOST_HAS_PRAGMA_ONCE | |
12 | # pragma once | |
13 | #endif | |
14 | ||
15 | #include <boost/stacktrace/frame.hpp> | |
16 | ||
17 | #include <boost/core/demangle.hpp> | |
18 | #include <boost/core/noncopyable.hpp> | |
19 | #include <boost/lexical_cast.hpp> | |
20 | #include <boost/stacktrace/detail/to_hex_array.hpp> | |
21 | #include <windows.h> | |
22 | #include "dbgeng.h" | |
23 | ||
24 | #include <boost/detail/winapi/get_current_process.hpp> | |
25 | ||
26 | #ifdef BOOST_MSVC | |
27 | # pragma comment(lib, "ole32.lib") | |
28 | # pragma comment(lib, "Dbgeng.lib") | |
29 | #endif | |
30 | ||
31 | ||
32 | #ifdef __CRT_UUID_DECL // for __MINGW32__ | |
33 | __CRT_UUID_DECL(IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8) | |
34 | __CRT_UUID_DECL(IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba) | |
35 | __CRT_UUID_DECL(IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50) | |
36 | #elif defined(DEFINE_GUID) && !defined(BOOST_MSVC) | |
37 | DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8); | |
38 | DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba); | |
39 | DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50); | |
40 | #endif | |
41 | ||
42 | ||
43 | ||
44 | // Testing. Remove later | |
45 | //# define __uuidof(x) ::IID_ ## x | |
46 | ||
47 | namespace boost { namespace stacktrace { namespace detail { | |
48 | ||
49 | class com_global_initer: boost::noncopyable { | |
50 | bool ok_; | |
51 | ||
52 | public: | |
53 | com_global_initer() BOOST_NOEXCEPT | |
54 | : ok_(false) | |
55 | { | |
56 | // COINIT_MULTITHREADED means that we must serialize access to the objects manually. | |
57 | // This is the fastest way to work. If user calls CoInitializeEx before us - we | |
58 | // can end up with other mode (which is OK for us). | |
59 | // | |
60 | // If we call CoInitializeEx befire user - user may end up with different mode, which is a problem. | |
61 | // So we need to call that initialization function as late as possible. | |
62 | const boost::detail::winapi::DWORD_ res = ::CoInitializeEx(0, COINIT_MULTITHREADED); | |
63 | ok_ = (res == S_OK || res == S_FALSE); | |
64 | } | |
65 | ||
66 | ~com_global_initer() BOOST_NOEXCEPT { | |
67 | if (ok_) { | |
68 | ::CoUninitialize(); | |
69 | } | |
70 | } | |
71 | }; | |
72 | ||
73 | ||
74 | template <class T> | |
75 | class com_holder: boost::noncopyable { | |
76 | T* holder_; | |
77 | ||
78 | public: | |
79 | com_holder(const com_global_initer&) BOOST_NOEXCEPT | |
80 | : holder_(0) | |
81 | {} | |
82 | ||
83 | T* operator->() const BOOST_NOEXCEPT { | |
84 | return holder_; | |
85 | } | |
86 | ||
87 | void** to_void_ptr_ptr() BOOST_NOEXCEPT { | |
88 | return reinterpret_cast<void**>(&holder_); | |
89 | } | |
90 | ||
91 | bool is_inited() const BOOST_NOEXCEPT { | |
92 | return !!holder_; | |
93 | } | |
94 | ||
95 | ~com_holder() BOOST_NOEXCEPT { | |
96 | if (holder_) { | |
97 | holder_->Release(); | |
98 | } | |
99 | } | |
100 | }; | |
101 | ||
102 | ||
103 | static std::string minwg_demangling_workaround(const std::string& s) { | |
104 | #ifdef BOOST_GCC | |
105 | if (s.empty()) { | |
106 | return s; | |
107 | } | |
108 | ||
109 | if (s[0] != '_') { | |
110 | return boost::core::demangle(('_' + s).c_str()); | |
111 | } | |
112 | ||
113 | return boost::core::demangle(s.c_str()); | |
114 | #else | |
115 | return s; | |
116 | #endif | |
117 | } | |
118 | ||
119 | class debugging_symbols: boost::noncopyable { | |
120 | static void try_init_com(com_holder< ::IDebugSymbols>& idebug, const com_global_initer& com) BOOST_NOEXCEPT { | |
121 | com_holder< ::IDebugClient> iclient(com); | |
122 | if (S_OK != ::DebugCreate(__uuidof(IDebugClient), iclient.to_void_ptr_ptr())) { | |
123 | return; | |
124 | } | |
125 | ||
126 | com_holder< ::IDebugControl> icontrol(com); | |
127 | const bool res0 = (S_OK == iclient->QueryInterface( | |
128 | __uuidof(IDebugControl), | |
129 | icontrol.to_void_ptr_ptr() | |
130 | )); | |
131 | if (!res0) { | |
132 | return; | |
133 | } | |
134 | ||
135 | const bool res1 = (S_OK == iclient->AttachProcess( | |
136 | 0, | |
137 | ::GetCurrentProcessId(), | |
138 | DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND | |
139 | )); | |
140 | if (!res1) { | |
141 | return; | |
142 | } | |
143 | ||
144 | if (S_OK != icontrol->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) { | |
145 | return; | |
146 | } | |
147 | ||
148 | // No cheking: QueryInterface sets the output parameter to NULL in case of error. | |
149 | iclient->QueryInterface(__uuidof(IDebugSymbols), idebug.to_void_ptr_ptr()); | |
150 | } | |
151 | ||
152 | #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED | |
153 | ||
154 | boost::stacktrace::detail::com_global_initer com_; | |
155 | com_holder< ::IDebugSymbols> idebug_; | |
156 | public: | |
157 | debugging_symbols() BOOST_NOEXCEPT | |
158 | : com_() | |
159 | , idebug_(com_) | |
160 | { | |
161 | try_init_com(idebug_, com_); | |
162 | } | |
163 | ||
164 | #else | |
165 | ||
166 | #ifdef BOOST_NO_CXX11_THREAD_LOCAL | |
167 | # error Your compiler does not support C++11 thread_local storage. It`s impossible to build with BOOST_STACKTRACE_USE_WINDBG_CACHED. | |
168 | #endif | |
169 | ||
170 | static com_holder< ::IDebugSymbols>& get_thread_local_debug_inst() BOOST_NOEXCEPT { | |
171 | // [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether | |
172 | // or not the member function is inline. | |
173 | static thread_local boost::stacktrace::detail::com_global_initer com; | |
174 | static thread_local com_holder< ::IDebugSymbols> idebug(com); | |
175 | ||
176 | if (!idebug.is_inited()) { | |
177 | try_init_com(idebug, com); | |
178 | } | |
179 | ||
180 | return idebug; | |
181 | } | |
182 | ||
183 | com_holder< ::IDebugSymbols>& idebug_; | |
184 | public: | |
185 | debugging_symbols() BOOST_NOEXCEPT | |
186 | : idebug_( get_thread_local_debug_inst() ) | |
187 | {} | |
188 | ||
189 | #endif // #ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED | |
190 | ||
191 | bool is_inited() const BOOST_NOEXCEPT { | |
192 | return idebug_.is_inited(); | |
193 | } | |
194 | ||
195 | std::string get_name_impl(const void* addr, std::string* module_name = 0) const { | |
196 | std::string result; | |
197 | if (!is_inited()) { | |
198 | return result; | |
199 | } | |
200 | const ULONG64 offset = reinterpret_cast<ULONG64>(addr); | |
201 | ||
202 | char name[256]; | |
203 | name[0] = '\0'; | |
204 | ULONG size = 0; | |
205 | bool res = (S_OK == idebug_->GetNameByOffset( | |
206 | offset, | |
207 | name, | |
208 | sizeof(name), | |
209 | &size, | |
210 | 0 | |
211 | )); | |
212 | ||
213 | if (!res && size != 0) { | |
214 | result.resize(size); | |
215 | res = (S_OK == idebug_->GetNameByOffset( | |
216 | offset, | |
217 | &result[0], | |
218 | static_cast<ULONG>(result.size()), | |
219 | &size, | |
220 | 0 | |
221 | )); | |
222 | } else if (res) { | |
223 | result = name; | |
224 | } | |
225 | ||
226 | if (!res) { | |
227 | result.clear(); | |
228 | return result; | |
229 | } | |
230 | ||
231 | const std::size_t delimiter = result.find_first_of('!'); | |
232 | if (module_name) { | |
233 | *module_name = result.substr(0, delimiter); | |
234 | } | |
235 | ||
236 | if (delimiter == std::string::npos) { | |
237 | // If 'delimiter' is equal to 'std::string::npos' then we have only module name. | |
238 | result.clear(); | |
239 | return result; | |
240 | } | |
241 | ||
242 | result = minwg_demangling_workaround( | |
243 | result.substr(delimiter + 1) | |
244 | ); | |
245 | ||
246 | return result; | |
247 | } | |
248 | ||
249 | std::size_t get_line_impl(const void* addr) const BOOST_NOEXCEPT { | |
250 | ULONG result = 0; | |
251 | if (!is_inited()) { | |
252 | return result; | |
253 | } | |
254 | ||
255 | const bool is_ok = (S_OK == idebug_->GetLineByOffset( | |
256 | reinterpret_cast<ULONG64>(addr), | |
257 | &result, | |
258 | 0, | |
259 | 0, | |
260 | 0, | |
261 | 0 | |
262 | )); | |
263 | ||
264 | return (is_ok ? result : 0); | |
265 | } | |
266 | ||
267 | std::pair<std::string, std::size_t> get_source_file_line_impl(const void* addr) const { | |
268 | std::pair<std::string, std::size_t> result; | |
269 | if (!is_inited()) { | |
270 | return result; | |
271 | } | |
272 | const ULONG64 offset = reinterpret_cast<ULONG64>(addr); | |
273 | ||
274 | char name[256]; | |
275 | name[0] = 0; | |
276 | ULONG size = 0; | |
277 | ULONG line_num = 0; | |
278 | bool res = (S_OK == idebug_->GetLineByOffset( | |
279 | offset, | |
280 | &line_num, | |
281 | name, | |
282 | sizeof(name), | |
283 | &size, | |
284 | 0 | |
285 | )); | |
286 | ||
287 | if (res) { | |
288 | result.first = name; | |
289 | result.second = line_num; | |
290 | return result; | |
291 | } | |
292 | ||
293 | if (!res && size == 0) { | |
294 | return result; | |
295 | } | |
296 | ||
297 | result.first.resize(size); | |
298 | res = (S_OK == idebug_->GetLineByOffset( | |
299 | offset, | |
300 | &line_num, | |
301 | &result.first[0], | |
302 | static_cast<ULONG>(result.first.size()), | |
303 | &size, | |
304 | 0 | |
305 | )); | |
306 | result.second = line_num; | |
307 | ||
308 | if (!res) { | |
309 | result.first.clear(); | |
310 | result.second = 0; | |
311 | } | |
312 | ||
313 | return result; | |
314 | } | |
315 | ||
316 | void to_string_impl(const void* addr, std::string& res) const { | |
317 | if (!is_inited()) { | |
318 | return; | |
319 | } | |
320 | ||
321 | std::string module_name; | |
322 | std::string name = this->get_name_impl(addr, &module_name); | |
323 | if (!name.empty()) { | |
324 | res += name; | |
325 | } else { | |
326 | res += to_hex_array(addr).data(); | |
327 | } | |
328 | ||
329 | std::pair<std::string, std::size_t> source_line = this->get_source_file_line_impl(addr); | |
330 | if (!source_line.first.empty() && source_line.second) { | |
331 | res += " at "; | |
332 | res += source_line.first; | |
333 | res += ':'; | |
334 | res += boost::lexical_cast<boost::array<char, 40> >(source_line.second).data(); | |
335 | } else if (!module_name.empty()) { | |
336 | res += " in "; | |
337 | res += module_name; | |
338 | } | |
339 | } | |
340 | }; | |
341 | ||
342 | std::string to_string(const frame* frames, std::size_t size) { | |
343 | boost::stacktrace::detail::debugging_symbols idebug; | |
344 | if (!idebug.is_inited()) { | |
345 | return std::string(); | |
346 | } | |
347 | ||
348 | std::string res; | |
349 | res.reserve(64 * size); | |
350 | for (std::size_t i = 0; i < size; ++i) { | |
351 | if (i < 10) { | |
352 | res += ' '; | |
353 | } | |
354 | res += boost::lexical_cast<boost::array<char, 40> >(i).data(); | |
355 | res += '#'; | |
356 | res += ' '; | |
357 | idebug.to_string_impl(frames[i].address(), res); | |
358 | res += '\n'; | |
359 | } | |
360 | ||
361 | return res; | |
362 | } | |
363 | ||
364 | } // namespace detail | |
365 | ||
366 | std::string frame::name() const { | |
367 | boost::stacktrace::detail::debugging_symbols idebug; | |
368 | return idebug.get_name_impl(addr_); | |
369 | } | |
370 | ||
371 | ||
372 | std::string frame::source_file() const { | |
373 | boost::stacktrace::detail::debugging_symbols idebug; | |
374 | return idebug.get_source_file_line_impl(addr_).first; | |
375 | } | |
376 | ||
377 | std::size_t frame::source_line() const { | |
378 | boost::stacktrace::detail::debugging_symbols idebug; | |
379 | return idebug.get_line_impl(addr_); | |
380 | } | |
381 | ||
382 | std::string to_string(const frame& f) { | |
383 | std::string res; | |
384 | ||
385 | boost::stacktrace::detail::debugging_symbols idebug; | |
386 | idebug.to_string_impl(f.address(), res); | |
387 | return res; | |
388 | } | |
389 | ||
390 | }} // namespace boost::stacktrace | |
391 | ||
392 | #endif // BOOST_STACKTRACE_DETAIL_FRAME_MSVC_IPP |