mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
mac: Report a metric for handler crashes
This installs signal handlers in the crashpad_handler process to log these crashes via the Crashpad.HandlerCrash.ExceptionCode.Mac histogram. This is roughly the same mechanism that’s used for Windows. The signal handler tries fairly hard to avoid swallowing signals, so that things appear to outside observers (including debuggers and crash handlers) identically to how they would look if no signal handler was present. The signal handler uses a different mapping schema than the existing Crashpad.ExceptionCode.Mac histogram for reasons explained in code comments. Because the mappings should not overlap, the new values may be added to the existing CrashpadMacExceptionCodes enum. BUG=crashpad:100 Change-Id: I9b8bda1c59d0a180501c285cdc672840a54f5efc Reviewed-on: https://chromium-review.googlesource.com/435451 Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
parent
7050c55fca
commit
948fd2d019
@ -14,9 +14,11 @@
|
|||||||
|
|
||||||
#include "handler/handler_main.h"
|
#include "handler/handler_main.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -38,6 +40,8 @@
|
|||||||
#include "handler/prune_crash_reports_thread.h"
|
#include "handler/prune_crash_reports_thread.h"
|
||||||
#include "tools/tool_support.h"
|
#include "tools/tool_support.h"
|
||||||
#include "util/file/file_io.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/map_insert.h"
|
||||||
#include "util/stdlib/string_number_conversion.h"
|
#include "util/stdlib/string_number_conversion.h"
|
||||||
#include "util/string/split_string.h"
|
#include "util/string/split_string.h"
|
||||||
@ -105,6 +109,122 @@ void Usage(const base::FilePath& me) {
|
|||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#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 {
|
struct ResetSIGTERMTraits {
|
||||||
static struct sigaction* InvalidValue() {
|
static struct sigaction* InvalidValue() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -126,9 +246,8 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
|
|||||||
g_exception_handler_server->Stop();
|
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* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr;
|
||||||
|
|
||||||
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
||||||
@ -139,15 +258,18 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
|||||||
else
|
else
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
#endif // OS_WIN
|
|
||||||
|
void InstallCrashHandler() {
|
||||||
|
g_original_exception_filter =
|
||||||
|
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int HandlerMain(int argc, char* argv[]) {
|
int HandlerMain(int argc, char* argv[]) {
|
||||||
#if defined(OS_WIN)
|
InstallCrashHandler();
|
||||||
g_original_exception_filter =
|
|
||||||
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const base::FilePath argv0(
|
const base::FilePath argv0(
|
||||||
ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
|
ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user