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