| Index: third_party/crashpad/crashpad/handler/handler_main.cc
|
| diff --git a/third_party/crashpad/crashpad/handler/handler_main.cc b/third_party/crashpad/crashpad/handler/handler_main.cc
|
| index 3ada8c3e50567990378061214fe03252783f5edf..9433a6af5186ec33ebf6c68fb17948adb227381d 100644
|
| --- a/third_party/crashpad/crashpad/handler/handler_main.cc
|
| +++ b/third_party/crashpad/crashpad/handler/handler_main.cc
|
| @@ -14,9 +14,11 @@
|
|
|
| #include "handler/handler_main.h"
|
|
|
| +#include <errno.h>
|
| #include <getopt.h>
|
| #include <stdint.h>
|
| #include <stdlib.h>
|
| +#include <sys/types.h>
|
|
|
| #include <map>
|
| #include <memory>
|
| @@ -38,6 +40,8 @@
|
| #include "handler/prune_crash_reports_thread.h"
|
| #include "tools/tool_support.h"
|
| #include "util/file/file_io.h"
|
| +#include "util/misc/metrics.h"
|
| +#include "util/numeric/in_range_cast.h"
|
| #include "util/stdlib/map_insert.h"
|
| #include "util/stdlib/string_number_conversion.h"
|
| #include "util/string/split_string.h"
|
| @@ -105,6 +109,122 @@ void Usage(const base::FilePath& me) {
|
|
|
| #if defined(OS_MACOSX)
|
|
|
| +struct sigaction g_original_crash_sigaction[NSIG];
|
| +
|
| +void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
| + // Is siginfo->si_code useful? The only interesting values on macOS are 0 (not
|
| + // useful, signals generated asynchronously such as by kill() or raise()) and
|
| + // small positive numbers (useful, signal generated via a hardware fault). The
|
| + // standard specifies these other constants, and while xnu never uses them,
|
| + // they are intended to denote signals generated asynchronously and are
|
| + // included here. Additionally, existing practice on other systems
|
| + // (acknowledged by the standard) is for negative numbers to indicate that a
|
| + // signal was generated asynchronously. Although xnu does not do this, allow
|
| + // for the possibility for completeness.
|
| + bool si_code_valid = !(siginfo->si_code <= 0 ||
|
| + siginfo->si_code == SI_USER ||
|
| + siginfo->si_code == SI_QUEUE ||
|
| + siginfo->si_code == SI_TIMER ||
|
| + siginfo->si_code == SI_ASYNCIO ||
|
| + siginfo->si_code == SI_MESGQ);
|
| +
|
| + // 0x5343 = 'SC', signifying “signal and code”, disambiguates from the schema
|
| + // used by ExceptionCodeForMetrics(). That system primarily uses Mach
|
| + // exception types and codes, which are not available to a POSIX signal
|
| + // handler. It does provide a way to encode only signal numbers, but does so
|
| + // with the understanding that certain “raw” signals would not be encountered
|
| + // without a Mach exception. Furthermore, it does not allow siginfo->si_code
|
| + // to be encoded, because that’s not available to Mach exception handlers. It
|
| + // would be a shame to lose that information available to a POSIX signal
|
| + // handler.
|
| + int metrics_code = 0x53430000 | (InRangeCast<uint8_t>(sig, 0xff) << 8);
|
| + if (si_code_valid) {
|
| + metrics_code |= InRangeCast<uint8_t>(siginfo->si_code, 0xff);
|
| + }
|
| + Metrics::HandlerCrashed(metrics_code);
|
| +
|
| + // Restore the previous signal handler.
|
| + DCHECK_GT(sig, 0);
|
| + DCHECK_LT(static_cast<size_t>(sig), arraysize(g_original_crash_sigaction));
|
| + struct sigaction* osa = &g_original_crash_sigaction[sig];
|
| + int rv = sigaction(sig, osa, nullptr);
|
| + DPLOG_IF(ERROR, rv != 0) << "sigaction " << sig;
|
| +
|
| + // If the signal was received synchronously resulting from a hardware fault,
|
| + // returning from the signal handler will cause the kernel to re-raise it,
|
| + // because this handler hasn’t done anything to alleviate the condition that
|
| + // caused the signal to be raised in the first place. With the old signal
|
| + // handler in place (expected to be SIG_DFL), it will cause the same behavior
|
| + // to be taken as though this signal handler had never been installed at all
|
| + // (expected to be a crash). This is ideal, because the signal is re-raised
|
| + // with the same properties and from the same context that initially triggered
|
| + // it, providing the best debugging experience.
|
| +
|
| + if ((sig != SIGILL && sig != SIGFPE && sig != SIGBUS && sig != SIGSEGV) ||
|
| + !si_code_valid) {
|
| + // Signals received other than via hardware faults, such as those raised
|
| + // asynchronously via kill() and raise(), and those arising via hardware
|
| + // traps such as int3 (resulting in SIGTRAP but advancing the instruction
|
| + // pointer), will not reoccur on their own when returning from the signal
|
| + // handler. Re-raise them or call to the previous signal handler as
|
| + // appropriate.
|
| + //
|
| + // Unfortunately, when SIGBUS is received asynchronously via kill(),
|
| + // siginfo->si_code makes it appear as though it was actually received via a
|
| + // hardware fault. See 10.12.3 xnu-3789.41.3/bsd/dev/i386/unix_signal.c
|
| + // sendsig(). An asynchronous SIGBUS will thus cause the handler-crashed
|
| + // metric to be logged but will not cause the process to terminate. This
|
| + // isn’t ideal, but asynchronous SIGBUS is an unexpected condition. The
|
| + // alternative, to re-raise here on any SIGBUS, is a bad idea because it
|
| + // would lose properties associated with the the original signal, which are
|
| + // very valuable for debugging and are visible to a Mach exception handler.
|
| + // Since SIGBUS is normally received synchronously in response to a hardware
|
| + // fault, don’t sweat the unexpected asynchronous case.
|
| + if (osa->sa_handler == SIG_DFL) {
|
| + // Because this signal handler executes with the signal blocked, this
|
| + // raise() cannot immediately deliver the signal. Delivery is deferred
|
| + // until this signal handler returns and the signal becomes unblocked. The
|
| + // re-raised signal will appear with the same context as where it was
|
| + // initially triggered.
|
| + rv = raise(sig);
|
| + DPLOG_IF(ERROR, rv != 0) << "raise";
|
| + } else if (osa->sa_handler != SIG_IGN) {
|
| + if (osa->sa_flags & SA_SIGINFO) {
|
| + osa->sa_sigaction(sig, siginfo, context);
|
| + } else {
|
| + osa->sa_handler(sig);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void InstallCrashHandler() {
|
| + struct sigaction sa = {};
|
| + sigemptyset(&sa.sa_mask);
|
| + sa.sa_flags = SA_SIGINFO;
|
| + sa.sa_sigaction = HandleCrashSignal;
|
| +
|
| + // These are the core-generating signals from 10.12.3
|
| + // xnu-3789.41.3/bsd/sys/signalvar.h sigprop: entries with SA_CORE are in the
|
| + // set.
|
| + const int kSignals[] = {SIGQUIT,
|
| + SIGILL,
|
| + SIGTRAP,
|
| + SIGABRT,
|
| + SIGEMT,
|
| + SIGFPE,
|
| + SIGBUS,
|
| + SIGSEGV,
|
| + SIGSYS};
|
| +
|
| + for (int sig : kSignals) {
|
| + DCHECK_GT(sig, 0);
|
| + DCHECK_LT(static_cast<size_t>(sig), arraysize(g_original_crash_sigaction));
|
| + int rv = sigaction(sig, &sa, &g_original_crash_sigaction[sig]);
|
| + PCHECK(rv == 0) << "sigaction " << sig;
|
| + }
|
| +}
|
| +
|
| struct ResetSIGTERMTraits {
|
| static struct sigaction* InvalidValue() {
|
| return nullptr;
|
| @@ -126,9 +246,8 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
|
| g_exception_handler_server->Stop();
|
| }
|
|
|
| -#endif // OS_MACOSX
|
| +#elif defined(OS_WIN)
|
|
|
| -#if defined(OS_WIN)
|
| LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr;
|
|
|
| LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
| @@ -139,15 +258,18 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
| else
|
| return EXCEPTION_CONTINUE_SEARCH;
|
| }
|
| -#endif // OS_WIN
|
| +
|
| +void InstallCrashHandler() {
|
| + g_original_exception_filter =
|
| + SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
| +}
|
| +
|
| +#endif // OS_MACOSX
|
|
|
| } // namespace
|
|
|
| int HandlerMain(int argc, char* argv[]) {
|
| -#if defined(OS_WIN)
|
| - g_original_exception_filter =
|
| - SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
| -#endif
|
| + InstallCrashHandler();
|
|
|
| const base::FilePath argv0(
|
| ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
|
|
|