| 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 9433a6af5186ec33ebf6c68fb17948adb227381d..5006cf6723f6a59ffcc700d74ae652dc44a61f36 100644
|
| --- a/third_party/crashpad/crashpad/handler/handler_main.cc
|
| +++ b/third_party/crashpad/crashpad/handler/handler_main.cc
|
| @@ -24,8 +24,10 @@
|
| #include <memory>
|
| #include <string>
|
| #include <utility>
|
| +#include <vector>
|
|
|
| #include "base/auto_reset.h"
|
| +#include "base/compiler_specific.h"
|
| #include "base/files/file_path.h"
|
| #include "base/files/scoped_file.h"
|
| #include "base/logging.h"
|
| @@ -64,6 +66,7 @@
|
| #include "util/win/exception_handler_server.h"
|
| #include "util/win/handle.h"
|
| #include "util/win/initial_client_data.h"
|
| +#include "util/win/session_end_watcher.h"
|
| #endif // OS_MACOSX
|
|
|
| namespace crashpad {
|
| @@ -93,6 +96,7 @@ void Usage(const base::FilePath& me) {
|
| #endif // OS_MACOSX
|
| " --metrics-dir=DIR store metrics files in DIR (only in Chromium)\n"
|
| " --no-rate-limit don't rate limit crash uploads\n"
|
| +" --no-upload-gzip don't use gzip compression when uploading\n"
|
| #if defined(OS_MACOSX)
|
| " --reset-own-crash-exception-port-to-system-default\n"
|
| " reset the server's exception handler to default\n"
|
| @@ -107,11 +111,66 @@ void Usage(const base::FilePath& me) {
|
| ToolSupport::UsageTail(me);
|
| }
|
|
|
| +// Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is
|
| +// to prevent multiple exit events from inadvertently being recorded, which
|
| +// might happen if a crash occurs during destruction in what would otherwise be
|
| +// a normal exit, or if a CallMetricsRecordNormalExit object is destroyed after
|
| +// something else logs an exit event.
|
| +void MetricsRecordExit(Metrics::LifetimeMilestone milestone) {
|
| + static bool once = [](Metrics::LifetimeMilestone milestone) {
|
| + Metrics::HandlerLifetimeMilestone(milestone);
|
| + return true;
|
| + }(milestone);
|
| + ALLOW_UNUSED_LOCAL(once);
|
| +}
|
| +
|
| +// Calls MetricsRecordExit() to record a failure, and returns EXIT_FAILURE for
|
| +// the convenience of callers in main() which can simply write “return
|
| +// ExitFailure();”.
|
| +int ExitFailure() {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kFailed);
|
| + return EXIT_FAILURE;
|
| +}
|
| +
|
| +class CallMetricsRecordNormalExit {
|
| + public:
|
| + CallMetricsRecordNormalExit() {}
|
| + ~CallMetricsRecordNormalExit() {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kExitedNormally);
|
| + }
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(CallMetricsRecordNormalExit);
|
| +};
|
| +
|
| #if defined(OS_MACOSX)
|
|
|
| -struct sigaction g_original_crash_sigaction[NSIG];
|
| +void InstallSignalHandler(const std::vector<int>& signals,
|
| + void (*handler)(int, siginfo_t*, void*)) {
|
| + struct sigaction sa = {};
|
| + sigemptyset(&sa.sa_mask);
|
| + sa.sa_flags = SA_SIGINFO;
|
| + sa.sa_sigaction = handler;
|
| +
|
| + for (int sig : signals) {
|
| + int rv = sigaction(sig, &sa, nullptr);
|
| + PCHECK(rv == 0) << "sigaction " << sig;
|
| + }
|
| +}
|
| +
|
| +void RestoreDefaultSignalHandler(int sig) {
|
| + struct sigaction sa = {};
|
| + sigemptyset(&sa.sa_mask);
|
| + sa.sa_flags = 0;
|
| + sa.sa_handler = SIG_DFL;
|
| + int rv = sigaction(sig, &sa, nullptr);
|
| + DPLOG_IF(ERROR, rv != 0) << "sigaction " << sig;
|
| + ALLOW_UNUSED_LOCAL(rv);
|
| +}
|
|
|
| void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
|
| +
|
| // 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
|
| @@ -143,22 +202,17 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
| }
|
| 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;
|
| + RestoreDefaultSignalHandler(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.
|
| + // caused the signal to be raised in the first place. With the default signal
|
| + // handler in place, 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) {
|
| @@ -166,8 +220,7 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
| // 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.
|
| + // handler. Re-raise them.
|
| //
|
| // Unfortunately, when SIGBUS is received asynchronously via kill(),
|
| // siginfo->si_code makes it appear as though it was actually received via a
|
| @@ -180,49 +233,68 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
| // 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);
|
| - }
|
| - }
|
| + //
|
| + // 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.
|
| + int rv = raise(sig);
|
| + DPLOG_IF(ERROR, rv != 0) << "raise";
|
| + ALLOW_UNUSED_LOCAL(rv);
|
| }
|
| }
|
|
|
| -void InstallCrashHandler() {
|
| - struct sigaction sa = {};
|
| - sigemptyset(&sa.sa_mask);
|
| - sa.sa_flags = SA_SIGINFO;
|
| - sa.sa_sigaction = HandleCrashSignal;
|
| +void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
|
| +
|
| + RestoreDefaultSignalHandler(sig);
|
|
|
| + // Re-raise the signal. See the explanation in HandleCrashSignal(). Note that
|
| + // no checks for signals arising from synchronous hardware faults are made
|
| + // because termination signals never originate in that way.
|
| + int rv = raise(sig);
|
| + DPLOG_IF(ERROR, rv != 0) << "raise";
|
| + ALLOW_UNUSED_LOCAL(rv);
|
| +}
|
| +
|
| +void InstallCrashHandler() {
|
| // 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;
|
| - }
|
| + const int kCrashSignals[] = {SIGQUIT,
|
| + SIGILL,
|
| + SIGTRAP,
|
| + SIGABRT,
|
| + SIGEMT,
|
| + SIGFPE,
|
| + SIGBUS,
|
| + SIGSEGV,
|
| + SIGSYS};
|
| + InstallSignalHandler(
|
| + std::vector<int>(&kCrashSignals[0],
|
| + &kCrashSignals[arraysize(kCrashSignals)]),
|
| + HandleCrashSignal);
|
| +
|
| + // Not a crash handler, but close enough. These are non-core-generating but
|
| + // terminating signals from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:
|
| + // entries with SA_KILL but not SA_CORE are in the set. SIGKILL is excluded
|
| + // because it is uncatchable.
|
| + const int kTerminateSignals[] = {SIGHUP,
|
| + SIGINT,
|
| + SIGPIPE,
|
| + SIGALRM,
|
| + SIGTERM,
|
| + SIGXCPU,
|
| + SIGXFSZ,
|
| + SIGVTALRM,
|
| + SIGPROF,
|
| + SIGUSR1,
|
| + SIGUSR2};
|
| + InstallSignalHandler(
|
| + std::vector<int>(&kTerminateSignals[0],
|
| + &kTerminateSignals[arraysize(kTerminateSignals)]),
|
| + HandleTerminateSignal);
|
| }
|
|
|
| struct ResetSIGTERMTraits {
|
| @@ -242,6 +314,9 @@ ExceptionHandlerServer* g_exception_handler_server;
|
|
|
| // This signal handler is only operative when being run from launchd.
|
| void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
|
| + // Don’t call MetricsRecordExit(). This is part of the normal exit path when
|
| + // running from launchd.
|
| +
|
| DCHECK(g_exception_handler_server);
|
| g_exception_handler_server->Stop();
|
| }
|
| @@ -251,6 +326,7 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
|
| LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr;
|
|
|
| LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
|
| Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode);
|
|
|
| if (g_original_exception_filter)
|
| @@ -259,9 +335,37 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
| return EXCEPTION_CONTINUE_SEARCH;
|
| }
|
|
|
| +// Handles events like Control-C and Control-Break on a console.
|
| +BOOL WINAPI ConsoleHandler(DWORD console_event) {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
|
| + return false;
|
| +}
|
| +
|
| +// Handles a WM_ENDSESSION message sent when the user session is ending.
|
| +class TerminateHandler final : public SessionEndWatcher {
|
| + public:
|
| + TerminateHandler() : SessionEndWatcher() {}
|
| + ~TerminateHandler() override {}
|
| +
|
| + private:
|
| + // SessionEndWatcher:
|
| + void SessionEnding() override {
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);
|
| + }
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TerminateHandler);
|
| +};
|
| +
|
| void InstallCrashHandler() {
|
| g_original_exception_filter =
|
| SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
| +
|
| + // These are termination handlers, not crash handlers, but that’s close
|
| + // enough. Note that destroying the TerminateHandler would wait for its thread
|
| + // to exit, which isn’t necessary or desirable.
|
| + SetConsoleCtrlHandler(ConsoleHandler, true);
|
| + static TerminateHandler* terminate_handler = new TerminateHandler();
|
| + ALLOW_UNUSED_LOCAL(terminate_handler);
|
| }
|
|
|
| #endif // OS_MACOSX
|
| @@ -270,6 +374,7 @@ void InstallCrashHandler() {
|
|
|
| int HandlerMain(int argc, char* argv[]) {
|
| InstallCrashHandler();
|
| + CallMetricsRecordNormalExit metrics_record_normal_exit;
|
|
|
| const base::FilePath argv0(
|
| ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));
|
| @@ -291,6 +396,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| #endif // OS_MACOSX
|
| kOptionMetrics,
|
| kOptionNoRateLimit,
|
| + kOptionNoUploadGzip,
|
| #if defined(OS_MACOSX)
|
| kOptionResetOwnCrashExceptionPortToSystemDefault,
|
| #elif defined(OS_WIN)
|
| @@ -317,11 +423,13 @@ int HandlerMain(int argc, char* argv[]) {
|
| InitialClientData initial_client_data;
|
| #endif // OS_MACOSX
|
| bool rate_limit;
|
| + bool upload_gzip;
|
| } options = {};
|
| #if defined(OS_MACOSX)
|
| options.handshake_fd = -1;
|
| #endif
|
| options.rate_limit = true;
|
| + options.upload_gzip = true;
|
|
|
| const option long_options[] = {
|
| {"annotation", required_argument, nullptr, kOptionAnnotation},
|
| @@ -340,6 +448,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| #endif // OS_MACOSX
|
| {"metrics-dir", required_argument, nullptr, kOptionMetrics},
|
| {"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
| + {"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
|
| #if defined(OS_MACOSX)
|
| {"reset-own-crash-exception-port-to-system-default",
|
| no_argument,
|
| @@ -362,7 +471,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| std::string value;
|
| if (!SplitStringFirst(optarg, '=', &key, &value)) {
|
| ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| std::string old_value;
|
| if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
|
| @@ -381,7 +490,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| options.handshake_fd < 0) {
|
| ToolSupport::UsageHint(me,
|
| "--handshake-fd requires a file descriptor");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| break;
|
| }
|
| @@ -394,7 +503,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| if (!options.initial_client_data.InitializeFromString(optarg)) {
|
| ToolSupport::UsageHint(
|
| me, "failed to parse --initial-client-data");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| break;
|
| }
|
| @@ -407,6 +516,10 @@ int HandlerMain(int argc, char* argv[]) {
|
| options.rate_limit = false;
|
| break;
|
| }
|
| + case kOptionNoUploadGzip: {
|
| + options.upload_gzip = false;
|
| + break;
|
| + }
|
| #if defined(OS_MACOSX)
|
| case kOptionResetOwnCrashExceptionPortToSystemDefault: {
|
| options.reset_own_crash_exception_port_to_system_default = true;
|
| @@ -424,15 +537,17 @@ int HandlerMain(int argc, char* argv[]) {
|
| }
|
| case kOptionHelp: {
|
| Usage(me);
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
|
| return EXIT_SUCCESS;
|
| }
|
| case kOptionVersion: {
|
| ToolSupport::Version(me);
|
| + MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
|
| return EXIT_SUCCESS;
|
| }
|
| default: {
|
| ToolSupport::UsageHint(me, nullptr);
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| }
|
| }
|
| @@ -442,34 +557,34 @@ int HandlerMain(int argc, char* argv[]) {
|
| #if defined(OS_MACOSX)
|
| if (options.handshake_fd < 0 && options.mach_service.empty()) {
|
| ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| if (options.handshake_fd >= 0 && !options.mach_service.empty()) {
|
| ToolSupport::UsageHint(
|
| me, "--handshake-fd and --mach-service are incompatible");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| #elif defined(OS_WIN)
|
| if (!options.initial_client_data.IsValid() && options.pipe_name.empty()) {
|
| ToolSupport::UsageHint(me,
|
| "--initial-client-data or --pipe-name is required");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| if (options.initial_client_data.IsValid() && !options.pipe_name.empty()) {
|
| ToolSupport::UsageHint(
|
| me, "--initial-client-data and --pipe-name are incompatible");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
| #endif // OS_MACOSX
|
|
|
| if (!options.database) {
|
| ToolSupport::UsageHint(me, "--database is required");
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
|
|
| if (argc) {
|
| ToolSupport::UsageHint(me, nullptr);
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
|
|
| #if defined(OS_MACOSX)
|
| @@ -494,7 +609,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| }
|
|
|
| if (!receive_right.is_valid()) {
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
|
|
| ExceptionHandlerServer exception_handler_server(
|
| @@ -511,6 +626,7 @@ int HandlerMain(int argc, char* argv[]) {
|
| // launchd.plist(5).
|
| //
|
| // Set up a SIGTERM handler that will call exception_handler_server.Stop().
|
| + // This replaces the HandleTerminateSignal handler for SIGTERM.
|
| struct sigaction sa = {};
|
| sigemptyset(&sa.sa_mask);
|
| sa.sa_flags = SA_SIGINFO;
|
| @@ -544,18 +660,20 @@ int HandlerMain(int argc, char* argv[]) {
|
| }
|
| }
|
|
|
| + Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted);
|
| +
|
| std::unique_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
|
| base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType(
|
| options.database))));
|
| if (!database) {
|
| - return EXIT_FAILURE;
|
| + return ExitFailure();
|
| }
|
|
|
| // TODO(scottmg): options.rate_limit should be removed when we have a
|
| // configurable database setting to control upload limiting.
|
| // See https://crashpad.chromium.org/bug/23.
|
| CrashReportUploadThread upload_thread(
|
| - database.get(), options.url, options.rate_limit);
|
| + database.get(), options.url, options.rate_limit, options.upload_gzip);
|
| upload_thread.Start();
|
|
|
| PruneCrashReportThread prune_thread(database.get(),
|
|
|