Add the --monitor-self argument to crashpad_handler

https://crbug.com/678959 added “fallback” crash reporting for
crashpad_handler on Windows, in a Chrome- and Windows-specific way. This
implements a more general self-monitor mechanism that will work on
multiple platforms and in the absence of Chrome.

When starting crashpad_handler (let’s call it the “first instance”) with
--monitor-self, it will start another crashpad_handler (the “second
instance”). The second instance monitors the first one for crashes. The
second instance will be started in mostly the same way as the first
instance, except --monitor-self will not be provided to the second
instance.

Bug: crashpad:143
Change-Id: I76f3f47d1762d8ecae1814357cb672c8b7bd5e95
Reviewed-on: https://chromium-review.googlesource.com/466267
Reviewed-by: Sigurður Ásgeirsson <siggi@chromium.org>
Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Mark Mentovai 2017-04-04 10:08:56 -04:00
Родитель 4b450c8137
Коммит 76a67a37b1
5 изменённых файлов: 192 добавлений и 64 удалений

Просмотреть файл

@ -138,6 +138,38 @@ establish the Crashpad client environment before running a program.
service declared in a jobs `MachServices` dictionary (see launchd.plist(5)).
The service name may also be completely unknown to the system.
* **--metrics-dir**=_DIR_
Metrics information will be written to _DIR_. This option only has an effect
when built as part of Chromium. In non-Chromium builds, and in the absence of
this option, metrics information will not be written.
* **--monitor-self**
Causes a second instance of the Crashpad handler program to be started,
monitoring the original instance for exceptions. The original instance will
become a client of the second one. The second instance will be started with
the same **--annotation**, **--database**, **--no-rate-limit**,
**--no-upload-gzip**, and **--url** arguments as the original one. The second
instance will not be started with a **--metrics-dir** argument even if the
original instance was.
Where supported by the underlying operating system, the second instance will
be restarted should it exit before the first instance. The second instance
will not be eligible to be started asynchronously.
* **--monitor-self-argument**=_ARGUMENT_
When directed by **--monitor-self** to start a second instance of the
Crashpad handler program, the second instance will be started with _ARGUMENT_
as one of its arguments. This option may appear zero, one, or more times.
This option has no effect in the absence of **--monitor-self**.
This supports embedding the Crashpad handler into a multi-purpose executable
that dispatches to the desired entry point based on a command-line argument.
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
be `--monitor-self`.
* **--no-rate-limit**
Do not rate limit the upload of crash reports. By default uploads are

Просмотреть файл

@ -20,6 +20,7 @@
#include <stdlib.h>
#include <sys/types.h>
#include <algorithm>
#include <map>
#include <memory>
#include <string>
@ -43,6 +44,7 @@
#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 +85,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 +96,22 @@ 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-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 +121,25 @@ void Usage(const base::FilePath& me) {
ToolSupport::UsageTail(me);
}
struct Options {
std::map<std::string, std::string> 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;
};
// 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 +216,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 theres nothing to re-enable.
}
void InstallCrashHandler() {
Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr);
@ -254,9 +289,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 reintsall the metrics-recording
// one.
g_original_exception_filter =
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
}
void InstallCrashHandler() {
ReinstallCrashHandler();
// These are termination handlers, not crash handlers, but thats close
// enough. Note that destroying the TerminateHandler would wait for its thread
@ -268,6 +311,46 @@ 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");
}
// 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
// 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 +376,15 @@ int HandlerMain(int argc, char* argv[]) {
kOptionMachService,
#endif // OS_MACOSX
kOptionMetrics,
kOptionMonitorSelf,
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 +393,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 +409,21 @@ 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-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,6 +431,13 @@ 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) {
@ -379,7 +456,8 @@ int HandlerMain(int argc, char* argv[]) {
break;
}
case kOptionDatabase: {
options.database = optarg;
options.database = base::FilePath(
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
break;
}
#if defined(OS_MACOSX)
@ -396,7 +474,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 +484,18 @@ 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 kOptionMonitorSelfArgument: {
options.monitor_self_arguments.push_back(optarg);
break;
}
case kOptionNoRateLimit: {
@ -418,16 +506,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 +564,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();
}
@ -485,16 +574,22 @@ int HandlerMain(int argc, char* argv[]) {
return ExitFailure();
}
#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 defined(OS_MACOSX)
if (options.mach_service.empty()) {
// Dont 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 +638,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 +650,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();
}

15
third_party/getopt/README.crashpad поставляемый
Просмотреть файл

@ -9,9 +9,12 @@ Description:
A public domain implementation of getopt.
Local Modifications:
- Minor compilation fixes applied for Windows.
- Add copy of copyright (Public domain) to the top of both files for Chromium's
checklicenses step.
- Compiled as .cc, and wrapped in namespace crashpad.
- memcmp() -> strncmp() in getopt.cc to make ASan happier about some string
manipulation.
- Minor compilation fixes applied for Windows.
- NO_ARG, REQUIRED_ARG, and OPTIONAL_ARG were renamed to the more traditional
no_argument, required_argument, and optional_argument for source
compatibility with BSD and glibc getopt_long().
- Add copy of copyright (Public domain) to the top of both files for Chromium's
checklicenses step.
- Compiled as .cc, and wrapped in namespace crashpad.
- memcmp() -> strncmp() in getopt.cc to make ASan happier about some string
manipulation.

11
third_party/getopt/getopt.cc поставляемый
Просмотреть файл

@ -310,16 +310,17 @@ getopt_internal (int argc, char **argv, char *shortopts,
}
return (optopt = '?');
}
has_arg = ((cp[1] == ':')
? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument);
possible_arg = argv[optind] + optwhere + 1;
optopt = *cp;
has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument
: required_argument)
: no_argument);
possible_arg = argv[optind] + optwhere + 1;
optopt = *cp;
}
/* get argument and reset optwhere */
arg_next = 0;
switch (has_arg)
{
case OPTIONAL_ARG:
case optional_argument:
if (*possible_arg == '=')
possible_arg++;
if (*possible_arg != '\0')

2
third_party/getopt/getopt.h поставляемый
Просмотреть файл

@ -14,7 +14,7 @@ using it.
/* macros defined by this include file */
#define no_argument 0
#define required_argument 1
#define OPTIONAL_ARG 2
#define optional_argument 2
/* types defined by this include file */