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