| // Copyright 2019 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/tests/crasher_wrapper.h" | |
| #include <lib/fdio/spawn.h> | |
| #include <lib/syslog/cpp/macros.h> | |
| #include <zircon/status.h> | |
| #include <zircon/syscalls/port.h> | |
| #include "src/lib/fsl/handles/object_info.h" | |
| namespace forensics { | |
| namespace exceptions { | |
| bool SpawnCrasher(ExceptionContext* pe) { | |
| zx::unowned_job current_job(zx_job_default()); | |
| if (zx_status_t res = zx::job::create(*current_job, 0, &pe->job); res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Coult not create current job handle."; | |
| return false; | |
| } | |
| if (zx_status_t res = pe->job.create_exception_channel(0, &pe->exception_channel); res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not create exception channel for job."; | |
| return false; | |
| } | |
| if (zx_status_t res = zx::port::create(0, &pe->port); res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not create a port."; | |
| return false; | |
| } | |
| constexpr uint64_t kKey = 0x1234; | |
| if (zx_status_t res = pe->exception_channel.wait_async(pe->port, kKey, ZX_CHANNEL_READABLE, 0); | |
| res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not wait async on exception channel."; | |
| return false; | |
| } | |
| // Create the process. | |
| const char* argv[] = {"crasher", nullptr}; | |
| constexpr char kCrasherPath[] = "/pkg/bin/exception_broker_crasher"; | |
| char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; | |
| if (zx_status_t res = | |
| fdio_spawn_etc(pe->job.get(), FDIO_SPAWN_CLONE_ALL, kCrasherPath, argv, nullptr, 0, | |
| nullptr, pe->process.reset_and_get_address(), err_msg); | |
| res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not spawn crasher process: " << err_msg; | |
| return false; | |
| } | |
| // Wait for the exception. | |
| zx_port_packet_t packet; | |
| if (zx_status_t res = pe->port.wait(zx::time::infinite(), &packet); res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not wait on port."; | |
| return false; | |
| } | |
| if ((packet.key != kKey) || (packet.type != ZX_PKT_TYPE_SIGNAL_ONE) || | |
| ((packet.signal.observed & ZX_CHANNEL_READABLE) == 0)) { | |
| FX_PLOGS(ERROR, ZX_ERR_WRONG_TYPE) << "Received wrong port packet."; | |
| return false; | |
| } | |
| // Read the exception. | |
| if (zx_status_t res = | |
| pe->exception_channel.read(0, &pe->exception_info, pe->exception.reset_and_get_address(), | |
| sizeof(pe->exception_info), 1, nullptr, nullptr); | |
| res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not read exception."; | |
| return false; | |
| } | |
| if (zx_status_t res = pe->exception.get_process(&pe->process); res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not get process for exception."; | |
| return false; | |
| } | |
| pe->process_koid = fsl::GetKoid(pe->process.get()); | |
| pe->process_name = fsl::GetObjectName(pe->process.get()); | |
| if (zx_status_t res = pe->exception.get_thread(&pe->thread); res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not get thread for exception."; | |
| return false; | |
| } | |
| pe->thread_koid = fsl::GetKoid(pe->thread.get()); | |
| pe->thread_name = fsl::GetObjectName(pe->thread.get()); | |
| return true; | |
| } | |
| bool MarkExceptionAsHandled(ExceptionContext* pe) { | |
| uint32_t state = ZX_EXCEPTION_STATE_HANDLED; | |
| if (zx_status_t res = pe->exception.set_property(ZX_PROP_EXCEPTION_STATE, &state, sizeof(state)); | |
| res != ZX_OK) { | |
| FX_PLOGS(ERROR, res) << "Could not set handled state to exception."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace exceptions | |
| } // namespace forensics |