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
//! 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] database The path to a Crashpad database. The handler will be
//! started with this path as its `--database` argument.

View File

@ -31,6 +31,7 @@
#include "util/linux/exception_information.h"
#include "util/linux/scoped_pr_set_dumpable.h"
#include "util/linux/scoped_pr_set_ptracer.h"
#include "util/linux/socket.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/double_fork_and_exec.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);
}
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);
}
@ -110,8 +111,90 @@ std::vector<std::string> BuildArgsToLaunchWithLinker(
#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.
class LaunchAtCrashHandler {
class LaunchAtCrashHandler : public SignalHandler {
public:
static LaunchAtCrashHandler* Get() {
static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
@ -129,33 +212,19 @@ class LaunchAtCrashHandler {
}
argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
&exception_information_));
&GetExceptionInfo()));
StringVectorToCStringVector(argv_strings_, &argv_);
return Signals::InstallCrashHandlers(HandleCrash, 0, &old_actions_);
return Install();
}
bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) {
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);
void HandleCrashImpl() override {
ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false);
ScopedPrSetDumpable set_dumpable(/* may_log= */ false);
pid_t pid = fork();
if (pid < 0) {
return false;
return;
}
if (pid == 0) {
if (set_envp_) {
@ -170,52 +239,72 @@ class LaunchAtCrashHandler {
int status;
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:
LaunchAtCrashHandler() = default;
~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<const char*> argv_;
std::vector<std::string> envp_strings_;
std::vector<const char*> envp_;
ExceptionInformation exception_information_;
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
bool set_envp_ = false;
static thread_local bool enabled_;
DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
};
thread_local bool LaunchAtCrashHandler::enabled_ = true;
// A pointer to the currently installed crash signal handler. This allows
// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
// using the currently configured crash handling strategy.
static LaunchAtCrashHandler* g_crash_handler;
class RequestCrashDumpHandler : public SignalHandler {
public:
static RequestCrashDumpHandler* Get() {
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
@ -232,11 +321,26 @@ bool CrashpadClient::StartHandler(
const std::vector<std::string>& arguments,
bool restartable,
bool asynchronous_start) {
// TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
// supports accepting new connections.
// https://crashpad.chromium.org/bug/30
NOTREACHED();
return false;
DCHECK(!restartable);
DCHECK(!asynchronous_start);
ScopedFileHandle client_sock, handler_sock;
if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock,
&handler_sock)) {
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)
@ -259,12 +363,7 @@ bool CrashpadClient::StartJavaHandlerAtCrash(
kInvalidFileHandle);
auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, env)) {
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
return signal_handler->Initialize(&argv, env);
}
// static
@ -304,12 +403,7 @@ bool CrashpadClient::StartHandlerWithLinkerAtCrash(
arguments,
kInvalidFileHandle);
auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, env)) {
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
return signal_handler->Initialize(&argv, env);
}
// static
@ -351,12 +445,7 @@ bool CrashpadClient::StartHandlerAtCrash(
handler, database, metrics_dir, url, annotations, arguments);
auto signal_handler = LaunchAtCrashHandler::Get();
if (signal_handler->Initialize(&argv, nullptr)) {
DCHECK(!g_crash_handler);
g_crash_handler = signal_handler;
return true;
}
return false;
return signal_handler->Initialize(&argv, nullptr);
}
// static
@ -378,7 +467,7 @@ bool CrashpadClient::StartHandlerForClient(
// static
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
if (!g_crash_handler) {
if (!SignalHandler::Get()) {
DLOG(ERROR) << "Crashpad isn't enabled";
return;
}
@ -395,21 +484,21 @@ void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
siginfo.si_signo = Signals::kSimulatedSigno;
siginfo.si_errno = 0;
siginfo.si_code = 0;
g_crash_handler->HandleCrashNonFatal(
SignalHandler::Get()->HandleCrash(
siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
}
// static
void CrashpadClient::CrashWithoutDump(const std::string& message) {
LaunchAtCrashHandler::Disable();
SignalHandler::DisableForThread();
LOG(FATAL) << message;
}
// static
void CrashpadClient::SetFirstChanceExceptionHandler(
FirstChanceHandler handler) {
DCHECK(g_crash_handler);
g_crash_handler->SetFirstChanceHandler(handler);
DCHECK(SignalHandler::Get());
SignalHandler::Get()->SetFirstChanceHandler(handler);
}
} // namespace crashpad

View File

@ -55,59 +55,55 @@ namespace crashpad {
namespace test {
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*) {
return true;
}
TEST(CrashpadClient, SimulateCrash) {
ScopedTempDir temp_dir;
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_handler"));
crashpad::CrashpadClient client;
ASSERT_TRUE(client.StartHandlerAtCrash(handler_path,
base::FilePath(temp_dir.path()),
base::FilePath(),
"",
std::map<std::string, std::string>(),
std::vector<std::string>()));
auto database =
CrashReportDatabase::InitializeWithoutCreating(temp_dir.path());
ASSERT_TRUE(database);
{
CrashpadClient::SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
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);
}
bool InstallHandler(CrashpadClient* client,
bool start_at_crash,
const base::FilePath& handler_path,
const base::FilePath& database_path) {
return start_at_crash
? client->StartHandlerAtCrash(handler_path,
database_path,
base::FilePath(),
"",
std::map<std::string, std::string>(),
std::vector<std::string>())
: client->StartHandler(handler_path,
database_path,
base::FilePath(),
"",
std::map<std::string, std::string>(),
std::vector<std::string>(),
false,
false);
}
constexpr char kTestAnnotationName[] = "name_of_annotation";
@ -152,7 +148,7 @@ void ValidateDump(const CrashReportDatabase::UploadReport* report) {
ADD_FAILURE();
}
CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
VMSize temp_dir_length;
@ -161,6 +157,9 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
std::string temp_dir(temp_dir_length, '\0');
CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);
StartHandlerForSelfTestOptions options;
CheckedReadFileExactly(in, &options, sizeof(options));
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_handler"));
@ -170,12 +169,10 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
test_annotation.Set(kTestAnnotationValue);
crashpad::CrashpadClient client;
if (!client.StartHandlerAtCrash(handler_path,
base::FilePath(temp_dir),
base::FilePath(),
"",
std::map<std::string, std::string>(),
std::vector<std::string>())) {
if (!InstallHandler(&client,
options.start_handler_at_crash,
handler_path,
base::FilePath(temp_dir))) {
return EXIT_FAILURE;
}
@ -185,17 +182,28 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
}
#endif
if (options.simulate_crash) {
if (options.set_first_chance_handler) {
client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
}
CRASHPAD_SIMULATE_CRASH();
return EXIT_SUCCESS;
}
__builtin_trap();
NOTREACHED();
return EXIT_SUCCESS;
}
class StartHandlerAtCrashTest : public MultiprocessExec {
class StartHandlerForSelfInChildTest : public MultiprocessExec {
public:
StartHandlerAtCrashTest() : MultiprocessExec() {
SetChildTestMainFunction("StartHandlerAtCrashChild");
SetExpectedChildTerminationBuiltinTrap();
StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)
: MultiprocessExec(), options_(options) {
SetChildTestMainFunction("StartHandlerForSelfTestChild");
if (!options.simulate_crash) {
SetExpectedChildTerminationBuiltinTrap();
}
}
private:
@ -206,6 +214,8 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));
ASSERT_TRUE(LoggingWriteFile(
WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));
ASSERT_TRUE(
LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));
// Wait for child to finish.
CheckedReadFileAtEOF(ReadPipeHandle());
@ -221,7 +231,11 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
reports.clear();
ASSERT_EQ(database->GetPendingReports(&reports),
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;
ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),
@ -229,14 +243,26 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
ValidateDump(report.get());
}
DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest);
StartHandlerForSelfTestOptions options_;
DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfInChildTest);
};
TEST(CrashpadClient, StartHandlerAtCrash) {
StartHandlerAtCrashTest test;
TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
if (Options().set_first_chance_handler && !Options().simulate_crash) {
// TODO(jperaza): test first chance handlers with real crashes.
return;
}
StartHandlerForSelfInChildTest test(Options());
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.
class StartHandlerForClientTest {
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
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*
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
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_
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
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**
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
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_
Causes the handler process to trace its parent process and exit. The parent
process should have an ExceptionInformation struct at
_EXCEPTION-INFORMATION-ADDRESS_.
* **--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.
_EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux
platforms.
* **--url**=_URL_

View File

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