blob: 10cfa44e4f541d343a8d9ac248decc58f99bdc7c [file] [log] [blame]
// 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