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 
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>
16 #include <boost/stacktrace/detail/to_dec_array.hpp>
17 #include <boost/stacktrace/detail/location_from_symbol.hpp>
18 #include <boost/core/demangle.hpp>
19 
20 #ifdef BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE
21 #   include BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE
22 #else
23 #   include <backtrace.h>
24 #endif
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 
libbacktrace_syminfo_callback(void * data,uintptr_t,const char * symname,uintptr_t,uintptr_t)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
libbacktrace_syminfo_callback(void * data,uintptr_t pc,const char * symname,uintptr_t symval)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 
libbacktrace_full_callback(void * data,uintptr_t,const char * filename,int lineno,const char * function)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 
libbacktrace_error_callback(void *,const char *,int)59 inline void libbacktrace_error_callback(void* /*data*/, const char* /*msg*/, int /*errnum*/) BOOST_NOEXCEPT {
60     // Do nothing, just return.
61 }
62 
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.
construct_state(const program_location & prog_location)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.
73 
74     // TODO: The most obvious solution:
75     //
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.
86 
87 
88 #ifndef BOOST_HAS_THREADS
89     static
90 #else
91 
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
97 #   elif defined(__GNUC__)
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     );
110     return state;
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 
prepare_function_nameboost::stacktrace::detail::to_string_using_backtrace120     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
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
137             );
138         }
139         line = data.line;
140     }
141 
prepare_source_locationboost::stacktrace::detail::to_string_using_backtrace142     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 += ':';
150         res += boost::stacktrace::detail::to_dec_array(line).data();
151         return true;
152     }
153 
to_string_using_backtraceboost::stacktrace::detail::to_string_using_backtrace154     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 
name_impl(const void * addr)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
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
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 
source_file() const195 std::string frame::source_file() const {
196     std::string res;
197 
198     if (!addr_) {
199         return res;
200     }
201 
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 
source_line() const219 std::size_t frame::source_line() const {
220     if (!addr_) {
221         return 0;
222     }
223 
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
245