Simplifying debug stuff, as I'm not going to bother with gdb.
This commit is contained in:
parent
ec326119cf
commit
0a8d6eec91
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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")
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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
|
|
|
@ -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_
|
|
|
@ -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_
|
|
|
@ -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_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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]() {
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue