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/debug/debugger.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace client {
|
||||
namespace xdp {
|
||||
|
||||
using namespace xe::debug::proto;
|
||||
|
||||
constexpr size_t kReceiveBufferSize = 1 * 1024 * 1024;
|
||||
constexpr size_t kTransmitBufferSize = 1 * 1024 * 1024;
|
||||
|
||||
XdpClient::XdpClient()
|
||||
DebugClient::DebugClient()
|
||||
: packet_reader_(kReceiveBufferSize), packet_writer_(kTransmitBufferSize) {
|
||||
receive_buffer_.resize(kReceiveBufferSize);
|
||||
}
|
||||
|
||||
XdpClient::~XdpClient() {
|
||||
DebugClient::~DebugClient() {
|
||||
socket_->Close();
|
||||
xe::threading::Wait(thread_.get(), true);
|
||||
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);
|
||||
if (!socket_) {
|
||||
XELOGE("Unable to connect to remote host");
|
||||
|
@ -69,7 +66,7 @@ bool XdpClient::Connect(std::string hostname, uint16_t port) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool XdpClient::HandleSocketEvent() {
|
||||
bool DebugClient::HandleSocketEvent() {
|
||||
if (!socket_->is_connected()) {
|
||||
// Known-disconnected.
|
||||
return false;
|
||||
|
@ -95,7 +92,7 @@ bool XdpClient::HandleSocketEvent() {
|
|||
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.
|
||||
packet_reader_.AppendBuffer(buffer, buffer_length);
|
||||
|
||||
|
@ -121,7 +118,7 @@ bool XdpClient::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool XdpClient::ProcessPacket(const proto::Packet* packet) {
|
||||
bool DebugClient::ProcessPacket(const proto::Packet* packet) {
|
||||
// Hold lock during processing.
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
|
@ -167,7 +164,7 @@ bool XdpClient::ProcessPacket(const proto::Packet* packet) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void XdpClient::Flush() {
|
||||
void DebugClient::Flush() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (!packet_writer_.buffer_offset()) {
|
||||
return;
|
||||
|
@ -176,7 +173,7 @@ void XdpClient::Flush() {
|
|||
packet_writer_.Reset();
|
||||
}
|
||||
|
||||
void XdpClient::Continue() {
|
||||
void DebugClient::Continue() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
packet_writer_.Begin(PacketType::kExecutionRequest);
|
||||
auto body = packet_writer_.Append<ExecutionRequest>();
|
||||
|
@ -185,7 +182,7 @@ void XdpClient::Continue() {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpClient::StepOne(uint32_t thread_id) {
|
||||
void DebugClient::StepOne(uint32_t thread_id) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
packet_writer_.Begin(PacketType::kExecutionRequest);
|
||||
auto body = packet_writer_.Append<ExecutionRequest>();
|
||||
|
@ -196,7 +193,7 @@ void XdpClient::StepOne(uint32_t thread_id) {
|
|||
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_);
|
||||
packet_writer_.Begin(PacketType::kExecutionRequest);
|
||||
auto body = packet_writer_.Append<ExecutionRequest>();
|
||||
|
@ -208,7 +205,7 @@ void XdpClient::StepTo(uint32_t thread_id, uint32_t target_pc) {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpClient::Interrupt() {
|
||||
void DebugClient::Interrupt() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
packet_writer_.Begin(PacketType::kExecutionRequest);
|
||||
auto body = packet_writer_.Append<ExecutionRequest>();
|
||||
|
@ -217,7 +214,7 @@ void XdpClient::Interrupt() {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpClient::Exit() {
|
||||
void DebugClient::Exit() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
packet_writer_.Begin(PacketType::kExecutionRequest);
|
||||
auto body = packet_writer_.Append<ExecutionRequest>();
|
||||
|
@ -226,7 +223,7 @@ void XdpClient::Exit() {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpClient::BeginUpdateAllState() {
|
||||
void DebugClient::BeginUpdateAllState() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
packet_writer_.Begin(PacketType::kModuleListRequest);
|
||||
|
@ -237,7 +234,5 @@ void XdpClient::BeginUpdateAllState() {
|
|||
Flush();
|
||||
}
|
||||
|
||||
} // namespace xdp
|
||||
} // namespace client
|
||||
} // namespace debug
|
||||
} // namespace xe
|
|
@ -7,8 +7,8 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_CLIENT_XDP_XDP_CLIENT_H_
|
||||
#define XENIA_DEBUG_CLIENT_XDP_XDP_CLIENT_H_
|
||||
#ifndef XENIA_DEBUG_DEBUG_CLIENT_H_
|
||||
#define XENIA_DEBUG_DEBUG_CLIENT_H_
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
@ -21,8 +21,6 @@
|
|||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace client {
|
||||
namespace xdp {
|
||||
|
||||
using proto::ModuleListEntry;
|
||||
using proto::ThreadListEntry;
|
||||
|
@ -45,10 +43,10 @@ class ClientListener {
|
|||
std::vector<const ThreadListEntry*> entries) = 0;
|
||||
};
|
||||
|
||||
class XdpClient {
|
||||
class DebugClient {
|
||||
public:
|
||||
XdpClient();
|
||||
~XdpClient();
|
||||
DebugClient();
|
||||
~DebugClient();
|
||||
|
||||
Socket* socket() const { return socket_.get(); }
|
||||
void set_listener(ClientListener* listener) { listener_ = listener; }
|
||||
|
@ -84,9 +82,7 @@ class XdpClient {
|
|||
ExecutionState execution_state_ = ExecutionState::kStopped;
|
||||
};
|
||||
|
||||
} // namespace xdp
|
||||
} // namespace client
|
||||
} // namespace debug
|
||||
} // 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>
|
||||
|
||||
|
@ -18,12 +18,10 @@
|
|||
#include "xenia/kernel/objects/xmodule.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 debug {
|
||||
namespace server {
|
||||
namespace xdp {
|
||||
|
||||
using namespace xe::debug::proto;
|
||||
using namespace xe::kernel;
|
||||
|
@ -32,19 +30,19 @@ constexpr size_t kReceiveBufferSize = 32 * 1024;
|
|||
constexpr size_t kReadBufferSize = 1 * 1024 * 1024;
|
||||
constexpr size_t kWriteBufferSize = 1 * 1024 * 1024;
|
||||
|
||||
XdpServer::XdpServer(Debugger* debugger)
|
||||
: DebugServer(debugger),
|
||||
DebugServer::DebugServer(Debugger* debugger)
|
||||
: debugger_(debugger),
|
||||
packet_reader_(kReadBufferSize),
|
||||
packet_writer_(kWriteBufferSize) {
|
||||
receive_buffer_.resize(kReceiveBufferSize);
|
||||
}
|
||||
|
||||
XdpServer::~XdpServer() = default;
|
||||
DebugServer::~DebugServer() = default;
|
||||
|
||||
bool XdpServer::Initialize() {
|
||||
bool DebugServer::Initialize() {
|
||||
post_event_ = xe::threading::Event::CreateAutoResetEvent(false);
|
||||
|
||||
socket_server_ = SocketServer::Create(uint16_t(FLAGS_xdp_server_port),
|
||||
socket_server_ = SocketServer::Create(uint16_t(FLAGS_debug_server_port),
|
||||
[this](std::unique_ptr<Socket> client) {
|
||||
AcceptClient(std::move(client));
|
||||
});
|
||||
|
@ -56,7 +54,7 @@ bool XdpServer::Initialize() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void XdpServer::PostSynchronous(std::function<void()> fn) {
|
||||
void DebugServer::PostSynchronous(std::function<void()> fn) {
|
||||
xe::threading::Fence fence;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(post_mutex_);
|
||||
|
@ -69,7 +67,7 @@ void XdpServer::PostSynchronous(std::function<void()> fn) {
|
|||
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 (client_) {
|
||||
// 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()) {
|
||||
// Known-disconnected.
|
||||
return false;
|
||||
|
@ -173,7 +171,7 @@ bool XdpServer::HandleClientEvent() {
|
|||
|
||||
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.
|
||||
packet_reader_.AppendBuffer(buffer, buffer_length);
|
||||
|
||||
|
@ -200,7 +198,7 @@ bool XdpServer::ProcessBuffer(const uint8_t* buffer, size_t buffer_length) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool XdpServer::ProcessPacket(const proto::Packet* packet) {
|
||||
bool DebugServer::ProcessPacket(const proto::Packet* packet) {
|
||||
auto emulator = debugger()->emulator();
|
||||
auto kernel_state = emulator->kernel_state();
|
||||
auto object_table = kernel_state->object_table();
|
||||
|
@ -292,7 +290,7 @@ bool XdpServer::ProcessPacket(const proto::Packet* packet) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void XdpServer::OnExecutionContinued() {
|
||||
void DebugServer::OnExecutionContinued() {
|
||||
packet_writer_.Begin(PacketType::kExecutionNotification);
|
||||
auto body = packet_writer_.Append<ExecutionNotification>();
|
||||
body->current_state = ExecutionNotification::State::kRunning;
|
||||
|
@ -300,7 +298,7 @@ void XdpServer::OnExecutionContinued() {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpServer::OnExecutionInterrupted() {
|
||||
void DebugServer::OnExecutionInterrupted() {
|
||||
packet_writer_.Begin(PacketType::kExecutionNotification);
|
||||
auto body = packet_writer_.Append<ExecutionNotification>();
|
||||
body->current_state = ExecutionNotification::State::kStopped;
|
||||
|
@ -309,7 +307,7 @@ void XdpServer::OnExecutionInterrupted() {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpServer::Flush() {
|
||||
void DebugServer::Flush() {
|
||||
if (!packet_writer_.buffer_offset()) {
|
||||
return;
|
||||
}
|
||||
|
@ -317,7 +315,7 @@ void XdpServer::Flush() {
|
|||
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);
|
||||
auto body = packet_writer_.Append<GenericResponse>();
|
||||
body->code = GenericResponse::Code::kSuccess;
|
||||
|
@ -325,8 +323,8 @@ void XdpServer::SendSuccess(proto::request_id_t request_id) {
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpServer::SendError(proto::request_id_t request_id,
|
||||
const char* error_message) {
|
||||
void DebugServer::SendError(proto::request_id_t request_id,
|
||||
const char* error_message) {
|
||||
packet_writer_.Begin(PacketType::kGenericResponse, request_id);
|
||||
auto body = packet_writer_.Append<GenericResponse>();
|
||||
body->code = GenericResponse::Code::kError;
|
||||
|
@ -335,8 +333,8 @@ void XdpServer::SendError(proto::request_id_t request_id,
|
|||
Flush();
|
||||
}
|
||||
|
||||
void XdpServer::SendError(proto::request_id_t request_id,
|
||||
const std::string& error_message) {
|
||||
void DebugServer::SendError(proto::request_id_t request_id,
|
||||
const std::string& error_message) {
|
||||
packet_writer_.Begin(PacketType::kGenericResponse, request_id);
|
||||
auto body = packet_writer_.Append<GenericResponse>();
|
||||
body->code = GenericResponse::Code::kError;
|
||||
|
@ -345,7 +343,5 @@ void XdpServer::SendError(proto::request_id_t request_id,
|
|||
Flush();
|
||||
}
|
||||
|
||||
} // namespace xdp
|
||||
} // namespace server
|
||||
} // namespace debug
|
||||
} // namespace xe
|
|
@ -11,10 +11,18 @@
|
|||
#define XENIA_DEBUG_DEBUG_SERVER_H_
|
||||
|
||||
#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/processor.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 debug {
|
||||
|
@ -23,27 +31,53 @@ class Debugger;
|
|||
|
||||
class DebugServer {
|
||||
public:
|
||||
virtual ~DebugServer() = default;
|
||||
DebugServer(Debugger* debugger);
|
||||
~DebugServer();
|
||||
|
||||
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?)
|
||||
// virtual void OnThreadCreated(ThreadState* thread_state) = 0;
|
||||
// virtual void OnThreadDestroyed(ThreadState* thread_state) = 0;
|
||||
// void OnThreadCreated(ThreadState* thread_state);
|
||||
// void OnThreadDestroyed(ThreadState* thread_state);
|
||||
|
||||
virtual void OnExecutionContinued() {}
|
||||
virtual void OnExecutionInterrupted() {}
|
||||
/*virtual void OnBreakpointHit(xe::cpu::ThreadState* thread_state,
|
||||
Breakpoint* breakpoint) = 0;*/
|
||||
void OnExecutionContinued();
|
||||
void OnExecutionInterrupted();
|
||||
/*void OnBreakpointHit(xe::cpu::ThreadState* thread_state,
|
||||
Breakpoint* breakpoint);*/
|
||||
|
||||
protected:
|
||||
DebugServer(Debugger* debugger) : debugger_(debugger) {}
|
||||
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);
|
||||
|
||||
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
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
#include "xenia/cpu/function.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/cpu/stack_walker.h"
|
||||
#include "xenia/debug/server/gdb/gdb_server.h"
|
||||
#include "xenia/debug/server/mi/mi_server.h"
|
||||
#include "xenia/debug/server/xdp/xdp_server.h"
|
||||
#include "xenia/debug/debug_server.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/kernel/objects/xkernel_module.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.");
|
||||
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 debug {
|
||||
|
||||
|
@ -91,24 +87,10 @@ bool Debugger::StartSession() {
|
|||
functions_trace_file_ = ChunkedMappedMemoryWriter::Open(
|
||||
functions_trace_path_, 32 * 1024 * 1024, true);
|
||||
|
||||
if (FLAGS_debug_server == "gdb") {
|
||||
server_ = std::make_unique<xe::debug::server::gdb::GdbServer>(this);
|
||||
if (!server_->Initialize()) {
|
||||
XELOGE("Unable to initialize GDB debug server");
|
||||
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;
|
||||
}
|
||||
server_ = std::make_unique<DebugServer>(this);
|
||||
if (!server_->Initialize()) {
|
||||
XELOGE("Unable to initialize XDP debug server");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -13,9 +13,6 @@ project("xenia-debug")
|
|||
defines({
|
||||
})
|
||||
includedirs({
|
||||
project_root.."/third_party/flatbuffers/include"
|
||||
})
|
||||
local_platform_files()
|
||||
recursive_platform_files("client")
|
||||
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 "xenia/debug/client/xdp/xdp_client.h"
|
||||
#include "xenia/debug/debug_client.h"
|
||||
#include "xenia/debug/ui/model/system.h"
|
||||
#include "xenia/ui/loop.h"
|
||||
|
||||
|
@ -31,7 +31,7 @@ class Application {
|
|||
|
||||
xe::ui::Loop* loop() { return loop_.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(); }
|
||||
|
||||
void Quit();
|
||||
|
@ -43,7 +43,7 @@ class Application {
|
|||
|
||||
std::unique_ptr<xe::ui::Loop> loop_;
|
||||
std::unique_ptr<MainWindow> main_window_;
|
||||
client::xdp::XdpClient client_;
|
||||
DebugClient client_;
|
||||
|
||||
std::unique_ptr<model::System> system_;
|
||||
};
|
||||
|
|
|
@ -21,8 +21,6 @@ namespace xe {
|
|||
namespace debug {
|
||||
namespace ui {
|
||||
|
||||
using namespace xe::debug::client::xdp;
|
||||
|
||||
using xe::ui::MenuItem;
|
||||
|
||||
const std::wstring kBaseTitle = L"xenia debugger";
|
||||
|
|
|
@ -42,7 +42,7 @@ class MainWindow {
|
|||
void OnClose();
|
||||
|
||||
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<el::Form> form_;
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace model {
|
|||
|
||||
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) {}
|
||||
|
||||
ExecutionState System::execution_state() {
|
||||
|
@ -83,7 +83,10 @@ void System::OnModulesUpdated(std::vector<const ModuleListEntry*> entries) {
|
|||
}
|
||||
}
|
||||
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]() {
|
||||
|
@ -112,7 +115,10 @@ void System::OnThreadsUpdated(std::vector<const ThreadListEntry*> entries) {
|
|||
}
|
||||
}
|
||||
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]() {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <vector>
|
||||
|
||||
#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/module.h"
|
||||
#include "xenia/debug/ui/model/thread.h"
|
||||
|
@ -28,14 +28,12 @@ namespace debug {
|
|||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
using xe::debug::client::xdp::ExecutionState;
|
||||
|
||||
class System : public client::xdp::ClientListener {
|
||||
class System : public ClientListener {
|
||||
public:
|
||||
System(xe::ui::Loop* loop, client::xdp::XdpClient* client);
|
||||
System(xe::ui::Loop* loop, DebugClient* client);
|
||||
|
||||
xe::ui::Loop* loop() const { return loop_; }
|
||||
client::xdp::XdpClient* client() const { return client_; }
|
||||
DebugClient* client() const { return client_; }
|
||||
|
||||
ExecutionState execution_state();
|
||||
|
||||
|
@ -57,7 +55,7 @@ class System : public client::xdp::ClientListener {
|
|||
std::vector<const proto::ThreadListEntry*> entries) override;
|
||||
|
||||
xe::ui::Loop* loop_ = nullptr;
|
||||
client::xdp::XdpClient* client_ = nullptr;
|
||||
DebugClient* client_ = nullptr;
|
||||
|
||||
std::recursive_mutex mutex_;
|
||||
std::vector<std::unique_ptr<Module>> modules_;
|
||||
|
|
|
@ -28,12 +28,12 @@ class View {
|
|||
std::string name() const { return name_; }
|
||||
el::LayoutBox* root_element() { return &root_element_; }
|
||||
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(); }
|
||||
|
||||
virtual el::Element* BuildUI() = 0;
|
||||
|
||||
virtual void Setup(xe::debug::client::xdp::XdpClient* client) = 0;
|
||||
virtual void Setup(xe::debug::DebugClient* client) = 0;
|
||||
|
||||
protected:
|
||||
View(std::string name) : name_(name) {}
|
||||
|
@ -41,7 +41,7 @@ class View {
|
|||
std::string name_;
|
||||
el::LayoutBox root_element_;
|
||||
std::unique_ptr<el::EventHandler> handler_;
|
||||
xe::debug::client::xdp::XdpClient* client_ = nullptr;
|
||||
xe::debug::DebugClient* client_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -16,8 +16,6 @@ namespace ui {
|
|||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
using namespace xe::debug::client::xdp;
|
||||
|
||||
CpuView::CpuView() : View("CPU") {}
|
||||
|
||||
CpuView::~CpuView() = default;
|
||||
|
@ -147,7 +145,7 @@ el::Element* CpuView::BuildUI() {
|
|||
return &root_element_;
|
||||
}
|
||||
|
||||
void CpuView::Setup(XdpClient* client) {
|
||||
void CpuView::Setup(DebugClient* client) {
|
||||
client_ = client;
|
||||
|
||||
system()->on_execution_state_changed.AddListener(
|
||||
|
|
|
@ -28,7 +28,7 @@ class CpuView : public View {
|
|||
|
||||
el::Element* BuildUI() override;
|
||||
|
||||
void Setup(xe::debug::client::xdp::XdpClient* client) override;
|
||||
void Setup(xe::debug::DebugClient* client) override;
|
||||
|
||||
protected:
|
||||
void UpdateElementState();
|
||||
|
|
|
@ -36,7 +36,7 @@ el::Element* GpuView::BuildUI() {
|
|||
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;
|
||||
|
||||
void Setup(xe::debug::client::xdp::XdpClient* client) override;
|
||||
void Setup(xe::debug::DebugClient* client) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue