mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
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:
parent
e78789b9e0
commit
c82309f0e5
@ -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") {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user