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