Skip to content

Commit

Permalink
Debugger: Organize WebSocket event handling.
Browse files Browse the repository at this point in the history
Just a starting point to organize it.  Trying to keep it simple.
  • Loading branch information
unknownbrackets committed Apr 14, 2018
1 parent e681379 commit 409d5b9
Show file tree
Hide file tree
Showing 12 changed files with 412 additions and 90 deletions.
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@
<ClCompile Include="..\ext\udis86\udis86.c" />
<ClCompile Include="AVIDump.cpp" />
<ClCompile Include="Debugger\WebSocket.cpp" />
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\SteppingBroadcaster.cpp" />
<ClCompile Include="FileSystems\BlobFileSystem.cpp" />
<ClCompile Include="HLE\KUBridge.cpp" />
<ClCompile Include="HLE\sceUsbCam.cpp" />
Expand Down Expand Up @@ -533,6 +536,10 @@
<ClInclude Include="..\ext\udis86\udis86.h" />
<ClInclude Include="AVIDump.h" />
<ClInclude Include="Debugger\WebSocket.h" />
<ClInclude Include="Debugger\WebSocket\Common.h" />
<ClInclude Include="Debugger\WebSocket\GameBroadcaster.h" />
<ClInclude Include="Debugger\WebSocket\LogBroadcaster.h" />
<ClInclude Include="Debugger\WebSocket\SteppingBroadcaster.h" />
<ClInclude Include="FileSystems\BlobFileSystem.h" />
<ClInclude Include="HLE\KUBridge.h" />
<ClInclude Include="HLE\sceUsbCam.h" />
Expand Down
24 changes: 24 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
<Filter Include="MIPS\IR">
<UniqueIdentifier>{119ac973-e457-4025-9e1e-4fb34022ae23}</UniqueIdentifier>
</Filter>
<Filter Include="Debugger\WebSocket">
<UniqueIdentifier>{c21d9bb5-614d-451b-8c0b-3078b29122d8}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ELF\ElfReader.cpp">
Expand Down Expand Up @@ -692,6 +695,15 @@
<ClCompile Include="Debugger\WebSocket.cpp">
<Filter>Debugger</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\SteppingBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1274,6 +1286,18 @@
<ClInclude Include="Debugger\WebSocket.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\LogBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\SteppingBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\GameBroadcaster.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\Common.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
Expand Down
130 changes: 40 additions & 90 deletions Core/Debugger/WebSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
#include <string>
#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<std::mutex> guard(lock_);
messages_[nextMessage_] = msg;
nextMessage_++;
if (nextMessage_ >= BUFFER_SIZE)
nextMessage_ -= BUFFER_SIZE;
count_++;
}

std::vector<LogMessage> GetMessages() {
std::lock_guard<std::mutex> 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<LogMessage> 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<std::string, DebuggerEventHandler> 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<uint8_t> &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;
}
42 changes: 42 additions & 0 deletions Core/Debugger/WebSocket/Common.h
Original file line number Diff line number Diff line change
@@ -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 <string>
#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();
}
};
38 changes: 38 additions & 0 deletions Core/Debugger/WebSocket/GameBroadcaster.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
36 changes: 36 additions & 0 deletions Core/Debugger/WebSocket/GameBroadcaster.h
Original file line number Diff line number Diff line change
@@ -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_;
};
Loading

0 comments on commit 409d5b9

Please sign in to comment.