Simplifying debug stuff, as I'm not going to bother with gdb.

This commit is contained in:
Ben Vanik 2015-08-04 18:39:51 -07:00
parent ec326119cf
commit 0a8d6eec91
30 changed files with 115 additions and 1567 deletions

View File

@ -7,33 +7,30 @@
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/debug/client/xdp/xdp_client.h" #include "xenia/debug/debug_client.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/debug/debugger.h"
namespace xe { namespace xe {
namespace debug { namespace debug {
namespace client {
namespace xdp {
using namespace xe::debug::proto; using namespace xe::debug::proto;
constexpr size_t kReceiveBufferSize = 1 * 1024 * 1024; constexpr size_t kReceiveBufferSize = 1 * 1024 * 1024;
constexpr size_t kTransmitBufferSize = 1 * 1024 * 1024; constexpr size_t kTransmitBufferSize = 1 * 1024 * 1024;
XdpClient::XdpClient() DebugClient::DebugClient()
: packet_reader_(kReceiveBufferSize), packet_writer_(kTransmitBufferSize) { : packet_reader_(kReceiveBufferSize), packet_writer_(kTransmitBufferSize) {
receive_buffer_.resize(kReceiveBufferSize); receive_buffer_.resize(kReceiveBufferSize);
} }
XdpClient::~XdpClient() { DebugClient::~DebugClient() {
socket_->Close(); socket_->Close();
xe::threading::Wait(thread_.get(), true); xe::threading::Wait(thread_.get(), true);
thread_.reset(); thread_.reset();
} }
bool XdpClient::Connect(std::string hostname, uint16_t port) { bool DebugClient::Connect(std::string hostname, uint16_t port) {
socket_ = Socket::Connect(std::move(hostname), port); socket_ = Socket::Connect(std::move(hostname), port);
if (!socket_) { if (!socket_) {
XELOGE("Unable to connect to remote host"); XELOGE("Unable to connect to remote host");
@ -69,7 +66,7 @@ bool XdpClient::Connect(std::string hostname, uint16_t port) {
return true; return true;
} }
bool XdpClient::HandleSocketEvent() { bool DebugClient::HandleSocketEvent() {
if (!socket_->is_connected()) { if (!socket_->is_connected()) {
// Known-disconnected. // Known-disconnected.
return false; return false;
@ -95,7 +92,7 @@ bool XdpClient::HandleSocketEvent() {
return true; return true;
} }
bool XdpClient::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) { bool DebugClient::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) {
// Grow and append the bytes to the receive buffer. // Grow and append the bytes to the receive buffer.
packet_reader_.AppendBuffer(buffer, buffer_length); packet_reader_.AppendBuffer(buffer, buffer_length);
@ -121,7 +118,7 @@ bool XdpClient::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) {
return true; return true;
} }
bool XdpClient::ProcessPacket(const proto::Packet* packet) { bool DebugClient::ProcessPacket(const proto::Packet* packet) {
// Hold lock during processing. // Hold lock during processing.
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
@ -167,7 +164,7 @@ bool XdpClient::ProcessPacket(const proto::Packet* packet) {
return true; return true;
} }
void XdpClient::Flush() { void DebugClient::Flush() {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
if (!packet_writer_.buffer_offset()) { if (!packet_writer_.buffer_offset()) {
return; return;
@ -176,7 +173,7 @@ void XdpClient::Flush() {
packet_writer_.Reset(); packet_writer_.Reset();
} }
void XdpClient::Continue() { void DebugClient::Continue() {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
packet_writer_.Begin(PacketType::kExecutionRequest); packet_writer_.Begin(PacketType::kExecutionRequest);
auto body = packet_writer_.Append<ExecutionRequest>(); auto body = packet_writer_.Append<ExecutionRequest>();
@ -185,7 +182,7 @@ void XdpClient::Continue() {
Flush(); Flush();
} }
void XdpClient::StepOne(uint32_t thread_id) { void DebugClient::StepOne(uint32_t thread_id) {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
packet_writer_.Begin(PacketType::kExecutionRequest); packet_writer_.Begin(PacketType::kExecutionRequest);
auto body = packet_writer_.Append<ExecutionRequest>(); auto body = packet_writer_.Append<ExecutionRequest>();
@ -196,7 +193,7 @@ void XdpClient::StepOne(uint32_t thread_id) {
Flush(); Flush();
} }
void XdpClient::StepTo(uint32_t thread_id, uint32_t target_pc) { void DebugClient::StepTo(uint32_t thread_id, uint32_t target_pc) {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
packet_writer_.Begin(PacketType::kExecutionRequest); packet_writer_.Begin(PacketType::kExecutionRequest);
auto body = packet_writer_.Append<ExecutionRequest>(); auto body = packet_writer_.Append<ExecutionRequest>();
@ -208,7 +205,7 @@ void XdpClient::StepTo(uint32_t thread_id, uint32_t target_pc) {
Flush(); Flush();
} }
void XdpClient::Interrupt() { void DebugClient::Interrupt() {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
packet_writer_.Begin(PacketType::kExecutionRequest); packet_writer_.Begin(PacketType::kExecutionRequest);
auto body = packet_writer_.Append<ExecutionRequest>(); auto body = packet_writer_.Append<ExecutionRequest>();
@ -217,7 +214,7 @@ void XdpClient::Interrupt() {
Flush(); Flush();
} }
void XdpClient::Exit() { void DebugClient::Exit() {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
packet_writer_.Begin(PacketType::kExecutionRequest); packet_writer_.Begin(PacketType::kExecutionRequest);
auto body = packet_writer_.Append<ExecutionRequest>(); auto body = packet_writer_.Append<ExecutionRequest>();
@ -226,7 +223,7 @@ void XdpClient::Exit() {
Flush(); Flush();
} }
void XdpClient::BeginUpdateAllState() { void DebugClient::BeginUpdateAllState() {
std::lock_guard<std::recursive_mutex> lock(mutex_); std::lock_guard<std::recursive_mutex> lock(mutex_);
packet_writer_.Begin(PacketType::kModuleListRequest); packet_writer_.Begin(PacketType::kModuleListRequest);
@ -237,7 +234,5 @@ void XdpClient::BeginUpdateAllState() {
Flush(); Flush();
} }
} // namespace xdp
} // namespace client
} // namespace debug } // namespace debug
} // namespace xe } // namespace xe

View File

@ -7,8 +7,8 @@
****************************************************************************** ******************************************************************************
*/ */
#ifndef XENIA_DEBUG_CLIENT_XDP_XDP_CLIENT_H_ #ifndef XENIA_DEBUG_DEBUG_CLIENT_H_
#define XENIA_DEBUG_CLIENT_XDP_XDP_CLIENT_H_ #define XENIA_DEBUG_DEBUG_CLIENT_H_
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -21,8 +21,6 @@
namespace xe { namespace xe {
namespace debug { namespace debug {
namespace client {
namespace xdp {
using proto::ModuleListEntry; using proto::ModuleListEntry;
using proto::ThreadListEntry; using proto::ThreadListEntry;
@ -45,10 +43,10 @@ class ClientListener {
std::vector<const ThreadListEntry*> entries) = 0; std::vector<const ThreadListEntry*> entries) = 0;
}; };
class XdpClient { class DebugClient {
public: public:
XdpClient(); DebugClient();
~XdpClient(); ~DebugClient();
Socket* socket() const { return socket_.get(); } Socket* socket() const { return socket_.get(); }
void set_listener(ClientListener* listener) { listener_ = listener; } void set_listener(ClientListener* listener) { listener_ = listener; }
@ -84,9 +82,7 @@ class XdpClient {
ExecutionState execution_state_ = ExecutionState::kStopped; ExecutionState execution_state_ = ExecutionState::kStopped;
}; };
} // namespace xdp
} // namespace client
} // namespace debug } // namespace debug
} // namespace xe } // namespace xe
#endif // XENIA_DEBUG_CLIENT_XDP_XDP_CLIENT_H_ #endif // XENIA_DEBUG_DEBUG_CLIENT_H_

View File

@ -7,7 +7,7 @@
****************************************************************************** ******************************************************************************
*/ */
#include "xenia/debug/server/xdp/xdp_server.h" #include "xenia/debug/debug_server.h"
#include <gflags/gflags.h> #include <gflags/gflags.h>
@ -18,12 +18,10 @@
#include "xenia/kernel/objects/xmodule.h" #include "xenia/kernel/objects/xmodule.h"
#include "xenia/kernel/objects/xthread.h" #include "xenia/kernel/objects/xthread.h"
DEFINE_int32(xdp_server_port, 9002, "Debugger XDP server TCP port."); DEFINE_int32(debug_server_port, 9002, "Debugger XDP server TCP port.");
namespace xe { namespace xe {
namespace debug { namespace debug {
namespace server {
namespace xdp {
using namespace xe::debug::proto; using namespace xe::debug::proto;
using namespace xe::kernel; using namespace xe::kernel;
@ -32,19 +30,19 @@ constexpr size_t kReceiveBufferSize = 32 * 1024;
constexpr size_t kReadBufferSize = 1 * 1024 * 1024; constexpr size_t kReadBufferSize = 1 * 1024 * 1024;
constexpr size_t kWriteBufferSize = 1 * 1024 * 1024; constexpr size_t kWriteBufferSize = 1 * 1024 * 1024;
XdpServer::XdpServer(Debugger* debugger) DebugServer::DebugServer(Debugger* debugger)
: DebugServer(debugger), : debugger_(debugger),
packet_reader_(kReadBufferSize), packet_reader_(kReadBufferSize),
packet_writer_(kWriteBufferSize) { packet_writer_(kWriteBufferSize) {
receive_buffer_.resize(kReceiveBufferSize); receive_buffer_.resize(kReceiveBufferSize);
} }
XdpServer::~XdpServer() = default; DebugServer::~DebugServer() = default;
bool XdpServer::Initialize() { bool DebugServer::Initialize() {
post_event_ = xe::threading::Event::CreateAutoResetEvent(false); post_event_ = xe::threading::Event::CreateAutoResetEvent(false);
socket_server_ = SocketServer::Create(uint16_t(FLAGS_xdp_server_port), socket_server_ = SocketServer::Create(uint16_t(FLAGS_debug_server_port),
[this](std::unique_ptr<Socket> client) { [this](std::unique_ptr<Socket> client) {
AcceptClient(std::move(client)); AcceptClient(std::move(client));
}); });
@ -56,7 +54,7 @@ bool XdpServer::Initialize() {
return true; return true;
} }
void XdpServer::PostSynchronous(std::function<void()> fn) { void DebugServer::PostSynchronous(std::function<void()> fn) {
xe::threading::Fence fence; xe::threading::Fence fence;
{ {
std::lock_guard<std::mutex> lock(post_mutex_); std::lock_guard<std::mutex> lock(post_mutex_);
@ -69,7 +67,7 @@ void XdpServer::PostSynchronous(std::function<void()> fn) {
fence.Wait(); fence.Wait();
} }
void XdpServer::AcceptClient(std::unique_ptr<Socket> client) { void DebugServer::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_) {
// TODO(benvanik): XDP say goodbye? // TODO(benvanik): XDP say goodbye?
@ -148,7 +146,7 @@ void XdpServer::AcceptClient(std::unique_ptr<Socket> client) {
}); });
} }
bool XdpServer::HandleClientEvent() { bool DebugServer::HandleClientEvent() {
if (!client_->is_connected()) { if (!client_->is_connected()) {
// Known-disconnected. // Known-disconnected.
return false; return false;
@ -173,7 +171,7 @@ bool XdpServer::HandleClientEvent() {
return true; return true;
} }
bool XdpServer::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) { bool DebugServer::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) {
// Grow and append the bytes to the receive buffer. // Grow and append the bytes to the receive buffer.
packet_reader_.AppendBuffer(buffer, buffer_length); packet_reader_.AppendBuffer(buffer, buffer_length);
@ -200,7 +198,7 @@ bool XdpServer::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) {
return true; return true;
} }
bool XdpServer::ProcessPacket(const proto::Packet* packet) { bool DebugServer::ProcessPacket(const proto::Packet* packet) {
auto emulator = debugger()->emulator(); auto emulator = debugger()->emulator();
auto kernel_state = emulator->kernel_state(); auto kernel_state = emulator->kernel_state();
auto object_table = kernel_state->object_table(); auto object_table = kernel_state->object_table();
@ -292,7 +290,7 @@ bool XdpServer::ProcessPacket(const proto::Packet* packet) {
return true; return true;
} }
void XdpServer::OnExecutionContinued() { void DebugServer::OnExecutionContinued() {
packet_writer_.Begin(PacketType::kExecutionNotification); packet_writer_.Begin(PacketType::kExecutionNotification);
auto body = packet_writer_.Append<ExecutionNotification>(); auto body = packet_writer_.Append<ExecutionNotification>();
body->current_state = ExecutionNotification::State::kRunning; body->current_state = ExecutionNotification::State::kRunning;
@ -300,7 +298,7 @@ void XdpServer::OnExecutionContinued() {
Flush(); Flush();
} }
void XdpServer::OnExecutionInterrupted() { void DebugServer::OnExecutionInterrupted() {
packet_writer_.Begin(PacketType::kExecutionNotification); packet_writer_.Begin(PacketType::kExecutionNotification);
auto body = packet_writer_.Append<ExecutionNotification>(); auto body = packet_writer_.Append<ExecutionNotification>();
body->current_state = ExecutionNotification::State::kStopped; body->current_state = ExecutionNotification::State::kStopped;
@ -309,7 +307,7 @@ void XdpServer::OnExecutionInterrupted() {
Flush(); Flush();
} }
void XdpServer::Flush() { void DebugServer::Flush() {
if (!packet_writer_.buffer_offset()) { if (!packet_writer_.buffer_offset()) {
return; return;
} }
@ -317,7 +315,7 @@ void XdpServer::Flush() {
packet_writer_.Reset(); packet_writer_.Reset();
} }
void XdpServer::SendSuccess(proto::request_id_t request_id) { void DebugServer::SendSuccess(proto::request_id_t request_id) {
packet_writer_.Begin(PacketType::kGenericResponse, request_id); packet_writer_.Begin(PacketType::kGenericResponse, request_id);
auto body = packet_writer_.Append<GenericResponse>(); auto body = packet_writer_.Append<GenericResponse>();
body->code = GenericResponse::Code::kSuccess; body->code = GenericResponse::Code::kSuccess;
@ -325,8 +323,8 @@ void XdpServer::SendSuccess(proto::request_id_t request_id) {
Flush(); Flush();
} }
void XdpServer::SendError(proto::request_id_t request_id, void DebugServer::SendError(proto::request_id_t request_id,
const char* error_message) { const char* error_message) {
packet_writer_.Begin(PacketType::kGenericResponse, request_id); packet_writer_.Begin(PacketType::kGenericResponse, request_id);
auto body = packet_writer_.Append<GenericResponse>(); auto body = packet_writer_.Append<GenericResponse>();
body->code = GenericResponse::Code::kError; body->code = GenericResponse::Code::kError;
@ -335,8 +333,8 @@ void XdpServer::SendError(proto::request_id_t request_id,
Flush(); Flush();
} }
void XdpServer::SendError(proto::request_id_t request_id, void DebugServer::SendError(proto::request_id_t request_id,
const std::string& error_message) { const std::string& error_message) {
packet_writer_.Begin(PacketType::kGenericResponse, request_id); packet_writer_.Begin(PacketType::kGenericResponse, request_id);
auto body = packet_writer_.Append<GenericResponse>(); auto body = packet_writer_.Append<GenericResponse>();
body->code = GenericResponse::Code::kError; body->code = GenericResponse::Code::kError;
@ -345,7 +343,5 @@ void XdpServer::SendError(proto::request_id_t request_id,
Flush(); Flush();
} }
} // namespace xdp
} // namespace server
} // namespace debug } // namespace debug
} // namespace xe } // namespace xe

View File

@ -11,10 +11,18 @@
#define XENIA_DEBUG_DEBUG_SERVER_H_ #define XENIA_DEBUG_DEBUG_SERVER_H_
#include <functional> #include <functional>
#include <list>
#include <memory>
#include <mutex>
#include "xenia/base/socket.h"
#include "xenia/base/threading.h"
#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"
#include "xenia/debug/proto/packet_reader.h"
#include "xenia/debug/proto/packet_writer.h"
#include "xenia/debug/proto/xdp_protocol.h"
namespace xe { namespace xe {
namespace debug { namespace debug {
@ -23,27 +31,53 @@ class Debugger;
class DebugServer { class DebugServer {
public: public:
virtual ~DebugServer() = default; DebugServer(Debugger* debugger);
~DebugServer();
Debugger* debugger() const { return debugger_; } Debugger* debugger() const { return debugger_; }
Socket* client() const { return client_.get(); }
virtual bool Initialize() = 0; bool Initialize();
virtual void PostSynchronous(std::function<void()> fn) = 0; void PostSynchronous(std::function<void()> fn);
// TODO(benvanik): better thread type (XThread?) // TODO(benvanik): better thread type (XThread?)
// virtual void OnThreadCreated(ThreadState* thread_state) = 0; // void OnThreadCreated(ThreadState* thread_state);
// virtual void OnThreadDestroyed(ThreadState* thread_state) = 0; // void OnThreadDestroyed(ThreadState* thread_state);
virtual void OnExecutionContinued() {} void OnExecutionContinued();
virtual void OnExecutionInterrupted() {} void OnExecutionInterrupted();
/*virtual void OnBreakpointHit(xe::cpu::ThreadState* thread_state, /*void OnBreakpointHit(xe::cpu::ThreadState* thread_state,
Breakpoint* breakpoint) = 0;*/ Breakpoint* breakpoint);*/
protected: private:
DebugServer(Debugger* debugger) : debugger_(debugger) {} void AcceptClient(std::unique_ptr<Socket> client);
bool HandleClientEvent();
bool ProcessBuffer(const uint8_t* buffer, size_t buffer_length);
bool ProcessPacket(const proto::Packet* packet);
void Flush();
void SendSuccess(proto::request_id_t request_id);
void SendError(proto::request_id_t request_id,
const char* error_message = nullptr);
void SendError(proto::request_id_t request_id,
const std::string& error_message);
Debugger* debugger_ = nullptr; Debugger* debugger_ = nullptr;
std::unique_ptr<SocketServer> socket_server_;
std::unique_ptr<Socket> client_;
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_;
proto::PacketReader packet_reader_;
proto::PacketWriter packet_writer_;
}; };
} // namespace debug } // namespace debug

View File

@ -21,9 +21,7 @@
#include "xenia/cpu/function.h" #include "xenia/cpu/function.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/cpu/stack_walker.h" #include "xenia/cpu/stack_walker.h"
#include "xenia/debug/server/gdb/gdb_server.h" #include "xenia/debug/debug_server.h"
#include "xenia/debug/server/mi/mi_server.h"
#include "xenia/debug/server/xdp/xdp_server.h"
#include "xenia/emulator.h" #include "xenia/emulator.h"
#include "xenia/kernel/objects/xkernel_module.h" #include "xenia/kernel/objects/xkernel_module.h"
#include "xenia/kernel/objects/xmodule.h" #include "xenia/kernel/objects/xmodule.h"
@ -43,8 +41,6 @@ DEFINE_bool(wait_for_debugger, false,
"Waits for a debugger to attach before starting the game."); "Waits for a debugger to attach before starting the game.");
DEFINE_bool(exit_with_debugger, true, "Exit whe the debugger disconnects."); DEFINE_bool(exit_with_debugger, true, "Exit whe the debugger disconnects.");
DEFINE_string(debug_server, "xdp", "Debug server protocol [gdb, mi, xdp].");
namespace xe { namespace xe {
namespace debug { namespace debug {
@ -91,24 +87,10 @@ bool Debugger::StartSession() {
functions_trace_file_ = ChunkedMappedMemoryWriter::Open( functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
functions_trace_path_, 32 * 1024 * 1024, true); functions_trace_path_, 32 * 1024 * 1024, true);
if (FLAGS_debug_server == "gdb") { server_ = std::make_unique<DebugServer>(this);
server_ = std::make_unique<xe::debug::server::gdb::GdbServer>(this); if (!server_->Initialize()) {
if (!server_->Initialize()) { XELOGE("Unable to initialize XDP debug server");
XELOGE("Unable to initialize GDB debug server"); return false;
return false;
}
} else if (FLAGS_debug_server == "gdb") {
server_ = std::make_unique<xe::debug::server::mi::MIServer>(this);
if (!server_->Initialize()) {
XELOGE("Unable to initialize MI debug server");
return false;
}
} else {
server_ = std::make_unique<xe::debug::server::xdp::XdpServer>(this);
if (!server_->Initialize()) {
XELOGE("Unable to initialize XDP debug server");
return false;
}
} }
return true; return true;

View File

@ -13,9 +13,6 @@ project("xenia-debug")
defines({ defines({
}) })
includedirs({ includedirs({
project_root.."/third_party/flatbuffers/include"
}) })
local_platform_files() local_platform_files()
recursive_platform_files("client")
recursive_platform_files("proto") recursive_platform_files("proto")
recursive_platform_files("server")

View File

@ -1,319 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/debug/server/gdb/gdb_command_processor.h"
#include "xenia/base/logging.h"
#include "xenia/base/string_buffer.h"
#include "xenia/debug/server/gdb/gdb_server.h"
namespace xe {
namespace debug {
namespace server {
namespace gdb {
constexpr size_t kReceiveBufferSize = 1 * 1024 * 1024;
constexpr size_t kTransmitBufferSize = 1 * 1024 * 1024;
GdbCommandProcessor::GdbCommandProcessor(GdbServer* server, Debugger* debugger)
: server_(server), debugger_(debugger) {
receive_buffer_.resize(kReceiveBufferSize);
transmit_buffer_.resize(kTransmitBufferSize);
}
bool GdbCommandProcessor::ProcessBuffer(const uint8_t* buffer,
size_t buffer_length) {
// Grow and append the bytes to the receive buffer.
while (receive_offset_ + buffer_length > receive_buffer_.capacity()) {
receive_buffer_.resize(receive_buffer_.size() * 2);
}
std::memcpy(receive_buffer_.data() + receive_offset_, buffer, buffer_length);
receive_offset_ += buffer_length;
// While there are bytes pending in the buffer we scan through looking for end
// markers. When we find one, we emit the packet and move on.
size_t process_offset = 0;
while (process_offset < receive_offset_) {
// Look for an end marker.
size_t end_offset = -1;
size_t buffer_end_offset = -1;
for (size_t i = process_offset; i < receive_offset_; ++i) {
if (receive_buffer_[i] == '#') {
end_offset = i - 1;
buffer_end_offset = i + 2;
break;
}
}
if (end_offset == -1) {
// No end marker found - break out for now.
break;
}
// Emit packet.
if (!ProcessPacket((const char*)(receive_buffer_.data() + process_offset),
end_offset - process_offset)) {
// Failed to process line.
std::string line(receive_buffer_.data() + process_offset,
receive_buffer_.data() + end_offset);
XELOGE("Failed to process incoming GDB line: %s", line.c_str());
return false;
}
process_offset = buffer_end_offset + 1;
}
// If we have leftover unprocessed bytes move them to the front of the buffer.
if (process_offset < receive_offset_) {
size_t remaining_bytes = receive_offset_ - process_offset;
std::memmove(receive_buffer_.data(),
receive_buffer_.data() + process_offset, remaining_bytes);
receive_offset_ = remaining_bytes;
} else {
receive_offset_ = 0;
}
return true;
}
bool GdbCommandProcessor::ProcessPacket(const char* buffer,
size_t buffer_length) {
// There may be any number of leading +'s or -'s.
size_t offset = 0;
for (; offset < buffer_length; ++offset) {
if (buffer[offset] == '$') {
// Command start.
++offset;
break;
} else if (buffer[offset] == '+') {
// Ack.
} else if (buffer[offset] == '-') {
// No good - means transmission error.
XELOGE("GDB client remorted transmission error");
return false;
}
}
std::string line((const char*)(buffer + offset), buffer_length - 1);
XELOGI("GDB -> %s", line.c_str());
// Immediately send ACK.
if (!no_ack_mode_) {
server_->client()->Send("+", 1);
}
const char* buffer_ptr = buffer + offset;
bool handled = false;
switch (buffer[offset]) {
case '!':
// Enable extended mode.
SendResponse("OK");
handled = true;
break;
case 'v':
// Verbose packets.
if (std::strstr(buffer_ptr, "vRun") == buffer_ptr) {
SendResponse("S05");
handled = true;
} else if (std::strstr(buffer_ptr, "vCont") == buffer_ptr) {
SendResponse("S05");
handled = true;
}
break;
case 'q':
// Query packets.
switch (buffer_ptr[1]) {
case 'C':
// Get current thread ID.
SendResponse("QC01");
handled = true;
break;
case 'R':
// Command.
SendResponse("OK");
handled = true;
break;
default:
if (std::strstr(buffer_ptr, "qSupported") == buffer_ptr) {
// Get/set feature support.
handled = Process_qSupported(line);
} else if (std::strstr(buffer_ptr, "qAttached") == buffer_ptr) {
// Check attach mode; 0 = new process, 1 = existing process.
SendResponse("0");
handled = true;
} else if (std::strstr(buffer_ptr, "qfThreadInfo") == buffer_ptr) {
// Start of thread list request.
SendResponse("m01");
handled = true;
} else if (std::strstr(buffer_ptr, "qsThreadInfo") == buffer_ptr) {
// Continuation of thread list request.
SendResponse("l"); // l = last.
handled = true;
}
break;
}
break;
case 'Q':
// Set packets.
switch (buffer_ptr[1]) {
default:
if (std::strstr(buffer_ptr, "QStartNoAckMode") == buffer_ptr) {
no_ack_mode_ = true;
SendResponse("OK");
handled = true;
}
break;
}
break;
case 'H':
// Set thread for subsequent operations.
SendResponse("OK");
handled = true;
break;
case 'g':
// Read all registers.
handled = ProcessReadRegisters(line);
break;
case 'G':
// Write all registers.
handled = true;
break;
case 'p':
// Read register.
handled = true;
break;
case 'P':
// Write register.
handled = true;
break;
case 'm':
// Read memory.
handled = true;
break;
case 'M':
// Write memory.
handled = true;
break;
case 'Z':
// Insert breakpoint.
handled = true;
break;
case 'z':
// Remove breakpoint.
handled = true;
break;
case '?':
// Query halt reason.
SendResponse("S05");
handled = true;
break;
case 'c':
// Continue (vCont should be used instead).
// NOTE: reply is sent on halt, not right now.
SendResponse("S05");
handled = true;
break;
case 's':
// Single step.
// NOTE: reply is sent on halt, not right now.
SendResponse("S05");
handled = true;
break;
}
if (!handled) {
XELOGE("Unknown GDB packet: %s", buffer_ptr);
SendResponse("");
}
return true;
}
void GdbCommandProcessor::SendResponse(const char* value, size_t length) {
XELOGI("GDB <- %s", value);
uint8_t checksum = 0;
for (size_t i = 0; i < length; ++i) {
uint8_t c = uint8_t(value[i]);
if (!c) {
break;
}
checksum += c;
}
char crc[4];
sprintf(crc, "#%.2X", checksum);
std::pair<const void*, size_t> buffers[] = {
{"$", 1}, {value, length}, {crc, 3},
};
server_->client()->Send(buffers, xe::countof(buffers));
}
bool GdbCommandProcessor::Process_qSupported(const std::string& line) {
StringBuffer response;
// Read in the features the client supports.
// qSupported[:gdbfeature[;gdbfeature]...]
size_t feature_offset = line.find(':');
while (feature_offset != std::string::npos) {
size_t next_offset = line.find(';', feature_offset + 1);
std::string feature =
line.substr(feature_offset + 1, next_offset - feature_offset - 1);
feature_offset = next_offset;
if (feature.find("multiprocess") == 0) {
bool is_supported = feature[12] == '+';
} else if (feature.find("xmlRegisters") == 0) {
// xmlRegisters=A,B,C
} else if (feature.find("qRelocInsn") == 0) {
bool is_supported = feature[10] == '+';
} else if (feature.find("swbreak") == 0) {
bool is_supported = feature[7] == '+';
} else if (feature.find("hwbreak") == 0) {
bool is_supported = feature[7] == '+';
} else {
XELOGW("Unknown GDB client support feature: %s", feature.c_str());
}
}
response.Append("PacketSize=4000;");
response.Append("QStartNoAckMode+;");
response.Append("qRelocInsn-;");
response.Append("multiprocess-;");
response.Append("ConditionalBreakpoints-;");
response.Append("ConditionalTracepoints-;");
response.Append("ReverseContinue-;");
response.Append("ReverseStep-;");
response.Append("swbreak+;");
response.Append("hwbreak+;");
SendResponse(response.GetString());
return true;
}
bool GdbCommandProcessor::ProcessReadRegisters(const std::string& line) {
StringBuffer response;
for (int32_t n = 0; n < 32; n++) {
// gpr
response.AppendFormat("%08X", n);
}
for (int64_t n = 0; n < 32; n++) {
// fpr
response.AppendFormat("%016llX", n);
}
response.AppendFormat("%08X", 0x8202FB40); // pc
response.AppendFormat("%08X", 65); // msr
response.AppendFormat("%08X", 66); // cr
response.AppendFormat("%08X", 67); // lr
response.AppendFormat("%08X", 68); // ctr
response.AppendFormat("%08X", 69); // xer
response.AppendFormat("%08X", 70); // fpscr
SendResponse(response.GetString());
return true;
}
} // namespace gdb
} // namespace server
} // namespace debug
} // namespace xe

View File

@ -1,59 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_GDB_GDB_COMMAND_PROCESSOR_H_
#define XENIA_DEBUG_SERVER_GDB_GDB_COMMAND_PROCESSOR_H_
#include <cstdint>
#include <vector>
#include "xenia/debug/debugger.h"
namespace xe {
namespace debug {
namespace server {
namespace gdb {
class GdbServer;
class GdbCommandProcessor {
public:
GdbCommandProcessor(GdbServer* server, Debugger* debugger);
~GdbCommandProcessor() = default;
bool ProcessBuffer(const uint8_t* buffer, size_t buffer_length);
private:
bool ProcessPacket(const char* buffer, size_t buffer_length);
void SendResponse(const char* value, size_t length);
void SendResponse(const char* value) {
SendResponse(value, std::strlen(value));
}
void SendResponse(std::string value) {
SendResponse(value.data(), value.size());
}
bool Process_qSupported(const std::string& line);
bool ProcessReadRegisters(const std::string& line);
GdbServer* server_ = nullptr;
Debugger* debugger_ = nullptr;
std::vector<uint8_t> receive_buffer_;
size_t receive_offset_ = 0;
std::vector<uint8_t> transmit_buffer_;
bool no_ack_mode_ = false;
};
} // namespace gdb
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_GDB_GDB_COMMAND_PROCESSOR_H_

View File

@ -1,130 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/debug/server/gdb/gdb_server.h"
#include <gflags/gflags.h>
#include "xenia/base/logging.h"
#include "xenia/debug/debugger.h"
#include "xenia/debug/server/gdb/gdb_command_processor.h"
DEFINE_int32(gdb_server_port, 9000, "Debugger gdbserver TCP port.");
namespace xe {
namespace debug {
namespace server {
namespace gdb {
constexpr size_t kReceiveBufferSize = 32 * 1024;
GdbServer::GdbServer(Debugger* debugger) : DebugServer(debugger) {
receive_buffer_.resize(kReceiveBufferSize);
command_processor_ = std::make_unique<GdbCommandProcessor>(this, debugger);
}
GdbServer::~GdbServer() = default;
bool GdbServer::Initialize() {
socket_server_ = SocketServer::Create(uint16_t(FLAGS_gdb_server_port),
[this](std::unique_ptr<Socket> client) {
AcceptClient(std::move(client));
});
if (!socket_server_) {
XELOGE("Unable to create GDB socket server - port in use?");
return false;
}
return true;
}
void GdbServer::PostSynchronous(std::function<void()> fn) { assert_always(); }
void GdbServer::AcceptClient(std::unique_ptr<Socket> client) {
// If we have an existing client, kill it and join its thread.
if (client_) {
// TODO(benvanik): GDB say goodbye?
client_->Close();
xe::threading::Wait(client_thread_.get(), true);
client_thread_.reset();
}
// Take ownership of the new one.
client_ = std::move(client);
// Create a thread to manage the connection.
client_thread_ = xe::threading::Thread::Create({}, [this]() {
// TODO(benvanik): GDB protocol stuff? Do we say hi?
// TODO(benvanik): move hello to thread
// Let the debugger know we are present.
debugger_->set_attached(true);
// Junk just to poke the remote client.
client_->Send("(gdb)\n");
// Main loop.
bool running = true;
while (running) {
auto wait_result = xe::threading::Wait(client_->wait_handle(), true);
switch (wait_result) {
case xe::threading::WaitResult::kSuccess:
// Event (read or close).
running = HandleClientEvent();
continue;
case xe::threading::WaitResult::kAbandoned:
case xe::threading::WaitResult::kFailed:
// Error - kill the thread.
running = false;
continue;
default:
// Eh. Continue.
continue;
}
}
// Kill client (likely already disconnected).
client_.reset();
// Notify debugger we are no longer attached.
debugger_->set_attached(false);
});
}
bool GdbServer::HandleClientEvent() {
if (!client_->is_connected()) {
// Known-disconnected.
return false;
}
// Attempt to read into our buffer.
size_t bytes_read =
client_->Receive(receive_buffer_.data(), receive_buffer_.capacity());
if (bytes_read == -1) {
// Disconnected.
return false;
} else if (bytes_read == 0) {
// No data available. Wait again.
return true;
}
// Pass off the command processor to do with it what it wants.
if (!command_processor_->ProcessBuffer(receive_buffer_.data(), bytes_read)) {
// Error.
XELOGE("Error processing incoming GDB command; forcing disconnect");
return false;
}
return true;
}
} // namespace gdb
} // namespace server
} // namespace debug
} // namespace xe

View File

@ -1,55 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_GDB_GDB_SERVER_H_
#define XENIA_DEBUG_SERVER_GDB_GDB_SERVER_H_
#include <memory>
#include "xenia/base/socket.h"
#include "xenia/base/threading.h"
#include "xenia/debug/debug_server.h"
namespace xe {
namespace debug {
namespace server {
namespace gdb {
class GdbCommandProcessor;
class GdbServer : public DebugServer {
public:
GdbServer(Debugger* debugger);
~GdbServer() override;
Socket* client() const { return client_.get(); }
bool Initialize() override;
void PostSynchronous(std::function<void()> fn) override;
private:
void AcceptClient(std::unique_ptr<Socket> client);
bool HandleClientEvent();
std::unique_ptr<SocketServer> socket_server_;
std::unique_ptr<Socket> client_;
std::unique_ptr<xe::threading::Thread> client_thread_;
std::vector<uint8_t> receive_buffer_;
std::unique_ptr<GdbCommandProcessor> command_processor_;
};
} // namespace gdb
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_GDB_GDB_SERVER_H_

View File

@ -1,232 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/debug/server/mi/mi_command_processor.h"
#include "xenia/base/logging.h"
#include "xenia/base/string_buffer.h"
#include "xenia/debug/server/mi/mi_reader.h"
#include "xenia/debug/server/mi/mi_server.h"
#include "xenia/debug/server/mi/mi_writer.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
constexpr size_t kReceiveBufferSize = 1 * 1024 * 1024;
constexpr size_t kTransmitBufferSize = 1 * 1024 * 1024;
MICommandProcessor::MICommandProcessor(MIServer* server, Debugger* debugger)
: server_(server), debugger_(debugger) {
receive_buffer_.resize(kReceiveBufferSize);
transmit_buffer_.resize(kTransmitBufferSize);
}
bool MICommandProcessor::ProcessBuffer(const uint8_t* buffer,
size_t buffer_length) {
// Grow and append the bytes to the receive buffer.
while (receive_offset_ + buffer_length > receive_buffer_.capacity()) {
receive_buffer_.resize(receive_buffer_.size() * 2);
}
std::memcpy(receive_buffer_.data() + receive_offset_, buffer, buffer_length);
receive_offset_ += buffer_length;
// While there are bytes pending in the buffer we scan through looking for end
// markers. When we find one, we emit the packet and move on.
size_t process_offset = 0;
while (process_offset < receive_offset_) {
// Look for an end marker.
size_t end_offset = -1;
size_t buffer_end_offset = -1;
for (size_t i = process_offset; i < receive_offset_; ++i) {
if (receive_buffer_[i] == '\r') {
end_offset = i - 1;
buffer_end_offset = i + 1;
break;
} else if (receive_buffer_[i] == '\n') {
end_offset = i - 1;
buffer_end_offset = i;
}
}
if (end_offset == -1) {
// No end marker found - break out for now.
break;
}
// Emit packet.
if (!ProcessPacket((const char*)(receive_buffer_.data() + process_offset),
end_offset - process_offset)) {
// Failed to process line.
std::string line(receive_buffer_.data() + process_offset,
receive_buffer_.data() + end_offset);
XELOGE("Failed to process incoming MI line: %s", line.c_str());
return false;
}
process_offset = buffer_end_offset + 1;
}
// If we have leftover unprocessed bytes move them to the front of the buffer.
if (process_offset < receive_offset_) {
size_t remaining_bytes = receive_offset_ - process_offset;
std::memmove(receive_buffer_.data(),
receive_buffer_.data() + process_offset, remaining_bytes);
receive_offset_ = remaining_bytes;
} else {
receive_offset_ = 0;
}
return true;
}
bool MICommandProcessor::ProcessPacket(const char* buffer,
size_t buffer_length) {
// Will likely have a request prefix, like '#####-....'.
auto buffer_ptr = std::strchr(buffer, '-');
if (!buffer_ptr) {
XELOGE("Malformed MI command");
return false;
}
std::string token;
if (buffer_ptr != buffer) {
token = std::string(buffer, buffer_ptr);
}
++buffer_ptr; // Skip leading '-'.
std::string line((const char*)buffer_ptr,
buffer_length - (buffer_ptr - buffer) + 1);
XELOGI("MI -> %s", line.c_str());
auto command = line.substr(0, line.find(' '));
auto args =
command.size() == line.size() ? "" : line.substr(command.size() + 1);
bool handled = false;
if (command == "gdb-set") {
auto key = args.substr(0, args.find(' '));
auto value = args.substr(args.find(' ') + 1);
if (key == "mi-async" || key == "target-async") {
bool is_enabled = value == "1" || value == "on";
SendResult(token, ResultClass::kDone);
handled = true;
} else if (key == "stop-on-solib-events") {
bool is_enabled = value == "1" || value == "on";
SendResult(token, ResultClass::kDone);
handled = true;
}
} else if (command == "file-exec-and-symbols") {
// args=foo
SendResult(token, ResultClass::kDone);
handled = true;
} else if (command == "break-insert") {
// args=main
SendResult(token, ResultClass::kDone);
handled = true;
} else if (command == "exec-run") {
exec_token_ = token;
SendResult(token, ResultClass::kRunning);
handled = true;
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.AppendRaw(exec_token_);
writer.BeginAsyncNotifyRecord("library-loaded");
writer.AppendString("id=123");
writer.AppendString("target-name=\"foo.xex\"");
writer.AppendString("host-name=\"foo.xex\"");
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
} else if (command == "exec-continue") {
exec_token_ = token;
SendResult(token, ResultClass::kRunning);
handled = true;
} else if (command == "exec-interrupt") {
SendResult(token, ResultClass::kDone);
handled = true;
// later:
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.AppendRaw(exec_token_);
writer.BeginAsyncExecRecord("stopped");
writer.AppendString("reason=\"signal-received\"");
writer.AppendString("signal-name=\"SIGINT\"");
writer.AppendString("signal-meaning=\"Interrupt\"");
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
} else if (command == "exec-abort") {
XELOGI("Debug client requested abort");
SendResult(token, ResultClass::kDone);
exit(1);
handled = true;
} else if (command == "gdb-exit") {
// TODO(benvanik): die better?
XELOGI("Debug client requested exit");
exit(1);
SendResult(token, ResultClass::kDone);
handled = true;
}
if (!handled) {
XELOGE("Unknown GDB packet: %s", buffer_ptr);
SendErrorResult(token, "Unknown/unimplemented");
}
return true;
}
void MICommandProcessor::SendConsoleStreamOutput(const char* value) {
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.BeginConsoleStreamOutput();
writer.AppendString(value);
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
}
void MICommandProcessor::SendTargetStreamOutput(const char* value) {
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.BeginTargetStreamOutput();
writer.AppendString(value);
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
}
void MICommandProcessor::SendLogStreamOutput(const char* value) {
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.BeginLogStreamOutput();
writer.AppendString(value);
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
}
void MICommandProcessor::SendResult(const std::string& prefix,
ResultClass result_class) {
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.AppendRaw(prefix);
writer.BeginResultRecord(result_class);
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
}
void MICommandProcessor::SendErrorResult(const std::string& prefix,
const char* message,
const char* code) {
MIWriter writer(transmit_buffer_.data(), transmit_buffer_.size());
writer.AppendRaw(prefix);
writer.BeginResultRecord(ResultClass::kError);
if (message) {
writer.AppendResult("msg", message);
}
if (code) {
writer.AppendResult("code", code);
}
writer.EndRecord();
server_->client()->Send(transmit_buffer_.data(), writer.buffer_offset());
}
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe

View File

@ -1,57 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_MI_MI_COMMAND_PROCESSOR_H_
#define XENIA_DEBUG_SERVER_MI_MI_COMMAND_PROCESSOR_H_
#include <cstdint>
#include <vector>
#include "xenia/debug/debugger.h"
#include "xenia/debug/server/mi/mi_protocol.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
class MIServer;
class MICommandProcessor {
public:
MICommandProcessor(MIServer* server, Debugger* debugger);
~MICommandProcessor() = default;
bool ProcessBuffer(const uint8_t* buffer, size_t buffer_length);
private:
bool ProcessPacket(const char* buffer, size_t buffer_length);
void SendConsoleStreamOutput(const char* value);
void SendTargetStreamOutput(const char* value);
void SendLogStreamOutput(const char* value);
void SendResult(const std::string& prefix, ResultClass result_class);
void SendErrorResult(const std::string& prefix, const char* message = nullptr,
const char* code = nullptr);
MIServer* server_ = nullptr;
Debugger* debugger_ = nullptr;
std::vector<uint8_t> receive_buffer_;
size_t receive_offset_ = 0;
std::vector<uint8_t> transmit_buffer_;
// Token of the last -exec-* command. Sent back on stops.
std::string exec_token_;
};
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_MI_MI_COMMAND_PROCESSOR_H_

View File

@ -1,46 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_MI_MI_PROTOCOL_H_
#define XENIA_DEBUG_SERVER_MI_MI_PROTOCOL_H_
#include <cstdint>
namespace xe {
namespace debug {
namespace server {
namespace mi {
enum class RecordToken : char {
kResult = '^',
kAsyncExec = '*',
kAsyncStatus = '+',
kAsyncNotify = '=',
};
enum class StreamToken : char {
kConsole = '~',
kTarget = '@',
kLog = '&',
};
enum class ResultClass {
kDone,
kRunning,
kConnected,
kError,
kExit,
};
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_MI_MI_PROTOCOL_H_

View File

@ -1,22 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/debug/server/mi/mi_reader.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
//
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe

View File

@ -1,31 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_MI_MI_READER_H_
#define XENIA_DEBUG_SERVER_MI_MI_READER_H_
#include <cstdint>
#include "xenia/debug/server/mi/mi_protocol.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
class MIReader {
public:
};
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_MI_MI_READER_H_

View File

@ -1,132 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/debug/server/mi/mi_server.h"
#include <gflags/gflags.h>
#include "xenia/base/logging.h"
#include "xenia/debug/debugger.h"
#include "xenia/debug/server/mi/mi_command_processor.h"
DEFINE_int32(mi_server_port, 9001, "Debugger MI server TCP port.");
namespace xe {
namespace debug {
namespace server {
namespace mi {
constexpr size_t kReceiveBufferSize = 32 * 1024;
MIServer::MIServer(Debugger* debugger) : DebugServer(debugger) {
receive_buffer_.resize(kReceiveBufferSize);
command_processor_ = std::make_unique<MICommandProcessor>(this, debugger);
}
MIServer::~MIServer() = default;
bool MIServer::Initialize() {
socket_server_ = SocketServer::Create(uint16_t(FLAGS_mi_server_port),
[this](std::unique_ptr<Socket> client) {
AcceptClient(std::move(client));
});
if (!socket_server_) {
XELOGE("Unable to create MI socket server - port in use?");
return false;
}
return true;
}
void MIServer::PostSynchronous(std::function<void()> fn) { assert_always(); }
void MIServer::AcceptClient(std::unique_ptr<Socket> client) {
// If we have an existing client, kill it and join its thread.
if (client_) {
// TODO(benvanik): GDB say goodbye?
client_->Close();
xe::threading::Wait(client_thread_.get(), true);
client_thread_.reset();
}
// Take ownership of the new one.
client_ = std::move(client);
// Create a thread to manage the connection.
client_thread_ = xe::threading::Thread::Create({}, [this]() {
// TODO(benvanik): GDB protocol stuff? Do we say hi?
// TODO(benvanik): move hello to thread
// Let the debugger know we are present.
debugger_->set_attached(true);
// Junk just to poke the remote client.
// The Microsoft MI engine seems to wait for *something* to come through on
// connection.
client_->Send("(gdb)\n");
// Main loop.
bool running = true;
while (running) {
auto wait_result = xe::threading::Wait(client_->wait_handle(), true);
switch (wait_result) {
case xe::threading::WaitResult::kSuccess:
// Event (read or close).
running = HandleClientEvent();
continue;
case xe::threading::WaitResult::kAbandoned:
case xe::threading::WaitResult::kFailed:
// Error - kill the thread.
running = false;
continue;
default:
// Eh. Continue.
continue;
}
}
// Kill client (likely already disconnected).
client_.reset();
// Notify debugger we are no longer attached.
debugger_->set_attached(false);
});
}
bool MIServer::HandleClientEvent() {
if (!client_->is_connected()) {
// Known-disconnected.
return false;
}
// Attempt to read into our buffer.
size_t bytes_read =
client_->Receive(receive_buffer_.data(), receive_buffer_.capacity());
if (bytes_read == -1) {
// Disconnected.
return false;
} else if (bytes_read == 0) {
// No data available. Wait again.
return true;
}
// Pass off the command processor to do with it what it wants.
if (!command_processor_->ProcessBuffer(receive_buffer_.data(), bytes_read)) {
// Error.
XELOGE("Error processing incoming GDB command; forcing disconnect");
return false;
}
return true;
}
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe

View File

@ -1,55 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_MI_MI_SERVER_H_
#define XENIA_DEBUG_SERVER_MI_MI_SERVER_H_
#include <memory>
#include "xenia/base/socket.h"
#include "xenia/base/threading.h"
#include "xenia/debug/debug_server.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
class MICommandProcessor;
class MIServer : public DebugServer {
public:
MIServer(Debugger* debugger);
~MIServer() override;
Socket* client() const { return client_.get(); }
bool Initialize() override;
void PostSynchronous(std::function<void()> fn) override;
private:
void AcceptClient(std::unique_ptr<Socket> client);
bool HandleClientEvent();
std::unique_ptr<SocketServer> socket_server_;
std::unique_ptr<Socket> client_;
std::unique_ptr<xe::threading::Thread> client_thread_;
std::vector<uint8_t> receive_buffer_;
std::unique_ptr<MICommandProcessor> command_processor_;
};
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_MI_MI_SERVER_H_

View File

@ -1,137 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/debug/server/mi/mi_writer.h"
#include "xenia/base/assert.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
void MIWriter::AppendRaw(const char* value, size_t length) {
assert_true(buffer_offset_ + length < buffer_capacity_);
std::memcpy(buffer_ + buffer_offset_, value, length);
buffer_offset_ += length;
}
void MIWriter::BeginRecord(RecordToken token, const char* record_class) {
assert_false(in_record_);
in_record_ = true;
size_t record_class_length = std::strlen(record_class);
assert_true(buffer_offset_ + 1 + record_class_length < buffer_capacity_);
buffer_[buffer_offset_++] = char(token);
std::memcpy(buffer_ + buffer_offset_, record_class, record_class_length);
buffer_offset_ += record_class_length;
}
void MIWriter::BeginResultRecord(ResultClass result_class) {
const char* record_class = "error";
switch (result_class) {
case ResultClass::kDone:
record_class = "done";
break;
case ResultClass::kRunning:
record_class = "running";
break;
case ResultClass::kConnected:
record_class = "connected";
break;
case ResultClass::kError:
record_class = "error";
break;
case ResultClass::kExit:
record_class = "exit";
break;
}
BeginRecord(RecordToken::kResult, record_class);
}
void MIWriter::BeginStreamRecord(StreamToken token) {
assert_false(in_record_);
in_record_ = true;
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = char(token);
}
void MIWriter::EndRecord() {
assert_true(in_record_);
in_record_ = false;
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = '\n';
}
void MIWriter::AppendArraySeparator() {
if (array_depth_) {
if (array_count_[array_depth_]) {
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = ',';
}
++array_count_[array_depth_];
}
}
void MIWriter::PushTuple() {
assert_true(in_record_);
AppendArraySeparator();
++array_depth_;
assert_true(array_depth_ < kMaxArrayDepth);
array_count_[array_depth_] = 0;
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = '{';
}
void MIWriter::PopTuple() {
--array_depth_;
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = '}';
}
void MIWriter::PushList() {
assert_true(in_record_);
AppendArraySeparator();
++array_depth_;
assert_true(array_depth_ < kMaxArrayDepth);
array_count_[array_depth_] = 0;
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = '[';
}
void MIWriter::PopList() {
--array_depth_;
assert_true(buffer_offset_ + 1 < buffer_capacity_);
buffer_[buffer_offset_++] = ']';
}
void MIWriter::AppendString(const char* value, size_t length) {
assert_true(in_record_);
AppendArraySeparator();
assert_true(buffer_offset_ + length < buffer_capacity_);
std::memcpy(buffer_ + buffer_offset_, value, length);
buffer_offset_ += length;
}
void MIWriter::AppendResult(const char* variable, const char* value,
size_t length) {
assert_true(in_record_);
AppendArraySeparator();
size_t variable_length = std::strlen(variable);
assert_true(buffer_offset_ + 1 + variable_length + length < buffer_capacity_);
std::memcpy(buffer_ + buffer_offset_, variable, variable_length);
buffer_offset_ += variable_length;
buffer_[buffer_offset_++] = '=';
std::memcpy(buffer_ + buffer_offset_, value, length);
buffer_offset_ += length;
}
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe

View File

@ -1,101 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_MI_MI_WRITER_H_
#define XENIA_DEBUG_SERVER_MI_MI_WRITER_H_
#include <cstdint>
#include <string>
#include "xenia/debug/server/mi/mi_protocol.h"
namespace xe {
namespace debug {
namespace server {
namespace mi {
// https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Output-Syntax.html#GDB_002fMI-Output-Syntax
class MIWriter {
public:
MIWriter(uint8_t* buffer, size_t buffer_capacity)
: buffer_(buffer), buffer_capacity_(buffer_capacity) {}
uint8_t* buffer() const { return buffer_; }
size_t buffer_capacity() const { return buffer_capacity_; }
size_t buffer_offset() const { return buffer_offset_; }
void AppendRaw(const char* value, size_t length);
void AppendRaw(const char* value) { AppendRaw(value, std::strlen(value)); }
void AppendRaw(const std::string& value) {
AppendRaw(value.c_str(), value.size());
}
void BeginResultRecord(ResultClass result_class);
void BeginAsyncExecRecord(const char* async_class) {
BeginRecord(RecordToken::kAsyncExec, async_class);
array_depth_ = 1;
array_count_[array_depth_] = 1;
}
void BeginAsyncStatusRecord(const char* async_class) {
BeginRecord(RecordToken::kAsyncStatus, async_class);
array_depth_ = 1;
array_count_[array_depth_] = 1;
}
void BeginAsyncNotifyRecord(const char* async_class) {
BeginRecord(RecordToken::kAsyncNotify, async_class);
array_depth_ = 1;
array_count_[array_depth_] = 1;
}
void BeginConsoleStreamOutput() { BeginStreamRecord(StreamToken::kConsole); }
void BeginTargetStreamOutput() { BeginStreamRecord(StreamToken::kTarget); }
void BeginLogStreamOutput() { BeginStreamRecord(StreamToken::kLog); }
void EndRecord();
void PushTuple();
void PopTuple();
void PushList();
void PopList();
void AppendString(const char* value, size_t length);
void AppendString(const char* value) {
AppendString(value, std::strlen(value));
}
void AppendString(const std::string& value) {
AppendString(value.c_str(), value.size());
}
void AppendResult(const char* variable, const char* value, size_t length);
void AppendResult(const char* variable, const char* value) {
AppendResult(variable, value, std::strlen(value));
}
void AppendResult(const char* variable, const std::string& value) {
AppendResult(variable, value.c_str(), value.size());
}
private:
void BeginRecord(RecordToken token, const char* record_class);
void BeginStreamRecord(StreamToken token);
void AppendArraySeparator();
uint8_t* buffer_ = nullptr;
size_t buffer_capacity_ = 0;
size_t buffer_offset_ = 0;
static const int kMaxArrayDepth = 32;
bool in_record_ = false;
int array_depth_ = 0;
int array_count_[kMaxArrayDepth];
};
} // namespace mi
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_MI_MI_WRITER_H_

View File

@ -1,76 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_
#define XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_
#include <list>
#include <memory>
#include <mutex>
#include "xenia/base/socket.h"
#include "xenia/base/threading.h"
#include "xenia/debug/debug_server.h"
#include "xenia/debug/proto/packet_reader.h"
#include "xenia/debug/proto/packet_writer.h"
#include "xenia/debug/proto/xdp_protocol.h"
namespace xe {
namespace debug {
namespace server {
namespace xdp {
class XdpServer : public DebugServer {
public:
XdpServer(Debugger* debugger);
~XdpServer() override;
Socket* client() const { return client_.get(); }
bool Initialize() override;
void PostSynchronous(std::function<void()> fn) override;
void OnExecutionContinued() override;
void OnExecutionInterrupted() override;
private:
void AcceptClient(std::unique_ptr<Socket> client);
bool HandleClientEvent();
bool ProcessBuffer(const uint8_t* buffer, size_t buffer_length);
bool ProcessPacket(const proto::Packet* packet);
void Flush();
void SendSuccess(proto::request_id_t request_id);
void SendError(proto::request_id_t request_id,
const char* error_message = nullptr);
void SendError(proto::request_id_t request_id,
const std::string& error_message);
std::unique_ptr<SocketServer> socket_server_;
std::unique_ptr<Socket> client_;
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_;
proto::PacketReader packet_reader_;
proto::PacketWriter packet_writer_;
};
} // namespace xdp
} // namespace server
} // namespace debug
} // namespace xe
#endif // XENIA_DEBUG_SERVER_XDP_XDP_SERVER_H_

View File

@ -12,7 +12,7 @@
#include <memory> #include <memory>
#include "xenia/debug/client/xdp/xdp_client.h" #include "xenia/debug/debug_client.h"
#include "xenia/debug/ui/model/system.h" #include "xenia/debug/ui/model/system.h"
#include "xenia/ui/loop.h" #include "xenia/ui/loop.h"
@ -31,7 +31,7 @@ class Application {
xe::ui::Loop* loop() { return loop_.get(); } xe::ui::Loop* loop() { return loop_.get(); }
MainWindow* main_window() const { return main_window_.get(); } MainWindow* main_window() const { return main_window_.get(); }
client::xdp::XdpClient* client() { return &client_; } DebugClient* client() { return &client_; }
model::System* system() const { return system_.get(); } model::System* system() const { return system_.get(); }
void Quit(); void Quit();
@ -43,7 +43,7 @@ class Application {
std::unique_ptr<xe::ui::Loop> loop_; std::unique_ptr<xe::ui::Loop> loop_;
std::unique_ptr<MainWindow> main_window_; std::unique_ptr<MainWindow> main_window_;
client::xdp::XdpClient client_; DebugClient client_;
std::unique_ptr<model::System> system_; std::unique_ptr<model::System> system_;
}; };

View File

@ -21,8 +21,6 @@ namespace xe {
namespace debug { namespace debug {
namespace ui { namespace ui {
using namespace xe::debug::client::xdp;
using xe::ui::MenuItem; using xe::ui::MenuItem;
const std::wstring kBaseTitle = L"xenia debugger"; const std::wstring kBaseTitle = L"xenia debugger";

View File

@ -42,7 +42,7 @@ class MainWindow {
void OnClose(); void OnClose();
Application* app_ = nullptr; Application* app_ = nullptr;
xe::debug::client::xdp::XdpClient* client_ = nullptr; xe::debug::DebugClient* client_ = nullptr;
std::unique_ptr<xe::ui::Window> window_; std::unique_ptr<xe::ui::Window> window_;
std::unique_ptr<el::Form> form_; std::unique_ptr<el::Form> form_;

View File

@ -18,7 +18,7 @@ namespace model {
using namespace xe::debug::proto; using namespace xe::debug::proto;
System::System(xe::ui::Loop* loop, client::xdp::XdpClient* client) System::System(xe::ui::Loop* loop, DebugClient* client)
: loop_(loop), client_(client) {} : loop_(loop), client_(client) {}
ExecutionState System::execution_state() { ExecutionState System::execution_state() {
@ -83,7 +83,10 @@ void System::OnModulesUpdated(std::vector<const ModuleListEntry*> entries) {
} }
} }
for (auto module_handle : extra_modules) { for (auto module_handle : extra_modules) {
modules_by_handle_[module_handle]->set_dead(true); auto module = modules_by_handle_.find(module_handle);
if (module != modules_by_handle_.end()) {
module->second->set_dead(true);
}
} }
} }
loop_->Post([this]() { loop_->Post([this]() {
@ -112,7 +115,10 @@ void System::OnThreadsUpdated(std::vector<const ThreadListEntry*> entries) {
} }
} }
for (auto thread_handle : extra_threads) { for (auto thread_handle : extra_threads) {
modules_by_handle_[thread_handle]->set_dead(true); auto thread = threads_by_handle_.find(thread_handle);
if (thread != threads_by_handle_.end()) {
thread->second->set_dead(true);
}
} }
} }
loop_->Post([this]() { loop_->Post([this]() {

View File

@ -17,7 +17,7 @@
#include <vector> #include <vector>
#include "xenia/base/delegate.h" #include "xenia/base/delegate.h"
#include "xenia/debug/client/xdp/xdp_client.h" #include "xenia/debug/debug_client.h"
#include "xenia/debug/ui/model/function.h" #include "xenia/debug/ui/model/function.h"
#include "xenia/debug/ui/model/module.h" #include "xenia/debug/ui/model/module.h"
#include "xenia/debug/ui/model/thread.h" #include "xenia/debug/ui/model/thread.h"
@ -28,14 +28,12 @@ namespace debug {
namespace ui { namespace ui {
namespace model { namespace model {
using xe::debug::client::xdp::ExecutionState; class System : public ClientListener {
class System : public client::xdp::ClientListener {
public: public:
System(xe::ui::Loop* loop, client::xdp::XdpClient* client); System(xe::ui::Loop* loop, DebugClient* client);
xe::ui::Loop* loop() const { return loop_; } xe::ui::Loop* loop() const { return loop_; }
client::xdp::XdpClient* client() const { return client_; } DebugClient* client() const { return client_; }
ExecutionState execution_state(); ExecutionState execution_state();
@ -57,7 +55,7 @@ class System : public client::xdp::ClientListener {
std::vector<const proto::ThreadListEntry*> entries) override; std::vector<const proto::ThreadListEntry*> entries) override;
xe::ui::Loop* loop_ = nullptr; xe::ui::Loop* loop_ = nullptr;
client::xdp::XdpClient* client_ = nullptr; DebugClient* client_ = nullptr;
std::recursive_mutex mutex_; std::recursive_mutex mutex_;
std::vector<std::unique_ptr<Module>> modules_; std::vector<std::unique_ptr<Module>> modules_;

View File

@ -28,12 +28,12 @@ class View {
std::string name() const { return name_; } std::string name() const { return name_; }
el::LayoutBox* root_element() { return &root_element_; } el::LayoutBox* root_element() { return &root_element_; }
xe::ui::Loop* loop() const { return Application::current()->loop(); } xe::ui::Loop* loop() const { return Application::current()->loop(); }
client::xdp::XdpClient* client() const { return client_; } DebugClient* client() const { return client_; }
model::System* system() const { return Application::current()->system(); } model::System* system() const { return Application::current()->system(); }
virtual el::Element* BuildUI() = 0; virtual el::Element* BuildUI() = 0;
virtual void Setup(xe::debug::client::xdp::XdpClient* client) = 0; virtual void Setup(xe::debug::DebugClient* client) = 0;
protected: protected:
View(std::string name) : name_(name) {} View(std::string name) : name_(name) {}
@ -41,7 +41,7 @@ class View {
std::string name_; std::string name_;
el::LayoutBox root_element_; el::LayoutBox root_element_;
std::unique_ptr<el::EventHandler> handler_; std::unique_ptr<el::EventHandler> handler_;
xe::debug::client::xdp::XdpClient* client_ = nullptr; xe::debug::DebugClient* client_ = nullptr;
}; };
} // namespace ui } // namespace ui

View File

@ -16,8 +16,6 @@ namespace ui {
namespace views { namespace views {
namespace cpu { namespace cpu {
using namespace xe::debug::client::xdp;
CpuView::CpuView() : View("CPU") {} CpuView::CpuView() : View("CPU") {}
CpuView::~CpuView() = default; CpuView::~CpuView() = default;
@ -147,7 +145,7 @@ el::Element* CpuView::BuildUI() {
return &root_element_; return &root_element_;
} }
void CpuView::Setup(XdpClient* client) { void CpuView::Setup(DebugClient* client) {
client_ = client; client_ = client;
system()->on_execution_state_changed.AddListener( system()->on_execution_state_changed.AddListener(

View File

@ -28,7 +28,7 @@ class CpuView : public View {
el::Element* BuildUI() override; el::Element* BuildUI() override;
void Setup(xe::debug::client::xdp::XdpClient* client) override; void Setup(xe::debug::DebugClient* client) override;
protected: protected:
void UpdateElementState(); void UpdateElementState();

View File

@ -36,7 +36,7 @@ el::Element* GpuView::BuildUI() {
return &root_element_; return &root_element_;
} }
void GpuView::Setup(xe::debug::client::xdp::XdpClient* client) { void GpuView::Setup(xe::debug::DebugClient* client) {
// //
} }

View File

@ -28,7 +28,7 @@ class GpuView : public View {
el::Element* BuildUI() override; el::Element* BuildUI() override;
void Setup(xe::debug::client::xdp::XdpClient* client) override; void Setup(xe::debug::DebugClient* client) override;
protected: protected:
}; };