fuchsia: Implement StartHandler() and ExceptionHandlerServer

StartHandler() binds to the default job's exception port, and launches
the handler process (normally this is crashpad_handler), passing it the
task handle and a handle to the exception port as startup parameters.
This follows the protocol used by crashlogger.

Additionally, implement ExceptionHandlerServer in crashpad_handler,
which contains the exception processing loop. It currently dispatches to
an empty CrashReportExceptionHandler where a report will be written
eventually.

Bug: crashpad:196
Change-Id: Ie27ff6f67adfbcc7d03551ae7e84a885da43df5a
Reviewed-on: https://chromium-review.googlesource.com/1043282
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Scott Graham 2018-05-04 12:20:52 -07:00 committed by Commit Bot
parent e78789b9e0
commit c82309f0e5
7 changed files with 194 additions and 13 deletions

View File

@ -87,6 +87,16 @@ static_library("client") {
libs = [ "rpcrt4.lib" ]
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
}
if (crashpad_is_fuchsia) {
if (crashpad_is_in_fuchsia) {
deps += [
"//zircon/public/lib/launchpad",
]
} else {
libs = [ "launchpad" ]
}
}
}
source_set("client_test") {

View File

@ -75,6 +75,9 @@ class CrashpadClient {
//! Crashpad. Optionally, use WaitForHandlerStart() to join with the
//! background thread and retrieve the status of handler startup.
//!
//! On Fuchsia, this method binds to the exception port of the current default
//! job, and starts a Crashpad handler to monitor that port.
//!
//! \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

@ -14,7 +14,16 @@
#include "client/crashpad_client.h"
#include <launchpad/launchpad.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/scoped_zx_handle.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/client_argv_handling.h"
#include "util/fuchsia/system_exception_port_key.h"
namespace crashpad {
@ -31,8 +40,79 @@ bool CrashpadClient::StartHandler(
const std::vector<std::string>& arguments,
bool restartable,
bool asynchronous_start) {
NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
return false;
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia.
zx_handle_t exception_port_raw;
zx_status_t status = zx_port_create(0, &exception_port_raw);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_port_create";
return false;
}
base::ScopedZxHandle exception_port(exception_port_raw);
status = zx_task_bind_exception_port(
zx_job_default(), exception_port.get(), kSystemExceptionPortKey, 0);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_task_bind_exception_port";
return false;
}
std::vector<std::string> argv_strings;
BuildHandlerArgvStrings(handler,
database,
metrics_dir,
url,
annotations,
arguments,
&argv_strings);
std::vector<const char*> argv;
ConvertArgvStrings(argv_strings, &argv);
// ConvertArgvStrings adds an unnecessary nullptr at the end of the argv list,
// which causes launchpad_set_args() to hang.
argv.pop_back();
launchpad_t* lp;
launchpad_create(zx_job_default(), argv[0], &lp);
launchpad_load_from_file(lp, argv[0]);
launchpad_set_args(lp, argv.size(), &argv[0]);
// TODO(scottmg): https://crashpad.chromium.org/bug/196, this is useful during
// bringup, but should probably be made minimal for real usage.
launchpad_clone(lp,
LP_CLONE_FDIO_NAMESPACE | LP_CLONE_FDIO_STDIO |
LP_CLONE_ENVIRON | LP_CLONE_DEFAULT_JOB);
// Follow the same protocol as devmgr and crashlogger in Zircon (that is,
// process handle as handle 0, with type USER0, exception port handle as
// handle 1, also with type PA_USER0) so that it's trivial to replace
// crashlogger with crashpad_handler. The exception port is passed on, so
// released here. Currently it is assumed that this process's default job
// handle is the exception port that should be monitored. In the future, it
// might be useful for this to be configurable by the client.
zx_handle_t handles[] = {ZX_HANDLE_INVALID, ZX_HANDLE_INVALID};
status =
zx_handle_duplicate(zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &handles[0]);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_handle_duplicate";
return false;
}
handles[1] = exception_port.release();
uint32_t handle_types[] = {PA_HND(PA_USER0, 0), PA_HND(PA_USER0, 1)};
launchpad_add_handles(lp, arraysize(handles), handles, handle_types);
const char* error_message;
zx_handle_t child_raw;
status = launchpad_go(lp, &child_raw, &error_message);
base::ScopedZxHandle child(child_raw);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "launchpad_go: " << error_message;
return false;
}
return true;
}
} // namespace crashpad

View File

@ -15,6 +15,8 @@
#ifndef CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
#define CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
#include <stdint.h>
#include <map>
#include <string>
@ -56,6 +58,24 @@ class CrashReportExceptionHandler {
~CrashReportExceptionHandler();
//! \brief Called when the exception handler server has caught an exception
//! and wants a crash dump to be taken.
//!
//! This function is expected to call `zx_task_resume()` in order to complete
//! handling of the exception.
//!
//! \note TODO(scottmg): This is not yet implemented.
//!
//! \param[in] type The type of exception, a `ZX_EXCP_*` value.
//! \param[in] pid The koid of the process which sustained the exception.
//! \param[in] tid The koid of the thread which sustained the exception.
//! \return `true` on success, or `false` with an error logged.
bool HandleException(uint32_t type,
uint64_t pid,
uint64_t tid) {
return false;
}
private:
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
};

