| // Implementation of <stacktrace> -*- C++ -*- | |
| // Copyright The GNU Toolchain Authors. | |
| // | |
| // This file is part of the GNU ISO C++ Library. This library is free | |
| // software; you can redistribute it and/or modify it under the | |
| // terms of the GNU General Public License as published by the | |
| // Free Software Foundation; either version 3. | |
| // This library is distributed in the hope that it will be useful, | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| // GNU General Public License for more details. | |
| // Under Section 7 of GPL version 3, you are granted additional | |
| // permissions described in the GCC Runtime Library Exception, version | |
| // 3.1, as published by the Free Software Foundation. | |
| // You should have received a copy of the GNU General Public License and | |
| // a copy of the GCC Runtime Library Exception along with this program; | |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
| // <http://www.gnu.org/licenses/>. | |
| #include <stacktrace> | |
| #include <cstdlib> | |
| #ifdef __cpp_lib_stacktrace // C++ >= 23 && hosted && HAVE_STACKTRACE | |
| struct __glibcxx_backtrace_state; | |
| extern "C" | |
| { | |
| __glibcxx_backtrace_state* | |
| __glibcxx_backtrace_create_state(const char*, int, | |
| void(*)(void*, const char*, int), | |
| void*); | |
| int | |
| __glibcxx_backtrace_simple(__glibcxx_backtrace_state*, int, | |
| int (*) (void*, __UINTPTR_TYPE__), | |
| void(*)(void*, const char*, int), | |
| void*); | |
| int | |
| __glibcxx_backtrace_pcinfo(__glibcxx_backtrace_state*, __UINTPTR_TYPE__, | |
| int (*)(void*, __UINTPTR_TYPE__, | |
| const char*, int, const char*), | |
| void(*)(void*, const char*, int), | |
| void*); | |
| int | |
| __glibcxx_backtrace_syminfo(__glibcxx_backtrace_state*, __UINTPTR_TYPE__ addr, | |
| void (*) (void*, __UINTPTR_TYPE__, const char*, | |
| __UINTPTR_TYPE__, __UINTPTR_TYPE__), | |
| void(*)(void*, const char*, int), | |
| void*); | |
| } | |
| namespace __cxxabiv1 | |
| { | |
| extern "C" char* | |
| __cxa_demangle(const char* mangled_name, char* output_buffer, size_t* length, | |
| int* status); | |
| } | |
| namespace std | |
| { | |
| namespace | |
| { | |
| char* | |
| demangle(const char* name) | |
| { | |
| int status; | |
| char* str = __cxxabiv1::__cxa_demangle(name, nullptr, nullptr, &status); | |
| if (status == 0) | |
| return str; | |
| else | |
| { | |
| std::free(str); | |
| return nullptr; | |
| } | |
| } | |
| void | |
| err_handler(void*, const char*, int) | |
| { } | |
| __glibcxx_backtrace_state* | |
| init() | |
| { | |
| #if __GTHREADS && ! defined(__cpp_threadsafe_static_init) | |
| # warning "std::stacktrace initialization will not be thread-safe" | |
| #endif | |
| static __glibcxx_backtrace_state* state | |
| = __glibcxx_backtrace_create_state(nullptr, 1, err_handler, nullptr); | |
| return state; | |
| } | |
| } | |
| void | |
| stacktrace_entry::_Info::_M_set_file(const char* filename) | |
| { | |
| if (filename && _M_file) | |
| _M_set(_M_file, filename); | |
| } | |
| void | |
| stacktrace_entry::_Info::_M_set_desc(const char* function) | |
| { | |
| if (function && _M_desc) | |
| { | |
| if (char* s = demangle(function)) | |
| { | |
| _M_set(_M_desc, s); | |
| std::free(s); | |
| } | |
| else | |
| _M_set(_M_desc, function); | |
| } | |
| } | |
| #pragma GCC diagnostic push | |
| // The closure types below don't escape so we don't care about their mangling. | |
| #pragma GCC diagnostic ignored "-Wabi" | |
| bool | |
| stacktrace_entry::_Info::_M_populate(native_handle_type pc) | |
| { | |
| auto cb = [](void* self, uintptr_t, const char* filename, int lineno, | |
| const char* function) -> int | |
| { | |
| auto& info = *static_cast<_Info*>(self); | |
| info._M_set_desc(function); | |
| info._M_set_file(filename); | |
| if (info._M_line) | |
| *info._M_line = lineno; | |
| return function != nullptr; | |
| }; | |
| const auto state = init(); | |
| if (::__glibcxx_backtrace_pcinfo(state, pc, +cb, err_handler, this)) | |
| return true; | |
| // If we get here then backtrace_pcinfo did not give us a function name. | |
| // If the caller wanted a function name, try again using backtrace_syminfo. | |
| if (_M_desc) | |
| { | |
| auto cb2 = [](void* self, uintptr_t, const char* symname, | |
| uintptr_t, uintptr_t) | |
| { | |
| static_cast<_Info*>(self)->_M_set_desc(symname); | |
| }; | |
| if (::__glibcxx_backtrace_syminfo(state, pc, +cb2, err_handler, this)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| #pragma GCC diagnostic pop | |
| // Ensure no tail-call optimization, so that this frame isn't reused for the | |
| // backtrace_simple call, so that the number of frames to skip doesn't vary. | |
| [[gnu::optimize("no-optimize-sibling-calls")]] | |
| int | |
| __stacktrace_impl::_S_current(int (*cb) (void*, __UINTPTR_TYPE__), void* obj, | |
| int skip) | |
| { | |
| const auto state = init(); | |
| // skip+2 because we don't want this function or its caller to be included. | |
| int r = ::__glibcxx_backtrace_simple(state, skip + 2, cb, err_handler, obj); | |
| // Could also use this to prevent tail-call optim: __asm ("" : "+g" (r)); | |
| return r; | |
| } | |
| } | |
| #endif |