linux: Implement StartHandler()

This CL adds a RequestCrashDumpHandler to request a crash dump over a
socket. Common functionality with LaunchAtCrashHandler is factored out
into a SignalHandler base class.

Bug: crashpad:284
Change-Id: I86293ef599a0dd6eea63c096a5c931c620c05ecc
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1568985
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2019-05-01 22:19:21 -07:00
parent e23286dc37
commit 607c80e0b8
5 changed files with 348 additions and 189 deletions

View File

@ -78,6 +78,10 @@ class CrashpadClient {
//! On Fuchsia, this method binds to the exception port of the current default //! On Fuchsia, this method binds to the exception port of the current default
//! job, and starts a Crashpad handler to monitor that port. //! job, and starts a Crashpad handler to monitor that port.
//! //!
//! On Linux, this method starts a Crashpad handler, connected to this process
//! via an `AF_UNIX` socket pair and installs signal handlers to request crash
//! dumps on the client's socket end.
//!
//! \param[in] handler The path to a Crashpad handler executable. //! \param[in] handler The path to a Crashpad handler executable.
//! \param[in] database The path to a Crashpad database. The handler will be //! \param[in] database The path to a Crashpad database. The handler will be
//! started with this path as its `--database` argument. //! started with this path as its `--database` argument.

View File

@ -31,6 +31,7 @@
#include "util/linux/exception_information.h" #include "util/linux/exception_information.h"
#include "util/linux/scoped_pr_set_dumpable.h" #include "util/linux/scoped_pr_set_dumpable.h"
#include "util/linux/scoped_pr_set_ptracer.h" #include "util/linux/scoped_pr_set_ptracer.h"
#include "util/linux/socket.h"
#include "util/misc/from_pointer_cast.h" #include "util/misc/from_pointer_cast.h"
#include "util/posix/double_fork_and_exec.h" #include "util/posix/double_fork_and_exec.h"
#include "util/posix/signals.h" #include "util/posix/signals.h"
@ -43,7 +44,7 @@ std::string FormatArgumentInt(const std::string& name, int value) {
return base::StringPrintf("--%s=%d", name.c_str(), value); return base::StringPrintf("--%s=%d", name.c_str(), value);
} }
std::string FormatArgumentAddress(const std::string& name, void* addr) { std::string FormatArgumentAddress(const std::string& name, const void* addr) {
return base::StringPrintf("--%s=%p", name.c_str(), addr); return base::StringPrintf("--%s=%p", name.c_str(), addr);
} }
@ -110,8 +111,90 @@ std::vector<std::string> BuildArgsToLaunchWithLinker(
#endif // OS_ANDROID #endif // OS_ANDROID
// A base class for Crashpad signal handler implementations.
class SignalHandler {
public:
// Returns the currently installed signal hander. May be `nullptr` if no
// handler has been installed.
static SignalHandler* Get() { return handler_; }
// Disables any installed Crashpad signal handler for the calling thread. If a
// crash signal is received, any previously installed (non-Crashpad) signal
// handler will be restored and the signal reraised.
static void DisableForThread() { disabled_for_thread_ = true; }
void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
first_chance_handler_ = handler;
}
// The base implementation for all signal handlers, suitable for calling
// directly to simulate signal delivery.
bool HandleCrash(int signo, siginfo_t* siginfo, void* context) {
if (disabled_for_thread_) {
return false;
}
if (first_chance_handler_ &&
first_chance_handler_(
signo, siginfo, static_cast<ucontext_t*>(context))) {
return true;
}
exception_information_.siginfo_address =
FromPointerCast<decltype(exception_information_.siginfo_address)>(
siginfo);
exception_information_.context_address =
FromPointerCast<decltype(exception_information_.context_address)>(
context);
exception_information_.thread_id = sys_gettid();
HandleCrashImpl();
return false;
}
protected:
SignalHandler() = default;
bool Install() {
DCHECK(!handler_);
handler_ = this;
return Signals::InstallCrashHandlers(
HandleOrReraiseSignal, 0, &old_actions_);
}
const ExceptionInformation& GetExceptionInfo() {
return exception_information_;
}
virtual void HandleCrashImpl() = 0;
private:
// The signal handler installed at OS-level.
static void HandleOrReraiseSignal(int signo,
siginfo_t* siginfo,
void* context) {
if (handler_->HandleCrash(signo, siginfo, context)) {
return;
}
Signals::RestoreHandlerAndReraiseSignalOnReturn(
siginfo, handler_->old_actions_.ActionForSignal(signo));
}
Signals::OldActions old_actions_ = {};
ExceptionInformation exception_information_ = {};
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
static SignalHandler* handler_;
static thread_local bool disabled_for_thread_;
DISALLOW_COPY_AND_ASSIGN(SignalHandler);
};
SignalHandler* SignalHandler::handler_ = nullptr;
thread_local bool SignalHandler::disabled_for_thread_ = false;
// Launches a single use handler to snapshot this process. // Launches a single use handler to snapshot this process.
class LaunchAtCrashHandler { class LaunchAtCrashHandler : public SignalHandler {
public: public:
static LaunchAtCrashHandler* Get() { static LaunchAtCrashHandler* Get() {
static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
@ -129,33 +212,19 @@ class LaunchAtCrashHandler {
} }
argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception", argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
&exception_information_)); &GetExceptionInfo()));
StringVectorToCStringVector(argv_strings_, &argv_); StringVectorToCStringVector(argv_strings_, &argv_);
return Signals::InstallCrashHandlers(HandleCrash, 0, &old_actions_); return Install();
} }
bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) { void HandleCrashImpl() override {
if (first_chance_handler_ &&
first_chance_handler_(
signo, siginfo, static_cast<ucontext_t*>(context))) {
return true;
}
exception_information_.siginfo_address =
FromPointerCast<decltype(exception_information_.siginfo_address)>(
siginfo);
exception_information_.context_address =
FromPointerCast<decltype(exception_information_.context_address)>(
context);
exception_information_.thread_id = syscall(SYS_gettid);
ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false); ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false);
ScopedPrSetDumpable set_dumpable(/* may_log= */ false); ScopedPrSetDumpable set_dumpable(/* may_log= */ false);
pid_t pid = fork(); pid_t pid = fork();
if (pid < 0) { if (pid < 0) {
return false; return;
} }
if (pid == 0) { if (pid == 0) {
if (set_envp_) { if (set_envp_) {
@ -170,52 +239,72 @@ class LaunchAtCrashHandler {
int status; int status;
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
return false;
} }
void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) {
if (enabled_ && HandleCrashNonFatal(signo, siginfo, context)) {
return;
}
Signals::RestoreHandlerAndReraiseSignalOnReturn(
siginfo, old_actions_.ActionForSignal(signo));
}
void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
first_chance_handler_ = handler;
}
static void Disable() { enabled_ = false; }
private: private:
LaunchAtCrashHandler() = default; LaunchAtCrashHandler() = default;
~LaunchAtCrashHandler() = delete; ~LaunchAtCrashHandler() = delete;
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
auto state = Get();
state->HandleCrashFatal(signo, siginfo, context);
}
Signals::OldActions old_actions_ = {};
std::vector<std::string> argv_strings_; std::vector<std::string> argv_strings_;
std::vector<const char*> argv_; std::vector<const char*> argv_;
std::vector<std::string> envp_strings_; std::vector<std::string> envp_strings_;
std::vector<const char*> envp_; std::vector<const char*> envp_;
ExceptionInformation exception_information_;
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
bool set_envp_ = false; bool set_envp_ = false;
static thread_local bool enabled_;
DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
}; };
thread_local bool LaunchAtCrashHandler::enabled_ = true;
// A pointer to the currently installed crash signal handler. This allows class RequestCrashDumpHandler : public SignalHandler {
// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash public:
// using the currently configured crash handling strategy. static RequestCrashDumpHandler* Get() {
static LaunchAtCrashHandler* g_crash_handler; static RequestCrashDumpHandler* instance = new RequestCrashDumpHandler();
return instance;
}
// pid < 0 indicates the handler pid should be determined by communicating
// over the socket.
// pid == 0 indicates it is not necessary to set the handler as this process'
// ptracer. e.g. if the handler has CAP_SYS_PTRACE or if this process is in a
// user namespace and the handler's uid matches the uid of the process that
// created the namespace.
// pid > 0 directly indicates what the handler's pid is expected to be, so
// retrieving this information from the handler is not necessary.
bool Initialize(ScopedFileHandle sock, pid_t pid) {
ExceptionHandlerClient client(sock.get(), true);
if (pid < 0) {
ucred creds;
if (!client.GetHandlerCredentials(&creds)) {
return false;
}
pid = creds.pid;
}
if (pid > 0 && client.SetPtracer(pid) != 0) {
LOG(ERROR) << "failed to set ptracer";
return false;
}
sock_to_handler_.reset(sock.release());
return Install();
}
void HandleCrashImpl() override {
ExceptionHandlerProtocol::ClientInformation info = {};
info.exception_information_address =
FromPointerCast<VMAddress>(&GetExceptionInfo());
ExceptionHandlerClient client(sock_to_handler_.get(), true);
client.RequestCrashDump(info);
}
private:
RequestCrashDumpHandler() = default;
~RequestCrashDumpHandler() = delete;
ScopedFileHandle sock_to_handler_;
DISALLOW_COPY_AND_ASSIGN(RequestCrashDumpHandler);
};
} // namespace } // namespace
@ -232,13 +321,28 @@ bool CrashpadClient::StartHandler(
const std::vector<std::string>& arguments, const std::vector<std::string>& arguments,
bool restartable, bool restartable,
bool asynchronous_start) { bool asynchronous_start) {
// TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever DCHECK(!restartable);
// supports accepting new connections. DCHECK(!asynchronous_start);
// https://crashpad.chromium.org/bug/30
NOTREACHED(); ScopedFileHandle client_sock, handler_sock;
if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock,
&handler_sock)) {
return false; return false;
} }
std::vector<std::string> argv = BuildHandlerArgvStrings(
handler, database, metrics_dir, url, annotations, arguments);
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
argv.push_back("--shared-client-connection");
if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
return false;
}
auto signal_handler = RequestCrashDumpHandler::Get();
return signal_handler->Initialize(std::move(client_sock), -1);
}
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// static // static
@ -259,12 +363,7 @@ bool CrashpadClient::StartJavaHandlerAtCrash(
kInvalidFileHandle); kInvalidFileHandle);
auto signal_handler = LaunchAtCrashHandler::Get(); auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, env)) { return signal_handler->Initialize(&argv, env);
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
} }
// static // static
@ -304,12 +403,7 @@ bool CrashpadClient::StartHandlerWithLinkerAtCrash(
arguments, arguments,
kInvalidFileHandle); kInvalidFileHandle);
auto signal_handler = LaunchAtCrashHandler::Get(); auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, env)) { return signal_handler->Initialize(&argv, env);
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
} }
// static // static
@ -351,12 +445,7 @@ bool CrashpadClient::StartHandlerAtCrash(
handler, database, metrics_dir, url, annotations, arguments); handler, database, metrics_dir, url, annotations, arguments);
auto signal_handler = LaunchAtCrashHandler::Get(); auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, nullptr)) { return signal_handler->Initialize(&argv, nullptr);
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
} }
// static // static
@ -378,7 +467,7 @@ bool CrashpadClient::StartHandlerForClient(
// static // static
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
if (!g_crash_handler) { if (!SignalHandler::Get()) {
DLOG(ERROR) << "Crashpad isn't enabled"; DLOG(ERROR) << "Crashpad isn't enabled";
return; return;
} }
@ -395,21 +484,21 @@ void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
siginfo.si_signo = Signals::kSimulatedSigno; siginfo.si_signo = Signals::kSimulatedSigno;
siginfo.si_errno = 0; siginfo.si_errno = 0;
siginfo.si_code = 0; siginfo.si_code = 0;
g_crash_handler->HandleCrashNonFatal( SignalHandler::Get()->HandleCrash(
siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context)); siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
} }
// static // static
void CrashpadClient::CrashWithoutDump(const std::string& message) { void CrashpadClient::CrashWithoutDump(const std::string& message) {
LaunchAtCrashHandler::Disable(); SignalHandler::DisableForThread();
LOG(FATAL) << message; LOG(FATAL) << message;
} }
// static // static
void CrashpadClient::SetFirstChanceExceptionHandler( void CrashpadClient::SetFirstChanceExceptionHandler(
FirstChanceHandler handler) { FirstChanceHandler handler) {
DCHECK(g_crash_handler); DCHECK(SignalHandler::Get());
g_crash_handler->SetFirstChanceHandler(handler); SignalHandler::Get()->SetFirstChanceHandler(handler);
} }
} // namespace crashpad } // namespace crashpad

