|  | // | 
|  | // Copyright 2018 The Abseil Authors. | 
|  | // | 
|  | // Licensed 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 | 
|  | // | 
|  | //      https://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 "absl/debugging/failure_signal_handler.h" | 
|  |  | 
|  | #include "absl/base/config.h" | 
|  |  | 
|  | #ifdef _WIN32 | 
|  | #include <windows.h> | 
|  | #else | 
|  | #include <pthread.h> | 
|  | #include <sched.h> | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef __APPLE__ | 
|  | #include <TargetConditionals.h> | 
|  | #endif | 
|  |  | 
|  | #ifdef ABSL_HAVE_MMAP | 
|  | #include <sys/mman.h> | 
|  | #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) | 
|  | #define MAP_ANONYMOUS MAP_ANON | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | #ifdef __linux__ | 
|  | #include <sys/prctl.h> | 
|  | #endif | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <atomic> | 
|  | #include <cerrno> | 
|  | #include <csignal> | 
|  | #include <cstdio> | 
|  | #include <cstring> | 
|  | #include <ctime> | 
|  |  | 
|  | #include "absl/base/attributes.h" | 
|  | #include "absl/base/internal/raw_logging.h" | 
|  | #include "absl/base/internal/sysinfo.h" | 
|  | #include "absl/debugging/internal/examine_stack.h" | 
|  | #include "absl/debugging/stacktrace.h" | 
|  |  | 
|  | #if !defined(_WIN32) && !defined(__wasi__) | 
|  | #define ABSL_HAVE_SIGACTION | 
|  | // Apple WatchOS and TVOS don't allow sigaltstack | 
|  | // Apple macOS has sigaltstack, but using it makes backtrace() unusable. | 
|  | #if !(defined(TARGET_OS_OSX) && TARGET_OS_OSX) &&     \ | 
|  | !(defined(TARGET_OS_WATCH) && TARGET_OS_WATCH) && \ | 
|  | !(defined(TARGET_OS_TV) && TARGET_OS_TV) && !defined(__QNX__) | 
|  | #define ABSL_HAVE_SIGALTSTACK | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | // ABSL_HAVE_PTHREAD_CPU_NUMBER_NP | 
|  | // | 
|  | // Checks whether pthread_cpu_number_np is available. | 
|  | #ifdef ABSL_HAVE_PTHREAD_CPU_NUMBER_NP | 
|  | #error ABSL_HAVE_PTHREAD_CPU_NUMBER_NP cannot be directly set | 
|  | #elif defined(__APPLE__) && defined(__has_include) &&              \ | 
|  | ((defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) &&    \ | 
|  | __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 110000) ||  \ | 
|  | (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) &&   \ | 
|  | __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 140200) || \ | 
|  | (defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__) &&    \ | 
|  | __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 70100) ||   \ | 
|  | (defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__) &&       \ | 
|  | __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 140200)) | 
|  | #define ABSL_HAVE_PTHREAD_CPU_NUMBER_NP 1 | 
|  | #endif | 
|  |  | 
|  | namespace absl { | 
|  | ABSL_NAMESPACE_BEGIN | 
|  |  | 
|  | ABSL_CONST_INIT static FailureSignalHandlerOptions fsh_options; | 
|  |  | 
|  | // Resets the signal handler for signo to the default action for that | 
|  | // signal, then raises the signal. | 
|  | static void RaiseToDefaultHandler(int signo) { | 
|  | signal(signo, SIG_DFL); | 
|  | raise(signo); | 
|  | } | 
|  |  | 
|  | struct FailureSignalData { | 
|  | const int signo; | 
|  | const char* const as_string; | 
|  | #ifdef ABSL_HAVE_SIGACTION | 
|  | struct sigaction previous_action; | 
|  | // StructSigaction is used to silence -Wmissing-field-initializers. | 
|  | using StructSigaction = struct sigaction; | 
|  | #define FSD_PREVIOUS_INIT FailureSignalData::StructSigaction() | 
|  | #else | 
|  | void (*previous_handler)(int); | 
|  | #define FSD_PREVIOUS_INIT SIG_DFL | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | ABSL_CONST_INIT static FailureSignalData failure_signal_data[] = { | 
|  | {SIGSEGV, "SIGSEGV", FSD_PREVIOUS_INIT}, | 
|  | {SIGILL, "SIGILL", FSD_PREVIOUS_INIT}, | 
|  | {SIGFPE, "SIGFPE", FSD_PREVIOUS_INIT}, | 
|  | {SIGABRT, "SIGABRT", FSD_PREVIOUS_INIT}, | 
|  | {SIGTERM, "SIGTERM", FSD_PREVIOUS_INIT}, | 
|  | #ifndef _WIN32 | 
|  | {SIGBUS, "SIGBUS", FSD_PREVIOUS_INIT}, | 
|  | {SIGTRAP, "SIGTRAP", FSD_PREVIOUS_INIT}, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | #undef FSD_PREVIOUS_INIT | 
|  |  | 
|  | static void RaiseToPreviousHandler(int signo) { | 
|  | // Search for the previous handler. | 
|  | for (const auto& it : failure_signal_data) { | 
|  | if (it.signo == signo) { | 
|  | #ifdef ABSL_HAVE_SIGACTION | 
|  | sigaction(signo, &it.previous_action, nullptr); | 
|  | #else | 
|  | signal(signo, it.previous_handler); | 
|  | #endif | 
|  | raise(signo); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Not found, use the default handler. | 
|  | RaiseToDefaultHandler(signo); | 
|  | } | 
|  |  | 
|  | namespace debugging_internal { | 
|  |  | 
|  | const char* FailureSignalToString(int signo) { | 
|  | for (const auto& it : failure_signal_data) { | 
|  | if (it.signo == signo) { | 
|  | return it.as_string; | 
|  | } | 
|  | } | 
|  | return ""; | 
|  | } | 
|  |  | 
|  | }  // namespace debugging_internal | 
|  |  | 
|  | #ifdef ABSL_HAVE_SIGALTSTACK | 
|  |  | 
|  | static bool SetupAlternateStackOnce() { | 
|  | #if defined(__wasm__) || defined(__asjms__) | 
|  | const size_t page_mask = getpagesize() - 1; | 
|  | #else | 
|  | const size_t page_mask = static_cast<size_t>(sysconf(_SC_PAGESIZE)) - 1; | 
|  | #endif | 
|  | size_t stack_size = | 
|  | (std::max(static_cast<size_t>(SIGSTKSZ), size_t{65536}) + page_mask) & | 
|  | ~page_mask; | 
|  | #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ | 
|  | defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER) | 
|  | // Account for sanitizer instrumentation requiring additional stack space. | 
|  | stack_size *= 5; | 
|  | #endif | 
|  |  | 
|  | stack_t sigstk; | 
|  | memset(&sigstk, 0, sizeof(sigstk)); | 
|  | sigstk.ss_size = stack_size; | 
|  |  | 
|  | #ifdef ABSL_HAVE_MMAP | 
|  | #ifndef MAP_STACK | 
|  | #define MAP_STACK 0 | 
|  | #endif | 
|  | sigstk.ss_sp = mmap(nullptr, sigstk.ss_size, PROT_READ | PROT_WRITE, | 
|  | MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); | 
|  | if (sigstk.ss_sp == MAP_FAILED) { | 
|  | ABSL_RAW_LOG(FATAL, "mmap() for alternate signal stack failed"); | 
|  | } | 
|  | #else | 
|  | sigstk.ss_sp = malloc(sigstk.ss_size); | 
|  | if (sigstk.ss_sp == nullptr) { | 
|  | ABSL_RAW_LOG(FATAL, "malloc() for alternate signal stack failed"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (sigaltstack(&sigstk, nullptr) != 0) { | 
|  | ABSL_RAW_LOG(FATAL, "sigaltstack() failed with errno=%d", errno); | 
|  | } | 
|  |  | 
|  | #ifdef __linux__ | 
|  | #if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) | 
|  | // Make a best-effort attempt to name the allocated region in | 
|  | // /proc/$PID/smaps. | 
|  | // | 
|  | // The call to prctl() may fail if the kernel was not configured with the | 
|  | // CONFIG_ANON_VMA_NAME kernel option.  This is OK since the call is | 
|  | // primarily a debugging aid. | 
|  | prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, sigstk.ss_sp, sigstk.ss_size, | 
|  | "absl-signalstack"); | 
|  | #endif | 
|  | #endif  // __linux__ | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | #ifdef ABSL_HAVE_SIGACTION | 
|  |  | 
|  | // Sets up an alternate stack for signal handlers once. | 
|  | // Returns the appropriate flag for sig_action.sa_flags | 
|  | // if the system supports using an alternate stack. | 
|  | static int MaybeSetupAlternateStack() { | 
|  | #ifdef ABSL_HAVE_SIGALTSTACK | 
|  | ABSL_ATTRIBUTE_UNUSED static const bool kOnce = SetupAlternateStackOnce(); | 
|  | return SA_ONSTACK; | 
|  | #else | 
|  | return 0; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static void InstallOneFailureHandler(FailureSignalData* data, | 
|  | void (*handler)(int, siginfo_t*, void*)) { | 
|  | struct sigaction act; | 
|  | memset(&act, 0, sizeof(act)); | 
|  | sigemptyset(&act.sa_mask); | 
|  | act.sa_flags |= SA_SIGINFO; | 
|  | // SA_NODEFER is required to handle SIGABRT from | 
|  | // ImmediateAbortSignalHandler(). | 
|  | act.sa_flags |= SA_NODEFER; | 
|  | if (fsh_options.use_alternate_stack) { | 
|  | act.sa_flags |= MaybeSetupAlternateStack(); | 
|  | } | 
|  | act.sa_sigaction = handler; | 
|  | ABSL_RAW_CHECK(sigaction(data->signo, &act, &data->previous_action) == 0, | 
|  | "sigaction() failed"); | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | static void InstallOneFailureHandler(FailureSignalData* data, | 
|  | void (*handler)(int)) { | 
|  | data->previous_handler = signal(data->signo, handler); | 
|  | ABSL_RAW_CHECK(data->previous_handler != SIG_ERR, "signal() failed"); | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | static void WriteSignalMessage(int signo, int cpu, | 
|  | void (*writerfn)(const char*)) { | 
|  | char buf[96]; | 
|  | char on_cpu[32] = {0}; | 
|  | if (cpu != -1) { | 
|  | snprintf(on_cpu, sizeof(on_cpu), " on cpu %d", cpu); | 
|  | } | 
|  | const char* const signal_string = | 
|  | debugging_internal::FailureSignalToString(signo); | 
|  | if (signal_string != nullptr && signal_string[0] != '\0') { | 
|  | snprintf(buf, sizeof(buf), "*** %s received at time=%ld%s ***\n", | 
|  | signal_string, | 
|  | static_cast<long>(time(nullptr)),  // NOLINT(runtime/int) | 
|  | on_cpu); | 
|  | } else { | 
|  | snprintf(buf, sizeof(buf), "*** Signal %d received at time=%ld%s ***\n", | 
|  | signo, static_cast<long>(time(nullptr)),  // NOLINT(runtime/int) | 
|  | on_cpu); | 
|  | } | 
|  | writerfn(buf); | 
|  | } | 
|  |  | 
|  | // `void*` might not be big enough to store `void(*)(const char*)`. | 
|  | struct WriterFnStruct { | 
|  | void (*writerfn)(const char*); | 
|  | }; | 
|  |  | 
|  | // Many of the absl::debugging_internal::Dump* functions in | 
|  | // examine_stack.h take a writer function pointer that has a void* arg | 
|  | // for historical reasons. failure_signal_handler_writer only takes a | 
|  | // data pointer. This function converts between these types. | 
|  | static void WriterFnWrapper(const char* data, void* arg) { | 
|  | static_cast<WriterFnStruct*>(arg)->writerfn(data); | 
|  | } | 
|  |  | 
|  | // Convenient wrapper around DumpPCAndFrameSizesAndStackTrace() for signal | 
|  | // handlers. "noinline" so that GetStackFrames() skips the top-most stack | 
|  | // frame for this function. | 
|  | ABSL_ATTRIBUTE_NOINLINE static void WriteStackTrace( | 
|  | void* ucontext, bool symbolize_stacktrace, | 
|  | void (*writerfn)(const char*, void*), void* writerfn_arg) { | 
|  | constexpr int kNumStackFrames = 32; | 
|  | void* stack[kNumStackFrames]; | 
|  | int frame_sizes[kNumStackFrames]; | 
|  | int min_dropped_frames; | 
|  | int depth = absl::GetStackFramesWithContext( | 
|  | stack, frame_sizes, kNumStackFrames, | 
|  | 1,  // Do not include this function in stack trace. | 
|  | ucontext, &min_dropped_frames); | 
|  | absl::debugging_internal::DumpPCAndFrameSizesAndStackTrace( | 
|  | absl::debugging_internal::GetProgramCounter(ucontext), stack, frame_sizes, | 
|  | depth, min_dropped_frames, symbolize_stacktrace, writerfn, writerfn_arg); | 
|  | } | 
|  |  | 
|  | // Called by AbslFailureSignalHandler() to write the failure info. It is | 
|  | // called once with writerfn set to WriteToStderr() and then possibly | 
|  | // with writerfn set to the user provided function. | 
|  | static void WriteFailureInfo(int signo, void* ucontext, int cpu, | 
|  | void (*writerfn)(const char*)) { | 
|  | WriterFnStruct writerfn_struct{writerfn}; | 
|  | WriteSignalMessage(signo, cpu, writerfn); | 
|  | WriteStackTrace(ucontext, fsh_options.symbolize_stacktrace, WriterFnWrapper, | 
|  | &writerfn_struct); | 
|  | } | 
|  |  | 
|  | // absl::SleepFor() can't be used here since AbslInternalSleepFor() | 
|  | // may be overridden to do something that isn't async-signal-safe on | 
|  | // some platforms. | 
|  | static void PortableSleepForSeconds(int seconds) { | 
|  | #ifdef _WIN32 | 
|  | Sleep(static_cast<DWORD>(seconds * 1000)); | 
|  | #else | 
|  | struct timespec sleep_time; | 
|  | sleep_time.tv_sec = seconds; | 
|  | sleep_time.tv_nsec = 0; | 
|  | while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) { | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef ABSL_HAVE_ALARM | 
|  | // AbslFailureSignalHandler() installs this as a signal handler for | 
|  | // SIGALRM, then sets an alarm to be delivered to the program after a | 
|  | // set amount of time. If AbslFailureSignalHandler() hangs for more than | 
|  | // the alarm timeout, ImmediateAbortSignalHandler() will abort the | 
|  | // program. | 
|  | static void ImmediateAbortSignalHandler(int) { RaiseToDefaultHandler(SIGABRT); } | 
|  | #endif | 
|  |  | 
|  | // absl::base_internal::GetTID() returns pid_t on most platforms, but | 
|  | // returns absl::base_internal::pid_t on Windows. | 
|  | using GetTidType = decltype(absl::base_internal::GetTID()); | 
|  | ABSL_CONST_INIT static std::atomic<GetTidType> failed_tid(0); | 
|  |  | 
|  | static int GetCpuNumber() { | 
|  | #ifdef ABSL_HAVE_SCHED_GETCPU | 
|  | return sched_getcpu(); | 
|  | #elif defined(ABSL_HAVE_PTHREAD_CPU_NUMBER_NP) | 
|  | size_t cpu_num; | 
|  | if (pthread_cpu_number_np(&cpu_num) == 0) { | 
|  | return static_cast<int>(cpu_num); | 
|  | } | 
|  | return -1; | 
|  | #else | 
|  | return -1; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifndef ABSL_HAVE_SIGACTION | 
|  | static void AbslFailureSignalHandler(int signo) { | 
|  | void* ucontext = nullptr; | 
|  | #else | 
|  | static void AbslFailureSignalHandler(int signo, siginfo_t*, void* ucontext) { | 
|  | #endif | 
|  |  | 
|  | const GetTidType this_tid = absl::base_internal::GetTID(); | 
|  | GetTidType previous_failed_tid = 0; | 
|  | if (!failed_tid.compare_exchange_strong(previous_failed_tid, this_tid, | 
|  | std::memory_order_acq_rel, | 
|  | std::memory_order_relaxed)) { | 
|  | ABSL_RAW_LOG( | 
|  | ERROR, | 
|  | "Signal %d raised at PC=%p while already in AbslFailureSignalHandler()", | 
|  | signo, absl::debugging_internal::GetProgramCounter(ucontext)); | 
|  | if (this_tid != previous_failed_tid) { | 
|  | // Another thread is already in AbslFailureSignalHandler(), so wait | 
|  | // a bit for it to finish. If the other thread doesn't kill us, | 
|  | // we do so after sleeping. | 
|  | PortableSleepForSeconds(3); | 
|  | RaiseToDefaultHandler(signo); | 
|  | // The recursively raised signal may be blocked until we return. | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Increase the chance that the CPU we report was the same CPU on which the | 
|  | // signal was received by doing this as early as possible, i.e. after | 
|  | // verifying that this is not a recursive signal handler invocation. | 
|  | int my_cpu = GetCpuNumber(); | 
|  |  | 
|  | #ifdef ABSL_HAVE_ALARM | 
|  | // Set an alarm to abort the program in case this code hangs or deadlocks. | 
|  | if (fsh_options.alarm_on_failure_secs > 0) { | 
|  | alarm(0);  // Cancel any existing alarms. | 
|  | signal(SIGALRM, ImmediateAbortSignalHandler); | 
|  | alarm(static_cast<unsigned int>(fsh_options.alarm_on_failure_secs)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // First write to stderr. | 
|  | WriteFailureInfo( | 
|  | signo, ucontext, my_cpu, +[](const char* data) { | 
|  | absl::raw_log_internal::AsyncSignalSafeWriteError(data, strlen(data)); | 
|  | }); | 
|  |  | 
|  | // Riskier code (because it is less likely to be async-signal-safe) | 
|  | // goes after this point. | 
|  | if (fsh_options.writerfn != nullptr) { | 
|  | WriteFailureInfo(signo, ucontext, my_cpu, fsh_options.writerfn); | 
|  | fsh_options.writerfn(nullptr); | 
|  | } | 
|  |  | 
|  | if (fsh_options.call_previous_handler) { | 
|  | RaiseToPreviousHandler(signo); | 
|  | } else { | 
|  | RaiseToDefaultHandler(signo); | 
|  | } | 
|  | } | 
|  |  | 
|  | void InstallFailureSignalHandler(const FailureSignalHandlerOptions& options) { | 
|  | fsh_options = options; | 
|  | for (auto& it : failure_signal_data) { | 
|  | InstallOneFailureHandler(&it, AbslFailureSignalHandler); | 
|  | } | 
|  | } | 
|  |  | 
|  | ABSL_NAMESPACE_END | 
|  | }  // namespace absl |