Fixing debugger startup race.

This commit is contained in:
Ben Vanik 2015-07-29 19:52:53 -07:00
parent 19901c4759
commit 122114d1d1
10 changed files with 78 additions and 11 deletions

View File

@ -10,6 +10,8 @@
#ifndef XENIA_DEBUG_DEBUG_SERVER_H_ #ifndef XENIA_DEBUG_DEBUG_SERVER_H_
#define XENIA_DEBUG_DEBUG_SERVER_H_ #define XENIA_DEBUG_DEBUG_SERVER_H_
#include <functional>
#include "xenia/cpu/function.h" #include "xenia/cpu/function.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/debug/breakpoint.h" #include "xenia/debug/breakpoint.h"
@ -27,6 +29,8 @@ class DebugServer {
virtual bool Initialize() = 0; virtual bool Initialize() = 0;
virtual void PostSynchronous(std::function<void()> fn) = 0;
// TODO(benvanik): better thread type (XThread?) // TODO(benvanik): better thread type (XThread?)
// virtual void OnThreadCreated(ThreadState* thread_state) = 0; // virtual void OnThreadCreated(ThreadState* thread_state) = 0;
// virtual void OnThreadDestroyed(ThreadState* thread_state) = 0; // virtual void OnThreadDestroyed(ThreadState* thread_state) = 0;

View File

@ -123,7 +123,7 @@ void Debugger::PreLaunch() {
// Start paused. // Start paused.
execution_state_ = ExecutionState::kRunning; execution_state_ = ExecutionState::kRunning;
Interrupt(); server_->PostSynchronous([this]() { Interrupt(); });
} else { } else {
// Start running. // Start running.
execution_state_ = ExecutionState::kRunning; execution_state_ = ExecutionState::kRunning;

View File

@ -44,6 +44,8 @@ bool GdbServer::Initialize() {
return true; return true;
} }
void GdbServer::PostSynchronous(std::function<void()> fn) { assert_always(); }
void GdbServer::AcceptClient(std::unique_ptr<Socket> client) { void GdbServer::AcceptClient(std::unique_ptr<Socket> client) {
// If we have an existing client, kill it and join its thread. // If we have an existing client, kill it and join its thread.
if (client_) { if (client_) {

View File

@ -32,6 +32,8 @@ class GdbServer : public DebugServer {
bool Initialize() override; bool Initialize() override;
void PostSynchronous(std::function<void()> fn) override;
private: private:
void AcceptClient(std::unique_ptr<Socket> client); void AcceptClient(std::unique_ptr<Socket> client);
bool HandleClientEvent(); bool HandleClientEvent();

View File

@ -44,6 +44,8 @@ bool MIServer::Initialize() {
return true; return true;
} }
void MIServer::PostSynchronous(std::function<void()> fn) { assert_always(); }
void MIServer::AcceptClient(std::unique_ptr<Socket> client) { void MIServer::AcceptClient(std::unique_ptr<Socket> client) {
// If we have an existing client, kill it and join its thread. // If we have an existing client, kill it and join its thread.
if (client_) { if (client_) {

View File

@ -32,6 +32,8 @@ class MIServer : public DebugServer {
bool Initialize() override; bool Initialize() override;
void PostSynchronous(std::function<void()> fn) override;
private: private:
void AcceptClient(std::unique_ptr<Socket> client); void AcceptClient(std::unique_ptr<Socket> client);
bool HandleClientEvent(); bool HandleClientEvent();

View File

@ -42,6 +42,8 @@ XdpServer::XdpServer(Debugger* debugger)
XdpServer::~XdpServer() = default; XdpServer::~XdpServer() = default;
bool XdpServer::Initialize() { bool XdpServer::Initialize() {
post_event_ = xe::threading::Event::CreateAutoResetEvent(false);
socket_server_ = SocketServer::Create(uint16_t(FLAGS_xdp_server_port), socket_server_ = SocketServer::Create(uint16_t(FLAGS_xdp_server_port),
[this](std::unique_ptr<Socket> client) { [this](std::unique_ptr<Socket> client) {
AcceptClient(std::move(client)); AcceptClient(std::move(client));
@ -54,6 +56,19 @@ bool XdpServer::Initialize() {
return true; return true;
} }
void XdpServer::PostSynchronous(std::function<void()> fn) {
xe::threading::Fence fence;
{
std::lock_guard<std::mutex> lock(post_mutex_);
post_queue_.push_back([&fence, fn]() {
fn();
fence.Signal();
});
}
post_event_->Set();
fence.Wait();
}
void XdpServer::AcceptClient(std::unique_ptr<Socket> client) { void XdpServer::AcceptClient(std::unique_ptr<Socket> client) {
// If we have an existing client, kill it and join its thread. // If we have an existing client, kill it and join its thread.
if (client_) { if (client_) {
@ -85,11 +100,35 @@ void XdpServer::AcceptClient(std::unique_ptr<Socket> client) {
// Main loop. // Main loop.
bool running = true; bool running = true;
while (running) { while (running) {
auto wait_result = xe::threading::Wait(client_->wait_handle(), true); xe::threading::WaitHandle* wait_handles[] = {
switch (wait_result) { client_->wait_handle(), post_event_.get(),
};
auto wait_result = xe::threading::WaitMultiple(
wait_handles, xe::countof(wait_handles), false, true);
switch (wait_result.first) {
case xe::threading::WaitResult::kSuccess: case xe::threading::WaitResult::kSuccess:
// Event (read or close). // Event (read or close).
switch (wait_result.second) {
case 0: {
running = HandleClientEvent(); running = HandleClientEvent();
} break;
case 1: {
bool has_remaining = true;
while (has_remaining) {
std::function<void()> fn;
{
std::lock_guard<std::mutex> lock(post_mutex_);
fn = std::move(post_queue_.front());
post_queue_.pop_front();
has_remaining = !post_queue_.empty();
if (!has_remaining) {
post_event_->Reset();
}
}
fn();
}
} break;
}
continue; continue;
case xe::threading::WaitResult::kAbandoned: case xe::threading::WaitResult::kAbandoned:
case xe::threading::WaitResult::kFailed: case xe::threading::WaitResult::kFailed:

View File

@ -10,7 +10,9 @@
#ifndef XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_ #ifndef XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_
#define XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_ #define XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_
#include <list>
#include <memory> #include <memory>
#include <mutex>
#include "xenia/base/socket.h" #include "xenia/base/socket.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
@ -33,6 +35,8 @@ class XdpServer : public DebugServer {
bool Initialize() override; bool Initialize() override;
void PostSynchronous(std::function<void()> fn) override;
void OnExecutionContinued() override; void OnExecutionContinued() override;
void OnExecutionInterrupted() override; void OnExecutionInterrupted() override;
@ -54,6 +58,11 @@ class XdpServer : public DebugServer {
std::unique_ptr<Socket> client_; std::unique_ptr<Socket> client_;
std::unique_ptr<xe::threading::Thread> client_thread_; std::unique_ptr<xe::threading::Thread> client_thread_;
std::mutex post_mutex_;
std::unique_ptr<xe::threading::Event> post_event_;
std::list<std::function<void()>> post_queue_;
std::vector<uint8_t> receive_buffer_; std::vector<uint8_t> receive_buffer_;
proto::PacketReader packet_reader_; proto::PacketReader packet_reader_;
proto::PacketWriter packet_writer_; proto::PacketWriter packet_writer_;

View File

@ -218,8 +218,10 @@ X_STATUS XUserModule::Launch(uint32_t flags) {
XELOGI("Launching module..."); XELOGI("Launching module...");
// Create a thread to run in. // Create a thread to run in.
auto thread = object_ref<XThread>( // We start suspended so we can run the debugger prep.
new XThread(kernel_state(), stack_size_, 0, entry_point_, 0, 0, true)); auto thread = object_ref<XThread>(new XThread(kernel_state(), stack_size_, 0,
entry_point_, 0,
X_CREATE_SUSPENDED, true));
X_STATUS result = thread->Create(); X_STATUS result = thread->Create();
if (XFAILED(result)) { if (XFAILED(result)) {
@ -227,6 +229,16 @@ X_STATUS XUserModule::Launch(uint32_t flags) {
return result; return result;
} }
// Waits for a debugger client, if desired.
if (emulator()->debugger()) {
emulator()->debugger()->PreLaunch();
}
// Resume the thread now.
// If the debugger has requested a suspend this will just decrement the
// suspend count without resuming it until the debugger wants.
thread->Resume();
// Wait until thread completes. // Wait until thread completes.
thread->Wait(0, 0, 0, nullptr); thread->Wait(0, 0, 0, nullptr);

View File

@ -179,11 +179,6 @@ int XboxkrnlModule::LaunchModule(const char* path) {
// Set as the main module, while running. // Set as the main module, while running.
kernel_state_->SetExecutableModule(module); kernel_state_->SetExecutableModule(module);
// Waits for a debugger client, if desired.
if (emulator()->debugger()) {
emulator()->debugger()->PreLaunch();
}
// Launch the module. // Launch the module.
// NOTE: this won't return until the module exits. // NOTE: this won't return until the module exits.
X_STATUS result_code = module->Launch(0); X_STATUS result_code = module->Launch(0);