mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
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:
parent
4b450c8137
commit
76a67a37b1
@ -138,6 +138,38 @@ establish the Crashpad client environment before running a program.
|
||||
service declared in a job’s `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 there’s 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 that’s 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");
|
||||
}
|
||||
|
||||
// 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 +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()) {
|
||||
// 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 +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();
|
||||
}
|
||||
|
3
third_party/getopt/README.crashpad
vendored
3
third_party/getopt/README.crashpad
vendored
@ -10,6 +10,9 @@ A public domain implementation of getopt.
|
||||
|
||||
Local Modifications:
|
||||
- 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.
|
||||
|
7
third_party/getopt/getopt.cc
vendored
7
third_party/getopt/getopt.cc
vendored
@ -310,8 +310,9 @@ getopt_internal (int argc, char **argv, char *shortopts,
|
||||
}
|
||||
return (optopt = '?');
|
||||
}
|
||||
has_arg = ((cp[1] == ':')
|
||||
? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument);
|
||||
has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument
|
||||
: required_argument)
|
||||
: no_argument);
|
||||
possible_arg = argv[optind] + optwhere + 1;
|
||||
optopt = *cp;
|
||||
}
|
||||
@ -319,7 +320,7 @@ getopt_internal (int argc, char **argv, char *shortopts,
|
||||
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
vendored
2
third_party/getopt/getopt.h
vendored
@ -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 */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user