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(), |