blob: 0e6c85673a662f277e26154019d0db9d8af7e9e5 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/BackTrace.h"
#ifdef HAS_EXECINFO
#include <execinfo.h>
#include <iostream>
#include <utility>
#include <cxxabi.h>
#endif
void pull_trace(const uint8_t frames_to_skip) {
#ifdef HAS_EXECINFO
void *stackBuffer[TRACE_BUFFER_SIZE + 1];
// retrieve current stack addresses
int trace_size = backtrace(stackBuffer, TRACE_BUFFER_SIZE);
char **symboltable = backtrace_symbols(stackBuffer, trace_size);
/**
* we can skip the signal handler, call to pull_trace, and the first entry for backtrace_symbols
*/
for (int i = frames_to_skip; i < trace_size; i++) {
char *start_parenthetical = nullptr;
char *functor = nullptr;
char *stop_parenthetical = nullptr;
for (char *p = symboltable[i]; *p; ++p) {
if (*p == '(') {
start_parenthetical = p;
} else if (*p == '+') {
functor = p;
} else if (*p == ')' && functor) {
stop_parenthetical = p;
break;
}
}
bool hasFunc = start_parenthetical && functor && stop_parenthetical;
if (hasFunc && start_parenthetical < functor) {
*start_parenthetical++ = '\0';
*functor++ = '\0';
*stop_parenthetical = '\0';
/**
* Demangle the names -- this requires calling cxx api to demangle the function name.
* not sending an allocated buffer, so we'll deallocate if status is zero.
*/
int status;
auto demangled = abi::__cxa_demangle(start_parenthetical, nullptr, nullptr, &status);
if (status == 0) {
TraceResolver::getResolver().addTraceLine(symboltable[i], demangled);
free(demangled);
} else {
TraceResolver::getResolver().addTraceLine(symboltable[i], start_parenthetical);
}
} else {
TraceResolver::getResolver().addTraceLine(symboltable[i], "");
}
}
free(symboltable);
#endif
}
BackTrace TraceResolver::getBackTrace(std::string thread_name, std::thread::native_handle_type thread_handle) {
// lock so that we only perform one backtrace at a time.
#ifdef HAS_EXECINFO
std::lock_guard<std::mutex> lock(mutex_);
caller_handle_ = pthread_self();
thread_handle_ = thread_handle;
trace_ = BackTrace(std::move(thread_name));
if (0 == thread_handle_ || pthread_equal(caller_handle_, thread_handle)) {
pull_trace();
} else {
if (thread_handle_ == 0) {
return std::move(trace_);
}
emplace_handler();
if (pthread_kill(thread_handle_, SIGUSR2) != 0) {
return std::move(trace_);
}
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGUSR2);
sigsuspend(&mask);
}
#else
// even if tracing is disabled, include thread name into the trace object
trace_ = BackTrace(std::move(thread_name));
#endif
return std::move(trace_);
}
#ifdef HAS_EXECINFO
static void handler(int, siginfo_t*, void*) {
// not the intended thread
if (!pthread_equal(pthread_self(), TraceResolver::getResolver().getThreadHandle())) {
return;
}
pull_trace();
pthread_kill(TraceResolver::getResolver().getCallerHandle(), SIGUSR2);
}
#endif
void emplace_handler() {
#ifdef HAS_EXECINFO
struct sigaction sa{};
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handler;
sigaction(SIGUSR2, &sa, nullptr);
#endif
}