diff --git a/docs/debugger.md b/docs/debugger.md new file mode 100644 index 000000000..dc1a29802 --- /dev/null +++ b/docs/debugger.md @@ -0,0 +1,37 @@ +# Debugger + +## Protocol + +Framed messages: + +``` +[4b type] (0=request, 1=notification, etc) +[4b content source id] +[4b request/response id] +[4b size] +[payload] +``` + +## Content Sources + +### `xe::dbg::sources::MemorySource` + +[ ] Paged view into memory +[ ] Search operations +[ ] Live streaming updates +[ ] Writes + +### `xe::dbg::sources::ProcessorSource` + +[ ] Thread list +[ ] State read/write (per thread) +[ ] Modules +[ ] Statistics +[ ] Basic control (pause/resume) +[ ] Breakpoints/checkpoints/traces +[ ] Trace stream + +### `xe::dbg::sources::ModuleSource` + +[ ] Paged view into all symbols +[ ] Get function contents (data, disasm, llvm, x86, etc) diff --git a/include/xenia/dbg/client.h b/include/xenia/dbg/client.h index e031c2bbc..5939468cc 100644 --- a/include/xenia/dbg/client.h +++ b/include/xenia/dbg/client.h @@ -18,17 +18,22 @@ namespace xe { namespace dbg { +class Debugger; + + class Client { public: - Client(); + Client(Debugger* debugger); virtual ~Client(); virtual int Setup() = 0; + void OnMessage(const uint8_t* data, size_t length); void Write(uint8_t* buffer, size_t length); virtual void Write(uint8_t** buffers, size_t* lengths, size_t count) = 0; protected: + Debugger* debugger_; }; diff --git a/include/xenia/dbg/content_source.h b/include/xenia/dbg/content_source.h index 3a6e8821a..5a879823e 100644 --- a/include/xenia/dbg/content_source.h +++ b/include/xenia/dbg/content_source.h @@ -22,20 +22,17 @@ namespace dbg { class ContentSource { public: - enum Type { - kTypeIndexed, - }; - - ContentSource(Type type); + ContentSource(Debugger* debugger, uint32_t source_id); virtual ~ContentSource(); - Type type(); + uint32_t source_id(); - virtual int DispatchRequest(Client* client, - const uint8_t* data, size_t length) = 0; + virtual int Dispatch(Client* client, uint8_t type, uint32_t request_id, + const uint8_t* data, size_t length) = 0; protected: - Type type_; + Debugger* debugger_; + uint32_t source_id_; }; diff --git a/include/xenia/dbg/debugger.h b/include/xenia/dbg/debugger.h index a9d75ad63..dbe7da236 100644 --- a/include/xenia/dbg/debugger.h +++ b/include/xenia/dbg/debugger.h @@ -14,6 +14,7 @@ #include #include +#include namespace xe { @@ -30,20 +31,24 @@ public: Debugger(xe_pal_ref pal); virtual ~Debugger(); - void RegisterContentSource(std::string& name, ContentSource* content_source); + void RegisterContentSource(ContentSource* content_source); int Startup(); + void Broadcast(uint32_t source_id, const uint8_t* data, size_t length); + private: - int DispatchRequest(Client* client, const char* source_name, - const uint8_t* data, size_t length); + void AddClient(Client* client); + void RemoveClient(Client* client); + int Dispatch(Client* client, const uint8_t* data, size_t length); friend class Client; private: xe_pal_ref pal_; auto_ptr listener_; - std::map content_sources_; + std::vector clients_; + std::map content_sources_; }; diff --git a/include/xenia/dbg/indexed_content_source.h b/include/xenia/dbg/indexed_content_source.h deleted file mode 100644 index be1d27709..000000000 --- a/include/xenia/dbg/indexed_content_source.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - ****************************************************************************** - * 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_KERNEL_DBG_INDEXED_CONTENT_SOURCE_H_ -#define XENIA_KERNEL_DBG_INDEXED_CONTENT_SOURCE_H_ - -#include -#include - -#include - - -namespace xe { -namespace dbg { - - -class Client; - - -class IndexedContentSource : public ContentSource { -public: - IndexedContentSource(); - virtual ~IndexedContentSource(); - - int DispatchRequest(Client* client, const uint8_t* data, size_t length); - -protected: - virtual int RequestIndex(Client* client, uint64_t req_id) = 0; - virtual int RequestRange(Client* client, uint64_t req_id, - uint64_t start_index, uint64_t end_index) = 0; - virtual int RequestEntry(Client* client, uint64_t req_id, uint64_t index) = 0; -}; - - -} // namespace dbg -} // namespace xe - - -#endif // XENIA_KERNEL_DBG_INDEXED_CONTENT_SOURCE_H_ diff --git a/src/dbg/client.cc b/src/dbg/client.cc index 813dea104..32c109ebb 100644 --- a/src/dbg/client.cc +++ b/src/dbg/client.cc @@ -9,15 +9,24 @@ #include +#include + using namespace xe; using namespace xe::dbg; -Client::Client() { +Client::Client(Debugger* debugger) : + debugger_(debugger) { + debugger_->AddClient(this); } Client::~Client() { + debugger_->RemoveClient(this); +} + +void Client::OnMessage(const uint8_t* data, size_t length) { + debugger_->Dispatch(this, data, length); } void Client::Write(uint8_t* buffer, size_t length) { diff --git a/src/dbg/content_source.cc b/src/dbg/content_source.cc index 61af4e2e6..01eb86dcd 100644 --- a/src/dbg/content_source.cc +++ b/src/dbg/content_source.cc @@ -14,14 +14,13 @@ using namespace xe; using namespace xe::dbg; -ContentSource::ContentSource(Type type) : - type_(type) { +ContentSource::ContentSource(Debugger* debugger, uint32_t source_id) : + debugger_(debugger), source_id_(source_id) { } ContentSource::~ContentSource() { } - -ContentSource::Type ContentSource::type() { - return type_; +uint32_t ContentSource::source_id() { + return source_id_; } diff --git a/src/dbg/debugger.cc b/src/dbg/debugger.cc index 502432cb8..bb908432d 100644 --- a/src/dbg/debugger.cc +++ b/src/dbg/debugger.cc @@ -30,13 +30,18 @@ Debugger::Debugger(xe_pal_ref pal) { pal_ = xe_pal_retain(pal); listener_ = auto_ptr(new WsListener( - pal_, FLAGS_remote_debug_port)); + this, pal_, FLAGS_remote_debug_port)); } Debugger::~Debugger() { - for (std::map::iterator it = content_sources_.begin(); - it != content_sources_.end(); ++it) { - xe_free(it->first); + for (std::vector::iterator it = clients_.begin(); + it != clients_.end(); ++it) { + delete *it; + } + clients_.clear(); + + for (std::map::iterator it = + content_sources_.begin(); it != content_sources_.end(); ++it) { delete it->second; } content_sources_.clear(); @@ -44,10 +49,9 @@ Debugger::~Debugger() { xe_pal_release(pal_); } -void Debugger::RegisterContentSource(std::string& name, - ContentSource* content_source) { - content_sources_.insert(std::pair( - xestrdupa(name.c_str()), content_source)); +void Debugger::RegisterContentSource(ContentSource* content_source) { + content_sources_.insert(std::pair( + content_source->source_id(), content_source)); } int Debugger::Startup() { @@ -69,12 +73,54 @@ int Debugger::Startup() { return 0; } -int Debugger::DispatchRequest(Client* client, const char* source_name, - const uint8_t* data, size_t length) { - std::map::iterator it = - content_sources_.find((char*)source_name); +void Debugger::Broadcast(uint32_t source_id, + const uint8_t* data, size_t length) { + uint32_t header[] = { + 0x00000001, + source_id, + 0, + length, + }; + uint8_t* buffers[] = { + (uint8_t*)header, + (uint8_t*)data, + }; + size_t lengths[] = { + sizeof(header), + length, + }; + for (std::vector::iterator it = clients_.begin(); + it != clients_.end(); ++it) { + Client* client = *it; + client->Write(buffers, lengths, XECOUNT(buffers)); + } +} + +void Debugger::AddClient(Client* client) { + clients_.push_back(client); +} + +void Debugger::RemoveClient(Client* client) { + for (std::vector::iterator it = clients_.begin(); + it != clients_.end(); ++it) { + if (*it == client) { + clients_.erase(it); + return; + } + } +} + +int Debugger::Dispatch(Client* client, const uint8_t* data, size_t length) { + uint32_t type = XEGETUINT32LE(data + 0); + uint32_t source_id = XEGETUINT32LE(data + 4); + uint32_t request_id = XEGETUINT32LE(data + 8); + uint32_t size = XEGETUINT32LE(data + 12); + + std::map::iterator it = + content_sources_.find(source_id); if (it == content_sources_.end()) { + XELOGW(XT("Content source %d not found, ignoring message"), source_id); return 1; } - return it->second->DispatchRequest(client, data, length); + return it->second->Dispatch(client, type, request_id, data + 16, size); } diff --git a/src/dbg/indexed_content_source.cc b/src/dbg/indexed_content_source.cc deleted file mode 100644 index c0fab8d89..000000000 --- a/src/dbg/indexed_content_source.cc +++ /dev/null @@ -1,28 +0,0 @@ -/** - ****************************************************************************** - * 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 - - -using namespace xe; -using namespace xe::dbg; - - -IndexedContentSource::IndexedContentSource() : - ContentSource(kTypeIndexed) { -} - -IndexedContentSource::~IndexedContentSource() { -} - -int IndexedContentSource::DispatchRequest(Client* client, - const uint8_t* data, size_t length) { - // - return 1; -} diff --git a/src/dbg/listener.cc b/src/dbg/listener.cc index 83686ea6f..35c1f520c 100644 --- a/src/dbg/listener.cc +++ b/src/dbg/listener.cc @@ -14,7 +14,8 @@ using namespace xe; using namespace xe::dbg; -Listener::Listener(xe_pal_ref pal) { +Listener::Listener(Debugger* debugger, xe_pal_ref pal) : + debugger_(debugger) { pal_ = xe_pal_retain(pal); } diff --git a/src/dbg/listener.h b/src/dbg/listener.h index 4ce466088..4c12931b1 100644 --- a/src/dbg/listener.h +++ b/src/dbg/listener.h @@ -18,16 +18,20 @@ namespace xe { namespace dbg { +class Debugger; + + class Listener { public: - Listener(xe_pal_ref pal); + Listener(Debugger* debugger, xe_pal_ref pal); virtual ~Listener(); virtual int Setup() = 0; virtual int WaitForClient() = 0; protected: - xe_pal_ref pal_; + Debugger* debugger_; + xe_pal_ref pal_; }; diff --git a/src/dbg/sources.gypi b/src/dbg/sources.gypi index c85b47ad4..c0addbc6e 100644 --- a/src/dbg/sources.gypi +++ b/src/dbg/sources.gypi @@ -4,7 +4,6 @@ 'client.cc', 'content_source.cc', 'debugger.cc', - 'indexed_content_source.cc', 'listener.cc', 'ws_client.cc', 'ws_listener.cc', diff --git a/src/dbg/ws_client.cc b/src/dbg/ws_client.cc index 65f2399cc..dea42a8f6 100644 --- a/src/dbg/ws_client.cc +++ b/src/dbg/ws_client.cc @@ -9,6 +9,8 @@ #include "dbg/ws_client.h" +#include + #include #include #include @@ -26,8 +28,8 @@ using namespace xe; using namespace xe::dbg; -WsClient::WsClient(int socket_id) : - Client(), +WsClient::WsClient(Debugger* debugger, int socket_id) : + Client(debugger), socket_id_(socket_id) { mutex_ = xe_mutex_alloc(1000); @@ -65,9 +67,24 @@ int WsClient::Setup() { setsockopt(socket_id_, IPPROTO_TCP, TCP_NODELAY, &opt_value, sizeof(opt_value)); - // launch thread/etc - EventThread(); + // TODO(benvanik): win32 support - put simple threads in PAL + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_t thread_handle; + int result_code = pthread_create( + &thread_handle, + &attr, + &StartCallbackPthreads, + this); + pthread_attr_destroy(&attr); + return result_code; +} + +void* WsClient::StartCallbackPthreads(void* param) { + WsClient* thread = reinterpret_cast(param); + thread->EventThread(); return 0; } @@ -126,27 +143,18 @@ void WsClientOnMsgCallback(wslay_event_context_ptr ctx, return; } + WsClient* client = reinterpret_cast(user_data); switch (arg->opcode) { case WSLAY_TEXT_FRAME: - // arg->msg, arg->msg_length + XELOGW(XT("Text frame ignored; use binary messages")); break; case WSLAY_BINARY_FRAME: - // arg->msg, arg->msg_length + client->OnMessage(arg->msg, arg->msg_length); break; default: // Unknown opcode - some frame stuff? break; } - - // TODO(benvanik): dispatch - - // WsClient* client = reinterpret_cast(user_data); - // char hello[] = "HELLO"; - // size_t len = sizeof(hello); - // uint8_t* bs[] = {(uint8_t*)&hello}; - // size_t lens[] = {len}; - // client->Write((uint8_t**)bs, lens, 1); - // printf("on msg %d, %ld\n", arg->opcode, arg->msg_length); } std::string EncodeBase64(const uint8_t* input, size_t length) { diff --git a/src/dbg/ws_client.h b/src/dbg/ws_client.h index c5730a77c..1246766cb 100644 --- a/src/dbg/ws_client.h +++ b/src/dbg/ws_client.h @@ -27,7 +27,7 @@ namespace dbg { class WsClient : public Client { public: - WsClient(int socket_id); + WsClient(Debugger* debugger, int socket_id); virtual ~WsClient(); int socket_id(); @@ -37,6 +37,8 @@ public: virtual void Write(uint8_t** buffers, size_t* lengths, size_t count); private: + static void* StartCallbackPthreads(void* param); + int PerformHandshake(); void EventThread(); diff --git a/src/dbg/ws_listener.cc b/src/dbg/ws_listener.cc index 1f200c4ed..8fa1edc2e 100644 --- a/src/dbg/ws_listener.cc +++ b/src/dbg/ws_listener.cc @@ -20,8 +20,8 @@ using namespace xe; using namespace xe::dbg; -WsListener::WsListener(xe_pal_ref pal, uint32_t port) : - Listener(pal), +WsListener::WsListener(Debugger* debugger, xe_pal_ref pal, uint32_t port) : + Listener(debugger, pal), port_(port) { } @@ -85,7 +85,7 @@ int WsListener::WaitForClient() { // Create the client object. // Note that the client will delete itself when done. - WsClient* client = new WsClient(client_socket_id); + WsClient* client = new WsClient(debugger_, client_socket_id); if (client->Setup()) { // Client failed to setup - abort. return 1; diff --git a/src/dbg/ws_listener.h b/src/dbg/ws_listener.h index 2e46d55bb..7195f00df 100644 --- a/src/dbg/ws_listener.h +++ b/src/dbg/ws_listener.h @@ -20,9 +20,12 @@ namespace xe { namespace dbg { +class Debugger; + + class WsListener : public Listener { public: - WsListener(xe_pal_ref pal, uint32_t port); + WsListener(Debugger* debugger, xe_pal_ref pal, uint32_t port); virtual ~WsListener(); virtual int Setup();