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 16eb11426d2f9125e6670877d33abf269cc1fced..f58f1ba4cd05862e23f6fdb4967f16cb9b7e87ef 100644 |
--- a/third_party/crashpad/crashpad/handler/handler_main.cc |
+++ b/third_party/crashpad/crashpad/handler/handler_main.cc |
@@ -20,6 +20,7 @@ |
#include <stdlib.h> |
#include <sys/types.h> |
+#include <algorithm> |
#include <map> |
#include <memory> |
#include <string> |
@@ -33,16 +34,20 @@ |
#include "base/logging.h" |
#include "base/metrics/persistent_histogram_allocator.h" |
#include "base/scoped_generic.h" |
+#include "base/strings/stringprintf.h" |
#include "base/strings/utf_string_conversions.h" |
#include "build/build_config.h" |
#include "client/crash_report_database.h" |
#include "client/crashpad_client.h" |
+#include "client/crashpad_info.h" |
#include "client/prune_crash_reports.h" |
+#include "client/simple_string_dictionary.h" |
#include "handler/crash_report_upload_thread.h" |
#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/misc/paths.h" |
#include "util/numeric/in_range_cast.h" |
#include "util/stdlib/map_insert.h" |
#include "util/stdlib/string_number_conversion.h" |
@@ -83,8 +88,8 @@ void Usage(const base::FilePath& me) { |
" --database=PATH store the crash report database at PATH\n" |
#if defined(OS_MACOSX) |
" --handshake-fd=FD establish communication with the client over FD\n" |
-" --mach-service=SERVICE register SERVICE with the bootstrap server\n" |
-#elif defined(OS_WIN) |
+#endif // OS_MACOSX |
+#if defined(OS_WIN) |
" --initial-client-data=HANDLE_request_crash_dump,\n" |
" HANDLE_request_non_crash_dump,\n" |
" HANDLE_non_crash_dump_completed,\n" |
@@ -94,15 +99,24 @@ void Usage(const base::FilePath& me) { |
" Address_non_crash_exception_information,\n" |
" Address_debug_critical_section\n" |
" use precreated data to register initial client\n" |
+#endif // OS_WIN |
+#if defined(OS_MACOSX) |
+" --mach-service=SERVICE register SERVICE with the bootstrap server\n" |
#endif // OS_MACOSX |
" --metrics-dir=DIR store metrics files in DIR (only in Chromium)\n" |
+" --monitor-self run a second handler to catch crashes in the first\n" |
+" --monitor-self-annotation=KEY=VALUE\n" |
+" set a module annotation in the handler\n" |
+" --monitor-self-argument=ARGUMENT\n" |
+" provide additional arguments to the second handler\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_WIN) |
+" --pipe-name=PIPE communicate with the client over PIPE\n" |
+#endif // OS_WIN |
#if defined(OS_MACOSX) |
" --reset-own-crash-exception-port-to-system-default\n" |
" reset the server's exception handler to default\n" |
-#elif defined(OS_WIN) |
-" --pipe-name=PIPE communicate with the client over PIPE\n" |
#endif // OS_MACOSX |
" --url=URL send crash reports to this Breakpad server URL,\n" |
" only if uploads are enabled for the database\n" |
@@ -112,6 +126,49 @@ void Usage(const base::FilePath& me) { |
ToolSupport::UsageTail(me); |
} |
+struct Options { |
+ std::map<std::string, std::string> annotations; |
+ std::map<std::string, std::string> monitor_self_annotations; |
+ std::string url; |
+ base::FilePath database; |
+ base::FilePath metrics_dir; |
+ std::vector<std::string> monitor_self_arguments; |
+#if defined(OS_MACOSX) |
+ std::string mach_service; |
+ int handshake_fd; |
+ bool reset_own_crash_exception_port_to_system_default; |
+#elif defined(OS_WIN) |
+ std::string pipe_name; |
+ InitialClientData initial_client_data; |
+#endif // OS_MACOSX |
+ bool monitor_self; |
+ bool rate_limit; |
+ bool upload_gzip; |
+}; |
+ |
+// Splits |key_value| on '=' and inserts the resulting key and value into |map|. |
+// If |key_value| has the wrong format, logs an error and returns false. If the |
+// key is already in the map, logs a warning, replaces the existing value, and |
+// returns true. If the key and value were inserted into the map, returns true. |
+// |argument| is used to give context to logged messages. |
+bool AddKeyValueToMap(std::map<std::string, std::string>* map, |
+ const std::string& key_value, |
+ const char* argument) { |
+ std::string key; |
+ std::string value; |
+ if (!SplitStringFirst(key_value, '=', &key, &value)) { |
+ LOG(ERROR) << argument << " requires KEY=VALUE"; |
+ return false; |
+ } |
+ |
+ std::string old_value; |
+ if (!MapInsertOrReplace(map, key, value, &old_value)) { |
+ LOG(WARNING) << argument << " has duplicate key " << key |
+ << ", discarding value " << old_value; |
+ } |
+ return true; |
+} |
+ |
// 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 |
@@ -188,6 +245,13 @@ void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) { |
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); |
} |
+void ReinstallCrashHandler() { |
+ // This is used to re-enable the metrics-recording crash handler after |
+ // MonitorSelf() sets up a Crashpad exception handler. On macOS, the |
+ // metrics-recording handler uses signals and the Crashpad handler uses Mach |
+ // exceptions, so there’s nothing to re-enable. |
+} |
+ |
void InstallCrashHandler() { |
Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr); |
@@ -254,9 +318,17 @@ class TerminateHandler final : public SessionEndWatcher { |
DISALLOW_COPY_AND_ASSIGN(TerminateHandler); |
}; |
-void InstallCrashHandler() { |
+void ReinstallCrashHandler() { |
+ // This is used to re-enable the metrics-recording crash handler after |
+ // MonitorSelf() sets up a Crashpad exception handler. The Crashpad handler |
+ // takes over the UnhandledExceptionFilter, so reinstall the metrics-recording |
+ // one. |
g_original_exception_filter = |
SetUnhandledExceptionFilter(&UnhandledExceptionHandler); |
+} |
+ |
+void InstallCrashHandler() { |
+ ReinstallCrashHandler(); |
// These are termination handlers, not crash handlers, but that’s close |
// enough. Note that destroying the TerminateHandler would wait for its thread |
@@ -268,6 +340,52 @@ void InstallCrashHandler() { |
#endif // OS_MACOSX |
+void MonitorSelf(const Options& options) { |
+ base::FilePath executable_path; |
+ if (!Paths::Executable(&executable_path)) { |
+ return; |
+ } |
+ |
+ if (std::find(options.monitor_self_arguments.begin(), |
+ options.monitor_self_arguments.end(), |
+ "--monitor-self") != options.monitor_self_arguments.end()) { |
+ LOG(WARNING) << "--monitor-self-argument=--monitor-self is not supported"; |
+ return; |
+ } |
+ std::vector<std::string> extra_arguments(options.monitor_self_arguments); |
+ if (!options.rate_limit) { |
+ extra_arguments.push_back("--no-rate-limit"); |
+ } |
+ if (!options.upload_gzip) { |
+ extra_arguments.push_back("--no-upload-gzip"); |
+ } |
+ for (const auto& iterator : options.monitor_self_annotations) { |
+ extra_arguments.push_back( |
+ base::StringPrintf("--monitor-self-annotation=%s=%s", |
+ iterator.first.c_str(), |
+ iterator.second.c_str())); |
+ } |
+ |
+ // Don’t use options.metrics_dir. The current implementation only allows one |
+ // instance of crashpad_handler to be writing metrics at a time, and it should |
+ // be the primary instance. |
+ CrashpadClient crashpad_client; |
+ if (!crashpad_client.StartHandler(executable_path, |
+ options.database, |
+ base::FilePath(), |
+ options.url, |
+ options.annotations, |
+ extra_arguments, |
+ true, |
+ false)) { |
+ return; |
+ } |
+ |
+ // Make sure that appropriate metrics will be recorded on crash before this |
+ // process is terminated. |
+ ReinstallCrashHandler(); |
+} |
+ |
} // namespace |
int HandlerMain(int argc, char* argv[]) { |
@@ -293,12 +411,16 @@ int HandlerMain(int argc, char* argv[]) { |
kOptionMachService, |
#endif // OS_MACOSX |
kOptionMetrics, |
+ kOptionMonitorSelf, |
+ kOptionMonitorSelfAnnotation, |
+ kOptionMonitorSelfArgument, |
kOptionNoRateLimit, |
kOptionNoUploadGzip, |
+#if defined(OS_WIN) |
+ kOptionPipeName, |
+#endif // OS_WIN |
#if defined(OS_MACOSX) |
kOptionResetOwnCrashExceptionPortToSystemDefault, |
-#elif defined(OS_WIN) |
- kOptionPipeName, |
#endif // OS_MACOSX |
kOptionURL, |
@@ -307,28 +429,6 @@ int HandlerMain(int argc, char* argv[]) { |
kOptionVersion = -3, |
}; |
- struct { |
- std::map<std::string, std::string> annotations; |
- std::string url; |
- const char* database; |
- const char* metrics; |
-#if defined(OS_MACOSX) |
- int handshake_fd; |
- std::string mach_service; |
- bool reset_own_crash_exception_port_to_system_default; |
-#elif defined(OS_WIN) |
- std::string pipe_name; |
- 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}, |
{"database", required_argument, nullptr, kOptionDatabase}, |
@@ -345,15 +445,25 @@ int HandlerMain(int argc, char* argv[]) { |
{"mach-service", required_argument, nullptr, kOptionMachService}, |
#endif // OS_MACOSX |
{"metrics-dir", required_argument, nullptr, kOptionMetrics}, |
+ {"monitor-self", no_argument, nullptr, kOptionMonitorSelf}, |
+ {"monitor-self-annotation", |
+ required_argument, |
+ nullptr, |
+ kOptionMonitorSelfAnnotation}, |
+ {"monitor-self-argument", |
+ required_argument, |
+ nullptr, |
+ kOptionMonitorSelfArgument}, |
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit}, |
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip}, |
+#if defined(OS_WIN) |
+ {"pipe-name", required_argument, nullptr, kOptionPipeName}, |
+#endif // OS_WIN |
#if defined(OS_MACOSX) |
{"reset-own-crash-exception-port-to-system-default", |
no_argument, |
nullptr, |
kOptionResetOwnCrashExceptionPortToSystemDefault}, |
-#elif defined(OS_WIN) |
- {"pipe-name", required_argument, nullptr, kOptionPipeName}, |
#endif // OS_MACOSX |
{"url", required_argument, nullptr, kOptionURL}, |
{"help", no_argument, nullptr, kOptionHelp}, |
@@ -361,25 +471,25 @@ int HandlerMain(int argc, char* argv[]) { |
{nullptr, 0, nullptr, 0}, |
}; |
+ Options options = {}; |
+#if defined(OS_MACOSX) |
+ options.handshake_fd = -1; |
+#endif |
+ options.rate_limit = true; |
+ options.upload_gzip = true; |
+ |
int opt; |
while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { |
switch (opt) { |
case kOptionAnnotation: { |
- std::string key; |
- std::string value; |
- if (!SplitStringFirst(optarg, '=', &key, &value)) { |
- ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE"); |
+ if (!AddKeyValueToMap(&options.annotations, optarg, "--annotation")) { |
return ExitFailure(); |
} |
- std::string old_value; |
- if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) { |
- LOG(WARNING) << "duplicate key " << key << ", discarding value " |
- << old_value; |
- } |
break; |
} |
case kOptionDatabase: { |
- options.database = optarg; |
+ options.database = base::FilePath( |
+ ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); |
break; |
} |
#if defined(OS_MACOSX) |
@@ -396,7 +506,8 @@ int HandlerMain(int argc, char* argv[]) { |
options.mach_service = optarg; |
break; |
} |
-#elif defined(OS_WIN) |
+#endif // OS_MACOSX |
+#if defined(OS_WIN) |
case kOptionInitialClientData: { |
if (!options.initial_client_data.InitializeFromString(optarg)) { |
ToolSupport::UsageHint( |
@@ -405,9 +516,26 @@ int HandlerMain(int argc, char* argv[]) { |
} |
break; |
} |
-#endif // OS_MACOSX |
+#endif // OS_WIN |
case kOptionMetrics: { |
- options.metrics = optarg; |
+ options.metrics_dir = base::FilePath( |
+ ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); |
+ break; |
+ } |
+ case kOptionMonitorSelf: { |
+ options.monitor_self = true; |
+ break; |
+ } |
+ case kOptionMonitorSelfAnnotation: { |
+ if (!AddKeyValueToMap(&options.monitor_self_annotations, |
+ optarg, |
+ "--monitor-self-annotation")) { |
+ return ExitFailure(); |
+ } |
+ break; |
+ } |
+ case kOptionMonitorSelfArgument: { |
+ options.monitor_self_arguments.push_back(optarg); |
break; |
} |
case kOptionNoRateLimit: { |
@@ -418,16 +546,17 @@ int HandlerMain(int argc, char* argv[]) { |
options.upload_gzip = false; |
break; |
} |
+#if defined(OS_WIN) |
+ case kOptionPipeName: { |
+ options.pipe_name = optarg; |
+ break; |
+ } |
+#endif // OS_WIN |
#if defined(OS_MACOSX) |
case kOptionResetOwnCrashExceptionPortToSystemDefault: { |
options.reset_own_crash_exception_port_to_system_default = true; |
break; |
} |
-#elif defined(OS_WIN) |
- case kOptionPipeName: { |
- options.pipe_name = optarg; |
- break; |
- } |
#endif // OS_MACOSX |
case kOptionURL: { |
options.url = optarg; |
@@ -475,7 +604,7 @@ int HandlerMain(int argc, char* argv[]) { |
} |
#endif // OS_MACOSX |
- if (!options.database) { |
+ if (options.database.empty()) { |
ToolSupport::UsageHint(me, "--database is required"); |
return ExitFailure(); |
} |
@@ -486,15 +615,41 @@ int HandlerMain(int argc, char* argv[]) { |
} |
#if defined(OS_MACOSX) |
+ if (options.reset_own_crash_exception_port_to_system_default) { |
+ CrashpadClient::UseSystemDefaultHandler(); |
+ } |
+#endif // OS_MACOSX |
+ |
+ if (options.monitor_self) { |
+ MonitorSelf(options); |
+ } |
+ |
+ if (!options.monitor_self_annotations.empty()) { |
+ // Establish these annotations even if --monitor-self is not present, in |
+ // case something such as generate_dump wants to try to access them later. |
+ // |
+ // If the handler is part of a multi-purpose executable, simple annotations |
+ // may already be present for this module. If they are, use them. |
+ CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); |
+ SimpleStringDictionary* module_annotations = |
+ crashpad_info->simple_annotations(); |
+ if (!module_annotations) { |
+ module_annotations = new SimpleStringDictionary(); |
+ crashpad_info->set_simple_annotations(module_annotations); |
+ } |
+ |
+ for (const auto& iterator : options.monitor_self_annotations) { |
+ module_annotations->SetKeyValue(iterator.first.c_str(), |
+ iterator.second.c_str()); |
+ } |
+ } |
+ |
+#if defined(OS_MACOSX) |
if (options.mach_service.empty()) { |
// Don’t do this when being run by launchd. See launchd.plist(5). |
CloseStdinAndStdout(); |
} |
- if (options.reset_own_crash_exception_port_to_system_default) { |
- CrashpadClient::UseSystemDefaultHandler(); |
- } |
- |
base::mac::ScopedMachReceiveRight receive_right; |
if (options.handshake_fd >= 0) { |
@@ -543,13 +698,11 @@ int HandlerMain(int argc, char* argv[]) { |
#endif // OS_MACOSX |
base::GlobalHistogramAllocator* histogram_allocator = nullptr; |
- if (options.metrics) { |
- const base::FilePath metrics_dir( |
- ToolSupport::CommandLineArgumentToFilePathStringType(options.metrics)); |
+ if (!options.metrics_dir.empty()) { |
static const char kMetricsName[] = "CrashpadMetrics"; |
const size_t kMetricsFileSize = 1 << 20; |
if (base::GlobalHistogramAllocator::CreateWithActiveFileInDir( |
- metrics_dir, kMetricsFileSize, 0, kMetricsName)) { |
+ options.metrics_dir, kMetricsFileSize, 0, kMetricsName)) { |
histogram_allocator = base::GlobalHistogramAllocator::Get(); |
histogram_allocator->CreateTrackingHistograms(kMetricsName); |
} |
@@ -557,9 +710,8 @@ int HandlerMain(int argc, char* argv[]) { |
Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted); |
- std::unique_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize( |
- base::FilePath(ToolSupport::CommandLineArgumentToFilePathStringType( |
- options.database)))); |
+ std::unique_ptr<CrashReportDatabase> database( |
+ CrashReportDatabase::Initialize(options.database)); |
if (!database) { |
return ExitFailure(); |
} |