1 //
2 //  Copyright (c) 2011 Bryce Lelbach
3 //  Copyright (c) 2011-2012 Hartmut Kaiser
4 //  Copyright (c) 2010 Artyom Beilis (Tonkikh)
5 //
6 //  Distributed under the Boost Software License, Version 1.0. (See
7 //  accompanying file LICENSE_1_0.txt or copy at
8 //  http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #include <hpx/config.hpp>
12 
13 #if defined(HPX_HAVE_STACKTRACES)
14 
15 #define HPX_BACKTRACE_SOURCE
16 #include <hpx/async.hpp>
17 #include <hpx/runtime/threads/thread.hpp>
18 
19 #include <boost/config.hpp>
20 
21 #include <hpx/util/backtrace/backtrace.hpp>
22 
23 #if (defined(__linux) || defined(__APPLE__) || defined(__sun)) \
24      && (!defined(__ANDROID__) || !defined(ANDROID))
25 #define BOOST_HAVE_EXECINFO
26 #define BOOST_HAVE_DLFCN
27 #if defined(__GNUC__) && !defined(__clang__)
28 #  define BOOST_HAVE_UNWIND
29 #endif
30 #endif
31 
32 #if defined(__GNUC__) && !defined(__bgq__)
33 #define BOOST_HAVE_ABI_CXA_DEMANGLE
34 #endif
35 
36 #ifdef BOOST_HAVE_EXECINFO
37 #include <execinfo.h>
38 #endif
39 
40 #ifdef BOOST_HAVE_ABI_CXA_DEMANGLE
41 #include <cxxabi.h>
42 #endif
43 
44 #ifdef BOOST_HAVE_DLFCN
45 #include <dlfcn.h>
46 #endif
47 #ifdef BOOST_HAVE_UNWIND
48 #include <unwind.h>
49 #endif
50 #include <cstddef>
51 #include <cstdint>
52 #include <cstdlib>
53 #include <cstring>
54 #include <iomanip>
55 #include <ostream>
56 #include <sstream>
57 #include <string>
58 #include <utility>
59 #include <vector>
60 
61 #if defined(HPX_MSVC)
62 #include <windows.h>
63 #include <winbase.h>
64 #include <stdlib.h>
65 #include <dbghelp.h>
66 #endif
67 
68 namespace hpx { namespace util {
69 
70     namespace stack_trace
71     {
72 #if defined(BOOST_HAVE_EXECINFO) && defined(BOOST_HAVE_UNWIND)
73         struct trace_data
74         {
trace_datahpx::util::stack_trace::trace_data75             trace_data(void **array,std::size_t size)
76               : array_(array), size_(size), cfa_(0), count_(std::size_t(-1))
77             {}
78 
79             void **array_;      // storage for the stack trace
80             std::size_t size_;  // number of frames
81             std::uint64_t cfa_;  // canonical frame address
82             std::size_t count_;
83         };
84 
85         _Unwind_Reason_Code trace_callback(_Unwind_Context* ctx,void* ptr);
trace_callback(_Unwind_Context * ctx,void * ptr)86         _Unwind_Reason_Code trace_callback(_Unwind_Context* ctx,void* ptr)
87         {
88             if (!ptr)
89                 return _URC_NO_REASON;
90 
91             trace_data& d = *(reinterpret_cast<trace_data*>(ptr));
92 
93             // First call.
94             if (std::size_t(-1) != d.count_)
95             {
96                 // Get the instruction pointer for this frame.
97                 d.array_[d.count_] = reinterpret_cast<void *>(_Unwind_GetIP(ctx));
98 
99                 // Get the CFA.
100                 std::uint64_t cfa = _Unwind_GetCFA(ctx);
101 
102                 // Check if we're at the end of the stack.
103                 if ((0 < d.count_) &&
104                     (d.array_[d.count_ - 1] == d.array_[d.count_]) &&
105                     (cfa == d.cfa_))
106                 {
107                     return _URC_END_OF_STACK;
108                 }
109 
110                 d.cfa_ = cfa;
111             }
112 
113             if (++d.count_ == d.size_)
114                 return _URC_END_OF_STACK;
115 
116             return _URC_NO_REASON;
117         }
118 
trace(void ** array,std::size_t n)119         HPX_API_EXPORT std::size_t trace(void **array,std::size_t n)
120         {
121             trace_data d(array,n);
122 
123             if (1 <= n)
124                 _Unwind_Backtrace(trace_callback, reinterpret_cast<void*>(&d));
125 
126             if ((1 < d.count_) && d.array_[d.count_ - 1])
127                 --d.count_;
128 
129             return (std::size_t(-1) != d.count_) ? d.count_ : 0;
130         }
131 
132 #elif defined(BOOST_HAVE_EXECINFO)
133 
134         HPX_API_EXPORT std::size_t trace(void **array,std::size_t n)
135         {
136             return :: backtrace(array,n);
137         }
138 
139 #elif defined(HPX_MSVC)
140 
141         HPX_API_EXPORT std::size_t trace(void **array,std::size_t n)
142         {
143 #if _WIN32_WINNT < 0x0600
144             // for Windows XP/Windows Server 2003
145             if(n>=63)
146                 n=62;
147 #endif
148             return RtlCaptureStackBackTrace(ULONG(0),ULONG(n),array,nullptr);
149         }
150 
151 #else
152 
153         HPX_API_EXPORT std::size_t trace(void ** /*array*/,std::size_t /*n*/)
154         {
155             return 0;
156         }
157 
158 #endif
159 
160 #if defined(BOOST_HAVE_DLFCN) && defined(BOOST_HAVE_ABI_CXA_DEMANGLE)
161 
get_symbol(void * ptr)162         HPX_API_EXPORT std::string get_symbol(void *ptr)
163         {
164             if(!ptr)
165                 return std::string();
166             std::ostringstream res;
167             res.imbue(std::locale::classic());
168             res << std::left << std::setw(sizeof(void*)*2) << std::setfill(' ')
169                 << ptr <<": ";
170             Dl_info info = {nullptr, nullptr, nullptr, nullptr};
171             if(dladdr(ptr,&info) == 0) {
172                 res << "???";
173             }
174             else {
175                 if(info.dli_sname) {
176                     int status = 0;
177                     char *demangled =
178                         abi::__cxa_demangle(info.dli_sname,nullptr,nullptr,&status);
179                     if(demangled) {
180                         res << demangled;
181                         free(demangled);
182                     }
183                     else {
184                         res << info.dli_sname;
185                     }
186                 }
187                 else {
188                     res << "???";
189                 }
190 
191                 std::ptrdiff_t offset = reinterpret_cast<char *>(ptr)
192                     - reinterpret_cast<char *>(info.dli_saddr);
193                 res << std::hex <<" + 0x" << offset ;
194 
195                 if(info.dli_fname)
196                     res << " in " << info.dli_fname;
197             }
198            return res.str();
199         }
200 
get_symbols(void * const * addresses,std::size_t size)201         HPX_API_EXPORT std::string get_symbols(void *const *addresses,
202             std::size_t size)
203         {
204             std::string res = std::to_string(size)
205                 + ((1==size)?" frame:":" frames:");
206             for(std::size_t i=0;i<size;i++) {
207                 std::string tmp = get_symbol(addresses[i]);
208                 if(!tmp.empty()) {
209                     res+='\n';
210                     res+=tmp;
211                 }
212             }
213             return res;
214         }
write_symbols(void * const * addresses,std::size_t size,std::ostream & out)215         HPX_API_EXPORT void write_symbols(void *const *addresses,
216             std::size_t size,std::ostream &out)
217         {
218             out << size << ((1==size)?" frame:":" frames:");
219             for(std::size_t i=0;i<size;i++) {
220                 std::string tmp = get_symbol(addresses[i]);
221                 if(!tmp.empty()) {
222                     out << '\n' << tmp;
223                 }
224             }
225             out << std::flush;
226         }
227 
228 #elif defined(BOOST_HAVE_EXECINFO)
229 
get_symbol(void * address)230         HPX_API_EXPORT std::string get_symbol(void *address)
231         {
232             char ** ptr = backtrace_symbols(&address,1);
233             try {
234                 if(ptr == nullptr)
235                     return std::string();
236                 std::string res = ptr[0];
237                 free(ptr);
238                 ptr = nullptr;
239                 return res;
240             }
241             catch(...) {
242                 free(ptr);
243                 throw;
244             }
245         }
246 
get_symbols(void * const * address,std::size_t size)247         HPX_API_EXPORT std::string get_symbols(void * const *address,
248             std::size_t size)
249         {
250             char ** ptr = backtrace_symbols(address,size);
251             try {
252                 if(ptr==nullptr)
253                     return std::string();
254                 std::string res = std::to_string(size)
255                     + ((1==size)?" frame:":" frames:");
256                 for(std::size_t i=0;i<size;i++) {
257                     res+='\n';
258                     res+=ptr[i];
259                 }
260                 free(ptr);
261                 ptr = nullptr;
262                 return res;
263             }
264             catch(...) {
265                 free(ptr);
266                 throw;
267             }
268         }
269 
write_symbols(void * const * addresses,std::size_t size,std::ostream & out)270         HPX_API_EXPORT void write_symbols(void *const *addresses,std::size_t size,
271             std::ostream &out)
272         {
273             char ** ptr = backtrace_symbols(addresses,size);
274             out << size << ((1==size)?" frame:":" frames:");
275             try {
276                 if(ptr==nullptr)
277                     return;
278                 for(std::size_t i=0;i<size;i++)
279                     out << '\n' << ptr[i];
280                 free(ptr);
281                 ptr = nullptr;
282                 out << std::flush;
283             }
284             catch(...) {
285                 free(ptr);
286                 throw;
287             }
288         }
289 
290 #elif defined(HPX_MSVC)
291 
292         namespace {
293             HANDLE hProcess = nullptr;
294             bool syms_ready = false;
295 
init()296             void init()
297             {
298                 if(hProcess == nullptr) {
299                     hProcess = GetCurrentProcess();
300                     SymSetOptions(SYMOPT_DEFERRED_LOADS);
301 
302                     if (SymInitialize(hProcess, nullptr, TRUE))
303                     {
304                         syms_ready = true;
305                     }
306                 }
307             }
308         }
309 
get_symbol(void * ptr)310         HPX_API_EXPORT std::string get_symbol(void *ptr)
311         {
312             if(ptr==nullptr)
313                 return std::string();
314             init();
315             std::ostringstream ss;
316             ss << std::left << std::setw(sizeof(void*)*2) << std::setfill(' ') << ptr;
317             if(syms_ready) {
318                 DWORD64  dwDisplacement = 0;
319                 DWORD64  dwAddress = (DWORD64)ptr;
320 
321                 std::vector<char> buffer(sizeof(SYMBOL_INFO) + MAX_SYM_NAME);
322                 PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)&buffer.front();
323 
324                 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
325                 pSymbol->MaxNameLen = MAX_SYM_NAME;
326 
327                 if (SymFromAddr(hProcess, dwAddress, &dwDisplacement, pSymbol))
328                 {
329                     ss <<": " << pSymbol->Name << std::hex << " +0x" << dwDisplacement;
330                 }
331                 else
332                 {
333                     ss << ": ???";
334                 }
335             }
336             return ss.str();
337         }
338 
get_symbols(void * const * addresses,std::size_t size)339         HPX_API_EXPORT std::string get_symbols(void *const *addresses,
340             std::size_t size)
341         {
342             std::string res = std::to_string(size)
343                 + ((1==size)?" frame:":" frames:");
344             for(std::size_t i=0;i<size;i++) {
345                 std::string tmp = get_symbol(addresses[i]);
346                 if(!tmp.empty()) {
347                     res+='\n';
348                     res+=tmp;
349                 }
350             }
351             return res;
352         }
353 
write_symbols(void * const * addresses,std::size_t size,std::ostream & out)354         HPX_API_EXPORT void write_symbols(void *const *addresses,
355             std::size_t size,std::ostream &out)
356         {
357             out << size << ((1==size)?" frame:":" frames:"); //-V128
358             for(std::size_t i=0;i<size;i++) {
359                 std::string tmp = get_symbol(addresses[i]);
360                 if(!tmp.empty()) {
361                     out << '\n' << tmp;
362                 }
363             }
364             out << std::flush;
365         }
366 
367 #else
368 
get_symbol(void * ptr)369         HPX_API_EXPORT std::string get_symbol(void *ptr)
370         {
371             if(!ptr)
372                 return std::string();
373             std::ostringstream res;
374             res.imbue(std::locale::classic());
375             res << std::left << std::setw(sizeof(void*)*2) << std::setfill(' ') << ptr;
376             return res.str();
377         }
378 
get_symbols(void * const * ptrs,std::size_t size)379         HPX_API_EXPORT std::string get_symbols(void *const *ptrs,std::size_t size)
380         {
381             if(!ptrs)
382                 return std::string();
383             std::ostringstream res;
384             res.imbue(std::locale::classic());
385             write_symbols(ptrs,size,res);
386             return res.str();
387         }
388 
write_symbols(void * const * addresses,std::size_t size,std::ostream & out)389         HPX_API_EXPORT void write_symbols(void *const *addresses,
390             std::size_t size,std::ostream &out)
391         {
392             out << size << ((1 == size)?" frame:":" frames:"); //-V128
393             for(std::size_t i=0;i<size;i++) {
394                 if(addresses[i]!=nullptr)
395                     out << '\n' << std::left << std::setw(sizeof(void*)*2)
396                     << std::setfill(' ') << addresses[i];
397             }
398             out << std::flush;
399         }
400 
401 #endif
402     } // stack_trace
403 
trace_on_new_stack() const404     std::string backtrace::trace_on_new_stack() const
405     {
406         if(frames_.empty())
407             return std::string();
408         if (nullptr == threads::get_self_ptr())
409             return trace();
410 
411         lcos::local::futures_factory<std::string()> p(
412             util::bind(stack_trace::get_symbols, &frames_.front(), frames_.size()));
413 
414         error_code ec(lightweight);
415         threads::thread_id_type tid = p.apply(
416             launch::fork, threads::thread_priority_default,
417             threads::thread_stacksize_medium,
418             threads::thread_schedule_hint(),
419             ec);
420         if (ec) return "<couldn't retrieve stack backtrace>";
421 
422         // make sure this thread is executed last
423         hpx::this_thread::yield_to(thread::id(std::move(tid)));
424 
425         return p.get_future().get(ec);
426     }
427 }} // hpx::util
428 
429 #endif
430 
431