From d548e7f77057c92bc88753b1f59b4952b56a6d72 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Mon, 16 Dec 2013 20:28:58 -0800 Subject: [PATCH] Starting revival of debugger system. Work on #41. --- .gitmodules | 3 + src/xenia/dbg/sources.gypi | 13 - src/xenia/debug/debug_client.cc | 26 ++ src/xenia/debug/debug_client.h | 40 ++ src/xenia/debug/debug_server.cc | 97 +++++ src/xenia/debug/debug_server.h | 53 +++ .../{dbg/listener.cc => debug/protocol.cc} | 10 +- .../{dbg/listener.h => debug/protocol.h} | 24 +- src/xenia/debug/protocols/gdb/gdb_client.cc | 350 ++++++++++++++++++ src/xenia/debug/protocols/gdb/gdb_client.h | 58 +++ src/xenia/debug/protocols/gdb/gdb_protocol.cc | 88 +++++ src/xenia/debug/protocols/gdb/gdb_protocol.h | 49 +++ src/xenia/debug/protocols/gdb/sources.gypi | 9 + src/xenia/debug/protocols/sources.gypi | 7 + src/xenia/debug/protocols/ws/simple_sha1.cc | 235 ++++++++++++ src/xenia/debug/protocols/ws/simple_sha1.h | 35 ++ src/xenia/debug/protocols/ws/sources.gypi | 11 + .../{dbg => debug/protocols/ws}/ws_client.cc | 51 +-- .../{dbg => debug/protocols/ws}/ws_client.h | 26 +- .../protocols/ws/ws_protocol.cc} | 36 +- .../protocols/ws/ws_protocol.h} | 28 +- src/xenia/debug/sources.gypi | 15 + src/xenia/emulator.cc | 19 +- src/xenia/emulator.h | 6 +- src/xenia/sources.gypi | 2 +- third_party/wslay | 1 + third_party/wslay.gypi | 59 +++ xenia.gyp | 3 + 28 files changed, 1254 insertions(+), 100 deletions(-) delete mode 100644 src/xenia/dbg/sources.gypi create mode 100644 src/xenia/debug/debug_client.cc create mode 100644 src/xenia/debug/debug_client.h create mode 100644 src/xenia/debug/debug_server.cc create mode 100644 src/xenia/debug/debug_server.h rename src/xenia/{dbg/listener.cc => debug/protocol.cc} (75%) rename src/xenia/{dbg/listener.h => debug/protocol.h} (69%) create mode 100644 src/xenia/debug/protocols/gdb/gdb_client.cc create mode 100644 src/xenia/debug/protocols/gdb/gdb_client.h create mode 100644 src/xenia/debug/protocols/gdb/gdb_protocol.cc create mode 100644 src/xenia/debug/protocols/gdb/gdb_protocol.h create mode 100644 src/xenia/debug/protocols/gdb/sources.gypi create mode 100644 src/xenia/debug/protocols/sources.gypi create mode 100644 src/xenia/debug/protocols/ws/simple_sha1.cc create mode 100644 src/xenia/debug/protocols/ws/simple_sha1.h create mode 100644 src/xenia/debug/protocols/ws/sources.gypi rename src/xenia/{dbg => debug/protocols/ws}/ws_client.cc (87%) rename src/xenia/{dbg => debug/protocols/ws}/ws_client.h (64%) rename src/xenia/{dbg/ws_listener.cc => debug/protocols/ws/ws_protocol.cc} (66%) rename src/xenia/{dbg/ws_listener.h => debug/protocols/ws/ws_protocol.h} (61%) create mode 100644 src/xenia/debug/sources.gypi create mode 160000 third_party/wslay create mode 100644 third_party/wslay.gypi diff --git a/.gitmodules b/.gitmodules index 8be05d2c6..17e51d523 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "third_party/beaengine"] path = third_party/beaengine url = https://github.com/benvanik/beaengine.git +[submodule "third_party/wslay"] + path = third_party/wslay + url = https://github.com/benvanik/wslay.git diff --git a/src/xenia/dbg/sources.gypi b/src/xenia/dbg/sources.gypi deleted file mode 100644 index 80b60dc46..000000000 --- a/src/xenia/dbg/sources.gypi +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2013 Ben Vanik. All Rights Reserved. -{ - 'sources': [ - 'client.cc', - 'client.h', - 'content_source.cc', - 'content_source.h', - 'debugger.cc', - 'debugger.h', - 'listener.cc', - 'listener.h', - ], -} diff --git a/src/xenia/debug/debug_client.cc b/src/xenia/debug/debug_client.cc new file mode 100644 index 000000000..1b5330c53 --- /dev/null +++ b/src/xenia/debug/debug_client.cc @@ -0,0 +1,26 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include + + +using namespace xe; +using namespace xe::debug; + + +DebugClient::DebugClient(DebugServer* debug_server) : + debug_server_(debug_server) { + debug_server_->AddClient(this); +} + +DebugClient::~DebugClient() { + debug_server_->RemoveClient(this); +} diff --git a/src/xenia/debug/debug_client.h b/src/xenia/debug/debug_client.h new file mode 100644 index 000000000..f13db9a04 --- /dev/null +++ b/src/xenia/debug/debug_client.h @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_DEBUG_CLIENT_H_ +#define XENIA_DEBUG_DEBUG_CLIENT_H_ + +#include +#include + + +XEDECLARECLASS2(xe, debug, DebugServer); + + +namespace xe { +namespace debug { + + +class DebugClient { +public: + DebugClient(DebugServer* debug_server); + virtual ~DebugClient(); + + virtual int Setup() = 0; + +protected: + DebugServer* debug_server_; +}; + + +} // namespace debug +} // namespace xe + + +#endif // XENIA_DEBUG_DEBUG_CLIENT_H_ diff --git a/src/xenia/debug/debug_server.cc b/src/xenia/debug/debug_server.cc new file mode 100644 index 000000000..4b0fe74bf --- /dev/null +++ b/src/xenia/debug/debug_server.cc @@ -0,0 +1,97 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include + +#include +#include +#include +#include +#include + + +using namespace xe; +using namespace xe::debug; + + +DEFINE_bool(wait_for_debugger, false, + "Whether to wait for the debugger to attach before launching."); + + +DebugServer::DebugServer(Emulator* emulator) : + emulator_(emulator) { + //protocols_.push_back( + // new protocols::gdb::GDBProtocol(this, gdb_port)); + protocols_.push_back( + new protocols::ws::WSProtocol(this)); +} + +DebugServer::~DebugServer() { + Shutdown(); +} + +int DebugServer::Startup() { + // HACK(benvanik): say we are ok even if we have no listener. + if (!protocols_.size()) { + return 0; + } + + // Start listeners. + // This may launch threads and such. + for (std::vector::iterator it = protocols_.begin(); + it != protocols_.end(); ++it) { + Protocol* protocol = *it; + if (protocol->Setup()) { + return 1; + } + } + + // If desired, wait until the first client connects. + if (FLAGS_wait_for_debugger) { + //XELOGI("Waiting for debugger on port %d...", FLAGS_remote_debug_port); + //if (listener_->WaitForClient()) { + // return 1; + //} + XELOGI("Debugger attached, continuing..."); + } + + return 0; +} + +void DebugServer::Shutdown() { + std::vector clients(clients_.begin(), clients_.end()); + clients_.clear(); + for (std::vector::iterator it = clients.begin(); + it != clients.end(); ++it) { + delete *it; + } + + std::vector protocols(protocols_.begin(), protocols_.end()); + protocols_.clear(); + for (std::vector::iterator it = protocols.begin(); + it != protocols.end(); ++it) { + delete *it; + } +} + +void DebugServer::AddClient(DebugClient* debug_client) { + clients_.push_back(debug_client); +} + +void DebugServer::RemoveClient(DebugClient* debug_client) { + for (std::vector::iterator it = clients_.begin(); + it != clients_.end(); ++it) { + if (*it == debug_client) { + clients_.erase(it); + return; + } + } +} diff --git a/src/xenia/debug/debug_server.h b/src/xenia/debug/debug_server.h new file mode 100644 index 000000000..0bf988e9b --- /dev/null +++ b/src/xenia/debug/debug_server.h @@ -0,0 +1,53 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_DEBUG_SERVER_H_ +#define XENIA_DEBUG_DEBUG_SERVER_H_ + +#include +#include + +#include + + +XEDECLARECLASS1(xe, Emulator); +XEDECLARECLASS2(xe, debug, DebugClient); +XEDECLARECLASS2(xe, debug, Protocol); + + +namespace xe { +namespace debug { + + +class DebugServer { +public: + DebugServer(Emulator* emulator); + virtual ~DebugServer(); + + int Startup(); + void Shutdown(); + +private: + void AddClient(DebugClient* debug_client); + void RemoveClient(DebugClient* debug_client); + + friend class DebugClient; + +private: + Emulator* emulator_; + std::vector protocols_; + std::vector clients_; +}; + + +} // namespace debug +} // namespace xe + + +#endif // XENIA_DEBUG_DEBUG_SERVER_H_ diff --git a/src/xenia/dbg/listener.cc b/src/xenia/debug/protocol.cc similarity index 75% rename from src/xenia/dbg/listener.cc rename to src/xenia/debug/protocol.cc index 4701709d3..190e7b53a 100644 --- a/src/xenia/dbg/listener.cc +++ b/src/xenia/debug/protocol.cc @@ -7,16 +7,16 @@ ****************************************************************************** */ -#include +#include using namespace xe; -using namespace xe::dbg; +using namespace xe::debug; -Listener::Listener(Debugger* debugger) : - debugger_(debugger) { +Protocol::Protocol(DebugServer* debug_server) : + debug_server_(debug_server) { } -Listener::~Listener() { +Protocol::~Protocol() { } diff --git a/src/xenia/dbg/listener.h b/src/xenia/debug/protocol.h similarity index 69% rename from src/xenia/dbg/listener.h rename to src/xenia/debug/protocol.h index 5326ffb54..d09d53c5f 100644 --- a/src/xenia/dbg/listener.h +++ b/src/xenia/debug/protocol.h @@ -7,35 +7,35 @@ ****************************************************************************** */ -#ifndef XENIA_DBG_LISTENER_H_ -#define XENIA_DBG_LISTENER_H_ +#ifndef XENIA_DEBUG_PROTOCOL_H_ +#define XENIA_DEBUG_PROTOCOL_H_ #include #include +XEDECLARECLASS2(xe, debug, DebugServer); + + namespace xe { -namespace dbg { +namespace debug { -class Debugger; - - -class Listener { +class Protocol { public: - Listener(Debugger* debugger); - virtual ~Listener(); + Protocol(DebugServer* debug_server); + virtual ~Protocol(); virtual int Setup() = 0; virtual int WaitForClient() = 0; protected: - Debugger* debugger_; + DebugServer* debug_server_; }; -} // namespace dbg +} // namespace debug } // namespace xe -#endif // XENIA_DBG_LISTENER_H_ +#endif // XENIA_DEBUG_PROTOCOL_H_ diff --git a/src/xenia/debug/protocols/gdb/gdb_client.cc b/src/xenia/debug/protocols/gdb/gdb_client.cc new file mode 100644 index 000000000..87468b1fa --- /dev/null +++ b/src/xenia/debug/protocols/gdb/gdb_client.cc @@ -0,0 +1,350 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include + + +using namespace xe; +using namespace xe::debug; +using namespace xe::debug::protocols::gdb; + +#if 0 +GDBClient::GDBClient(DebugServer* debug_server, int socket_id) : + DebugClient(debug_server), + thread_(NULL), + socket_id_(socket_id) { + mutex_ = xe_mutex_alloc(1000); + + loop_ = xe_socket_loop_create(socket_id); +} + +GDBClient::~GDBClient() { + xe_mutex_t* mutex = mutex_; + xe_mutex_lock(mutex); + + mutex_ = NULL; + + xe_socket_close(socket_id_); + socket_id_ = 0; + + xe_socket_loop_destroy(loop_); + loop_ = NULL; + + xe_mutex_unlock(mutex); + xe_mutex_free(mutex); + + xe_thread_release(thread_); +} + +int GDBClient::socket_id() { + return socket_id_; +} + +int GDBClient::Setup() { + // Prep the socket. + xe_socket_set_keepalive(socket_id_, true); + xe_socket_set_nodelay(socket_id_, true); + + thread_ = xe_thread_create("Debugger Client", StartCallback, this); + return xe_thread_start(thread_); +} + +void GDBClient::StartCallback(void* param) { + GDBClient* client = reinterpret_cast(param); + client->EventThread(); +} + +namespace { + +int64_t WsClientSendCallback(wslay_event_context_ptr ctx, + const uint8_t* data, size_t len, int flags, + void* user_data) { + GDBClient* client = reinterpret_cast(user_data); + + int error_code = 0; + int64_t r; + while ((r = xe_socket_send(client->socket_id(), data, len, 0, + &error_code)) == -1 && error_code == EINTR); + if (r == -1) { + if (error_code == EAGAIN || error_code == EWOULDBLOCK) { + wslay_event_set_error(ctx, GDBLAY_ERR_WOULDBLOCK); + } else { + wslay_event_set_error(ctx, GDBLAY_ERR_CALLBACK_FAILURE); + } + } + return r; +} + +int64_t WsClientRecvCallback(wslay_event_context_ptr ctx, + uint8_t* data, size_t len, int flags, + void* user_data) { + GDBClient* client = reinterpret_cast(user_data); + + int error_code = 0; + int64_t r; + while ((r = xe_socket_recv(client->socket_id(), data, len, 0, + &error_code)) == -1 && error_code == EINTR); + if (r == -1) { + if (error_code == EAGAIN || error_code == EWOULDBLOCK) { + wslay_event_set_error(ctx, GDBLAY_ERR_WOULDBLOCK); + } else { + wslay_event_set_error(ctx, GDBLAY_ERR_CALLBACK_FAILURE); + } + } else if (r == 0) { + wslay_event_set_error(ctx, GDBLAY_ERR_CALLBACK_FAILURE); + r = -1; + } + return r; +} + +void GDBClientOnMsgCallback(wslay_event_context_ptr ctx, + const struct wslay_event_on_msg_recv_arg* arg, + void* user_data) { + if (wslay_is_ctrl_frame(arg->opcode)) { + // Ignore control frames. + return; + } + + GDBClient* client = reinterpret_cast(user_data); + switch (arg->opcode) { + case GDBLAY_TEXT_FRAME: + XELOGW("Text frame ignored; use binary messages"); + break; + case GDBLAY_BINARY_FRAME: + client->OnMessage(arg->msg, arg->msg_length); + break; + default: + // Unknown opcode - some frame stuff? + break; + } +} + +std::string EncodeBase64(const uint8_t* input, size_t length) { + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string result; + size_t remaining = length; + size_t n = 0; + while (remaining) { + result.push_back(b64[input[n] >> 2]); + result.push_back(b64[((input[n] & 0x03) << 4) | + ((input[n + 1] & 0xf0) >> 4)]); + remaining--; + if (remaining) { + result.push_back(b64[((input[n + 1] & 0x0f) << 2) | + ((input[n + 2] & 0xc0) >> 6)]); + remaining--; + } else { + result.push_back('='); + } + if (remaining) { + result.push_back(b64[input[n + 2] & 0x3f]); + remaining--; + } else { + result.push_back('='); + } + n += 3; + } + return result; +} + +} + +int GDBClient::PerformHandshake() { + std::string headers; + uint8_t buffer[4096]; + int error_code = 0; + int64_t r; + while (true) { + while ((r = xe_socket_recv(socket_id_, buffer, sizeof(buffer), 0, + &error_code)) == -1 && error_code == EINTR); + if (r == -1) { + if (error_code == EWOULDBLOCK || error_code == EAGAIN) { + if (!headers.size()) { + // Nothing read yet - spin. + continue; + } + break; + } else { + XELOGE("HTTP header read failure"); + return 1; + } + } else if (r == 0) { + // EOF. + XELOGE("HTTP header EOF"); + return 2; + } else { + headers.append(buffer, buffer + r); + if (headers.size() > 8192) { + XELOGE("HTTP headers exceeded max buffer size"); + return 3; + } + } + } + + if (headers.find("\r\n\r\n") == std::string::npos) { + XELOGE("Incomplete HTTP headers: %s", headers.c_str()); + return 1; + } + + // Parse the headers to verify its a websocket request. + std::string::size_type keyhdstart; + if (headers.find("Upgrade: websocket\r\n") == std::string::npos || + headers.find("Connection: Upgrade\r\n") == std::string::npos || + (keyhdstart = headers.find("Sec-WebSocket-Key: ")) == + std::string::npos) { + XELOGW("HTTP connection does not contain websocket headers"); + return 2; + } + keyhdstart += 19; + std::string::size_type keyhdend = headers.find("\r\n", keyhdstart); + std::string client_key = headers.substr(keyhdstart, keyhdend - keyhdstart); + std::string accept_key = client_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + uint8_t accept_sha[20]; + SHA1((uint8_t*)accept_key.c_str(), accept_key.size(), accept_sha); + accept_key = EncodeBase64(accept_sha, sizeof(accept_sha)); + + // Write the response to upgrade the connection. + std::string response = + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: " + accept_key + "\r\n" + "\r\n"; + size_t write_offset = 0; + size_t write_length = response.size(); + while (true) { + while ((r = xe_socket_send(socket_id_, + (uint8_t*)response.c_str() + write_offset, + write_length, 0, &error_code)) == -1 && + error_code == EINTR); + if (r == -1) { + if (error_code == EAGAIN || error_code == EWOULDBLOCK) { + break; + } else { + XELOGE("HTTP response write failure"); + return 4; + } + } else { + write_offset += r; + write_length -= r; + if (!write_length) { + break; + } + } + } + + return 0; +} + +void GDBClient::EventThread() { + // Enable non-blocking IO on the socket. + xe_socket_set_nonblock(socket_id_, true); + + // First run the HTTP handshake. + // This will fail if the connection is not for websockets. + if (PerformHandshake()) { + return; + } + + // Prep callbacks. + struct wslay_event_callbacks callbacks = { + (wslay_event_recv_callback)WsClientRecvCallback, + (wslay_event_send_callback)WsClientSendCallback, + NULL, + NULL, + NULL, + NULL, + GDBClientOnMsgCallback, + }; + + // Prep the websocket server context. + wslay_event_context_ptr ctx; + wslay_event_context_server_init(&ctx, &callbacks, this); + + // Loop forever. + while (wslay_event_want_read(ctx) || wslay_event_want_write(ctx)) { + // Wait on the event. + if (xe_socket_loop_poll(loop_, + !!wslay_event_want_read(ctx), + !!wslay_event_want_write(ctx))) { + break; + } + + // Handle any self-generated events to queue messages. + if (xe_socket_loop_check_queued_write(loop_)) { + xe_mutex_lock(mutex_); + for (std::vector::iterator it = + pending_messages_.begin(); it != pending_messages_.end(); it++) { + struct wslay_event_msg* msg = &*it; + wslay_event_queue_msg(ctx, msg); + } + pending_messages_.clear(); + xe_mutex_unlock(mutex_); + } + + // Handle websocket messages. + if ((xe_socket_loop_check_socket_recv(loop_) && wslay_event_recv(ctx)) || + (xe_socket_loop_check_socket_send(loop_) && wslay_event_send(ctx))) { + // Error handling the event. + XELOGE("Error handling WebSocket data"); + break; + } + } + + wslay_event_context_free(ctx); +} + +void GDBClient::Write(uint8_t** buffers, size_t* lengths, size_t count) { + if (!count) { + return; + } + + size_t combined_length; + uint8_t* combined_buffer; + if (count == 1) { + // Single buffer, just copy. + combined_length = lengths[0]; + combined_buffer = (uint8_t*)xe_malloc(lengths[0]); + XEIGNORE(xe_copy_memory(combined_buffer, combined_length, + buffers[0], lengths[0])); + } else { + // Multiple buffers, merge. + combined_length = 0; + for (size_t n = 0; n < count; n++) { + combined_length += lengths[n]; + } + combined_buffer = (uint8_t*)xe_malloc(combined_length); + for (size_t n = 0, offset = 0; n < count; n++) { + XEIGNORE(xe_copy_memory( + combined_buffer + offset, combined_length - offset, + buffers[n], lengths[n])); + offset += lengths[n]; + } + } + + struct wslay_event_msg msg = { + GDBLAY_BINARY_FRAME, + combined_buffer, + combined_length, + }; + + xe_mutex_lock(mutex_); + pending_messages_.push_back(msg); + bool needs_signal = pending_messages_.size() == 1; + xe_mutex_unlock(mutex_); + + if (needs_signal) { + // Notify the poll(). + xe_socket_loop_set_queued_write(loop_); + } +} +#endif diff --git a/src/xenia/debug/protocols/gdb/gdb_client.h b/src/xenia/debug/protocols/gdb/gdb_client.h new file mode 100644 index 000000000..eefa62b23 --- /dev/null +++ b/src/xenia/debug/protocols/gdb/gdb_client.h @@ -0,0 +1,58 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_PROTOCOLS_GDB_GDB_CLIENT_H_ +#define XENIA_DEBUG_PROTOCOLS_GDB_GDB_CLIENT_H_ + +#include +#include + +#include + +#include + + +namespace xe { +namespace debug { +namespace protocols { +namespace gdb { + + +class GDBClient : public DebugClient { +public: + GDBClient(DebugServer* debug_server, int socket_id); + virtual ~GDBClient(); + + socket_t socket_id(); + + virtual int Setup(); + + void Write(uint8_t** buffers, size_t* lengths, size_t count); + +private: + static void StartCallback(void* param); + + int PerformHandshake(); + void EventThread(); + + xe_thread_ref thread_; + + socket_t socket_id_; + xe_socket_loop_t* loop_; + xe_mutex_t* mutex_; +}; + + +} // namespace gdb +} // namespace protocols +} // namespace debug +} // namespace xe + + +#endif // XENIA_DEBUG_PROTOCOLS_GDB_GDB_CLIENT_H_ diff --git a/src/xenia/debug/protocols/gdb/gdb_protocol.cc b/src/xenia/debug/protocols/gdb/gdb_protocol.cc new file mode 100644 index 000000000..1446aa1f7 --- /dev/null +++ b/src/xenia/debug/protocols/gdb/gdb_protocol.cc @@ -0,0 +1,88 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include + +#include + + +DEFINE_int32(gdb_debug_port, 6201, + "Remote debugging port for GDB TCP connections."); + + +using namespace xe; +using namespace xe::debug; +using namespace xe::debug::protocols::gdb; + + +GDBProtocol::GDBProtocol(DebugServer* debug_server) : + Protocol(debug_server) { + port_ = FLAGS_gdb_debug_port; +} + +GDBProtocol::~GDBProtocol() { + if (socket_id_) { + xe_socket_close(socket_id_); + } +} + +int GDBProtocol::Setup() { + if (port_ == 0 || port_ == -1) { + return 0; + } + + xe_socket_init(); + + socket_id_ = xe_socket_create_tcp(); + if (socket_id_ == XE_INVALID_SOCKET) { + return 1; + } + + xe_socket_set_keepalive(socket_id_, true); + xe_socket_set_reuseaddr(socket_id_, true); + xe_socket_set_nodelay(socket_id_, true); + + if (xe_socket_bind(socket_id_, port_)) { + XELOGE("Could not bind listen socket: %d", errno); + return 1; + } + + if (xe_socket_listen(socket_id_)) { + xe_socket_close(socket_id_); + return 1; + } + + return 0; +} + +int GDBProtocol::WaitForClient() { + if (!socket_id_) { + return 1; + } + + // Accept the first connection we get. + xe_socket_connection_t client_info; + if (xe_socket_accept(socket_id_, &client_info)) { + return 1; + } + + XELOGI("GDB debugger connected from %s", client_info.addr); + + // Create the client object. + // Note that the client will delete itself when done. + GDBClient* client = new GDBClient(debug_server_, client_info.socket); + if (client->Setup()) { + // Client failed to setup - abort. + return 1; + } + + return 0; +} diff --git a/src/xenia/debug/protocols/gdb/gdb_protocol.h b/src/xenia/debug/protocols/gdb/gdb_protocol.h new file mode 100644 index 000000000..94a63bd58 --- /dev/null +++ b/src/xenia/debug/protocols/gdb/gdb_protocol.h @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_PROTOCOLS_GDB_GDB_PROTOCOL_H_ +#define XENIA_DEBUG_PROTOCOLS_GDB_GDB_PROTOCOL_H_ + +#include +#include + +#include + + +XEDECLARECLASS2(xe, debug, DebugServer); + + +namespace xe { +namespace debug { +namespace protocols { +namespace gdb { + + +class GDBProtocol : public Protocol { +public: + GDBProtocol(DebugServer* debug_server); + virtual ~GDBProtocol(); + + virtual int Setup(); + virtual int WaitForClient(); + +protected: + uint32_t port_; + + socket_t socket_id_; +}; + + +} // namespace gdb +} // namespace protocols +} // namespace debug +} // namespace xe + + +#endif // XENIA_DEBUG_PROTOCOLS_GDB_GDB_PROTOCOL_H_ diff --git a/src/xenia/debug/protocols/gdb/sources.gypi b/src/xenia/debug/protocols/gdb/sources.gypi new file mode 100644 index 000000000..c0d2231f6 --- /dev/null +++ b/src/xenia/debug/protocols/gdb/sources.gypi @@ -0,0 +1,9 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'sources': [ + 'gdb_client.cc', + 'gdb_client.h', + 'gdb_protocol.cc', + 'gdb_protocol.h', + ], +} diff --git a/src/xenia/debug/protocols/sources.gypi b/src/xenia/debug/protocols/sources.gypi new file mode 100644 index 000000000..aa87c61fd --- /dev/null +++ b/src/xenia/debug/protocols/sources.gypi @@ -0,0 +1,7 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'includes': [ + 'gdb/sources.gypi', + 'ws/sources.gypi', + ], +} diff --git a/src/xenia/debug/protocols/ws/simple_sha1.cc b/src/xenia/debug/protocols/ws/simple_sha1.cc new file mode 100644 index 000000000..797ec7191 --- /dev/null +++ b/src/xenia/debug/protocols/ws/simple_sha1.cc @@ -0,0 +1,235 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#if XE_PLATFORM(WIN32) +#include +#else +#include +#endif // WIN32 + + +using namespace xe; +using namespace xe::debug::protocols::ws; + + +namespace { + + +// http://git.kernel.org/?p=git/git.git;a=blob;f=block-sha1/sha1.c + +/* + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was initially based on the Mozilla SHA1 implementation, although + * none of the original Mozilla code remains. + */ + +typedef struct { + size_t size; + uint32_t H[5]; + uint32_t W[16]; +} SHA_CTX; + +#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) +#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) +#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) + +#define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) + +#define get_be32(p) ntohl(*(unsigned int *)(p)) +#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) + +#define W(x) (array[(x)&15]) + +#define SHA_SRC(t) get_be32((unsigned char *) block + (t)*4) +#define SHA_MIX(t) SHA_ROL(W((t)+13) ^ W((t)+8) ^ W((t)+2) ^ W(t), 1); + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + unsigned int TEMP = input(t); setW(t, TEMP); \ + E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ + B = SHA_ROR(B, 2); } while (0) + +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) + +static void SHA1_Block(SHA_CTX *ctx, const void *block) { + uint32_t A = ctx->H[0]; + uint32_t B = ctx->H[1]; + uint32_t C = ctx->H[2]; + uint32_t D = ctx->H[3]; + uint32_t E = ctx->H[4]; + + uint32_t array[16]; + + /* Round 1 - iterations 0-16 take their input from 'block' */ + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); + + /* Round 2 */ + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); + + /* Round 3 */ + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); + + /* Round 4 */ + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} + +void SHA1_Update(SHA_CTX *ctx, const void *data, unsigned long len) +{ + uint32_t lenW = ctx->size & 63; + ctx->size += len; + + if (lenW) { + uint32_t left = 64 - lenW; + if (len < left) { + left = len; + } + memcpy(lenW + (char *)ctx->W, data, left); + lenW = (lenW + left) & 63; + len -= left; + data = ((const char *)data + left); + if (lenW) { + return; + } + SHA1_Block(ctx, ctx->W); + } + while (len >= 64) { + SHA1_Block(ctx, data); + data = ((const char *)data + 64); + len -= 64; + } + if (len) { + memcpy(ctx->W, data, len); + } +} + +} + + +void xe::debug::protocols::ws::SHA1( + const uint8_t* data, size_t length, uint8_t out_hash[20]) { + static const uint8_t pad[64] = { 0x80 }; + + SHA_CTX ctx = { + 0, + { + 0x67452301, + 0xefcdab89, + 0x98badcfe, + 0x10325476, + 0xc3d2e1f0, + }, + { 0 } + }; + + SHA1_Update(&ctx, data, (unsigned long)length); + + uint32_t padlen[2] = { + htonl((uint32_t)(ctx.size >> 29)), + htonl((uint32_t)(ctx.size << 3)), + }; + + size_t i = ctx.size & 63; + SHA1_Update(&ctx, pad, 1 + (63 & (55 - i))); + SHA1_Update(&ctx, padlen, 8); + + for (size_t n = 0; n < 5; n++) { + put_be32(out_hash + n * 4, ctx.H[n]); + } +} diff --git a/src/xenia/debug/protocols/ws/simple_sha1.h b/src/xenia/debug/protocols/ws/simple_sha1.h new file mode 100644 index 000000000..25379799e --- /dev/null +++ b/src/xenia/debug/protocols/ws/simple_sha1.h @@ -0,0 +1,35 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license = see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_DEBUG_PROTOCOLS_WS_SIMPLE_SHA1_H_ +#define XENIA_DEBUG_PROTOCOLS_WS_SIMPLE_SHA1_H_ + +#include +#include + + +namespace xe { +namespace debug { +namespace protocols { +namespace ws { + + +// This is a (likely) slow SHA1 designed for use on small values such as +// Websocket security keys. If we need something more complex it'd be best +// to use a real library. +void SHA1(const uint8_t* data, size_t length, uint8_t out_hash[20]); + + +} // namespace ws +} // namespace protocols +} // namespace debug +} // namespace xe + + +#endif // XENIA_DEBUG_PROTOCOLS_WS_SIMPLE_SHA1_H_ diff --git a/src/xenia/debug/protocols/ws/sources.gypi b/src/xenia/debug/protocols/ws/sources.gypi new file mode 100644 index 000000000..f3313acf2 --- /dev/null +++ b/src/xenia/debug/protocols/ws/sources.gypi @@ -0,0 +1,11 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'sources': [ + 'simple_sha1.cc', + 'simple_sha1.h', + 'ws_client.cc', + 'ws_client.h', + 'ws_protocol.cc', + 'ws_protocol.h', + ], +} diff --git a/src/xenia/dbg/ws_client.cc b/src/xenia/debug/protocols/ws/ws_client.cc similarity index 87% rename from src/xenia/dbg/ws_client.cc rename to src/xenia/debug/protocols/ws/ws_client.cc index bfccd8786..def967e2b 100644 --- a/src/xenia/dbg/ws_client.cc +++ b/src/xenia/debug/protocols/ws/ws_client.cc @@ -7,10 +7,10 @@ ****************************************************************************** */ -#include +#include -#include -#include +#include +#include #if XE_PLATFORM(WIN32) // Required for wslay. @@ -21,11 +21,12 @@ typedef SSIZE_T ssize_t; using namespace xe; -using namespace xe::dbg; +using namespace xe::debug; +using namespace xe::debug::protocols::ws; -WsClient::WsClient(Debugger* debugger, int socket_id) : - Client(debugger), +WSClient::WSClient(DebugServer* debug_server, int socket_id) : + DebugClient(debug_server), thread_(NULL), socket_id_(socket_id) { mutex_ = xe_mutex_alloc(1000); @@ -33,7 +34,7 @@ WsClient::WsClient(Debugger* debugger, int socket_id) : loop_ = xe_socket_loop_create(socket_id); } -WsClient::~WsClient() { +WSClient::~WSClient() { xe_mutex_t* mutex = mutex_; xe_mutex_lock(mutex); @@ -51,30 +52,30 @@ WsClient::~WsClient() { xe_thread_release(thread_); } -int WsClient::socket_id() { +int WSClient::socket_id() { return socket_id_; } -int WsClient::Setup() { +int WSClient::Setup() { // Prep the socket. xe_socket_set_keepalive(socket_id_, true); xe_socket_set_nodelay(socket_id_, true); - thread_ = xe_thread_create("Debugger Client", StartCallback, this); + thread_ = xe_thread_create("WS Debugger Client", StartCallback, this); return xe_thread_start(thread_); } -void WsClient::StartCallback(void* param) { - WsClient* client = reinterpret_cast(param); +void WSClient::StartCallback(void* param) { + WSClient* client = reinterpret_cast(param); client->EventThread(); } namespace { -int64_t WsClientSendCallback(wslay_event_context_ptr ctx, +int64_t WSClientSendCallback(wslay_event_context_ptr ctx, const uint8_t* data, size_t len, int flags, void* user_data) { - WsClient* client = reinterpret_cast(user_data); + WSClient* client = reinterpret_cast(user_data); int error_code = 0; int64_t r; @@ -90,10 +91,10 @@ int64_t WsClientSendCallback(wslay_event_context_ptr ctx, return r; } -int64_t WsClientRecvCallback(wslay_event_context_ptr ctx, +int64_t WSClientRecvCallback(wslay_event_context_ptr ctx, uint8_t* data, size_t len, int flags, void* user_data) { - WsClient* client = reinterpret_cast(user_data); + WSClient* client = reinterpret_cast(user_data); int error_code = 0; int64_t r; @@ -112,7 +113,7 @@ int64_t WsClientRecvCallback(wslay_event_context_ptr ctx, return r; } -void WsClientOnMsgCallback(wslay_event_context_ptr ctx, +void WSClientOnMsgCallback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg* arg, void* user_data) { if (wslay_is_ctrl_frame(arg->opcode)) { @@ -120,13 +121,13 @@ void WsClientOnMsgCallback(wslay_event_context_ptr ctx, return; } - WsClient* client = reinterpret_cast(user_data); + WSClient* client = reinterpret_cast(user_data); switch (arg->opcode) { case WSLAY_TEXT_FRAME: XELOGW("Text frame ignored; use binary messages"); break; case WSLAY_BINARY_FRAME: - client->OnMessage(arg->msg, arg->msg_length); + //client->OnMessage(arg->msg, arg->msg_length); break; default: // Unknown opcode - some frame stuff? @@ -165,7 +166,7 @@ std::string EncodeBase64(const uint8_t* input, size_t length) { } -int WsClient::PerformHandshake() { +int WSClient::PerformHandshake() { std::string headers; uint8_t buffer[4096]; int error_code = 0; @@ -252,7 +253,7 @@ int WsClient::PerformHandshake() { return 0; } -void WsClient::EventThread() { +void WSClient::EventThread() { // Enable non-blocking IO on the socket. xe_socket_set_nonblock(socket_id_, true); @@ -264,13 +265,13 @@ void WsClient::EventThread() { // Prep callbacks. struct wslay_event_callbacks callbacks = { - (wslay_event_recv_callback)WsClientRecvCallback, - (wslay_event_send_callback)WsClientSendCallback, + (wslay_event_recv_callback)WSClientRecvCallback, + (wslay_event_send_callback)WSClientSendCallback, NULL, NULL, NULL, NULL, - WsClientOnMsgCallback, + WSClientOnMsgCallback, }; // Prep the websocket server context. @@ -310,7 +311,7 @@ void WsClient::EventThread() { wslay_event_context_free(ctx); } -void WsClient::Write(uint8_t** buffers, size_t* lengths, size_t count) { +void WSClient::Write(uint8_t** buffers, size_t* lengths, size_t count) { if (!count) { return; } diff --git a/src/xenia/dbg/ws_client.h b/src/xenia/debug/protocols/ws/ws_client.h similarity index 64% rename from src/xenia/dbg/ws_client.h rename to src/xenia/debug/protocols/ws/ws_client.h index 8236c50f4..016ad0ae6 100644 --- a/src/xenia/dbg/ws_client.h +++ b/src/xenia/debug/protocols/ws/ws_client.h @@ -7,34 +7,36 @@ ****************************************************************************** */ -#ifndef XENIA_DBG_WS_CLIENT_H_ -#define XENIA_DBG_WS_CLIENT_H_ +#ifndef XENIA_DEBUG_PROTOCOLS_WS_WS_CLIENT_H_ +#define XENIA_DEBUG_PROTOCOLS_WS_WS_CLIENT_H_ #include #include #include -#include +#include struct wslay_event_msg; namespace xe { -namespace dbg { +namespace debug { +namespace protocols { +namespace ws { -class WsClient : public Client { +class WSClient : public DebugClient { public: - WsClient(Debugger* debugger, int socket_id); - virtual ~WsClient(); + WSClient(DebugServer* debug_server, int socket_id); + virtual ~WSClient(); socket_t socket_id(); virtual int Setup(); - virtual void Write(uint8_t** buffers, size_t* lengths, size_t count); + void Write(uint8_t** buffers, size_t* lengths, size_t count); private: static void StartCallback(void* param); @@ -42,7 +44,7 @@ private: int PerformHandshake(); void EventThread(); - xe_thread_ref thread_; + xe_thread_ref thread_; socket_t socket_id_; xe_socket_loop_t* loop_; @@ -51,8 +53,10 @@ private: }; -} // namespace dbg +} // namespace ws +} // namespace protocols +} // namespace debug } // namespace xe -#endif // XENIA_DBG_WS_CLIENT_H_ +#endif // XENIA_DEBUG_PROTOCOLS_WS_WS_CLIENT_H_ diff --git a/src/xenia/dbg/ws_listener.cc b/src/xenia/debug/protocols/ws/ws_protocol.cc similarity index 66% rename from src/xenia/dbg/ws_listener.cc rename to src/xenia/debug/protocols/ws/ws_protocol.cc index c07bb8bf0..24ae269e2 100644 --- a/src/xenia/dbg/ws_listener.cc +++ b/src/xenia/debug/protocols/ws/ws_protocol.cc @@ -7,28 +7,38 @@ ****************************************************************************** */ -#include +#include -#include +#include + +#include using namespace xe; -using namespace xe::dbg; +using namespace xe::debug; +using namespace xe::debug::protocols::ws; -WsListener::WsListener(Debugger* debugger, uint32_t port) : - Listener(debugger), - port_(port) { +DEFINE_int32(ws_debug_port, 6200, + "Remote debugging port for ws:// connections."); + +WSProtocol::WSProtocol(DebugServer* debug_server) : + Protocol(debug_server) { + port_ = FLAGS_ws_debug_port; } -WsListener::~WsListener() { +WSProtocol::~WSProtocol() { if (socket_id_) { xe_socket_close(socket_id_); } } -int WsListener::Setup() { +int WSProtocol::Setup() { + if (port_ == 0 || port_ == -1) { + return 0; + } + xe_socket_init(); socket_id_ = xe_socket_create_tcp(); @@ -53,18 +63,22 @@ int WsListener::Setup() { return 0; } -int WsListener::WaitForClient() { +int WSProtocol::WaitForClient() { + if (!socket_id_) { + return 1; + } + // Accept the first connection we get. xe_socket_connection_t client_info; if (xe_socket_accept(socket_id_, &client_info)) { return 1; } - XELOGI("Debugger connected from %s", client_info.addr); + XELOGI("WS debugger connected from %s", client_info.addr); // Create the client object. // Note that the client will delete itself when done. - WsClient* client = new WsClient(debugger_, client_info.socket); + WSClient* client = new WSClient(debug_server_, client_info.socket); if (client->Setup()) { // Client failed to setup - abort. return 1; diff --git a/src/xenia/dbg/ws_listener.h b/src/xenia/debug/protocols/ws/ws_protocol.h similarity index 61% rename from src/xenia/dbg/ws_listener.h rename to src/xenia/debug/protocols/ws/ws_protocol.h index c4c7df6d6..d21d33338 100644 --- a/src/xenia/dbg/ws_listener.h +++ b/src/xenia/debug/protocols/ws/ws_protocol.h @@ -7,26 +7,28 @@ ****************************************************************************** */ -#ifndef XENIA_DBG_WS_LISTENER_H_ -#define XENIA_DBG_WS_LISTENER_H_ +#ifndef XENIA_DEBUG_PROTOCOLS_WS_WS_PROTOCOL_H_ +#define XENIA_DEBUG_PROTOCOLS_WS_WS_PROTOCOL_H_ #include #include -#include +#include + + +XEDECLARECLASS2(xe, debug, DebugServer); namespace xe { -namespace dbg { +namespace debug { +namespace protocols { +namespace ws { -class Debugger; - - -class WsListener : public Listener { +class WSProtocol : public Protocol { public: - WsListener(Debugger* debugger, uint32_t port); - virtual ~WsListener(); + WSProtocol(DebugServer* debug_server); + virtual ~WSProtocol(); virtual int Setup(); virtual int WaitForClient(); @@ -38,8 +40,10 @@ protected: }; -} // namespace dbg +} // namespace ws +} // namespace protocols +} // namespace debug } // namespace xe -#endif // XENIA_DBG_WS_LISTENER_H_ +#endif // XENIA_DEBUG_PROTOCOLS_WS_WS_PROTOCOL_H_ diff --git a/src/xenia/debug/sources.gypi b/src/xenia/debug/sources.gypi new file mode 100644 index 000000000..abaf1d60a --- /dev/null +++ b/src/xenia/debug/sources.gypi @@ -0,0 +1,15 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'sources': [ + 'debug_client.cc', + 'debug_client.h', + 'debug_server.cc', + 'debug_server.h', + 'protocol.cc', + 'protocol.h', + ], + + 'includes': [ + 'protocols/sources.gypi', + ], +} diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 919209076..6c77d2399 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -23,7 +23,7 @@ using namespace xe; using namespace xe::apu; using namespace xe::cpu; -using namespace xe::dbg; +using namespace xe::debug; using namespace xe::gpu; using namespace xe::hid; using namespace xe::kernel; @@ -34,7 +34,7 @@ using namespace xe::kernel::xboxkrnl::fs; Emulator::Emulator(const xechar_t* command_line) : memory_(0), - debugger_(0), + debug_server_(0), processor_(0), audio_system_(0), graphics_system_(0), input_system_(0), export_resolver_(0), file_system_(0), @@ -45,6 +45,11 @@ Emulator::Emulator(const xechar_t* command_line) : Emulator::~Emulator() { // Note that we delete things in the reverse order they were initialized. + // Disconnect all debug clients first. + if (debug_server_) { + debug_server_->Shutdown(); + } + delete xam_; delete xboxkrnl_; @@ -55,7 +60,7 @@ Emulator::~Emulator() { delete audio_system_; delete processor_; - delete debugger_; + delete debug_server_; delete export_resolver_; } @@ -74,8 +79,8 @@ X_STATUS Emulator::Setup() { XEEXPECTNOTNULL(export_resolver_); // Create the debugger. - debugger_ = new Debugger(this); - XEEXPECTNOTNULL(debugger_); + debug_server_ = new DebugServer(this); + XEEXPECTNOTNULL(debug_server_); // Initialize the CPU. processor_ = new Processor(this); @@ -115,7 +120,7 @@ X_STATUS Emulator::Setup() { // Prepare the debugger. // This may pause waiting for connections. - result = debugger_->Startup(); + result = debug_server_->Startup(); XEEXPECTZERO(result); return result; diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index c6fcbcfe8..5072317b6 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -18,7 +18,7 @@ XEDECLARECLASS1(xe, ExportResolver); XEDECLARECLASS2(xe, apu, AudioSystem); XEDECLARECLASS2(xe, cpu, Processor); -XEDECLARECLASS2(xe, dbg, Debugger); +XEDECLARECLASS2(xe, debug, DebugServer); XEDECLARECLASS2(xe, gpu, GraphicsSystem); XEDECLARECLASS2(xe, hid, InputSystem); XEDECLARECLASS3(xe, kernel, xam, XamModule); @@ -37,7 +37,7 @@ public: const xechar_t* command_line() const { return command_line_; } Memory* memory() const { return memory_; } - dbg::Debugger* debugger() const { return debugger_; } + debug::DebugServer* debug_server() const { return debug_server_; } cpu::Processor* processor() const { return processor_; } apu::AudioSystem* audio_system() const { return audio_system_; } @@ -57,7 +57,7 @@ private: xechar_t command_line_[XE_MAX_PATH]; Memory* memory_; - dbg::Debugger* debugger_; + debug::DebugServer* debug_server_; cpu::Processor* processor_; apu::AudioSystem* audio_system_; diff --git a/src/xenia/sources.gypi b/src/xenia/sources.gypi index 7e16616a3..775ff7943 100644 --- a/src/xenia/sources.gypi +++ b/src/xenia/sources.gypi @@ -29,7 +29,7 @@ 'apu/sources.gypi', 'core/sources.gypi', 'cpu/sources.gypi', - 'dbg/sources.gypi', + 'debug/sources.gypi', 'gpu/sources.gypi', 'hid/sources.gypi', 'kernel/sources.gypi', diff --git a/third_party/wslay b/third_party/wslay new file mode 160000 index 000000000..b3571f1ed --- /dev/null +++ b/third_party/wslay @@ -0,0 +1 @@ +Subproject commit b3571f1ed27093e910ab8675d0c8333e5a2818ea diff --git a/third_party/wslay.gypi b/third_party/wslay.gypi new file mode 100644 index 000000000..de99a01f1 --- /dev/null +++ b/third_party/wslay.gypi @@ -0,0 +1,59 @@ +# Copyright 2013 Ben Vanik. All Rights Reserved. +{ + 'targets': [ + { + 'target_name': 'wslay', + 'type': '<(library)', + + 'direct_dependent_settings': { + 'include_dirs': [ + 'wslay/lib/includes/', + ], + + 'defines': [ + 'WSLAY_VERSION=1', + ], + + # libraries: ws2_32 on windows + }, + + 'defines': [ + 'WSLAY_VERSION="1"', + ], + + 'conditions': [ + ['OS != "win"', { + 'defines': [ + 'HAVE_ARPA_INET_H=1', + 'HAVE_NETINET_IN_H=1', + ], + }], + ['OS == "win"', { + 'defines': [ + 'HAVE_WINSOCK2_H=1', + 'ssize_t=long long', + ], + }], + ], + + 'include_dirs': [ + 'wslay/lib/', + 'wslay/lib/includes/', + ], + + 'sources': [ + 'wslay/lib/includes/wslay/wslay.h', + 'wslay/lib/wslay_event.c', + 'wslay/lib/wslay_event.h', + 'wslay/lib/wslay_frame.c', + 'wslay/lib/wslay_frame.h', + 'wslay/lib/wslay_net.c', + 'wslay/lib/wslay_net.h', + 'wslay/lib/wslay_queue.c', + 'wslay/lib/wslay_queue.h', + 'wslay/lib/wslay_stack.c', + 'wslay/lib/wslay_stack.h', + ], + } + ] +} diff --git a/xenia.gyp b/xenia.gyp index 72799a3cb..db2e89f9e 100644 --- a/xenia.gyp +++ b/xenia.gyp @@ -6,6 +6,7 @@ 'third_party/beaengine.gypi', 'third_party/gflags.gypi', 'third_party/sparsehash.gypi', + 'third_party/wslay.gypi', ], 'default_configuration': 'release', @@ -248,12 +249,14 @@ 'asmjit', 'beaengine', 'gflags', + 'wslay', 'alloy', ], 'export_dependent_settings': [ 'asmjit', 'beaengine', 'gflags', + 'wslay', 'alloy', ],