blob: 4a073d5518f7d956327e45a7a565e6072591c186 [file] [log] [blame]
// 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 <test-utils/test-utils.h>
#include <unittest/unittest.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;
};
bool 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);
BEGIN_HELPER;
// 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:
unittest_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:
unittest_printf("thread %lu exiting\n", info.tid);
ASSERT_TRUE(handle_thread_exiting(data->inferior, &info, std::move(exception)));
break;
default:
unittest_printf("Unexpected exception %s (%u) on thread %lu\n",
tu_exception_to_string(info.type), info.type, info.tid);
break;
}
END_HELPER;
}
} // namespace
bool SuspendOnStartTest() {
BEGIN_TEST;
springboard_t* sb;
zx_handle_t inferior, channel;
if (!setup_inferior(kTestSuspendOnStart, &sb, &inferior, &channel))
return false;
// 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);
if (!start_inferior(sb))
return false;
// The remaining testing happens at this point as threads start.
// This testing is done in |suspend_on_start_test_handler()|.
if (!shutdown_inferior(channel, inferior))
return false;
// 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);
END_TEST;
}
BEGIN_TEST_CASE(suspend_on_start_tests)
RUN_TEST(SuspendOnStartTest);
END_TEST_CASE(suspend_on_start_tests)