Skip to content

Commit

Permalink
Debugger: Serve a simple log listener WebSocket.
Browse files Browse the repository at this point in the history
Planning to add more functionality to it, and sharing the reporting port
is possibly not ideal - although it would make discovery easier.
  • Loading branch information
unknownbrackets committed Apr 13, 2018
1 parent 1dc1126 commit bb5bc71
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/SymbolMap.h
Core/Debugger/DisassemblyManager.cpp
Core/Debugger/DisassemblyManager.h
Core/Debugger/WebSocket.cpp
Core/Debugger/WebSocket.h
Core/Dialog/PSPDialog.cpp
Core/Dialog/PSPDialog.h
Core/Dialog/PSPGamedataInstallDialog.cpp
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
<ClCompile Include="..\ext\udis86\syn.c" />
<ClCompile Include="..\ext\udis86\udis86.c" />
<ClCompile Include="AVIDump.cpp" />
<ClCompile Include="Debugger\WebSocket.cpp" />
<ClCompile Include="FileSystems\BlobFileSystem.cpp" />
<ClCompile Include="HLE\KUBridge.cpp" />
<ClCompile Include="HLE\sceUsbCam.cpp" />
Expand Down Expand Up @@ -531,6 +532,7 @@
<ClInclude Include="..\ext\udis86\udint.h" />
<ClInclude Include="..\ext\udis86\udis86.h" />
<ClInclude Include="AVIDump.h" />
<ClInclude Include="Debugger\WebSocket.h" />
<ClInclude Include="FileSystems\BlobFileSystem.h" />
<ClInclude Include="HLE\KUBridge.h" />
<ClInclude Include="HLE\sceUsbCam.h" />
Expand Down
6 changes: 6 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,9 @@
<ClCompile Include="Replay.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1268,6 +1271,9 @@
<ClInclude Include="Replay.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
Expand Down
157 changes: 157 additions & 0 deletions Core/Debugger/WebSocket.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright (c) 2017- 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 <algorithm>
#include <string>
#include "net/websocket_server.h"
#include "Core/Debugger/WebSocket.h"
#include "Common/LogManager.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;
};

// TODO: Use a real json serializer?
static std::string JSONString(const std::string &str) {
size_t pos = 0;
std::string escaped;

auto update = [&](size_t current, size_t skip = 0) {
size_t end = current;
if (pos < end)
escaped += str.substr(pos, end - pos);
pos = end + skip;
};

for (size_t i = 0; i < str.size(); ++i) {
if (str[i] == '\\' || str[i] == '"' || str[i] == '/') {
update(i);
escaped += '\\';
} else if (str[i] == '\r') {
update(i, 1);
escaped += "\\r";
} else if (str[i] == '\n') {
update(i, 1);
escaped += "\\n";
} else if (str[i] == '\t') {
update(i, 1);
escaped += "\\t";
} else if (str[i] < 32) {
update(i, 1);
escaped += StringFromFormat("\\u%04x", str[i]);
}
}

if (pos != 0) {
update(str.size());
return escaped;
}
return str;
}

struct DebuggerLogEvent {
std::string header;
std::string message;
int level;
const char *channel;

operator std::string() {
static const char *const fmt = R"({"event":"log","header":"%s","message":"%s","level":%d,"channel":"%s"})";
return StringFromFormat(fmt, JSONString(header).c_str(), JSONString(message).c_str(), level, JSONString(channel).c_str());
}
};

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);

// TODO: Handle incoming messages.
ws->SetTextHandler([&](const std::string &t) {
ws->Send(R"({"event":"error","message":"Bad message","level":2})");
});
ws->SetBinaryHandler([&](const std::vector<uint8_t> &d) {
ws->Send(R"({"event":"error","message":"Bad message","level":2})");
});

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;
}

if (LogManager::GetInstance())
LogManager::GetInstance()->RemoveListener(logListener);
delete logListener;
delete ws;
}
24 changes: 24 additions & 0 deletions Core/Debugger/WebSocket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2017- 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 http {
class Request;
}

void HandleDebuggerRequest(const http::Request &request);
7 changes: 6 additions & 1 deletion UI/RemoteISOScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "Common/Common.h"
#include "Common/FileUtil.h"
#include "Core/Config.h"
#include "Core/Debugger/WebSocket.h"
#include "UI/RemoteISOScreen.h"

using namespace UI;
Expand Down Expand Up @@ -86,7 +87,7 @@ static void RegisterServer(int port) {
static void ExecuteServer() {
setCurrentThreadName("HTTPServer");

auto http = new http::Server(new threading::SameThreadExecutor());
auto http = new http::Server(new threading::NewThreadExecutor());

std::map<std::string, std::string> paths;
for (std::string filename : g_Config.recentIsos) {
Expand Down Expand Up @@ -161,6 +162,10 @@ static void ExecuteServer() {
http->RegisterHandler(pair.first.c_str(), handler);
}

// TODO: This isn't an ideal place to put this... should separate.
// But kinda nice to only have it listen on one port?
http->RegisterHandler("/debugger", &HandleDebuggerRequest);

if (!http->Listen(g_Config.iRemoteISOPort)) {
if (!http->Listen(0)) {
ERROR_LOG(FILESYS, "Unable to listen on any port");
Expand Down
1 change: 1 addition & 0 deletions android/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/TextureReplacer.cpp \
$(SRC)/Core/Debugger/Breakpoints.cpp \
$(SRC)/Core/Debugger/SymbolMap.cpp \
$(SRC)/Core/Debugger/WebSocket.cpp \
$(SRC)/Core/Dialog/PSPDialog.cpp \
$(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \
$(SRC)/Core/Dialog/PSPMsgDialog.cpp \
Expand Down

0 comments on commit bb5bc71

Please sign in to comment.