handler: Add --monitor-self-annotations

--monitor-self-annotations allows the Crashpad-using application to push
module-level annotations in to crashpad_handler. These annotations will
appear in any crash report written for that handler by --monitor-self.

Bug: crashpad:143
Change-Id: If47395da75a90be4f4bdce0630ce95ea93f9fcf3
Reviewed-on: https://chromium-review.googlesource.com/467746
Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Mark Mentovai 2017-04-04 14:26:52 -04:00
parent 45305395ad
commit 8f07f7481a
2 changed files with 78 additions and 14 deletions

View File

@ -149,15 +149,28 @@ establish the Crashpad client environment before running a program.
Causes a second instance of the Crashpad handler program to be started, Causes a second instance of the Crashpad handler program to be started,
monitoring the original instance for exceptions. The original instance will monitoring the original instance for exceptions. The original instance will
become a client of the second one. The second instance will be started with become a client of the second one. The second instance will be started with
the same **--annotation**, **--database**, **--no-rate-limit**, the same **--annotation**, **--database**, **--monitor-self-annotation**,
**--no-upload-gzip**, and **--url** arguments as the original one. The second **--no-rate-limit**, **--no-upload-gzip**, and **--url** arguments as the
instance will not be started with a **--metrics-dir** argument even if the original one. The second instance will not be started with a
original instance was. **--metrics-dir** argument even if the original instance was.
Where supported by the underlying operating system, the second instance will Where supported by the underlying operating system, the second instance will
be restarted should it exit before the first instance. The second instance be restarted should it exit before the first instance. The second instance
will not be eligible to be started asynchronously. will not be eligible to be started asynchronously.
* **--monitor-self-annotation**=_KEY_=_VALUE_
Sets a module-level annotation mapping _KEY_ to _VALUE_ in the Crashpad
handler. This option may appear zero, one, or more times.
If **--monitor-self** is in use, the second instance of the Crashpad handler
program will find these annotations stored in the original instance and will
include them in any crash reports written for the original instance.
These annotations will only appear in crash reports written for the Crashpad
handler itself. To apply a process-level annotation to all crash reports
written by an instance of the Crashpad handler, use **--annotation** instead.
* **--monitor-self-argument**=_ARGUMENT_ * **--monitor-self-argument**=_ARGUMENT_
When directed by **--monitor-self** to start a second instance of the When directed by **--monitor-self** to start a second instance of the

View File