View File

@ -14,16 +14,47 @@
#include "handler/fuchsia/exception_handler_server.h"
#include <zircon/syscalls/exception.h>
#include <zircon/syscalls/port.h>
#include <utility>
#include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h"
#include "handler/fuchsia/crash_report_exception_handler.h"
#include "util/fuchsia/system_exception_port_key.h"
namespace crashpad {
ExceptionHandlerServer::ExceptionHandlerServer() {}
ExceptionHandlerServer::ExceptionHandlerServer(
base::ScopedZxHandle root_job,
base::ScopedZxHandle exception_port)
: root_job_(std::move(root_job)),
exception_port_(std::move(exception_port)) {}
ExceptionHandlerServer::~ExceptionHandlerServer() {}
ExceptionHandlerServer::~ExceptionHandlerServer() = default;
void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) {
NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
while (true) {
zx_port_packet_t packet;
zx_status_t status =
zx_port_wait(exception_port_.get(), ZX_TIME_INFINITE, &packet, 1);
if (status != ZX_OK) {
ZX_LOG(ERROR, status) << "zx_port_wait, aborting";
return;
}
if (packet.key != kSystemExceptionPortKey) {
LOG(ERROR) << "unexpected packet key, ignoring";
continue;
}
bool result = handler->HandleException(
packet.type, packet.exception.pid, packet.exception.tid);
if (!result) {
LOG(ERROR) << "HandleException failed";
}
}
}
} // namespace crashpad

View File

@ -16,17 +16,25 @@
#define CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
#include "base/macros.h"
#include "base/fuchsia/scoped_zx_handle.h"
namespace crashpad {
class CrashReportExceptionHandler;
//! \brief Runs the main exception-handling server in Crashpad's handler
//! process. This class is not yet implemented.
//! process.
class ExceptionHandlerServer {
public:
//! \brief Constructs an ExceptionHandlerServer object.
ExceptionHandlerServer();
//!
//! \param[in] root_job The root of the tree of processes that will be handled
//! by this server. It is assumed that \a exception_port is the exception
//! port of this job.
//! \param[in] exception_port The exception port that this server will
//! monitor.
ExceptionHandlerServer(base::ScopedZxHandle root_job,
base::ScopedZxHandle exception_port);
~ExceptionHandlerServer();
//! \brief Runs the exception-handling server.
@ -36,6 +44,9 @@ class ExceptionHandlerServer {
void Run(CrashReportExceptionHandler* handler);
private:
base::ScopedZxHandle root_job_;
base::ScopedZxHandle exception_port_;
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
};

View File

@ -83,6 +83,9 @@
#include "util/win/initial_client_data.h"
#include "util/win/session_end_watcher.h"
#elif defined(OS_FUCHSIA)
#include <zircon/process.h>
#include <zircon/processargs.h>
#include "handler/fuchsia/crash_report_exception_handler.h"
#include "handler/fuchsia/exception_handler_server.h"
#elif defined(OS_LINUX)
@ -390,17 +393,19 @@ void InstallCrashHandler() {
ALLOW_UNUSED_LOCAL(terminate_handler);
}
#elif defined(OS_FUCHSIA) || defined(OS_LINUX)
#elif defined(OS_FUCHSIA)
void InstallCrashHandler() {
// TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196
// TODO(jperaza): Linux: https://crashpad.chromium.org/bug/30
NOTREACHED();
// There's nothing to do here. Crashes in this process will already be caught
// here because this handler process is in the same job that has had its
// exception port bound.
// TODO(scottmg): This should collect metrics on handler crashes, at a
// minimum. https://crashpad.chromium.org/bug/230.
}
void ReinstallCrashHandler() {
// TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196
// TODO(jperaza): Linux: https://crashpad.chromium.org/bug/30
NOTREACHED();
}
@ -901,7 +906,28 @@ int HandlerMain(int argc,
if (!options.pipe_name.empty()) {
exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
}
#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
#elif defined(OS_FUCHSIA)
// These handles are logically "moved" into these variables when retrieved by
// zx_get_startup_handle(). Both are given to ExceptionHandlerServer which
// owns them in this process. There is currently no "connect-later" mode on
// Fuchsia, all the binding must be done by the client before starting
// crashpad_handler.
base::ScopedZxHandle root_job(zx_get_startup_handle(PA_HND(PA_USER0, 0)));
if (!root_job.is_valid()) {
LOG(ERROR) << "no process handle passed in startup handle 0";
return EXIT_FAILURE;
}
base::ScopedZxHandle exception_port(
zx_get_startup_handle(PA_HND(PA_USER0, 1)));
if (!exception_port.is_valid()) {
LOG(ERROR) << "no exception port handle passed in startup handle 1";
return EXIT_FAILURE;
}
ExceptionHandlerServer exception_handler_server(std::move(root_job),
std::move(exception_port));
#elif defined(OS_LINUX) || defined(OS_ANDROID)
ExceptionHandlerServer exception_handler_server;
#endif // OS_MACOSX