static zstring_view demangler(char *stack_memory, size_t stack_memory_size, zstring_view name) {
if (!has_demangler) {
return name;
}
assert(demangler_path != "");
int channel[2];
if (pipe(channel) < 0) {
writeLogfSS(Error, "Can't create pipe %d", errno);
has_demangler = false;
return name;
}
scope_guard {
close(channel[0]);
close(channel[1]);
};
pid_t pid = fork();
if (pid < -1) {
writeLogfSS(Error, "Can't perform fork %d", errno);
has_demangler = false;
return name;
}
if (pid == 0) {
/* child */
CHECKED_SS(dup2(channel[1], STDOUT_FILENO));
char const *argv[] = { demangler_path.c_str(), name.data(), nullptr };
execv(demangler_path.c_str(), const_cast<char **>(argv));
_exit(EXIT_FAILURE);
}
close(channel[1]);
channel[1] = -1;
scope_guard {
if (::waitpid(pid, nullptr, WNOHANG) == 0) {
kill(pid, 9);
::waitpid(pid, nullptr, WNOHANG);
}
};
pollfd pfd[1] = {};
pfd[0].fd = channel[0];
pfd[0].revents = 0;
pfd[0].events = POLLIN;
struct timespec start_time;
if (clock_gettime(CLOCK_MONOTONIC, &start_time) < 0) {
writeLogfSS(Error, "Can't perform clock_gettime %d", errno);
has_demangler = false;
return name;
}
struct timespec current_time;
constexpr int timeout = 100;
int time_wait = timeout;
size_t size = stack_memory_size;
size_t offset = 0;
for (;;) {
int ret = ::poll(pfd, 1, time_wait);
if (ret == 0) {
time_wait = 0;
break;
}
if (ret < 0) {
writeLogfSS(Error, "Can't perform poll %d", errno);
has_demangler = false;
return name;
}
int n = ::read(channel[0], stack_memory + offset, size);
if (n == 0) {
break;
}
if (n < 0) {
writeLogfSS(Error, "Can't perform read %d", errno);
has_demangler = false;
return name;
}
offset += n;
size -= n;
if (clock_gettime(CLOCK_MONOTONIC, ¤t_time) < 0) {
writeLogfSS(Error, "Can't perform clock_gettime %d", errno);
has_demangler = false;
return name;
}
int diff_ms = diff_in_ms(start_time, current_time);
time_wait = (timeout > diff_ms)? timeout - diff_ms : 0;
if (time_wait == 0) {
break;
}
}
if (time_wait == 0) {
return name;
}
return stack_memory;
}
void libbacktrace_symbol_callback(void *data, uintptr_t pc,
const char *symname,
uintptr_t symval,
uintptr_t symsize) {
auto frame_additional_data = reinterpret_cast<frame_data *>(data);
char memory[4096];
auto symbol = demangler(memory, sizeof(memory), symname);
writeLogfSS(Alert, "%d# %s+0x%lx", frame_additional_data->frame_number, symbol.data(), pc - symval + 1);
}
inline int libbacktrace_full_callback(void *data, uintptr_t pc,
const char *filename, int lineno,
const char *function) {
auto frame_additional_data = reinterpret_cast<frame_data *>(data);
if (filename) {
char memory[4096];
auto demangled_function = demangler(memory, sizeof(memory), function);
writeLogfSS(Alert, "%d# %s at %s:%d", frame_additional_data->frame_number, demangled_function.data(), filename, lineno);
} else if (function) {
char memory[4096];
auto demangled_function = demangler(memory, sizeof(memory), function);
writeLogfSS(Alert, "%d# %s (pc 0x%lx)", frame_additional_data->frame_number, demangled_function.data(), pc);
} else if (pc != (uintptr_t)-1) {
backtrace_syminfo(state, pc, libbacktrace_symbol_callback, libbacktrace_error_callback, data);
}
frame_additional_data->frame_number++;
return 0;
}
static void stacktrace(int _) {
writeLogfSS(Alert, "Signal 11: Segmentation fault\nbacktrace()");
assert(state);
frame_data frame_additional_data;
backtrace_full(state, 2, libbacktrace_full_callback, libbacktrace_error_callback, &frame_additional_data);
_exit(139);
}
static fs::path executable_path() noexcept {
char program_path[PATH_MAX];
ssize_t len = readlink("/proc/self/exe", program_path, sizeof(program_path)-1);
if (len != -1) {
program_path[len] = '\0';
return program_path;
}
return {};
}
void setup_backtrace_handlers() {
state = backtrace_create_state(0, 1, libbacktrace_error_callback, 0);
auto path = executable_path().parent_path();
std::error_code ec;
if (fs::exists(path / "demangler", ec)) {
has_demangler = true;
demangler_path = path / "demangler";
}
std::set_terminate([]() {
if (auto exc = std::current_exception()) {
try {
if (exc) {
std::rethrow_exception(exc);
}
} catch (const std::exception &e) {
report_exception(e, "Unhandled exception");
} catch (...) {
report_current_exception();
std::abort();
}
}
std::abort();
});
struct sigaction sasigsegv;
sasigsegv.sa_handler = &stacktrace;
sasigsegv.sa_flags = 0;
sigaction(SIGSEGV, &sasigsegv, nullptr);
}