@ -34,11 +34,14 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/metrics/persistent_histogram_allocator.h" #include "base/metrics/persistent_histogram_allocator.h"
#include "base/scoped_generic.h" #include "base/scoped_generic.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "client/crash_report_database.h" #include "client/crash_report_database.h"
#include "client/crashpad_client.h" #include "client/crashpad_client.h"
#include "client/crashpad_info.h"
#include "client/prune_crash_reports.h" #include "client/prune_crash_reports.h"
#include "client/simple_string_dictionary.h"
#include "handler/crash_report_upload_thread.h" #include "handler/crash_report_upload_thread.h"
#include "handler/prune_crash_reports_thread.h" #include "handler/prune_crash_reports_thread.h"
#include "tools/tool_support.h" #include "tools/tool_support.h"
@ -102,6 +105,8 @@ void Usage(const base::FilePath& me) {
#endif // OS_MACOSX #endif // OS_MACOSX
" --metrics-dir=DIR store metrics files in DIR (only in Chromium)\n" " --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 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" " --monitor-self-argument=ARGUMENT\n"
" provide additional arguments to the second handler\n" " provide additional arguments to the second handler\n"
" --no-rate-limit don't rate limit crash uploads\n" " --no-rate-limit don't rate limit crash uploads\n"
@ -123,6 +128,7 @@ void Usage(const base::FilePath& me) {
struct Options { struct Options {
std::map<std::string, std::string> annotations; std::map<std::string, std::string> annotations;
std::map<std::string, std::string> monitor_self_annotations;
std::string url; std::string url;
base::FilePath database; base::FilePath database;
base::FilePath metrics_dir; base::FilePath metrics_dir;
@ -140,6 +146,29 @@ struct Options {
bool upload_gzip; 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 // Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is
// to prevent multiple exit events from inadvertently being recorded, which // to prevent multiple exit events from inadvertently being recorded, which
// might happen if a crash occurs during destruction in what would otherwise be // might happen if a crash occurs during destruction in what would otherwise be
@ -292,7 +321,7 @@ class TerminateHandler final : public SessionEndWatcher {
void ReinstallCrashHandler() { void ReinstallCrashHandler() {
// This is used to re-enable the metrics-recording crash handler after // This is used to re-enable the metrics-recording crash handler after
// MonitorSelf() sets up a Crashpad exception handler. The Crashpad handler // MonitorSelf() sets up a Crashpad exception handler. The Crashpad handler
// takes over the UnhandledExceptionFilter, so reintsall the metrics-recording // takes over the UnhandledExceptionFilter, so reinstall the metrics-recording
// one. // one.
g_original_exception_filter = g_original_exception_filter =
SetUnhandledExceptionFilter(&UnhandledExceptionHandler); SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
@ -330,6 +359,12 @@ void MonitorSelf(const Options& options) {
if (!options.upload_gzip) { if (!options.upload_gzip) {
extra_arguments.push_back("--no-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()));
}
// Dont use options.metrics_dir. The current implementation only allows one // Dont use options.metrics_dir. The current implementation only allows one
// instance of crashpad_handler to be writing metrics at a time, and it should // instance of crashpad_handler to be writing metrics at a time, and it should
@ -377,6 +412,7 @@ int HandlerMain(int argc, char* argv[]) {
#endif // OS_MACOSX #endif // OS_MACOSX
kOptionMetrics, kOptionMetrics,
kOptionMonitorSelf, kOptionMonitorSelf,
kOptionMonitorSelfAnnotation,
kOptionMonitorSelfArgument, kOptionMonitorSelfArgument,
kOptionNoRateLimit, kOptionNoRateLimit,
kOptionNoUploadGzip, kOptionNoUploadGzip,
@ -410,6 +446,10 @@ int HandlerMain(int argc, char* argv[]) {
#endif // OS_MACOSX #endif // OS_MACOSX
{"metrics-dir", required_argument, nullptr, kOptionMetrics}, {"metrics-dir", required_argument, nullptr, kOptionMetrics},
{"monitor-self", no_argument, nullptr, kOptionMonitorSelf}, {"monitor-self", no_argument, nullptr, kOptionMonitorSelf},
{"monitor-self-annotation",
required_argument,
nullptr,
kOptionMonitorSelfAnnotation},
{"monitor-self-argument", {"monitor-self-argument",
required_argument, required_argument,
nullptr, nullptr,
@ -442,17 +482,9 @@ int HandlerMain(int argc, char* argv[]) {
while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
switch (opt) { switch (opt) {
case kOptionAnnotation: { case kOptionAnnotation: {
std::string key; if (!AddKeyValueToMap(&options.annotations, optarg, "--annotation")) {
std::string value;
if (!SplitStringFirst(optarg, '=', &key, &value)) {
ToolSupport::UsageHint(me, "--annotation requires KEY=VALUE");
return ExitFailure(); return ExitFailure();
} }
std::string old_value;
if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {
LOG(WARNING) << "duplicate key " << key << ", discarding value "
<< old_value;
}
break; break;
} }
case kOptionDatabase: { case kOptionDatabase: {
@ -494,6 +526,14 @@ int HandlerMain(int argc, char* argv[]) {
options.monitor_self = true; options.monitor_self = true;
break; break;
} }
case kOptionMonitorSelfAnnotation: {
if (!AddKeyValueToMap(&options.monitor_self_annotations,
optarg,
"--monitor-self-annotation")) {
return ExitFailure();
}
break;
}
case kOptionMonitorSelfArgument: { case kOptionMonitorSelfArgument: {
options.monitor_self_arguments.push_back(optarg); options.monitor_self_arguments.push_back(optarg);
break; break;
@ -584,6 +624,17 @@ int HandlerMain(int argc, char* argv[]) {
MonitorSelf(options); 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.
SimpleStringDictionary* module_annotations = new SimpleStringDictionary();
for (const auto& iterator : options.monitor_self_annotations) {
module_annotations->SetKeyValue(iterator.first.c_str(),
iterator.second.c_str());
}
CrashpadInfo::GetCrashpadInfo()->set_simple_annotations(module_annotations);
}
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
if (options.mach_service.empty()) { if (options.mach_service.empty()) {
// Dont do this when being run by launchd. See launchd.plist(5). // Dont do this when being run by launchd. See launchd.plist(5).