mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 07:14:10 +08:00
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:
parent
e23286dc37
commit
607c80e0b8
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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_
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user