| // 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 <lib/zx/exception.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <zircon/syscalls/port.h> |
| |
| #include <utility> |
| |
| #include <test-utils/test-utils.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "debugger.h" |
| #include "inferior-control.h" |
| #include "inferior.h" |
| #include "utils.h" |
| |
| namespace { |
| |
| struct suspend_on_start_test_state_t { |
| int started_threads = 0; |
| }; |
| |
| void suspend_on_start_test_handler(inferior_data_t* data, const zx_port_packet_t* packet, |
| void* handler_arg) { |
| auto* test_state = reinterpret_cast<suspend_on_start_test_state_t*>(handler_arg); |
| |
| // This test is supposed to only get an exception and nothing else. |
| zx_info_handle_basic_t basic_info; |
| zx_status_t status = zx_object_get_info(data->exception_channel, ZX_INFO_HANDLE_BASIC, |
| &basic_info, sizeof(basic_info), nullptr, nullptr); |
| ASSERT_EQ(status, ZX_OK); |
| ASSERT_EQ(basic_info.koid, packet->key); |
| |
| zx::exception exception; |
| zx_exception_info_t info; |
| uint32_t num_bytes = sizeof(info); |
| uint32_t num_handles = 1; |
| status = zx_channel_read(data->exception_channel, 0, &info, exception.reset_and_get_address(), |
| num_bytes, num_handles, nullptr, nullptr); |
| ASSERT_EQ(status, ZX_OK); |
| |
| switch (info.type) { |
| case ZX_EXCP_THREAD_STARTING: |
| printf("thread %lu starting\n", info.tid); |
| // We only want to resume the first thread. |
| test_state->started_threads++; |
| break; |
| case ZX_EXCP_THREAD_EXITING: |
| printf("thread %lu exiting\n", info.tid); |
| ASSERT_NO_FATAL_FAILURE(handle_thread_exiting(data->inferior, &info, std::move(exception))); |
| break; |
| default: |
| printf("Unexpected exception %s (%u) on thread %lu\n", tu_exception_to_string(info.type), |
| info.type, info.tid); |
| break; |
| } |
| } |
| |
| } // namespace |
| |
| TEST(SuspendOnStartTests, SuspendOnStartTest) { |
| springboard_t* sb; |
| zx_handle_t inferior, channel; |
| ASSERT_NO_FATAL_FAILURE(setup_inferior(kTestSuspendOnStart, &sb, &inferior, &channel)); |
| |
| // Attach to the inferior now because we want to see thread starting |
| // exceptions. |
| zx_handle_t port = ZX_HANDLE_INVALID; |
| EXPECT_EQ(zx_port_create(0, &port), ZX_OK); |
| size_t max_threads = 2; |
| inferior_data_t* inferior_data = attach_inferior(inferior, port, max_threads); |
| |
| suspend_on_start_test_state_t test_state = {}; |
| thrd_t wait_inf_thread = |
| start_wait_inf_thread(inferior_data, suspend_on_start_test_handler, &test_state); |
| EXPECT_NE(port, ZX_HANDLE_INVALID); |
| |
| ASSERT_NO_FATAL_FAILURE(start_inferior(sb)); |
| |
| // The remaining testing happens at this point as threads start. |
| // This testing is done in |suspend_on_start_test_handler()|. |
| |
| ASSERT_NO_FATAL_FAILURE(shutdown_inferior(channel, inferior)); |
| |
| // Stop the waiter thread before closing the port that it's waiting on. |
| join_wait_inf_thread(wait_inf_thread); |
| |
| detach_inferior(inferior_data, true); |
| |
| zx_handle_close(port); |
| zx_handle_close(channel); |
| zx_handle_close(inferior); |
| } |