mac: Make crashpad_handler get its receive right from its client

Previously, crashpad_handler made its own receive right, and transferred
a corresponding send right to its client. There are two advantages to
making the receive right in the client:

 - It is possible to monitor the receive right for a port-destroyed
   notificaiton in the client, allowing the handler to be restarted if
   it dies.
 - For the future run-from-launchd mode (bug crashpad:25), the handler
   will obtain its receive right from the bootstrap server instead of
   making its own. Having the handler get its receive right from
   different sources allows more code to be shared than if it were to
   sometimes get a receive right and sometimes make a receive right and
   transfer a send right.

This includes a restructuring in crashpad_client_mac.cc that will make
it easier to give it an option to restart crashpad_handler if it dies.
The handler starting logic should all behave the same as before.

BUG=crashpad:68
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/1409073013 .
This commit is contained in:
Mark Mentovai 2015-10-29 18:09:03 -04:00
parent 9d03d54d0b
commit fc7d8b3a27
10 changed files with 263 additions and 179 deletions

View File

@ -19,11 +19,13 @@
#include <unistd.h>
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/stringprintf.h"
#include "util/mach/child_port_handshake.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/implicit_cast.h"
#include "util/posix/close_multiple.h"
namespace crashpad {
@ -77,6 +79,199 @@ bool SetCrashExceptionPorts(exception_handler_t exception_handler) {
MACHINE_THREAD_STATE);
}
//! \brief Starts a Crashpad handler.
class HandlerStarter final {
public:
//! \brief Starts a Crashpad handler.
//!
//! All parameters are as in CrashpadClient::StartHandler().
//!
//! \return On success, a send right to the Crashpad handler that has been
//! started. On failure, `MACH_PORT_NULL` with a message logged.
static base::mac::ScopedMachSendRight Start(
const base::FilePath& handler,
const base::FilePath& database,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments) {
base::mac::ScopedMachReceiveRight receive_right(
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
if (receive_right == kMachPortNull) {
return base::mac::ScopedMachSendRight();
}
mach_port_t port;
mach_msg_type_name_t right_type;
kern_return_t kr = mach_port_extract_right(mach_task_self(),
receive_right.get(),
MACH_MSG_TYPE_MAKE_SEND,
&port,
&right_type);
if (kr != KERN_SUCCESS) {
MACH_LOG(ERROR, kr) << "mach_port_extract_right";
return base::mac::ScopedMachSendRight();
}
base::mac::ScopedMachSendRight send_right(port);
DCHECK_EQ(port, receive_right.get());
DCHECK_EQ(right_type,
implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND));
if (!CommonStart(handler,
database,
url,
annotations,
arguments,
receive_right.Pass())) {
return base::mac::ScopedMachSendRight();
}
return send_right;
}
private:
static bool CommonStart(const base::FilePath& handler,
const base::FilePath& database,
const std::string& url,
const std::map<std::string, std::string>& annotations,
const std::vector<std::string>& arguments,
base::mac::ScopedMachReceiveRight receive_right) {
// Set up the arguments for execve() first. These arent needed until
// execve() is called, but its dangerous to do this in a child process
// after fork().
ChildPortHandshake child_port_handshake;
base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD();
// Use handler as argv[0], followed by arguments directed by this methods
// parameters and a --handshake-fd argument. |arguments| are added first so
// that if it erroneously contains an argument such as --url, the actual
// |url| argument passed to this method will supersede it. In normal
// command-line processing, the last parameter wins in the case of a
// conflict.
std::vector<std::string> argv(1, handler.value());
argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1);
for (const std::string& argument : arguments) {
argv.push_back(argument);
}
if (!database.value().empty()) {
argv.push_back(FormatArgumentString("database", database.value()));
}
if (!url.empty()) {
argv.push_back(FormatArgumentString("url", url));
}
for (const auto& kv : annotations) {
argv.push_back(
FormatArgumentString("annotation", kv.first + '=' + kv.second));
}
argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get()));
const char* handler_c = handler.value().c_str();
// argv_c contains const char* pointers and is terminated by nullptr. argv
// is required because the pointers in argv_c need to point somewhere, and
// they cant point to temporaries such as those returned by
// FormatArgumentString().
std::vector<const char*> argv_c;
argv_c.reserve(argv.size() + 1);
for (const std::string& argument : argv) {
argv_c.push_back(argument.c_str());
}
argv_c.push_back(nullptr);
// Double-fork(). The three processes involved are parent, child, and
// grandchild. The grandchild will become the handler process. The child
// exits immediately after spawning the grandchild, so the grandchild
// becomes an orphan and its parent process ID becomes 1. This relieves the
// parent and child of the responsibility for reaping the grandchild with
// waitpid() or similar. The handler process is expected to outlive the
// parent process, so the parent shouldnt be concerned with reaping it.
// This approach means that accidental early termination of the handler
// process will not result in a zombie process.
pid_t pid = fork();
if (pid < 0) {
PLOG(ERROR) << "fork";
return false;
}
if (pid == 0) {
// Child process.
// Call setsid(), creating a new process group and a new session, both led
// by this process. The new process group has no controlling terminal.
// This disconnects it from signals generated by the parent process
// terminal.
//
// setsid() is done in the child instead of the grandchild so that the
// grandchild will not be a session leader. If it were a session leader,
// an accidental open() of a terminal device without O_NOCTTY would make
// that terminal the controlling terminal.
//
// Its not desirable for the handler to have a controlling terminal. The
// handler monitors clients on its own and manages its own lifetime,
// exiting when it loses all clients and when it deems it appropraite to
// do so. It may serve clients in different process groups or sessions
// than its original client, and receiving signals intended for its
// original clients process group could be harmful in that case.
PCHECK(setsid() != -1) << "setsid";
pid = fork();
if (pid < 0) {
PLOG(FATAL) << "fork";
}
if (pid > 0) {
// Child process.
// _exit() instead of exit(), because fork() was called.
_exit(EXIT_SUCCESS);
}
// Grandchild process.
CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get());
// &argv_c[0] is a pointer to a pointer to const char data, but because of
// how C (not C++) works, execvp() wants a pointer to a const pointer to
// char data. It modifies neither the data nor the pointers, so the
// const_cast is safe.
execvp(handler_c, const_cast<char* const*>(&argv_c[0]));
PLOG(FATAL) << "execvp " << handler_c;
}
// Parent process.
// Close the write side of the pipe, so that the handler process is the only
// process that can write to it.
server_write_fd.reset();
// waitpid() for the child, so that it does not become a zombie process. The
// child normally exits quickly.
int status;
pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
PCHECK(wait_pid != -1) << "waitpid";
DCHECK_EQ(wait_pid, pid);
if (WIFSIGNALED(status)) {
LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status);
} else if (!WIFEXITED(status)) {
DLOG(WARNING) << "intermediate process: unknown termination " << status;
} else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
LOG(WARNING) << "intermediate process: exit status "
<< WEXITSTATUS(status);
}
// Rendezvous with the handler running in the grandchild process.
if (!child_port_handshake.RunClient(receive_right.get(),
MACH_MSG_TYPE_MOVE_RECEIVE)) {
return false;
}
ignore_result(receive_right.release());
return true;
}
DISALLOW_IMPLICIT_CONSTRUCTORS(HandlerStarter);
};
} // namespace
CrashpadClient::CrashpadClient()
@ -94,127 +289,13 @@ bool CrashpadClient::StartHandler(
const std::vector<std::string>& arguments) {
DCHECK(!exception_port_.is_valid());
// Set up the arguments for execve() first. These arent needed until execve()
// is called, but its dangerous to do this in a child process after fork().
ChildPortHandshake child_port_handshake;
base::ScopedFD client_read_fd = child_port_handshake.ClientReadFD();
// Use handler as argv[0], followed by arguments directed by this methods
// parameters and a --handshake-fd argument. |arguments| are added first so
// that if it erroneously contains an argument such as --url, the actual |url|
// argument passed to this method will supersede it. In normal command-line
// processing, the last parameter wins in the case of a conflict.
std::vector<std::string> argv(1, handler.value());
argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1);
for (const std::string& argument : arguments) {
argv.push_back(argument);
}
if (!database.value().empty()) {
argv.push_back(FormatArgumentString("database", database.value()));
}
if (!url.empty()) {
argv.push_back(FormatArgumentString("url", url));
}
for (const auto& kv : annotations) {
argv.push_back(
FormatArgumentString("annotation", kv.first + '=' + kv.second));
}
argv.push_back(FormatArgumentInt("handshake-fd", client_read_fd.get()));
// argv_c contains const char* pointers and is terminated by nullptr. argv
// is required because the pointers in argv_c need to point somewhere, and
// they cant point to temporaries such as those returned by
// FormatArgumentString().
std::vector<const char*> argv_c;
argv_c.reserve(argv.size() + 1);
for (const std::string& argument : argv) {
argv_c.push_back(argument.c_str());
}
argv_c.push_back(nullptr);
// Double-fork(). The three processes involved are parent, child, and
// grandchild. The grandchild will become the handler process. The child exits
// immediately after spawning the grandchild, so the grandchild becomes an
// orphan and its parent process ID becomes 1. This relieves the parent and
// child of the responsibility for reaping the grandchild with waitpid() or
// similar. The handler process is expected to outlive the parent process, so
// the parent shouldnt be concerned with reaping it. This approach means that
// accidental early termination of the handler process will not result in a
// zombie process.
pid_t pid = fork();
if (pid < 0) {
PLOG(ERROR) << "fork";
exception_port_ = HandlerStarter::Start(
handler, database, url, annotations, arguments);
if (!exception_port_.is_valid()) {
return false;
}
if (pid == 0) {
// Child process.
// Call setsid(), creating a new process group and a new session, both led
// by this process. The new process group has no controlling terminal. This
// disconnects it from signals generated by the parent process terminal.
//
// setsid() is done in the child instead of the grandchild so that the
// grandchild will not be a session leader. If it were a session leader, an
// accidental open() of a terminal device without O_NOCTTY would make that
// terminal the controlling terminal.
//
// Its not desirable for the handler to have a controlling terminal. The
// handler monitors clients on its own and manages its own lifetime, exiting
// when it loses all clients and when it deems it appropraite to do so. It
// may serve clients in different process groups or sessions than its
// original client, and receiving signals intended for its original clients
// process group could be harmful in that case.
PCHECK(setsid() != -1) << "setsid";
pid = fork();
if (pid < 0) {
PLOG(FATAL) << "fork";
}
if (pid > 0) {
// Child process.
// _exit() instead of exit(), because fork() was called.
_exit(EXIT_SUCCESS);
}
// Grandchild process.
CloseMultipleNowOrOnExec(STDERR_FILENO + 1, client_read_fd.get());
// &argv_c[0] is a pointer to a pointer to const char data, but because of
// how C (not C++) works, execvp() wants a pointer to a const pointer to
// char data. It modifies neither the data nor the pointers, so the
// const_cast is safe.
execvp(handler.value().c_str(), const_cast<char* const*>(&argv_c[0]));
PLOG(FATAL) << "execvp " << handler.value();
}
// Parent process.
client_read_fd.reset();
// waitpid() for the child, so that it does not become a zombie process. The
// child normally exits quickly.
int status;
pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
PCHECK(wait_pid != -1) << "waitpid";
DCHECK_EQ(wait_pid, pid);
if (WIFSIGNALED(status)) {
LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status);
} else if (!WIFEXITED(status)) {
DLOG(WARNING) << "intermediate process: unknown termination " << status;
} else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
LOG(WARNING) << "intermediate process: exit status " << WEXITSTATUS(status);
}
// Rendezvous with the handler running in the grandchild process.
exception_port_.reset(child_port_handshake.RunServer(
ChildPortHandshake::PortRightType::kSendRight));
return exception_port_.is_valid();
return true;
}
bool CrashpadClient::UseHandler() {

View File

@ -125,7 +125,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
bool* destroy_complex_request) override {
if (exception_port != exception_port_) {
LOG(WARNING) << "exception port mismatch";
return MIG_BAD_ID;
return KERN_FAILURE;
}
return exception_interface_->CatchMachException(behavior,
@ -172,7 +172,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
// to craft and send a no-senders notification via its exception port, and
// cause the handler to stop processing exceptions and exit.
LOG(WARNING) << "notify port mismatch";
return MIG_BAD_ID;
return KERN_FAILURE;
}
running_ = false;
@ -199,11 +199,11 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
// called.
if (notify != notify_port_) {
LOG(WARNING) << "notify port mismatch";
return MIG_BAD_ID;
return KERN_FAILURE;
}
NOTREACHED();
return KERN_FAILURE;
return MIG_BAD_ID;
}
UniversalMachExcServer mach_exc_server_;
@ -219,8 +219,9 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
} // namespace
ExceptionHandlerServer::ExceptionHandlerServer()
: receive_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) {
ExceptionHandlerServer::ExceptionHandlerServer(
base::mac::ScopedMachReceiveRight receive_port)
: receive_port_(receive_port.Pass()) {
CHECK(receive_port_.is_valid());
}

View File

@ -28,19 +28,24 @@ namespace crashpad {
//! process.
class ExceptionHandlerServer {
public:
ExceptionHandlerServer();
//! \brief Constructs an ExceptionHandlerServer object.
//!
//! \param[in] receive_port The port that exception messages and no-senders
//! notifications will be received on.
explicit ExceptionHandlerServer(
base::mac::ScopedMachReceiveRight receive_port);
~ExceptionHandlerServer();
//! \brief Runs the exception-handling server.
//!
//! \param[in] exception_interface An object to send exception messages to.
//!
//! This method monitors receive_port() for exception messages and no-senders
//! notifications. It continues running until it has no more clients,
//! indicated by the receipt of a no-senders notification. It is important to
//! assure that a send right has been transferred to a client (or queued by
//! `mach_msg()` to be sent to a client) prior to calling this method, or it
//! will detect that it is sender-less and return immediately.
//! This method monitors the receive port for exception messages and
//! no-senders notifications. It continues running until it has no more
//! clients, indicated by the receipt of a no-senders notification. It is
//! important to assure that a send right exists in a client (or has been
//! queued by `mach_msg()` to be sent to a client) prior to calling this
//! method, or it will detect that it is sender-less and return immediately.
//!
//! All exception messages will be passed to \a exception_interface.
//!
@ -48,17 +53,10 @@ class ExceptionHandlerServer {
//!
//! If an unexpected condition that prevents this method from functioning is
//! encountered, it will log a message and terminate execution. Receipt of an
//! invalid message on receive_port() will cause a message to be logged, but
//! invalid message on the receive port will cause a message to be logged, but
//! this method will continue running normally.
void Run(UniversalMachExcServer::Interface* exception_interface);
//! \brief Returns the receive right that will be monitored for exception
//! messages.
//!
//! The caller does not take ownership of this port. The caller must not use
//! this port for any purpose other than to make send rights for clients.
mach_port_t receive_port() const { return receive_port_.get(); }
private:
base::mac::ScopedMachReceiveRight receive_port_;

View File

@ -34,6 +34,7 @@
#if defined(OS_MACOSX)
#include <libgen.h>
#include "base/mac/scoped_mach_port.h"
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/exception_handler_server.h"
#include "util/mach/child_port_handshake.h"
@ -211,21 +212,21 @@ int HandlerMain(int argc, char* argv[]) {
}
#if defined(OS_MACOSX)
CloseStdinAndStdout();
if (options.reset_own_crash_exception_port_to_system_default) {
CrashpadClient::UseSystemDefaultHandler();
}
#endif
#if defined(OS_MACOSX)
ExceptionHandlerServer exception_handler_server;
CloseStdinAndStdout();
if (!ChildPortHandshake::RunClientForFD(
base::mac::ScopedMachReceiveRight receive_right(
ChildPortHandshake::RunServerForFD(
base::ScopedFD(options.handshake_fd),
exception_handler_server.receive_port(),
MACH_MSG_TYPE_MAKE_SEND)) {
ChildPortHandshake::PortRightType::kReceiveRight));
if (!receive_right.is_valid()) {
return EXIT_FAILURE;
}
ExceptionHandlerServer exception_handler_server(receive_right.Pass());
#elif defined(OS_WIN)
ExceptionHandlerServer exception_handler_server(options.pipe_name);
#endif // OS_MACOSX

View File

@ -41,16 +41,18 @@ class RunServerThread : public Thread {
public:
// Instantiates a thread which will invoke server->Run(delegate, pipe_name);
RunServerThread(ExceptionHandlerServer* server,
ExceptionHandlerServer::Delegate* delegate)
: server_(server), delegate_(delegate) {}
ExceptionHandlerServer::Delegate* delegate,
const std::string& pipe_name)
: server_(server), delegate_(delegate), pipe_name_(pipe_name) {}
~RunServerThread() override {}
private:
// Thread:
void ThreadMain() override { server_->Run(delegate_); }
void ThreadMain() override { server_->Run(delegate_, pipe_name_); }
ExceptionHandlerServer* server_;
ExceptionHandlerServer::Delegate* delegate_;
std::string pipe_name_;
DISALLOW_COPY_AND_ASSIGN(RunServerThread);
};
@ -128,8 +130,9 @@ void TestCrashingChild(const base::string16& directory_modification) {
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
CrashingDelegate delegate(server_ready.get(), completed.get());
ExceptionHandlerServer exception_handler_server(pipe_name);
RunServerThread server_thread(&exception_handler_server, &delegate);
ExceptionHandlerServer exception_handler_server;
RunServerThread server_thread(
&exception_handler_server, &delegate, pipe_name);
server_thread.Start();
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
&exception_handler_server, &server_thread);
@ -230,8 +233,9 @@ void TestDumpWithoutCrashingChild(
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
SimulateDelegate delegate(server_ready.get(), completed.get());
ExceptionHandlerServer exception_handler_server(pipe_name);
RunServerThread server_thread(&exception_handler_server, &delegate);
ExceptionHandlerServer exception_handler_server;
RunServerThread server_thread(
&exception_handler_server, &delegate, pipe_name);
server_thread.Start();
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
&exception_handler_server, &server_thread);

View File

@ -87,7 +87,7 @@ class ChildPortHandshakeTest;
//! ChildPortHandshake child_port_handshake;
//! base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD();
//! std::string server_write_fd_string =
//! base::StringPrintf("%d", server_write_fd);
//! base::StringPrintf("%d", server_write_fd.get());
//!
//! pid_t pid = fork();
//! if (pid == 0) {
@ -97,8 +97,8 @@ class ChildPortHandshakeTest;
//! // server_write_fd. Let the child know what file descriptor to use for
//! // server_write_fd by passing it as argv[1]. Example code for the child
//! // process is below.
//! CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd);
//! execlp("child", "child", server_write_fd_string.c_str(), nullptr);
//! CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get());
//! execlp("./child", "child", server_write_fd_string.c_str(), nullptr);
//! }
//!
//! // Parent
@ -122,7 +122,7 @@ class ChildPortHandshakeTest;
//!
//! // Send the receive right to the child process, retaining the send right
//! // for use in the parent process.
//! if (child_port_handshake.RunClient(receive_right,
//! if (child_port_handshake.RunClient(receive_right.get(),
//! MACH_MSG_TYPE_MOVE_RECEIVE)) {
//! ignore_result(receive_right.release());
//! }

View File

@ -157,8 +157,8 @@ class MachMessageServer {
//! #kPersistent, the timeout applies to the overall duration of this
//! function, not to any individual `mach_msg()` call.
//!
//! \return On success, `KERN_SUCCESS` (when \a persistent is #kOneShot) or
//! `MACH_RCV_TIMED_OUT` (when \a persistent is #kOneShot and \a
//! \return On success, `MACH_MSG_SUCCESS` (when \a persistent is #kOneShot)
//! or `MACH_RCV_TIMED_OUT` (when \a persistent is #kOneShot and \a
//! timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function
//! has no successful return value when \a persistent is #kPersistent and
//! \a timeout_ms is #kMachMessageTimeoutWaitIndefinitely. On failure,

View File

@ -234,9 +234,8 @@ class ClientData {
ExceptionHandlerServer::Delegate::~Delegate() {
}
ExceptionHandlerServer::ExceptionHandlerServer(const std::string& pipe_name)
: pipe_name_(pipe_name),
port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
ExceptionHandlerServer::ExceptionHandlerServer()
: port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
clients_lock_(),
clients_() {
}
@ -244,12 +243,13 @@ ExceptionHandlerServer::ExceptionHandlerServer(const std::string& pipe_name)
ExceptionHandlerServer::~ExceptionHandlerServer() {
}
void ExceptionHandlerServer::Run(Delegate* delegate) {
void ExceptionHandlerServer::Run(Delegate* delegate,
const std::string& pipe_name) {
uint64_t shutdown_token = base::RandUint64();
// We create two pipe instances, so that there's one listening while the
// PipeServiceProc is processing a registration.
ScopedKernelHANDLE thread_handles[2];
base::string16 pipe_name_16(base::UTF8ToUTF16(pipe_name_));
base::string16 pipe_name_16(base::UTF8ToUTF16(pipe_name));
for (int i = 0; i < arraysize(thread_handles); ++i) {
HANDLE pipe =
CreateNamedPipe(pipe_name_16.c_str(),
@ -311,7 +311,7 @@ void ExceptionHandlerServer::Run(Delegate* delegate) {
message.type = ClientToServerMessage::kShutdown;
message.shutdown.token = shutdown_token;
ServerToClientMessage response;
SendToCrashHandlerServer(pipe_name_16,
SendToCrashHandlerServer(base::UTF8ToUTF16(pipe_name),
reinterpret_cast<ClientToServerMessage&>(message),
&response);
}

View File

@ -61,18 +61,16 @@ class ExceptionHandlerServer {
};
//! \brief Constructs the exception handling server.
//!
//! \param[in] pipe_name The name of the pipe to listen on. Must be of the
//! form "\\.\pipe\<some_name>".
explicit ExceptionHandlerServer(const std::string& pipe_name);
ExceptionHandlerServer();
~ExceptionHandlerServer();
//! \brief Runs the exception-handling server.
//!
//! \param[in] delegate The interface to which the exceptions are delegated
//! when they are caught in Run(). Ownership is not transferred.
void Run(Delegate* delegate);
//! \param[in] pipe_name The name of the pipe to listen on. Must be of the
//! form "\\.\pipe\<some_name>".
void Run(Delegate* delegate, const std::string& pipe_name);
//! \brief Stops the exception-handling server. Returns immediately. The
//! object must not be destroyed until Run() returns.
@ -86,7 +84,6 @@ class ExceptionHandlerServer {
static void __stdcall OnNonCrashDumpEvent(void* ctx, BOOLEAN);
static void __stdcall OnProcessEnd(void* ctx, BOOLEAN);
std::string pipe_name_;
ScopedKernelHANDLE port_;
base::Lock clients_lock_;

View File

@ -36,18 +36,20 @@ namespace {
// Runs the ExceptionHandlerServer on a background thread.
class RunServerThread : public Thread {
public:
// Instantiates a thread which will invoke server->Run(delegate).
// Instantiates a thread which will invoke server->Run(delegate, pipe_name).
RunServerThread(ExceptionHandlerServer* server,
ExceptionHandlerServer::Delegate* delegate)
: server_(server), delegate_(delegate) {}
ExceptionHandlerServer::Delegate* delegate,
const std::string& pipe_name)
: server_(server), delegate_(delegate), pipe_name_(pipe_name) {}
~RunServerThread() override {}
private:
// Thread:
void ThreadMain() override { server_->Run(delegate_); }
void ThreadMain() override { server_->Run(delegate_, pipe_name_); }
ExceptionHandlerServer* server_;
ExceptionHandlerServer::Delegate* delegate_;
std::string pipe_name_;
DISALLOW_COPY_AND_ASSIGN(RunServerThread);
};
@ -83,8 +85,8 @@ class ExceptionHandlerServerTest : public testing::Test {
base::StringPrintf("%08x", GetCurrentProcessId())),
server_ready_(CreateEvent(nullptr, false, false, nullptr)),
delegate_(server_ready_.get()),
server_(pipe_name_),
server_thread_(&server_, &delegate_) {}
server_(),
server_thread_(&server_, &delegate_, pipe_name_) {}
TestDelegate& delegate() { return delegate_; }
ExceptionHandlerServer& server() { return server_; }