View File

@ -55,59 +55,55 @@ namespace crashpad {
namespace test { namespace test {
namespace { namespace {
struct StartHandlerForSelfTestOptions {
bool start_handler_at_crash;
bool simulate_crash;
bool set_first_chance_handler;
};
class StartHandlerForSelfTest
: public testing::TestWithParam<std::tuple<bool, bool, bool>> {
public:
StartHandlerForSelfTest() = default;
~StartHandlerForSelfTest() = default;
void SetUp() override {
std::tie(options_.start_handler_at_crash,
options_.simulate_crash,
options_.set_first_chance_handler) = GetParam();
}
const StartHandlerForSelfTestOptions& Options() const { return options_; }
private:
StartHandlerForSelfTestOptions options_;
DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfTest);
};
bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) { bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {
return true; return true;
} }
TEST(CrashpadClient, SimulateCrash) { bool InstallHandler(CrashpadClient* client,
ScopedTempDir temp_dir; bool start_at_crash,
const base::FilePath& handler_path,
base::FilePath handler_path = TestPaths::Executable().DirName().Append( const base::FilePath& database_path) {
FILE_PATH_LITERAL("crashpad_handler")); return start_at_crash
? client->StartHandlerAtCrash(handler_path,
crashpad::CrashpadClient client; database_path,
ASSERT_TRUE(client.StartHandlerAtCrash(handler_path,
base::FilePath(temp_dir.path()),
base::FilePath(), base::FilePath(),
"", "",
std::map<std::string, std::string>(), std::map<std::string, std::string>(),
std::vector<std::string>())); std::vector<std::string>())
: client->StartHandler(handler_path,
auto database = database_path,
CrashReportDatabase::InitializeWithoutCreating(temp_dir.path()); base::FilePath(),
ASSERT_TRUE(database); "",
std::map<std::string, std::string>(),
{ std::vector<std::string>(),
CrashpadClient::SetFirstChanceExceptionHandler(HandleCrashSuccessfully); false,
false);
CRASHPAD_SIMULATE_CRASH();
std::vector<CrashReportDatabase::Report> reports;
ASSERT_EQ(database->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
EXPECT_EQ(reports.size(), 0u);
reports.clear();
ASSERT_EQ(database->GetCompletedReports(&reports),
CrashReportDatabase::kNoError);
EXPECT_EQ(reports.size(), 0u);
}
{
CrashpadClient::SetFirstChanceExceptionHandler(nullptr);
CRASHPAD_SIMULATE_CRASH();
std::vector<CrashReportDatabase::Report> reports;
ASSERT_EQ(database->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
EXPECT_EQ(reports.size(), 1u);
reports.clear();
ASSERT_EQ(database->GetCompletedReports(&reports),
CrashReportDatabase::kNoError);
EXPECT_EQ(reports.size(), 0u);
}
} }
constexpr char kTestAnnotationName[] = "name_of_annotation"; constexpr char kTestAnnotationName[] = "name_of_annotation";
@ -152,7 +148,7 @@ void ValidateDump(const CrashReportDatabase::UploadReport* report) {
ADD_FAILURE(); ADD_FAILURE();
} }
CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
FileHandle in = StdioFileHandle(StdioStream::kStandardInput); FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
VMSize temp_dir_length; VMSize temp_dir_length;
@ -161,6 +157,9 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
std::string temp_dir(temp_dir_length, '\0'); std::string temp_dir(temp_dir_length, '\0');
CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length); CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);
StartHandlerForSelfTestOptions options;
CheckedReadFileExactly(in, &options, sizeof(options));
base::FilePath handler_path = TestPaths::Executable().DirName().Append( base::FilePath handler_path = TestPaths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_handler")); FILE_PATH_LITERAL("crashpad_handler"));
@ -170,12 +169,10 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
test_annotation.Set(kTestAnnotationValue); test_annotation.Set(kTestAnnotationValue);
crashpad::CrashpadClient client; crashpad::CrashpadClient client;
if (!client.StartHandlerAtCrash(handler_path, if (!InstallHandler(&client,
base::FilePath(temp_dir), options.start_handler_at_crash,
base::FilePath(), handler_path,
"", base::FilePath(temp_dir))) {
std::map<std::string, std::string>(),
std::vector<std::string>())) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -185,18 +182,29 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
} }
#endif #endif
if (options.simulate_crash) {
if (options.set_first_chance_handler) {
client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
}
CRASHPAD_SIMULATE_CRASH();
return EXIT_SUCCESS;
}
__builtin_trap(); __builtin_trap();
NOTREACHED(); NOTREACHED();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
class StartHandlerAtCrashTest : public MultiprocessExec { class StartHandlerForSelfInChildTest : public MultiprocessExec {
public: public:
StartHandlerAtCrashTest() : MultiprocessExec() { StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)
SetChildTestMainFunction("StartHandlerAtCrashChild"); : MultiprocessExec(), options_(options) {
SetChildTestMainFunction("StartHandlerForSelfTestChild");
if (!options.simulate_crash) {
SetExpectedChildTerminationBuiltinTrap(); SetExpectedChildTerminationBuiltinTrap();
} }
}
private: private:
void MultiprocessParent() override { void MultiprocessParent() override {
@ -206,6 +214,8 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length))); WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));
ASSERT_TRUE(LoggingWriteFile( ASSERT_TRUE(LoggingWriteFile(
WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length)); WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));
ASSERT_TRUE(
LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));
// Wait for child to finish. // Wait for child to finish.
CheckedReadFileAtEOF(ReadPipeHandle()); CheckedReadFileAtEOF(ReadPipeHandle());
@ -221,7 +231,11 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
reports.clear(); reports.clear();
ASSERT_EQ(database->GetPendingReports(&reports), ASSERT_EQ(database->GetPendingReports(&reports),
CrashReportDatabase::kNoError); CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 1u); ASSERT_EQ(reports.size(), options_.set_first_chance_handler ? 0u : 1u);
if (options_.set_first_chance_handler) {
return;
}
std::unique_ptr<const CrashReportDatabase::UploadReport> report; std::unique_ptr<const CrashReportDatabase::UploadReport> report;
ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report), ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),
@ -229,14 +243,26 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
ValidateDump(report.get()); ValidateDump(report.get());
} }
DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest); StartHandlerForSelfTestOptions options_;
DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfInChildTest);
}; };
TEST(CrashpadClient, StartHandlerAtCrash) { TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
StartHandlerAtCrashTest test; if (Options().set_first_chance_handler && !Options().simulate_crash) {
// TODO(jperaza): test first chance handlers with real crashes.
return;
}
StartHandlerForSelfInChildTest test(Options());
test.Run(); test.Run();
} }
INSTANTIATE_TEST_SUITE_P(StartHandlerForSelfTestSuite,
StartHandlerForSelfTest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool()));
// Test state for starting the handler for another process. // Test state for starting the handler for another process.
class StartHandlerForClientTest { class StartHandlerForClientTest {
public: public:

View File

@ -121,13 +121,6 @@ establish the Crashpad client environment before running a program.
Either this option or **--mach-service**, but not both, is required. This Either this option or **--mach-service**, but not both, is required. This
option is only valid on macOS. option is only valid on macOS.
* **--no-identify-client-via-url**
Do not add client-identifying fields to the URL. By default, `"prod"`,
`"ver"`, and `"guid"` annotations are added to the upload URL as name-value
pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this
option disables that behavior.
* **--initial-client-data**=*HANDLE_request_crash_dump*,*HANDLE_request_non_crash_dump*,*HANDLE_non_crash_dump_completed*,*HANDLE_first_pipe_instance*,*HANDLE_client_process*,*Address_crash_exception_information*,*Address_non_crash_exception_information*,*Address_debug_critical_section* * **--initial-client-data**=*HANDLE_request_crash_dump*,*HANDLE_request_non_crash_dump*,*HANDLE_non_crash_dump_completed*,*HANDLE_first_pipe_instance*,*HANDLE_client_process*,*Address_crash_exception_information*,*Address_non_crash_exception_information*,*Address_debug_critical_section*
Register the initial client using the inherited handles and data provided. Register the initial client using the inherited handles and data provided.
@ -141,6 +134,13 @@ establish the Crashpad client environment before running a program.
client to register, and exits when all clients have exited, after waiting for client to register, and exits when all clients have exited, after waiting for
any uploads in progress to complete. any uploads in progress to complete.
* **--initial-client-fd**=_FD_
Wait for client requests on _FD_. Either this option or
**--trace-parent-with-exception**, but not both, is required. The handler
exits when all client connections have been closed. This option is only valid
on Linux platforms.
* **--mach-service**=_SERVICE_ * **--mach-service**=_SERVICE_
Check in with the bootstrap server under the name _SERVICE_. Either this Check in with the bootstrap server under the name _SERVICE_. Either this
@ -198,6 +198,13 @@ establish the Crashpad client environment before running a program.
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
be `--monitor-self`. be `--monitor-self`.
* **--no-identify-client-via-url**
Do not add client-identifying fields to the URL. By default, `"prod"`,
`"ver"`, and `"guid"` annotations are added to the upload URL as name-value
pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this
option disables that behavior.
* **--no-periodic-tasks** * **--no-periodic-tasks**
Do not scan for new pending crash reports or prune the crash report database. Do not scan for new pending crash reports or prune the crash report database.
@ -245,17 +252,24 @@ establish the Crashpad client environment before running a program.
parent process. This option is only valid on macOS. Use of this option is parent process. This option is only valid on macOS. Use of this option is
discouraged. It should not be used absent extraordinary circumstances. discouraged. It should not be used absent extraordinary circumstances.
* **--sanitization-information**=_SANITIZATION-INFORMATION-ADDRESS_
Provides sanitization settings in a SanitizationInformation struct at
_SANITIZATION-INFORMATION-ADDRESS_. This option requires
**--trace-parent-with-exception** and is only valid on Linux platforms.
* **--shared-client-connection**
Indicates that the file descriptor provided by **--initial-client-fd** is
shared among mulitple clients. Using a broker process is not supported for
clients using this option. This option is only valid on Linux platforms.
* **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_ * **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_
Causes the handler process to trace its parent process and exit. The parent Causes the handler process to trace its parent process and exit. The parent
process should have an ExceptionInformation struct at process should have an ExceptionInformation struct at
_EXCEPTION-INFORMATION-ADDRESS_. _EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux
platforms.
* **--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_ * **--url**=_URL_

View File

@ -118,6 +118,9 @@ void Usage(const base::FilePath& me) {
" Address_debug_critical_section\n" " Address_debug_critical_section\n"
" use precreated data to register initial client\n" " use precreated data to register initial client\n"
#endif // OS_WIN #endif // OS_WIN
#if defined(OS_ANDROID) || defined(OS_LINUX)
" --initial-client-fd=FD a socket connected to a client.\n"
#endif // OS_ANDROID || OS_LINUX
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
" --mach-service=SERVICE register SERVICE with the bootstrap server\n" " --mach-service=SERVICE register SERVICE with the bootstrap server\n"
#endif // OS_MACOSX #endif // OS_MACOSX
@ -141,11 +144,13 @@ void Usage(const base::FilePath& me) {
" reset the server's exception handler to default\n" " reset the server's exception handler to default\n"
#endif // OS_MACOSX #endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID) #if defined(OS_LINUX) || defined(OS_ANDROID)
" --sanitization-information=SANITIZATION_INFORMATION_ADDRESS\n"
" the address of a SanitizationInformation struct.\n"
" --shared-client-connection the file descriptor provided by\n"
" --initial-client-fd is shared among multiple\n"
" clients\n"
" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n" " --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n"
" request a dump for the handler's parent process\n" " request a dump for the handler's parent process\n"
" --initial-client-fd=FD a socket connected to a client.\n"
" --sanitization_information=SANITIZATION_INFORMATION_ADDRESS\n"
" the address of a SanitizationInformation struct.\n"
#endif // OS_LINUX || OS_ANDROID #endif // OS_LINUX || OS_ANDROID
" --url=URL send crash reports to this Breakpad server URL,\n" " --url=URL send crash reports to this Breakpad server URL,\n"
" only if uploads are enabled for the database\n" " only if uploads are enabled for the database\n"
@ -168,8 +173,9 @@ struct Options {
bool reset_own_crash_exception_port_to_system_default; bool reset_own_crash_exception_port_to_system_default;
#elif defined(OS_LINUX) || defined(OS_ANDROID) #elif defined(OS_LINUX) || defined(OS_ANDROID)
VMAddress exception_information_address; VMAddress exception_information_address;
int initial_client_fd;
VMAddress sanitization_information_address; VMAddress sanitization_information_address;
int initial_client_fd;
bool shared_client_connection;
#elif defined(OS_WIN) #elif defined(OS_WIN)
std::string pipe_name; std::string pipe_name;
InitialClientData initial_client_data; InitialClientData initial_client_data;
@ -530,6 +536,9 @@ int HandlerMain(int argc,
#if defined(OS_WIN) #if defined(OS_WIN)
kOptionInitialClientData, kOptionInitialClientData,
#endif // OS_WIN #endif // OS_WIN
#if defined(OS_ANDROID) || defined(OS_LINUX)
kOptionInitialClientFD,
#endif // OS_ANDROID || OS_LINUX
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
kOptionMachService, kOptionMachService,
#endif // OS_MACOSX #endif // OS_MACOSX
@ -548,9 +557,9 @@ int HandlerMain(int argc,
kOptionResetOwnCrashExceptionPortToSystemDefault, kOptionResetOwnCrashExceptionPortToSystemDefault,
#endif // OS_MACOSX #endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID) #if defined(OS_LINUX) || defined(OS_ANDROID)
kOptionTraceParentWithException,
kOptionInitialClientFD,
kOptionSanitizationInformation, kOptionSanitizationInformation,
kOptionSharedClientConnection,
kOptionTraceParentWithException,
#endif #endif
kOptionURL, kOptionURL,
@ -571,6 +580,9 @@ int HandlerMain(int argc,
nullptr, nullptr,
kOptionInitialClientData}, kOptionInitialClientData},
#endif // OS_MACOSX #endif // OS_MACOSX
#if defined(OS_ANDROID) || defined(OS_LINUX)
{"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
#endif // OS_ANDROID || OS_LINUX
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
{"mach-service", required_argument, nullptr, kOptionMachService}, {"mach-service", required_argument, nullptr, kOptionMachService},
#endif // OS_MACOSX #endif // OS_MACOSX
@ -601,15 +613,18 @@ int HandlerMain(int argc,
kOptionResetOwnCrashExceptionPortToSystemDefault}, kOptionResetOwnCrashExceptionPortToSystemDefault},
#endif // OS_MACOSX #endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID) #if defined(OS_LINUX) || defined(OS_ANDROID)
{"trace-parent-with-exception",
required_argument,
nullptr,
kOptionTraceParentWithException},
{"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
{"sanitization-information", {"sanitization-information",
required_argument, required_argument,
nullptr, nullptr,
kOptionSanitizationInformation}, kOptionSanitizationInformation},
{"shared-client-connection",
no_argument,
nullptr,
kOptionSharedClientConnection},
{"trace-parent-with-exception",
required_argument,
nullptr,
kOptionTraceParentWithException},
#endif // OS_LINUX || OS_ANDROID #endif // OS_LINUX || OS_ANDROID
{"url", required_argument, nullptr, kOptionURL}, {"url", required_argument, nullptr, kOptionURL},
{"help", no_argument, nullptr, kOptionHelp}, {"help", no_argument, nullptr, kOptionHelp},
@ -622,14 +637,12 @@ int HandlerMain(int argc,
options.handshake_fd = -1; options.handshake_fd = -1;
#endif #endif
options.identify_client_via_url = true; options.identify_client_via_url = true;
#if defined(OS_LINUX) || defined(OS_ANDROID)
options.initial_client_fd = kInvalidFileHandle;
#endif
options.periodic_tasks = true; options.periodic_tasks = true;
options.rate_limit = true; options.rate_limit = true;
options.upload_gzip = true; options.upload_gzip = true;
#if defined(OS_LINUX) || defined(OS_ANDROID)
options.exception_information_address = 0;
options.initial_client_fd = kInvalidFileHandle;
options.sanitization_information_address = 0;
#endif
int opt; int opt;
while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
@ -670,6 +683,15 @@ int HandlerMain(int argc,
break; break;
} }
#endif // OS_WIN #endif // OS_WIN
#if defined(OS_ANDROID) || defined(OS_LINUX)
case kOptionInitialClientFD: {
if (!base::StringToInt(optarg, &options.initial_client_fd)) {
ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
return ExitFailure();
}
break;
}
#endif // OS_ANDROID || OS_LINUX
case kOptionMetrics: { case kOptionMetrics: {
options.metrics_dir = base::FilePath( options.metrics_dir = base::FilePath(
ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
@ -720,21 +742,6 @@ int HandlerMain(int argc,
} }
#endif // OS_MACOSX #endif // OS_MACOSX
#if defined(OS_LINUX) || defined(OS_ANDROID) #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;
}
case kOptionSanitizationInformation: { case kOptionSanitizationInformation: {
if (!StringToNumber(optarg, if (!StringToNumber(optarg,
&options.sanitization_information_address)) { &options.sanitization_information_address)) {
@ -744,6 +751,18 @@ int HandlerMain(int argc,
} }
break; break;
} }
case kOptionSharedClientConnection: {
options.shared_client_connection = true;
break;
}
case kOptionTraceParentWithException: {
if (!StringToNumber(optarg, &options.exception_information_address)) {
ToolSupport::UsageHint(
me, "failed to parse --trace-parent-with-exception");
return ExitFailure();
}
break;
}
#endif // OS_LINUX || OS_ANDROID #endif // OS_LINUX || OS_ANDROID
case kOptionURL: { case kOptionURL: {
options.url = optarg; options.url = optarg;
@ -793,7 +812,7 @@ int HandlerMain(int argc,
if (!options.exception_information_address && if (!options.exception_information_address &&
options.initial_client_fd == kInvalidFileHandle) { options.initial_client_fd == kInvalidFileHandle) {
ToolSupport::UsageHint( ToolSupport::UsageHint(
me, "--trace-parent-with-exception or --initial_client_fd is required"); me, "--trace-parent-with-exception or --initial-client-fd is required");
return ExitFailure(); return ExitFailure();
} }
if (options.sanitization_information_address && if (options.sanitization_information_address &&
@ -803,6 +822,12 @@ int HandlerMain(int argc,
"--sanitization_information requires --trace-parent-with-exception"); "--sanitization_information requires --trace-parent-with-exception");
return ExitFailure(); return ExitFailure();
} }
if (options.shared_client_connection &&
options.initial_client_fd == kInvalidFileHandle) {
ToolSupport::UsageHint(
me, "--shared-client-connection requires --initial-client-fd");
return ExitFailure();
}
#endif // OS_MACOSX #endif // OS_MACOSX
if (options.database.empty()) { if (options.database.empty()) {
@ -994,7 +1019,8 @@ int HandlerMain(int argc,
#elif defined(OS_LINUX) || defined(OS_ANDROID) #elif defined(OS_LINUX) || defined(OS_ANDROID)
if (options.initial_client_fd == kInvalidFileHandle || if (options.initial_client_fd == kInvalidFileHandle ||
!exception_handler_server.InitializeWithClient( !exception_handler_server.InitializeWithClient(
ScopedFileHandle(options.initial_client_fd), false)) { ScopedFileHandle(options.initial_client_fd),
options.shared_client_connection)) {
return ExitFailure(); return ExitFailure();
} }
#endif // OS_WIN #endif // OS_WIN