blob: 3f97baadfd00ab56b4896c5d787a439f0a7c03ca [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#include "src/developer/forensics/exceptions/process_handler.h"
#include <lib/fdio/spawn.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/processargs.h>
#include <array>
#include "src/lib/fsl/handles/object_info.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace forensics {
namespace exceptions {
namespace {
bool SpawnSubprocess(zx::channel* client, zx::process* subprocess) {
static size_t subprocess_num{1};
const std::string subprocess_name(fxl::StringPrintf("exception_handler_%03zu", subprocess_num++));
const std::array<const char*, 2> args = {
subprocess_name.c_str(),
nullptr,
};
// Create a channel to pass as a startup handle.
zx::channel channel, server;
if (const zx_status_t status = zx::channel::create(0u, &channel, &server); status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to create channel";
return false;
}
const std::array actions = {
fdio_spawn_action_t{
.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
.h =
{
.id = PA_HND(PA_USER0, 0),
.handle = server.release(),
},
},
};
// Create the sub-process.
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH] = {};
zx::process process;
if (const zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL,
"/pkg/bin/exception_handler", args.data(),
/*environ=*/nullptr, actions.size(), actions.data(),
process.reset_and_get_address(), err_msg);
status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to launch exception handler process: " << err_msg;
return false;
}
*client = std::move(channel);
*subprocess = std::move(process);
return true;
}
} // namespace
ProcessHandler::ProcessHandler(async_dispatcher_t* dispatcher, LogMonikerFn log_moniker,
fit::closure on_available)
: dispatcher_(dispatcher),
log_moniker_(std::move(log_moniker)),
on_available_(std::move(on_available)) {
crash_reporter_.set_error_handler([this](const zx_status_t status) {
FX_PLOGS(WARNING, status) << "Lost connection to subprocess";
on_available_();
});
}
ProcessHandler::~ProcessHandler() {
if (subprocess_.is_valid()) {
subprocess_.kill();
}
}
void ProcessHandler::Handle(zx::exception exception, zx::process process, zx::thread thread) {
if (!crash_reporter_.is_bound()) {
zx::channel client;
// If we are not able to spawn a sub-process, we will have to lose the exception.
if (!SpawnSubprocess(&client, &subprocess_)) {
const std::string thread_name =
(thread.is_valid()) ? fsl::GetObjectName(thread.get()) : "unknown";
const std::string process_name =
(process.is_valid()) ? fsl::GetObjectName(process.get()) : "unknown";
FX_LOGS(WARNING) << "Dropping the exception for thread '" << thread_name << "' in process '"
<< process_name << "'";
on_available_();
return;
}
crash_reporter_.Bind(std::move(client), dispatcher_);
}
crash_reporter_->Send(std::move(exception), std::move(process), std::move(thread),
[this](const ::fidl::StringPtr moniker) {
// Log |moniker| before calling |on_available_| because the lambda may
// recurse.
if (moniker) {
log_moniker_(*moniker);
}
on_available_();
});
}
} // namespace exceptions
} // namespace forensics