diff options
author | 2017-01-19 20:04:27 +0000 | |
---|---|---|
committer | 2017-01-30 18:13:51 +0000 | |
commit | 21482ad3cd5fc113b0c601c93febdb9e8a9484ea (patch) | |
tree | 7ea6a29fe89f72257c453ba9387b8159049222a6 | |
parent | f6fe51ddf1e37597482928017856ae139595667e (diff) |
Optionally dump ART crash information to logcat when running on Android.
This logging feature does not use debuggerd. It is only
enabled when the environment variable ANDROID_ROOT is set
to something different than "/system".
Also, refactor runtime/runtime_android.cc and
runtime/runtime_linux.cc.
Test: Observe device logcat when dalvikvm crashes
Bug: 32466479
Change-Id: If59ce838fdb5b9bb6638b73fcd074b7065275c6c
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/runtime_android.cc | 53 | ||||
-rw-r--r-- | runtime/runtime_common.cc | 389 | ||||
-rw-r--r-- | runtime/runtime_common.h | 87 | ||||
-rw-r--r-- | runtime/runtime_linux.cc | 378 |
5 files changed, 500 insertions, 408 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 7f985139bb..9b8f5df72f 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -185,6 +185,7 @@ cc_defaults { "reflection.cc", "runtime.cc", "runtime_callbacks.cc", + "runtime_common.cc", "runtime_options.cc", "signal_catcher.cc", "stack.cc", diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc index 0a996a9e55..495296cf7d 100644 --- a/runtime/runtime_android.cc +++ b/runtime/runtime_android.cc @@ -14,56 +14,33 @@ * limitations under the License. */ +#include "runtime.h" + #include <signal.h> -#include <string.h> -#include <sys/utsname.h> -#include <inttypes.h> -#include "base/logging.h" -#include "base/mutex.h" -#include "thread-inl.h" -#include "utils.h" +#include <cstring> -namespace art { +#include "runtime_common.h" -static constexpr bool kUseSignalHandler = false; +namespace art { struct sigaction old_action; -void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) { - static bool handling_unexpected_signal = false; - if (handling_unexpected_signal) { - LogHelper::LogLineLowStack(__FILE__, - __LINE__, - ::android::base::FATAL_WITHOUT_ABORT, - "HandleUnexpectedSignal reentered\n"); - _exit(1); - } - handling_unexpected_signal = true; - gAborting++; // set before taking any locks - MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); - Runtime* runtime = Runtime::Current(); - if (runtime != nullptr) { - // Print this out first in case DumpObject faults. - LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage(); - } +void HandleUnexpectedSignalAndroid(int signal_number, siginfo_t* info, void* raw_context) { + HandleUnexpectedSignalCommon(signal_number, info, raw_context, /* running_on_linux */ false); + // Run the old signal handler. old_action.sa_sigaction(signal_number, info, raw_context); } void Runtime::InitPlatformSignalHandlers() { - if (kUseSignalHandler) { - struct sigaction action; - memset(&action, 0, sizeof(action)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = HandleUnexpectedSignal; - // Use the three-argument sa_sigaction handler. - action.sa_flags |= SA_SIGINFO; - // Use the alternate signal stack so we can catch stack overflows. - action.sa_flags |= SA_ONSTACK; - int rc = 0; - rc += sigaction(SIGSEGV, &action, &old_action); - CHECK_EQ(rc, 0); + // Enable the signal handler dumping crash information to the logcat + // when the Android root is not "/system". + const char* android_root = getenv("ANDROID_ROOT"); + if (android_root != nullptr && strcmp(android_root, "/system") != 0) { + InitPlatformSignalHandlersCommon(HandleUnexpectedSignalAndroid, + &old_action, + /* handle_timeout_signal */ false); } } diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc new file mode 100644 index 0000000000..1d8c16d22b --- /dev/null +++ b/runtime/runtime_common.cc @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 + * + * 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 "runtime_common.h" + +#include <signal.h> + +#include <cinttypes> +#include <iostream> +#include <sstream> +#include <string> + +#include "android-base/stringprintf.h" + +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "native_stack_dump.h" +#include "thread-inl.h" +#include "thread_list.h" + +namespace art { + +using android::base::StringPrintf; + +static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; + +const char* GetSignalName(int signal_number) { + switch (signal_number) { + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGILL: return "SIGILL"; + case SIGPIPE: return "SIGPIPE"; + case SIGSEGV: return "SIGSEGV"; +#if defined(SIGSTKFLT) + case SIGSTKFLT: return "SIGSTKFLT"; +#endif + case SIGTRAP: return "SIGTRAP"; + } + return "??"; +} + +const char* GetSignalCodeName(int signal_number, int signal_code) { + // Try the signal-specific codes... + switch (signal_number) { + case SIGILL: + switch (signal_code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + break; + case SIGBUS: + switch (signal_code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + } + break; + case SIGFPE: + switch (signal_code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + break; + case SIGSEGV: + switch (signal_code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + case SEGV_ACCERR: return "SEGV_ACCERR"; +#if defined(SEGV_BNDERR) + case SEGV_BNDERR: return "SEGV_BNDERR"; +#endif + } + break; + case SIGTRAP: + switch (signal_code) { + case TRAP_BRKPT: return "TRAP_BRKPT"; + case TRAP_TRACE: return "TRAP_TRACE"; + } + break; + } + // Then the other codes... + switch (signal_code) { + case SI_USER: return "SI_USER"; +#if defined(SI_KERNEL) + case SI_KERNEL: return "SI_KERNEL"; +#endif + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; + case SI_ASYNCIO: return "SI_ASYNCIO"; +#if defined(SI_SIGIO) + case SI_SIGIO: return "SI_SIGIO"; +#endif +#if defined(SI_TKILL) + case SI_TKILL: return "SI_TKILL"; +#endif + } + // Then give up... + return "?"; +} + +void UContext::Dump(std::ostream& os) const { + // TODO: support non-x86 hosts. +#if defined(__APPLE__) && defined(__i386__) + DumpRegister32(os, "eax", context->__ss.__eax); + DumpRegister32(os, "ebx", context->__ss.__ebx); + DumpRegister32(os, "ecx", context->__ss.__ecx); + DumpRegister32(os, "edx", context->__ss.__edx); + os << '\n'; + + DumpRegister32(os, "edi", context->__ss.__edi); + DumpRegister32(os, "esi", context->__ss.__esi); + DumpRegister32(os, "ebp", context->__ss.__ebp); + DumpRegister32(os, "esp", context->__ss.__esp); + os << '\n'; + + DumpRegister32(os, "eip", context->__ss.__eip); + os << " "; + DumpRegister32(os, "eflags", context->__ss.__eflags); + DumpX86Flags(os, context->__ss.__eflags); + os << '\n'; + + DumpRegister32(os, "cs", context->__ss.__cs); + DumpRegister32(os, "ds", context->__ss.__ds); + DumpRegister32(os, "es", context->__ss.__es); + DumpRegister32(os, "fs", context->__ss.__fs); + os << '\n'; + DumpRegister32(os, "gs", context->__ss.__gs); + DumpRegister32(os, "ss", context->__ss.__ss); +#elif defined(__linux__) && defined(__i386__) + DumpRegister32(os, "eax", context.gregs[REG_EAX]); + DumpRegister32(os, "ebx", context.gregs[REG_EBX]); + DumpRegister32(os, "ecx", context.gregs[REG_ECX]); + DumpRegister32(os, "edx", context.gregs[REG_EDX]); + os << '\n'; + + DumpRegister32(os, "edi", context.gregs[REG_EDI]); + DumpRegister32(os, "esi", context.gregs[REG_ESI]); + DumpRegister32(os, "ebp", context.gregs[REG_EBP]); + DumpRegister32(os, "esp", context.gregs[REG_ESP]); + os << '\n'; + + DumpRegister32(os, "eip", context.gregs[REG_EIP]); + os << " "; + DumpRegister32(os, "eflags", context.gregs[REG_EFL]); + DumpX86Flags(os, context.gregs[REG_EFL]); + os << '\n'; + + DumpRegister32(os, "cs", context.gregs[REG_CS]); + DumpRegister32(os, "ds", context.gregs[REG_DS]); + DumpRegister32(os, "es", context.gregs[REG_ES]); + DumpRegister32(os, "fs", context.gregs[REG_FS]); + os << '\n'; + DumpRegister32(os, "gs", context.gregs[REG_GS]); + DumpRegister32(os, "ss", context.gregs[REG_SS]); +#elif defined(__linux__) && defined(__x86_64__) + DumpRegister64(os, "rax", context.gregs[REG_RAX]); + DumpRegister64(os, "rbx", context.gregs[REG_RBX]); + DumpRegister64(os, "rcx", context.gregs[REG_RCX]); + DumpRegister64(os, "rdx", context.gregs[REG_RDX]); + os << '\n'; + + DumpRegister64(os, "rdi", context.gregs[REG_RDI]); + DumpRegister64(os, "rsi", context.gregs[REG_RSI]); + DumpRegister64(os, "rbp", context.gregs[REG_RBP]); + DumpRegister64(os, "rsp", context.gregs[REG_RSP]); + os << '\n'; + + DumpRegister64(os, "r8 ", context.gregs[REG_R8]); + DumpRegister64(os, "r9 ", context.gregs[REG_R9]); + DumpRegister64(os, "r10", context.gregs[REG_R10]); + DumpRegister64(os, "r11", context.gregs[REG_R11]); + os << '\n'; + + DumpRegister64(os, "r12", context.gregs[REG_R12]); + DumpRegister64(os, "r13", context.gregs[REG_R13]); + DumpRegister64(os, "r14", context.gregs[REG_R14]); + DumpRegister64(os, "r15", context.gregs[REG_R15]); + os << '\n'; + + DumpRegister64(os, "rip", context.gregs[REG_RIP]); + os << " "; + DumpRegister32(os, "eflags", context.gregs[REG_EFL]); + DumpX86Flags(os, context.gregs[REG_EFL]); + os << '\n'; + + DumpRegister32(os, "cs", (context.gregs[REG_CSGSFS]) & 0x0FFFF); + DumpRegister32(os, "gs", (context.gregs[REG_CSGSFS] >> 16) & 0x0FFFF); + DumpRegister32(os, "fs", (context.gregs[REG_CSGSFS] >> 32) & 0x0FFFF); + os << '\n'; +#else + os << "Unknown architecture/word size/OS in ucontext dump"; +#endif +} + +void UContext::DumpRegister32(std::ostream& os, const char* name, uint32_t value) const { + os << StringPrintf(" %6s: 0x%08x", name, value); +} + +void UContext::DumpRegister64(std::ostream& os, const char* name, uint64_t value) const { + os << StringPrintf(" %6s: 0x%016" PRIx64, name, value); +} + +void UContext::DumpX86Flags(std::ostream& os, uint32_t flags) const { + os << " ["; + if ((flags & (1 << 0)) != 0) { + os << " CF"; + } + if ((flags & (1 << 2)) != 0) { + os << " PF"; + } + if ((flags & (1 << 4)) != 0) { + os << " AF"; + } + if ((flags & (1 << 6)) != 0) { + os << " ZF"; + } + if ((flags & (1 << 7)) != 0) { + os << " SF"; + } + if ((flags & (1 << 8)) != 0) { + os << " TF"; + } + if ((flags & (1 << 9)) != 0) { + os << " IF"; + } + if ((flags & (1 << 10)) != 0) { + os << " DF"; + } + if ((flags & (1 << 11)) != 0) { + os << " OF"; + } + os << " ]"; +} + +int GetTimeoutSignal() { +#if defined(__APPLE__) + // Mac does not support realtime signals. + UNUSED(kUseSigRTTimeout); + return -1; +#else + return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1; +#endif +} + +static bool IsTimeoutSignal(int signal_number) { + return signal_number == GetTimeoutSignal(); +} + +void HandleUnexpectedSignalCommon(int signal_number, + siginfo_t* info, + void* raw_context, + bool running_on_linux) { + bool handle_timeout_signal = running_on_linux; + bool dump_on_stderr = running_on_linux; + + static bool handling_unexpected_signal = false; + if (handling_unexpected_signal) { + LogHelper::LogLineLowStack(__FILE__, + __LINE__, + ::android::base::FATAL_WITHOUT_ABORT, + "HandleUnexpectedSignal reentered\n"); + if (handle_timeout_signal) { + if (IsTimeoutSignal(signal_number)) { + // Ignore a recursive timeout. + return; + } + } + _exit(1); + } + handling_unexpected_signal = true; + + gAborting++; // set before taking any locks + MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); + + bool has_address = (signal_number == SIGILL || signal_number == SIGBUS || + signal_number == SIGFPE || signal_number == SIGSEGV); + + OsInfo os_info; + const char* cmd_line = GetCmdLine(); + if (cmd_line == nullptr) { + cmd_line = "<unset>"; // Because no-one called InitLogging. + } + pid_t tid = GetTid(); + std::string thread_name(GetThreadName(tid)); + UContext thread_context(raw_context); + Backtrace thread_backtrace(raw_context); + + std::ostringstream stream; + stream << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n" + << StringPrintf("Fatal signal %d (%s), code %d (%s)", + signal_number, + GetSignalName(signal_number), + info->si_code, + GetSignalCodeName(signal_number, info->si_code)) + << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << '\n' + << "OS: " << Dumpable<OsInfo>(os_info) << '\n' + << "Cmdline: " << cmd_line << '\n' + << "Thread: " << tid << " \"" << thread_name << "\"" << '\n' + << "Registers:\n" << Dumpable<UContext>(thread_context) << '\n' + << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << '\n'; + if (dump_on_stderr) { + // Note: We are using cerr directly instead of LOG macros to ensure even just partial output + // makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable + // considering this is an abort situation. + std::cerr << stream.str() << std::flush; + } else { + LOG(FATAL_WITHOUT_ABORT) << stream.str() << std::flush; + } + if (kIsDebugBuild && signal_number == SIGSEGV) { + PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + } + + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + if (handle_timeout_signal && IsTimeoutSignal(signal_number)) { + // Special timeout signal. Try to dump all threads. + // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts + // are of value here. + runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); + std::cerr << std::endl; + } + + if (dump_on_stderr) { + std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl; + } else { + LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage(); + } + } +} + +void InitPlatformSignalHandlersCommon(void (*newact)(int, siginfo_t*, void*), + struct sigaction* oldact, + bool handle_timeout_signal) { + struct sigaction action; + memset(&action, 0, sizeof(action)); + sigemptyset(&action.sa_mask); + action.sa_sigaction = newact; + // Use the three-argument sa_sigaction handler. + action.sa_flags |= SA_SIGINFO; + // Use the alternate signal stack so we can catch stack overflows. + action.sa_flags |= SA_ONSTACK; + + int rc = 0; + rc += sigaction(SIGABRT, &action, oldact); + rc += sigaction(SIGBUS, &action, oldact); + rc += sigaction(SIGFPE, &action, oldact); + rc += sigaction(SIGILL, &action, oldact); + rc += sigaction(SIGPIPE, &action, oldact); + rc += sigaction(SIGSEGV, &action, oldact); +#if defined(SIGSTKFLT) + rc += sigaction(SIGSTKFLT, &action, oldact); +#endif + rc += sigaction(SIGTRAP, &action, oldact); + // Special dump-all timeout. + if (handle_timeout_signal && GetTimeoutSignal() != -1) { + rc += sigaction(GetTimeoutSignal(), &action, oldact); + } + CHECK_EQ(rc, 0); +} + +} // namespace art diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h new file mode 100644 index 0000000000..52817fd34f --- /dev/null +++ b/runtime/runtime_common.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 + * + * 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. + */ + +#ifndef ART_RUNTIME_RUNTIME_COMMON_H_ +#define ART_RUNTIME_RUNTIME_COMMON_H_ + +// Code shared by runtime/runtime_android.cc and runtime/runtime_linux.cc. + +#include <sys/utsname.h> +#include <ucontext.h> + +#include <iomanip> + +#include "base/dumpable.h" +#include "native_stack_dump.h" +#include "utils.h" + +namespace art { + +struct Backtrace { + public: + explicit Backtrace(void* raw_context) : raw_context_(raw_context) {} + void Dump(std::ostream& os) const { + DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_); + } + private: + // Stores the context of the signal that was unexpected and will terminate the runtime. The + // DumpNativeStack code will take care of casting it to the expected type. This is required + // as our signal handler runs on an alternate stack. + void* raw_context_; +}; + +struct OsInfo { + void Dump(std::ostream& os) const { + utsname info; + uname(&info); + // Linux 2.6.38.8-gg784 (x86_64) + // Darwin 11.4.0 (x86_64) + os << info.sysname << " " << info.release << " (" << info.machine << ")"; + } +}; + +const char* GetSignalName(int signal_number); +const char* GetSignalCodeName(int signal_number, int signal_code); + +struct UContext { + explicit UContext(void* raw_context) + : context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) {} + + void Dump(std::ostream& os) const; + + void DumpRegister32(std::ostream& os, const char* name, uint32_t value) const; + void DumpRegister64(std::ostream& os, const char* name, uint64_t value) const; + + void DumpX86Flags(std::ostream& os, uint32_t flags) const; + + mcontext_t& context; +}; + +// Return the signal number we recognize as timeout. -1 means not active/supported. +int GetTimeoutSignal(); + +void HandleUnexpectedSignalCommon(int signal_number, + siginfo_t* info, + void* raw_context, + bool running_on_linux); + +void InitPlatformSignalHandlersCommon(void (*newact)(int, siginfo_t*, void*), + struct sigaction* oldact, + bool handle_timeout_signal); + +} // namespace art + +#endif // ART_RUNTIME_RUNTIME_COMMON_H_ diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index b8894d2569..ad61cf373b 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -17,359 +17,19 @@ #include "runtime.h" #include <signal.h> -#include <string.h> -#include <sys/utsname.h> -#include <inttypes.h> #include <iostream> -#include <sstream> -#include "android-base/stringprintf.h" - -#include "base/dumpable.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "native_stack_dump.h" -#include "thread-inl.h" -#include "thread_list.h" -#include "utils.h" +#include "runtime_common.h" namespace art { -using android::base::StringPrintf; - -static constexpr bool kUseSigRTTimeout = true; -static constexpr bool kDumpNativeStackOnTimeout = true; - -struct Backtrace { - public: - explicit Backtrace(void* raw_context) : raw_context_(raw_context) {} - void Dump(std::ostream& os) const { - DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_); - } - private: - // Stores the context of the signal that was unexpected and will terminate the runtime. The - // DumpNativeStack code will take care of casting it to the expected type. This is required - // as our signal handler runs on an alternate stack. - void* raw_context_; -}; - -struct OsInfo { - void Dump(std::ostream& os) const { - utsname info; - uname(&info); - // Linux 2.6.38.8-gg784 (x86_64) - // Darwin 11.4.0 (x86_64) - os << info.sysname << " " << info.release << " (" << info.machine << ")"; - } -}; - -static const char* GetSignalName(int signal_number) { - switch (signal_number) { - case SIGABRT: return "SIGABRT"; - case SIGBUS: return "SIGBUS"; - case SIGFPE: return "SIGFPE"; - case SIGILL: return "SIGILL"; - case SIGPIPE: return "SIGPIPE"; - case SIGSEGV: return "SIGSEGV"; -#if defined(SIGSTKFLT) - case SIGSTKFLT: return "SIGSTKFLT"; -#endif - case SIGTRAP: return "SIGTRAP"; - } - return "??"; -} - -static const char* GetSignalCodeName(int signal_number, int signal_code) { - // Try the signal-specific codes... - switch (signal_number) { - case SIGILL: - switch (signal_code) { - case ILL_ILLOPC: return "ILL_ILLOPC"; - case ILL_ILLOPN: return "ILL_ILLOPN"; - case ILL_ILLADR: return "ILL_ILLADR"; - case ILL_ILLTRP: return "ILL_ILLTRP"; - case ILL_PRVOPC: return "ILL_PRVOPC"; - case ILL_PRVREG: return "ILL_PRVREG"; - case ILL_COPROC: return "ILL_COPROC"; - case ILL_BADSTK: return "ILL_BADSTK"; - } - break; - case SIGBUS: - switch (signal_code) { - case BUS_ADRALN: return "BUS_ADRALN"; - case BUS_ADRERR: return "BUS_ADRERR"; - case BUS_OBJERR: return "BUS_OBJERR"; - } - break; - case SIGFPE: - switch (signal_code) { - case FPE_INTDIV: return "FPE_INTDIV"; - case FPE_INTOVF: return "FPE_INTOVF"; - case FPE_FLTDIV: return "FPE_FLTDIV"; - case FPE_FLTOVF: return "FPE_FLTOVF"; - case FPE_FLTUND: return "FPE_FLTUND"; - case FPE_FLTRES: return "FPE_FLTRES"; - case FPE_FLTINV: return "FPE_FLTINV"; - case FPE_FLTSUB: return "FPE_FLTSUB"; - } - break; - case SIGSEGV: - switch (signal_code) { - case SEGV_MAPERR: return "SEGV_MAPERR"; - case SEGV_ACCERR: return "SEGV_ACCERR"; -#if defined(SEGV_BNDERR) - case SEGV_BNDERR: return "SEGV_BNDERR"; -#endif - } - break; - case SIGTRAP: - switch (signal_code) { - case TRAP_BRKPT: return "TRAP_BRKPT"; - case TRAP_TRACE: return "TRAP_TRACE"; - } - break; - } - // Then the other codes... - switch (signal_code) { - case SI_USER: return "SI_USER"; -#if defined(SI_KERNEL) - case SI_KERNEL: return "SI_KERNEL"; -#endif - case SI_QUEUE: return "SI_QUEUE"; - case SI_TIMER: return "SI_TIMER"; - case SI_MESGQ: return "SI_MESGQ"; - case SI_ASYNCIO: return "SI_ASYNCIO"; -#if defined(SI_SIGIO) - case SI_SIGIO: return "SI_SIGIO"; -#endif -#if defined(SI_TKILL) - case SI_TKILL: return "SI_TKILL"; -#endif - } - // Then give up... - return "?"; -} - -struct UContext { - explicit UContext(void* raw_context) : - context(reinterpret_cast<ucontext_t*>(raw_context)->uc_mcontext) { - } - - void Dump(std::ostream& os) const { - // TODO: support non-x86 hosts (not urgent because this code doesn't run on targets). -#if defined(__APPLE__) && defined(__i386__) - DumpRegister32(os, "eax", context->__ss.__eax); - DumpRegister32(os, "ebx", context->__ss.__ebx); - DumpRegister32(os, "ecx", context->__ss.__ecx); - DumpRegister32(os, "edx", context->__ss.__edx); - os << '\n'; - - DumpRegister32(os, "edi", context->__ss.__edi); - DumpRegister32(os, "esi", context->__ss.__esi); - DumpRegister32(os, "ebp", context->__ss.__ebp); - DumpRegister32(os, "esp", context->__ss.__esp); - os << '\n'; - - DumpRegister32(os, "eip", context->__ss.__eip); - os << " "; - DumpRegister32(os, "eflags", context->__ss.__eflags); - DumpX86Flags(os, context->__ss.__eflags); - os << '\n'; - - DumpRegister32(os, "cs", context->__ss.__cs); - DumpRegister32(os, "ds", context->__ss.__ds); - DumpRegister32(os, "es", context->__ss.__es); - DumpRegister32(os, "fs", context->__ss.__fs); - os << '\n'; - DumpRegister32(os, "gs", context->__ss.__gs); - DumpRegister32(os, "ss", context->__ss.__ss); -#elif defined(__linux__) && defined(__i386__) - DumpRegister32(os, "eax", context.gregs[REG_EAX]); - DumpRegister32(os, "ebx", context.gregs[REG_EBX]); - DumpRegister32(os, "ecx", context.gregs[REG_ECX]); - DumpRegister32(os, "edx", context.gregs[REG_EDX]); - os << '\n'; - - DumpRegister32(os, "edi", context.gregs[REG_EDI]); - DumpRegister32(os, "esi", context.gregs[REG_ESI]); - DumpRegister32(os, "ebp", context.gregs[REG_EBP]); - DumpRegister32(os, "esp", context.gregs[REG_ESP]); - os << '\n'; - - DumpRegister32(os, "eip", context.gregs[REG_EIP]); - os << " "; - DumpRegister32(os, "eflags", context.gregs[REG_EFL]); - DumpX86Flags(os, context.gregs[REG_EFL]); - os << '\n'; - - DumpRegister32(os, "cs", context.gregs[REG_CS]); - DumpRegister32(os, "ds", context.gregs[REG_DS]); - DumpRegister32(os, "es", context.gregs[REG_ES]); - DumpRegister32(os, "fs", context.gregs[REG_FS]); - os << '\n'; - DumpRegister32(os, "gs", context.gregs[REG_GS]); - DumpRegister32(os, "ss", context.gregs[REG_SS]); -#elif defined(__linux__) && defined(__x86_64__) - DumpRegister64(os, "rax", context.gregs[REG_RAX]); - DumpRegister64(os, "rbx", context.gregs[REG_RBX]); - DumpRegister64(os, "rcx", context.gregs[REG_RCX]); - DumpRegister64(os, "rdx", context.gregs[REG_RDX]); - os << '\n'; - - DumpRegister64(os, "rdi", context.gregs[REG_RDI]); - DumpRegister64(os, "rsi", context.gregs[REG_RSI]); - DumpRegister64(os, "rbp", context.gregs[REG_RBP]); - DumpRegister64(os, "rsp", context.gregs[REG_RSP]); - os << '\n'; - - DumpRegister64(os, "r8 ", context.gregs[REG_R8]); - DumpRegister64(os, "r9 ", context.gregs[REG_R9]); - DumpRegister64(os, "r10", context.gregs[REG_R10]); - DumpRegister64(os, "r11", context.gregs[REG_R11]); - os << '\n'; - - DumpRegister64(os, "r12", context.gregs[REG_R12]); - DumpRegister64(os, "r13", context.gregs[REG_R13]); - DumpRegister64(os, "r14", context.gregs[REG_R14]); - DumpRegister64(os, "r15", context.gregs[REG_R15]); - os << '\n'; - - DumpRegister64(os, "rip", context.gregs[REG_RIP]); - os << " "; - DumpRegister32(os, "eflags", context.gregs[REG_EFL]); - DumpX86Flags(os, context.gregs[REG_EFL]); - os << '\n'; - - DumpRegister32(os, "cs", (context.gregs[REG_CSGSFS]) & 0x0FFFF); - DumpRegister32(os, "gs", (context.gregs[REG_CSGSFS] >> 16) & 0x0FFFF); - DumpRegister32(os, "fs", (context.gregs[REG_CSGSFS] >> 32) & 0x0FFFF); - os << '\n'; -#else - os << "Unknown architecture/word size/OS in ucontext dump"; -#endif - } - - void DumpRegister32(std::ostream& os, const char* name, uint32_t value) const { - os << StringPrintf(" %6s: 0x%08x", name, value); - } - - void DumpRegister64(std::ostream& os, const char* name, uint64_t value) const { - os << StringPrintf(" %6s: 0x%016" PRIx64, name, value); - } +void HandleUnexpectedSignalLinux(int signal_number, siginfo_t* info, void* raw_context) { + HandleUnexpectedSignalCommon(signal_number, info, raw_context, /* running_on_linux */ true); - void DumpX86Flags(std::ostream& os, uint32_t flags) const { - os << " ["; - if ((flags & (1 << 0)) != 0) { - os << " CF"; - } - if ((flags & (1 << 2)) != 0) { - os << " PF"; - } - if ((flags & (1 << 4)) != 0) { - os << " AF"; - } - if ((flags & (1 << 6)) != 0) { - os << " ZF"; - } - if ((flags & (1 << 7)) != 0) { - os << " SF"; - } - if ((flags & (1 << 8)) != 0) { - os << " TF"; - } - if ((flags & (1 << 9)) != 0) { - os << " IF"; - } - if ((flags & (1 << 10)) != 0) { - os << " DF"; - } - if ((flags & (1 << 11)) != 0) { - os << " OF"; - } - os << " ]"; - } - - mcontext_t& context; -}; - -// Return the signal number we recognize as timeout. -1 means not active/supported. -static int GetTimeoutSignal() { -#if defined(__APPLE__) - // Mac does not support realtime signals. - UNUSED(kUseSigRTTimeout); - return -1; -#else - return kUseSigRTTimeout ? (SIGRTMIN + 2) : -1; -#endif -} - -static bool IsTimeoutSignal(int signal_number) { - return signal_number == GetTimeoutSignal(); -} - -void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_context) { - static bool handlingUnexpectedSignal = false; - if (handlingUnexpectedSignal) { - LogHelper::LogLineLowStack(__FILE__, - __LINE__, - ::android::base::FATAL_WITHOUT_ABORT, - "HandleUnexpectedSignal reentered\n"); - if (IsTimeoutSignal(signal_number)) { - // Ignore a recursive timeout. - return; - } - _exit(1); - } - handlingUnexpectedSignal = true; - - gAborting++; // set before taking any locks - MutexLock mu(Thread::Current(), *Locks::unexpected_signal_lock_); - - bool has_address = (signal_number == SIGILL || signal_number == SIGBUS || - signal_number == SIGFPE || signal_number == SIGSEGV); - - OsInfo os_info; - const char* cmd_line = GetCmdLine(); - if (cmd_line == nullptr) { - cmd_line = "<unset>"; // Because no-one called InitLogging. - } - pid_t tid = GetTid(); - std::string thread_name(GetThreadName(tid)); - UContext thread_context(raw_context); - Backtrace thread_backtrace(raw_context); - - // Note: We are using cerr directly instead of LOG macros to ensure even just partial output - // makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable - // considering this is an abort situation. - - std::cerr << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n" - << StringPrintf("Fatal signal %d (%s), code %d (%s)", - signal_number, GetSignalName(signal_number), - info->si_code, - GetSignalCodeName(signal_number, info->si_code)) - << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << std::endl - << "OS: " << Dumpable<OsInfo>(os_info) << std::endl - << "Cmdline: " << cmd_line << std::endl - << "Thread: " << tid << " \"" << thread_name << "\"" << std::endl - << "Registers:\n" << Dumpable<UContext>(thread_context) << std::endl - << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << std::endl; - if (kIsDebugBuild && signal_number == SIGSEGV) { - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); - } - Runtime* runtime = Runtime::Current(); - if (runtime != nullptr) { - if (IsTimeoutSignal(signal_number)) { - // Special timeout signal. Try to dump all threads. - // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts - // are of value here. - runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout); - std::cerr << std::endl; - } - std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl; - } if (getenv("debug_db_uid") != nullptr || getenv("art_wait_for_gdb_on_crash") != nullptr) { + pid_t tid = GetTid(); + std::string thread_name(GetThreadName(tid)); std::cerr << "********************************************************\n" << "* Process " << getpid() << " thread " << tid << " \"" << thread_name << "\"" @@ -398,31 +58,9 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex void Runtime::InitPlatformSignalHandlers() { // On the host, we don't have debuggerd to dump a stack for us when something unexpected happens. - struct sigaction action; - memset(&action, 0, sizeof(action)); - sigemptyset(&action.sa_mask); - action.sa_sigaction = HandleUnexpectedSignal; - // Use the three-argument sa_sigaction handler. - action.sa_flags |= SA_SIGINFO; - // Use the alternate signal stack so we can catch stack overflows. - action.sa_flags |= SA_ONSTACK; - - int rc = 0; - rc += sigaction(SIGABRT, &action, nullptr); - rc += sigaction(SIGBUS, &action, nullptr); - rc += sigaction(SIGFPE, &action, nullptr); - rc += sigaction(SIGILL, &action, nullptr); - rc += sigaction(SIGPIPE, &action, nullptr); - rc += sigaction(SIGSEGV, &action, nullptr); -#if defined(SIGSTKFLT) - rc += sigaction(SIGSTKFLT, &action, nullptr); -#endif - rc += sigaction(SIGTRAP, &action, nullptr); - // Special dump-all timeout. - if (GetTimeoutSignal() != -1) { - rc += sigaction(GetTimeoutSignal(), &action, nullptr); - } - CHECK_EQ(rc, 0); + InitPlatformSignalHandlersCommon(HandleUnexpectedSignalLinux, + nullptr, + /* handle_timeout_signal */ true); } } // namespace art |