diff --git a/handler/crashpad_handler.md b/handler/crashpad_handler.md index 5eff39ae..003ee2ef 100644 --- a/handler/crashpad_handler.md +++ b/handler/crashpad_handler.md @@ -73,6 +73,13 @@ when run normally from a shell using only the basename (without an explicit stdio will be hooked up as expected to the parent console so that logging output will be visible. +On Linux/Android, the handler may create a crash dump for its parent process +using **--trace-parent-with-exception**. In this mode, the handler process +creates a crash dump for its parent and exits. Alternatively, the handler may +be launched with **--initial-client-fd** which will start the server connected +to an initial client. The server will exit when all connected client sockets are +closed. + It is not normally appropriate to invoke this program directly. Usually, it will be invoked by a Crashpad client using the Crashpad client library, or started by another system service. On macOS, arbitrary programs may be run with a Crashpad @@ -238,6 +245,18 @@ establish the Crashpad client environment before running a program. parent process. This option is only valid on macOS. Use of this option is discouraged. It should not be used absent extraordinary circumstances. + * **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_ + + Causes the handler process to trace its parent process and exit. The parent + process should have an ExceptionInformation struct at + _EXCEPTION-INFORMATION-ADDRESS_. + + * **--initial-client-fd**=_FD_ + + Starts the excetion handler server with an initial ExceptionHandlerClient + connected on the socket _FD_. The server will exit when all connected client + sockets have been closed. + * **--url**=_URL_ If uploads are enabled, sends crash reports to the Breakpad-type crash report diff --git a/handler/handler.gyp b/handler/handler.gyp index f29706cd..60a6f251 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -58,13 +58,6 @@ 'win/crash_report_exception_handler.cc', 'win/crash_report_exception_handler.h', ], - 'conditions': [ - ['OS=="android"', { - 'sources!': [ - 'handler_main.cc', - ], - }], - ], 'target_conditions': [ ['OS=="android"', { 'sources/': [ diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 451214a4..dd7adf65 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -34,6 +34,7 @@ #include "base/logging.h" #include "base/metrics/persistent_histogram_allocator.h" #include "base/scoped_generic.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -46,6 +47,7 @@ #include "handler/prune_crash_reports_thread.h" #include "tools/tool_support.h" #include "util/file/file_io.h" +#include "util/misc/address_types.h" #include "util/misc/metrics.h" #include "util/misc/paths.h" #include "util/numeric/in_range_cast.h" @@ -54,7 +56,13 @@ #include "util/string/split_string.h" #include "util/synchronization/semaphore.h" -#if defined(OS_MACOSX) +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include + +#include "handler/linux/crash_report_exception_handler.h" +#include "handler/linux/exception_handler_server.h" +#include "util/posix/signals.h" +#elif defined(OS_MACOSX) #include #include @@ -129,6 +137,11 @@ void Usage(const base::FilePath& me) { " --reset-own-crash-exception-port-to-system-default\n" " reset the server's exception handler to default\n" #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) +" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n" +" request a dump for the handler's parent process\n" +" --initial-client-fd=FD a socket connected to a client.\n" +#endif // OS_LINUX || OS_ANDROID " --url=URL send crash reports to this Breakpad server URL,\n" " only if uploads are enabled for the database\n" " --help display this help and exit\n" @@ -148,6 +161,9 @@ struct Options { std::string mach_service; int handshake_fd; bool reset_own_crash_exception_port_to_system_default; +#elif defined(OS_LINUX) || defined(OS_ANDROID) + VMAddress exception_information_address; + int initial_client_fd; #elif defined(OS_WIN) std::string pipe_name; InitialClientData initial_client_data; @@ -214,7 +230,9 @@ class CallMetricsRecordNormalExit { DISALLOW_COPY_AND_ASSIGN(CallMetricsRecordNormalExit); }; -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID) + +Signals::OldActions g_old_crash_signal_handlers; void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) { MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed); @@ -250,7 +268,9 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) { } Metrics::HandlerCrashed(metrics_code); - Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + struct sigaction* old_action = + g_old_crash_signal_handlers.ActionForSignal(sig); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action); } void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) { @@ -258,6 +278,8 @@ void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) { Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); } +#if defined(OS_MACOSX) + void ReinstallCrashHandler() { // This is used to re-enable the metrics-recording crash handler after // MonitorSelf() sets up a Crashpad exception handler. On macOS, the @@ -296,6 +318,23 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { g_exception_handler_server->Stop(); } +#else + +void ReinstallCrashHandler() { + // This is used to re-enable the metrics-recording crash handler after + // MonitorSelf() sets up a Crashpad signal handler. + Signals::InstallCrashHandlers( + HandleCrashSignal, 0, &g_old_crash_signal_handlers); +} + +void InstallCrashHandler() { + ReinstallCrashHandler(); + + Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr); +} + +#endif // OS_MACOSX + #elif defined(OS_WIN) LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr; @@ -401,6 +440,16 @@ void MonitorSelf(const Options& options) { // instance of crashpad_handler to be writing metrics at a time, and it should // be the primary instance. CrashpadClient crashpad_client; +#if defined(OS_LINUX) || defined(OS_ANDROID) + if (!crashpad_client.StartHandlerAtCrash(executable_path, + options.database, + base::FilePath(), + options.url, + options.annotations, + extra_arguments)) { + return; + } +#else if (!crashpad_client.StartHandler(executable_path, options.database, base::FilePath(), @@ -411,6 +460,7 @@ void MonitorSelf(const Options& options) { false)) { return; } +#endif // Make sure that appropriate metrics will be recorded on crash before this // process is terminated. @@ -477,6 +527,10 @@ int HandlerMain(int argc, #if defined(OS_MACOSX) kOptionResetOwnCrashExceptionPortToSystemDefault, #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) + kOptionTraceParentWithException, + kOptionInitialClientFD, +#endif kOptionURL, // Standard options. @@ -525,6 +579,13 @@ int HandlerMain(int argc, nullptr, kOptionResetOwnCrashExceptionPortToSystemDefault}, #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) + {"trace-parent-with-exception", + required_argument, + nullptr, + kOptionTraceParentWithException}, + {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD}, +#endif // OS_LINUX || OS_ANDROID {"url", required_argument, nullptr, kOptionURL}, {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, @@ -539,6 +600,10 @@ int HandlerMain(int argc, options.periodic_tasks = true; options.rate_limit = true; options.upload_gzip = true; +#if defined(OS_LINUX) || defined(OS_ANDROID) + options.exception_information_address = 0; + options.initial_client_fd = kInvalidFileHandle; +#endif int opt; while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { @@ -628,6 +693,23 @@ int HandlerMain(int argc, break; } #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) + case kOptionTraceParentWithException: { + if (!StringToNumber(optarg, &options.exception_information_address)) { + ToolSupport::UsageHint( + me, "failed to parse --trace-parent-with-exception"); + return ExitFailure(); + } + break; + } + case kOptionInitialClientFD: { + if (!base::StringToInt(optarg, &options.initial_client_fd)) { + ToolSupport::UsageHint(me, "failed to parse --initial-client-fd"); + return ExitFailure(); + } + break; + } +#endif // OS_LINUX || OS_ANDROID case kOptionURL: { options.url = optarg; break; @@ -672,6 +754,14 @@ int HandlerMain(int argc, me, "--initial-client-data and --pipe-name are incompatible"); return ExitFailure(); } +#elif defined(OS_LINUX) || defined(OS_ANDROID) + if (!options.exception_information_address && + options.initial_client_fd == kInvalidFileHandle) { + ToolSupport::UsageHint( + me, + "--exception_information_address or --initial_client_fd is required"); + return ExitFailure(); + } #endif // OS_MACOSX if (options.database.empty()) { @@ -714,6 +804,50 @@ int HandlerMain(int argc, } } + std::unique_ptr database( + CrashReportDatabase::Initialize(options.database)); + if (!database) { + return ExitFailure(); + } + + ScopedStoppable upload_thread; + if (!options.url.empty()) { + // 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::Options upload_thread_options; + upload_thread_options.identify_client_via_url = + options.identify_client_via_url; + upload_thread_options.rate_limit = options.rate_limit; + upload_thread_options.upload_gzip = options.upload_gzip; + upload_thread_options.watch_pending_reports = options.periodic_tasks; + + upload_thread.Reset(new CrashReportUploadThread( + database.get(), options.url, upload_thread_options)); + upload_thread.Get()->Start(); + } + + CrashReportExceptionHandler exception_handler( + database.get(), + static_cast(upload_thread.Get()), + &options.annotations, + user_stream_sources); + + #if defined(OS_LINUX) || defined(OS_ANDROID) + if (options.exception_information_address) { + return exception_handler.HandleException(getppid(), + options.exception_information_address) ? + EXIT_SUCCESS : ExitFailure(); + } +#endif // OS_LINUX || OS_ANDROID + + ScopedStoppable prune_thread; + if (options.periodic_tasks) { + prune_thread.Reset(new PruneCrashReportThread( + database.get(), PruneCondition::GetDefault())); + prune_thread.Get()->Start(); + } + #if defined(OS_MACOSX) if (options.mach_service.empty()) { // Don’t do this when being run by launchd. See launchd.plist(5). @@ -767,7 +901,7 @@ int HandlerMain(int argc, if (!options.pipe_name.empty()) { exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name)); } -#elif defined(OS_FUCHSIA) || defined(OS_LINUX) +#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) ExceptionHandlerServer exception_handler_server; #endif // OS_MACOSX @@ -784,47 +918,17 @@ int HandlerMain(int argc, Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted); - std::unique_ptr database( - CrashReportDatabase::Initialize(options.database)); - if (!database) { - return ExitFailure(); - } - - ScopedStoppable upload_thread; - if (!options.url.empty()) { - // 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::Options upload_thread_options; - upload_thread_options.identify_client_via_url = - options.identify_client_via_url; - upload_thread_options.rate_limit = options.rate_limit; - upload_thread_options.upload_gzip = options.upload_gzip; - upload_thread_options.watch_pending_reports = options.periodic_tasks; - - upload_thread.Reset(new CrashReportUploadThread( - database.get(), options.url, upload_thread_options)); - upload_thread.Get()->Start(); - } - - ScopedStoppable prune_thread; - if (options.periodic_tasks) { - prune_thread.Reset(new PruneCrashReportThread( - database.get(), PruneCondition::GetDefault())); - prune_thread.Get()->Start(); - } - - CrashReportExceptionHandler exception_handler( - database.get(), - static_cast(upload_thread.Get()), - &options.annotations, - user_stream_sources); - #if defined(OS_WIN) if (options.initial_client_data.IsValid()) { exception_handler_server.InitializeWithInheritedDataForInitialClient( options.initial_client_data, &exception_handler); } +#elif defined(OS_LINUX) || defined(OS_ANDROID) + if (options.initial_client_fd == kInvalidFileHandle || + !exception_handler_server.InitializeWithClient( + ScopedFileHandle(options.initial_client_fd))) { + return ExitFailure(); + } #endif // OS_WIN exception_handler_server.Run(&exception_handler);