From 653ae78d7d518aff048b8344bfde10f540695880 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 14 Apr 2018 08:54:04 -0700 Subject: [PATCH] Debugger: Organize WebSocket event handling. Just a starting point to organize it. Trying to keep it simple. --- CMakeLists.txt | 7 + Core/Core.vcxproj | 7 + Core/Core.vcxproj.filters | 24 ++++ Core/Debugger/WebSocket.cpp | 130 ++++++------------ Core/Debugger/WebSocket/Common.h | 42 ++++++ Core/Debugger/WebSocket/GameBroadcaster.cpp | 38 +++++ Core/Debugger/WebSocket/GameBroadcaster.h | 36 +++++ Core/Debugger/WebSocket/LogBroadcaster.cpp | 112 +++++++++++++++ Core/Debugger/WebSocket/LogBroadcaster.h | 35 +++++ .../WebSocket/SteppingBroadcaster.cpp | 32 +++++ Core/Debugger/WebSocket/SteppingBroadcaster.h | 36 +++++ android/jni/Android.mk | 3 + 12 files changed, 412 insertions(+), 90 deletions(-) create mode 100644 Core/Debugger/WebSocket/Common.h create mode 100644 Core/Debugger/WebSocket/GameBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/GameBroadcaster.h create mode 100644 Core/Debugger/WebSocket/LogBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/LogBroadcaster.h create mode 100644 Core/Debugger/WebSocket/SteppingBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/SteppingBroadcaster.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d92c93b073b7..dcd1f542d2dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1430,6 +1430,13 @@ add_library(${CoreLibName} ${CoreLinkType} Core/Debugger/DisassemblyManager.h Core/Debugger/WebSocket.cpp Core/Debugger/WebSocket.h + Core/Debugger/WebSocket/Common.h + Core/Debugger/WebSocket/GameBroadcaster.cpp + Core/Debugger/WebSocket/GameBroadcaster.h + Core/Debugger/WebSocket/LogBroadcaster.cpp + Core/Debugger/WebSocket/LogBroadcaster.h + Core/Debugger/WebSocket/SteppingBroadcaster.cpp + Core/Debugger/WebSocket/SteppingBroadcaster.h Core/Dialog/PSPDialog.cpp Core/Dialog/PSPDialog.h Core/Dialog/PSPGamedataInstallDialog.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 672adc6c1dca..3954efcbb48b 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -185,6 +185,9 @@ + + + @@ -533,6 +536,10 @@ + + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 89a505a8f4ec..524d763eda4f 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -70,6 +70,9 @@ {119ac973-e457-4025-9e1e-4fb34022ae23} + + {c21d9bb5-614d-451b-8c0b-3078b29122d8} + @@ -692,6 +695,15 @@ Debugger + + Debugger\WebSocket + + + Debugger\WebSocket + + + Debugger\WebSocket + @@ -1274,6 +1286,18 @@ Debugger + + Debugger\WebSocket + + + Debugger\WebSocket + + + Debugger\WebSocket + + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 3d9cb95a6f1c..3225b8d9f961 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -15,113 +15,63 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include -#include -#include "json/json_writer.h" -#include "net/websocket_server.h" #include "Core/Debugger/WebSocket.h" -#include "Common/LogManager.h" +#include "Core/Debugger/WebSocket/Common.h" -// TODO: Move this to its own file? -class DebuggerLogListener : public LogListener { -public: - void Log(const LogMessage &msg) override { - std::lock_guard guard(lock_); - messages_[nextMessage_] = msg; - nextMessage_++; - if (nextMessage_ >= BUFFER_SIZE) - nextMessage_ -= BUFFER_SIZE; - count_++; - } - - std::vector GetMessages() { - std::lock_guard guard(lock_); - int splitPoint; - int readCount; - if (read_ + BUFFER_SIZE < count_) { - // We'll start with our oldest then. - splitPoint = nextMessage_; - readCount = Count(); - } else { - splitPoint = read_; - readCount = count_ - read_; - } - - read_ = count_; - - std::vector results; - int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE); - for (int i = splitPoint; i < splitEnd; ++i) { - results.push_back(messages_[i]); - readCount--; - } - for (int i = 0; i < readCount; ++i) { - results.push_back(messages_[i]); - } - - return results; - } - - int Count() const { - return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE; - } - -private: - enum { BUFFER_SIZE = 128 }; - LogMessage messages_[BUFFER_SIZE]; - std::mutex lock_; - int nextMessage_ = 0; - int count_ = 0; - int read_ = 0; -}; +#include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/Debugger/WebSocket/LogBroadcaster.h" +#include "Core/Debugger/WebSocket/SteppingBroadcaster.h" -struct DebuggerLogEvent { - std::string header; - std::string message; - int level; - const char *channel; +// TODO: Just for now, testing... +static void WebSocketTestEvent(net::WebSocketServer *ws, const json_value *data) { + ws->Send(DebuggerErrorEvent("Test message", LogTypes::LNOTICE)); +} - operator std::string() { - JsonWriter j; - j.begin(); - j.writeString("event", "log"); - j.writeString("header", header); - j.writeString("message", message); - j.writeInt("level", level); - j.writeString("channel", channel); - j.end(); - return j.str(); - } -}; +typedef void (*DebuggerEventHandler)(net::WebSocketServer *ws, const json_value *data); +static const std::unordered_map debuggerEvents({ + {"test", &WebSocketTestEvent}, +}); void HandleDebuggerRequest(const http::Request &request) { net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org"); if (!ws) return; - DebuggerLogListener *logListener = new DebuggerLogListener(); - if (LogManager::GetInstance()) - LogManager::GetInstance()->AddListener(logListener); + LogBroadcaster logger; + GameBroadcaster game; + SteppingBroadcaster stepping; - // TODO: Handle incoming messages. ws->SetTextHandler([&](const std::string &t) { - ws->Send(R"({"event":"error","message":"Bad message","level":2})"); + JsonReader reader(t.c_str(), t.size()); + if (!reader.ok()) { + ws->Send(DebuggerErrorEvent("Bad message: invalid JSON", LogTypes::LERROR)); + return; + } + + const json_value *root = reader.root(); + const json_value *eventNode = root->get("event", JSON_STRING); + if (!eventNode) { + ws->Send(DebuggerErrorEvent("Bad message: no event property", LogTypes::LERROR)); + return; + } + + auto eventFunc = debuggerEvents.find(eventNode->string_value); + if (eventFunc != debuggerEvents.end()) { + eventFunc->second(ws, root); + } else { + ws->Send(DebuggerErrorEvent("Bad message: unknown event", LogTypes::LERROR)); + } }); ws->SetBinaryHandler([&](const std::vector &d) { - ws->Send(R"({"event":"error","message":"Bad message","level":2})"); + ws->Send(DebuggerErrorEvent("Bad message", LogTypes::LERROR)); }); - while (ws->Process(0.1f)) { - auto messages = logListener->GetMessages(); - // TODO: Check for other conditions? - for (auto msg : messages) { - ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log}); - } - continue; + while (ws->Process(1.0f / 60.0f)) { + // These send events that aren't just responses to requests. + logger.Broadcast(ws); + game.Broadcast(ws); + stepping.Broadcast(ws); } - if (LogManager::GetInstance()) - LogManager::GetInstance()->RemoveListener(logListener); - delete logListener; delete ws; } diff --git a/Core/Debugger/WebSocket/Common.h b/Core/Debugger/WebSocket/Common.h new file mode 100644 index 000000000000..ed1213943858 --- /dev/null +++ b/Core/Debugger/WebSocket/Common.h @@ -0,0 +1,42 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include "ext/vjson/json.h" +#include "json/json_writer.h" +#include "net/websocket_server.h" +#include "Common/Log.h" + +struct DebuggerErrorEvent { + DebuggerErrorEvent(const std::string m, LogTypes::LOG_LEVELS l) : message(m), level(l) { + } + + std::string message; + LogTypes::LOG_LEVELS level; + + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", "error"); + j.writeString("message", message); + j.writeInt("level", level); + j.end(); + return j.str(); + } +}; diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp new file mode 100644 index 000000000000..86ee9df777e3 --- /dev/null +++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Debugger/WebSocket/Common.h" +#include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/System.h" + +void GameBroadcaster::Broadcast(net::WebSocketServer *ws) { + // TODO: This is ugly. Implement proper information instead. + // TODO: Should probably include info about which game, etc. + GlobalUIState state = GetUIState(); + if (prevState_ != state) { + if (state == UISTATE_PAUSEMENU) { + ws->Send(R"({"event":"game_pause"})"); + } else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) { + ws->Send(R"({"event":"game_resume"})"); + } else if (state == UISTATE_INGAME) { + ws->Send(R"({"event":"game_start"})"); + } else if (state == UISTATE_MENU) { + ws->Send(R"({"event":"game_quit"})"); + } + prevState_ = state; + } +} diff --git a/Core/Debugger/WebSocket/GameBroadcaster.h b/Core/Debugger/WebSocket/GameBroadcaster.h new file mode 100644 index 000000000000..63ef3eb346d9 --- /dev/null +++ b/Core/Debugger/WebSocket/GameBroadcaster.h @@ -0,0 +1,36 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/System.h" + +namespace net { +class WebSocketServer; +} + +struct GameBroadcaster { +public: + GameBroadcaster() { + prevState_ = GetUIState(); + } + + void Broadcast(net::WebSocketServer *ws); + +private: + GlobalUIState prevState_; +}; diff --git a/Core/Debugger/WebSocket/LogBroadcaster.cpp b/Core/Debugger/WebSocket/LogBroadcaster.cpp new file mode 100644 index 000000000000..c7f034267ab0 --- /dev/null +++ b/Core/Debugger/WebSocket/LogBroadcaster.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include "Common/LogManager.h" +#include "Core/Debugger/WebSocket/Common.h" +#include "Core/Debugger/WebSocket/LogBroadcaster.h" + +class DebuggerLogListener : public LogListener { +public: + void Log(const LogMessage &msg) override { + std::lock_guard guard(lock_); + messages_[nextMessage_] = msg; + nextMessage_++; + if (nextMessage_ >= BUFFER_SIZE) + nextMessage_ -= BUFFER_SIZE; + count_++; + } + + std::vector GetMessages() { + std::lock_guard guard(lock_); + int splitPoint; + int readCount; + if (read_ + BUFFER_SIZE < count_) { + // We'll start with our oldest then. + splitPoint = nextMessage_; + readCount = Count(); + } else { + splitPoint = read_; + readCount = count_ - read_; + } + + read_ = count_; + + std::vector results; + int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE); + for (int i = splitPoint; i < splitEnd; ++i) { + results.push_back(messages_[i]); + readCount--; + } + for (int i = 0; i < readCount; ++i) { + results.push_back(messages_[i]); + } + + return results; + } + + int Count() const { + return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE; + } + +private: + enum { BUFFER_SIZE = 1024 }; + LogMessage messages_[BUFFER_SIZE]; + std::mutex lock_; + int nextMessage_ = 0; + int count_ = 0; + int read_ = 0; +}; + +LogBroadcaster::LogBroadcaster() { + listener_ = new DebuggerLogListener(); + if (LogManager::GetInstance()) + LogManager::GetInstance()->AddListener(listener_); +} + +LogBroadcaster::~LogBroadcaster() { +if (LogManager::GetInstance()) + LogManager::GetInstance()->RemoveListener(listener_); + delete listener_; +} + +struct DebuggerLogEvent { + std::string header; + std::string message; + int level; + const char *channel; + + operator std::string() { + JsonWriter j; + j.begin(); + j.writeString("event", "log"); + j.writeString("header", header); + j.writeString("message", message); + j.writeInt("level", level); + j.writeString("channel", channel); + j.end(); + return j.str(); + } +}; + +void LogBroadcaster::Broadcast(net::WebSocketServer *ws) { + auto messages = listener_->GetMessages(); + for (auto msg : messages) { + ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log}); + } +} diff --git a/Core/Debugger/WebSocket/LogBroadcaster.h b/Core/Debugger/WebSocket/LogBroadcaster.h new file mode 100644 index 000000000000..13f26d4a066f --- /dev/null +++ b/Core/Debugger/WebSocket/LogBroadcaster.h @@ -0,0 +1,35 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +namespace net { +class WebSocketServer; +} + +class DebuggerLogListener; + +struct LogBroadcaster { +public: + LogBroadcaster(); + ~LogBroadcaster(); + + void Broadcast(net::WebSocketServer *ws); + +private: + DebuggerLogListener *listener_; +}; diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp new file mode 100644 index 000000000000..60405b4c485d --- /dev/null +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/Core.h" +#include "Core/Debugger/WebSocket/Common.h" +#include "Core/Debugger/WebSocket/SteppingBroadcaster.h" +#include "Core/System.h" + +void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) { + // TODO: This is somewhat primitive. It'd be nice to register a callback with Core instead? + if (coreState != prevState_) { + if (Core_IsStepping() && PSP_IsInited()) { + // TODO: Should send more data proactively. + ws->Send(R"({"event":"cpu_stepping"})"); + } + prevState_ = coreState; + } +} diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.h b/Core/Debugger/WebSocket/SteppingBroadcaster.h new file mode 100644 index 000000000000..df196d9ff8ae --- /dev/null +++ b/Core/Debugger/WebSocket/SteppingBroadcaster.h @@ -0,0 +1,36 @@ +// Copyright (c) 2018- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/Core.h" + +namespace net { +class WebSocketServer; +} + +struct SteppingBroadcaster { +public: + SteppingBroadcaster() { + prevState_ = coreState; + } + + void Broadcast(net::WebSocketServer *ws); + +private: + CoreState prevState_; +}; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 7e2d18ba935d..7f3227f9eb14 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -299,6 +299,9 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/Breakpoints.cpp \ $(SRC)/Core/Debugger/SymbolMap.cpp \ $(SRC)/Core/Debugger/WebSocket.cpp \ + $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ + $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ $(SRC)/Core/Dialog/PSPMsgDialog.cpp \