]>
Commit | Line | Data |
---|---|---|
1e59de90 | 1 | // Copyright Antony Polukhin, 2016-2022. |
b32b8144 FG |
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_LIBBACKTRACE_IMPLS_HPP | |
8 | #define BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP | |
9 | ||
10 | #include <boost/config.hpp> | |
11 | #ifdef BOOST_HAS_PRAGMA_ONCE | |
12 | # pragma once | |
13 | #endif | |
14 | ||
15 | #include <boost/stacktrace/detail/to_hex_array.hpp> | |
92f5a8d4 | 16 | #include <boost/stacktrace/detail/to_dec_array.hpp> |
b32b8144 FG |
17 | #include <boost/stacktrace/detail/location_from_symbol.hpp> |
18 | #include <boost/core/demangle.hpp> | |
b32b8144 | 19 | |
92f5a8d4 TL |
20 | #ifdef BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE |
21 | # include BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE | |
22 | #else | |
23 | # include <backtrace.h> | |
24 | #endif | |
b32b8144 FG |
25 | |
26 | namespace boost { namespace stacktrace { namespace detail { | |
27 | ||
28 | ||
29 | struct pc_data { | |
30 | std::string* function; | |
31 | std::string* filename; | |
32 | std::size_t line; | |
33 | }; | |
34 | ||
92f5a8d4 TL |
35 | inline void libbacktrace_syminfo_callback(void *data, uintptr_t /*pc*/, const char *symname, uintptr_t /*symval*/, uintptr_t /*symsize*/) { |
36 | pc_data& d = *static_cast<pc_data*>(data); | |
37 | if (d.function && symname) { | |
38 | *d.function = symname; | |
39 | } | |
40 | } | |
41 | ||
42 | // Old versions of libbacktrace have different signature for the callback | |
43 | inline void libbacktrace_syminfo_callback(void *data, uintptr_t pc, const char *symname, uintptr_t symval) { | |
44 | boost::stacktrace::detail::libbacktrace_syminfo_callback(data, pc, symname, symval, 0); | |
45 | } | |
46 | ||
b32b8144 FG |
47 | inline int libbacktrace_full_callback(void *data, uintptr_t /*pc*/, const char *filename, int lineno, const char *function) { |
48 | pc_data& d = *static_cast<pc_data*>(data); | |
49 | if (d.filename && filename) { | |
50 | *d.filename = filename; | |
51 | } | |
52 | if (d.function && function) { | |
53 | *d.function = function; | |
54 | } | |
55 | d.line = lineno; | |
56 | return 0; | |
57 | } | |
58 | ||
59 | inline void libbacktrace_error_callback(void* /*data*/, const char* /*msg*/, int /*errnum*/) BOOST_NOEXCEPT { | |
60 | // Do nothing, just return. | |
61 | } | |
62 | ||
92f5a8d4 TL |
63 | // Not async-signal-safe, so this method is not called from async-safe functions. |
64 | // | |
65 | // This function is not async signal safe because: | |
66 | // * Dynamic initialization of a block-scope variable with static storage duration could lock a mutex | |
67 | // * No guarantees on `backtrace_create_state` function. | |
68 | // | |
69 | // Currently `backtrace_create_state` can not detect file name on Windows https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82543 | |
70 | // That's why we provide a `prog_location` here. | |
71 | BOOST_SYMBOL_VISIBLE inline ::backtrace_state* construct_state(const program_location& prog_location) BOOST_NOEXCEPT { | |
72 | // [dcl.inline]: A static local variable in an inline function with external linkage always refers to the same object. | |
b32b8144 | 73 | |
92f5a8d4 | 74 | // TODO: The most obvious solution: |
b32b8144 | 75 | // |
92f5a8d4 TL |
76 | //static ::backtrace_state* state = ::backtrace_create_state( |
77 | // prog_location.name(), | |
78 | // 1, // allow safe concurrent usage of the same state | |
79 | // boost::stacktrace::detail::libbacktrace_error_callback, | |
80 | // 0 // pointer to data that will be passed to callback | |
81 | //); | |
82 | // | |
83 | // | |
84 | // Unfortunately, that solution segfaults when `construct_state()` function is in .so file | |
85 | // and multiple threads concurrently work with state. | |
b32b8144 | 86 | |
b32b8144 | 87 | |
92f5a8d4 TL |
88 | #ifndef BOOST_HAS_THREADS |
89 | static | |
90 | #else | |
b32b8144 | 91 | |
92f5a8d4 TL |
92 | // Result of `construct_state()` invocation is not stored by the callers, so `thread_local` |
93 | // gives a single `state` per thread and that state is not shared between threads in any way. | |
94 | ||
95 | # ifndef BOOST_NO_CXX11_THREAD_LOCAL | |
96 | thread_local | |
1e59de90 | 97 | # elif defined(__GNUC__) && !defined(__clang__) |
92f5a8d4 TL |
98 | static __thread |
99 | # else | |
100 | /* just a local variable */ | |
101 | # endif | |
102 | ||
103 | #endif | |
104 | ::backtrace_state* state = ::backtrace_create_state( | |
105 | prog_location.name(), | |
106 | 0, | |
107 | boost::stacktrace::detail::libbacktrace_error_callback, | |
108 | 0 | |
109 | ); | |
b32b8144 | 110 | return state; |
b32b8144 FG |
111 | } |
112 | ||
113 | struct to_string_using_backtrace { | |
114 | std::string res; | |
115 | boost::stacktrace::detail::program_location prog_location; | |
116 | ::backtrace_state* state; | |
117 | std::string filename; | |
118 | std::size_t line; | |
119 | ||
120 | void prepare_function_name(const void* addr) { | |
121 | boost::stacktrace::detail::pc_data data = {&res, &filename, 0}; | |
122 | if (state) { | |
123 | ::backtrace_pcinfo( | |
124 | state, | |
125 | reinterpret_cast<uintptr_t>(addr), | |
126 | boost::stacktrace::detail::libbacktrace_full_callback, | |
127 | boost::stacktrace::detail::libbacktrace_error_callback, | |
128 | &data | |
92f5a8d4 TL |
129 | ) |
130 | || | |
131 | ::backtrace_syminfo( | |
132 | state, | |
133 | reinterpret_cast<uintptr_t>(addr), | |
134 | boost::stacktrace::detail::libbacktrace_syminfo_callback, | |
135 | boost::stacktrace::detail::libbacktrace_error_callback, | |
136 | &data | |
b32b8144 FG |
137 | ); |
138 | } | |
139 | line = data.line; | |
140 | } | |
141 | ||
142 | bool prepare_source_location(const void* /*addr*/) { | |
143 | if (filename.empty() || !line) { | |
144 | return false; | |
145 | } | |
146 | ||
147 | res += " at "; | |
148 | res += filename; | |
149 | res += ':'; | |
92f5a8d4 | 150 | res += boost::stacktrace::detail::to_dec_array(line).data(); |
b32b8144 FG |
151 | return true; |
152 | } | |
153 | ||
154 | to_string_using_backtrace() BOOST_NOEXCEPT { | |
155 | state = boost::stacktrace::detail::construct_state(prog_location); | |
156 | } | |
157 | }; | |
158 | ||
159 | template <class Base> class to_string_impl_base; | |
160 | typedef to_string_impl_base<to_string_using_backtrace> to_string_impl; | |
161 | ||
162 | inline std::string name_impl(const void* addr) { | |
163 | std::string res; | |
164 | ||
165 | boost::stacktrace::detail::program_location prog_location; | |
166 | ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location); | |
167 | ||
168 | boost::stacktrace::detail::pc_data data = {&res, 0, 0}; | |
169 | if (state) { | |
170 | ::backtrace_pcinfo( | |
171 | state, | |
172 | reinterpret_cast<uintptr_t>(addr), | |
173 | boost::stacktrace::detail::libbacktrace_full_callback, | |
174 | boost::stacktrace::detail::libbacktrace_error_callback, | |
175 | &data | |
92f5a8d4 TL |
176 | ) |
177 | || | |
178 | ::backtrace_syminfo( | |
179 | state, | |
180 | reinterpret_cast<uintptr_t>(addr), | |
181 | boost::stacktrace::detail::libbacktrace_syminfo_callback, | |
182 | boost::stacktrace::detail::libbacktrace_error_callback, | |
183 | &data | |
b32b8144 FG |
184 | ); |
185 | } | |
186 | if (!res.empty()) { | |
187 | res = boost::core::demangle(res.c_str()); | |
188 | } | |
189 | ||
190 | return res; | |
191 | } | |
192 | ||
193 | } // namespace detail | |
194 | ||
195 | std::string frame::source_file() const { | |
196 | std::string res; | |
197 | ||
92f5a8d4 TL |
198 | if (!addr_) { |
199 | return res; | |
200 | } | |
201 | ||
b32b8144 FG |
202 | boost::stacktrace::detail::program_location prog_location; |
203 | ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location); | |
204 | ||
205 | boost::stacktrace::detail::pc_data data = {0, &res, 0}; | |
206 | if (state) { | |
207 | ::backtrace_pcinfo( | |
208 | state, | |
209 | reinterpret_cast<uintptr_t>(addr_), | |
210 | boost::stacktrace::detail::libbacktrace_full_callback, | |
211 | boost::stacktrace::detail::libbacktrace_error_callback, | |
212 | &data | |
213 | ); | |
214 | } | |
215 | ||
216 | return res; | |
217 | } | |
218 | ||
219 | std::size_t frame::source_line() const { | |
92f5a8d4 TL |
220 | if (!addr_) { |
221 | return 0; | |
222 | } | |
223 | ||
b32b8144 FG |
224 | boost::stacktrace::detail::program_location prog_location; |
225 | ::backtrace_state* state = boost::stacktrace::detail::construct_state(prog_location); | |
226 | ||
227 | boost::stacktrace::detail::pc_data data = {0, 0, 0}; | |
228 | if (state) { | |
229 | ::backtrace_pcinfo( | |
230 | state, | |
231 | reinterpret_cast<uintptr_t>(addr_), | |
232 | boost::stacktrace::detail::libbacktrace_full_callback, | |
233 | boost::stacktrace::detail::libbacktrace_error_callback, | |
234 | &data | |
235 | ); | |
236 | } | |
237 | ||
238 | return data.line; | |
239 | } | |
240 | ||
241 | ||
242 | }} // namespace boost::stacktrace | |
243 | ||
244 | #endif // BOOST_STACKTRACE_DETAIL_LIBBACKTRACE_IMPLS_HPP |