| // 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 |