Permalink
Browse files

Merge pull request #11345 from unknownbrackets/debugger

Expose GE dump recording in WebSocket API
  • Loading branch information...
hrydgard committed Sep 1, 2018
2 parents d916373 + 3608626 commit 3d1e0e012bf9a9adc728da3bc063141c36fa6834
Showing with 489 additions and 245 deletions.
  1. +4 −0 CMakeLists.txt
  2. +2 −0 Core/Core.vcxproj
  3. +6 −0 Core/Core.vcxproj.filters
  4. +21 −23 Core/Debugger/WebSocket.cpp
  5. +1 −1 Core/Debugger/WebSocket/BreakpointSubscriber.cpp
  6. +1 −1 Core/Debugger/WebSocket/BreakpointSubscriber.h
  7. +1 −1 Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
  8. +1 −1 Core/Debugger/WebSocket/CPUCoreSubscriber.h
  9. +4 −7 Core/Debugger/WebSocket/DisasmSubscriber.cpp
  10. +1 −2 Core/Debugger/WebSocket/DisasmSubscriber.h
  11. +1 −1 Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
  12. +1 −1 Core/Debugger/WebSocket/GPUBufferSubscriber.h
  13. +107 −0 Core/Debugger/WebSocket/GPURecordSubscriber.cpp
  14. +22 −0 Core/Debugger/WebSocket/GPURecordSubscriber.h
  15. +1 −1 Core/Debugger/WebSocket/GameSubscriber.cpp
  16. +1 −1 Core/Debugger/WebSocket/GameSubscriber.h
  17. +1 −1 Core/Debugger/WebSocket/HLESubscriber.cpp
  18. +1 −1 Core/Debugger/WebSocket/HLESubscriber.h
  19. +3 −7 Core/Debugger/WebSocket/SteppingSubscriber.cpp
  20. +1 −2 Core/Debugger/WebSocket/SteppingSubscriber.h
  21. +8 −0 Core/Debugger/WebSocket/WebSocketUtils.h
  22. +2 −1 Core/HLE/sceDisplay.cpp
  23. +0 −7 Core/Host.h
  24. +4 −4 GPU/Common/TextureCacheCommon.cpp
  25. +2 −6 GPU/D3D11/DrawEngineD3D11.cpp
  26. +2 −2 GPU/D3D11/GPU_D3D11.cpp
  27. +100 −0 GPU/Debugger/Debugger.cpp
  28. +45 −0 GPU/Debugger/Debugger.h
  29. +36 −13 GPU/Debugger/Record.cpp
  30. +5 −1 GPU/Debugger/Record.h
  31. +7 −3 GPU/Debugger/Stepping.cpp
  32. +4 −4 GPU/Debugger/Stepping.h
  33. +2 −2 GPU/Directx9/DrawEngineDX9.cpp
  34. +2 −1 GPU/Directx9/GPU_DX9.cpp
  35. +2 −4 GPU/GLES/DrawEngineGLES.cpp
  36. +2 −2 GPU/GLES/GPU_GLES.cpp
  37. +2 −0 GPU/GPU.vcxproj
  38. +6 −0 GPU/GPU.vcxproj.filters
  39. +4 −5 GPU/GPUCommon.cpp
  40. +2 −2 GPU/Software/SoftGpu.cpp
  41. +2 −3 GPU/Software/TransformUnit.cpp
  42. +2 −2 GPU/Vulkan/DrawEngineVulkan.cpp
  43. +2 −3 GPU/Vulkan/GPU_Vulkan.cpp
  44. +6 −6 UI/GameSettingsScreen.cpp
  45. +2 −0 UWP/GPU_UWP/GPU_UWP.vcxproj
  46. +12 −2 UWP/GPU_UWP/GPU_UWP.vcxproj.filters
  47. +39 −101 Windows/GEDebugger/GEDebugger.cpp
  48. +2 −14 Windows/GEDebugger/GEDebugger.h
  49. +0 −5 Windows/WindowsHost.h
  50. +2 −0 android/jni/Android.mk
  51. +2 −1 libretro/Makefile.common
View
@@ -1357,6 +1357,8 @@ set(GPU_SOURCES
GPU/Common/SplineCommon.h
GPU/Debugger/Breakpoints.cpp
GPU/Debugger/Breakpoints.h
GPU/Debugger/Debugger.cpp
GPU/Debugger/Debugger.h
GPU/Debugger/Record.cpp
GPU/Debugger/Record.h
GPU/Debugger/Stepping.cpp
@@ -1430,6 +1432,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/GameSubscriber.h
Core/Debugger/WebSocket/GPUBufferSubscriber.cpp
Core/Debugger/WebSocket/GPUBufferSubscriber.h
Core/Debugger/WebSocket/GPURecordSubscriber.cpp
Core/Debugger/WebSocket/GPURecordSubscriber.h
Core/Debugger/WebSocket/HLESubscriber.cpp
Core/Debugger/WebSocket/HLESubscriber.h
Core/Debugger/WebSocket/LogBroadcaster.cpp
View
@@ -188,6 +188,7 @@
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\GameSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\GPUBufferSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\HLESubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
@@ -549,6 +550,7 @@
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GPUBufferSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\HLESubscriber.h" />
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />
@@ -734,6 +734,9 @@
<ClCompile Include="HLE\sceUsbAcc.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@@ -1361,6 +1364,9 @@
<ClInclude Include="HLE\sceUsbAcc.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
@@ -53,24 +53,20 @@
#include "Core/Debugger/WebSocket/DisasmSubscriber.h"
#include "Core/Debugger/WebSocket/GameSubscriber.h"
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
#include "Core/Debugger/WebSocket/HLESubscriber.h"
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"
typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map);
typedef void (*Subscribershutdown)(void *p);
struct SubscriberInfo {
SubscriberInit init;
Subscribershutdown shutdown;
};
static const std::vector<SubscriberInfo> subscribers({
{ &WebSocketBreakpointInit, nullptr },
{ &WebSocketCPUCoreInit, nullptr },
{ &WebSocketDisasmInit, &WebSocketDisasmShutdown },
{ &WebSocketGameInit, nullptr },
{ &WebSocketGPUBufferInit, nullptr },
{ &WebSocketHLEInit, nullptr },
{ &WebSocketSteppingInit, &WebSocketSteppingShutdown },
typedef DebuggerSubscriber *(*SubscriberInit)(DebuggerEventHandlerMap &map);
static const std::vector<SubscriberInit> subscribers({
&WebSocketBreakpointInit,
&WebSocketCPUCoreInit,
&WebSocketDisasmInit,
&WebSocketGameInit,
&WebSocketGPUBufferInit,
&WebSocketGPURecordInit,
&WebSocketHLEInit,
&WebSocketSteppingInit,
});
// To handle webserver restart, keep track of how many running.
@@ -132,10 +128,10 @@ void HandleDebuggerRequest(const http::Request &request) {
SteppingBroadcaster stepping;
std::unordered_map<std::string, DebuggerEventHandler> eventHandlers;
std::vector<void *> subscriberData;
for (auto info : subscribers) {
std::vector<DebuggerSubscriber *> subscriberData;
for (auto init : subscribers) {
std::lock_guard<std::mutex> guard(lifecycleLock);
subscriberData.push_back(info.init(eventHandlers));
subscriberData.push_back(init(eventHandlers));
}
// There's a tradeoff between responsiveness to incoming events, and polling for changes.
@@ -178,6 +174,12 @@ void HandleDebuggerRequest(const http::Request &request) {
game.Broadcast(ws);
stepping.Broadcast(ws);
for (size_t i = 0; i < subscribers.size(); ++i) {
if (subscriberData[i]) {
subscriberData[i]->Broadcast(ws);
}
}
if (stopRequested) {
ws->Close(net::WebSocketClose::GOING_AWAY);
}
@@ -188,11 +190,7 @@ void HandleDebuggerRequest(const http::Request &request) {
std::lock_guard<std::mutex> guard(lifecycleLock);
for (size_t i = 0; i < subscribers.size(); ++i) {
if (subscribers[i].shutdown) {
subscribers[i].shutdown(subscriberData[i]);
} else {
assert(!subscriberData[i]);
}
delete subscriberData[i];
}
delete ws;
@@ -23,7 +23,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MIPS/MIPSDebugInterface.h"
void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd;
map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate;
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);
void WebSocketCPUBreakpointAdd(DebuggerRequest &req);
void WebSocketCPUBreakpointUpdate(DebuggerRequest &req);
@@ -25,7 +25,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSDebugInterface.h"
void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.stepping"] = &WebSocketCPUStepping;
map["cpu.resume"] = &WebSocketCPUResume;
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketCPUCoreInit(DebuggerEventHandlerMap &map);
void WebSocketCPUStepping(DebuggerRequest &req);
void WebSocketCPUResume(DebuggerRequest &req);
@@ -28,11 +28,12 @@
#include "Core/MIPS/MIPSAsm.h"
#include "Core/MIPS/MIPSDebugInterface.h"
struct WebSocketDisasmState {
class WebSocketDisasmState : public DebuggerSubscriber {
public:
WebSocketDisasmState() {
disasm_.setCpu(currentDebugMIPS);
}
~WebSocketDisasmState() {
~WebSocketDisasmState() override {
disasm_.clear();
}
@@ -49,7 +50,7 @@ struct WebSocketDisasmState {
DisassemblyManager disasm_;
};
void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketDisasmState();
map["memory.base"] = std::bind(&WebSocketDisasmState::Base, p, std::placeholders::_1);
map["memory.disasm"] = std::bind(&WebSocketDisasmState::Disasm, p, std::placeholders::_1);
@@ -59,10 +60,6 @@ void *WebSocketDisasmInit(DebuggerEventHandlerMap &map) {
return p;
}
void WebSocketDisasmShutdown(void *p) {
delete static_cast<WebSocketDisasmState *>(p);
}
static DebugInterface *CPUFromRequest(DebuggerRequest &req) {
if (!req.HasParam("thread"))
return currentDebugMIPS;
@@ -19,5 +19,4 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
void WebSocketDisasmShutdown(void *p);
DebuggerSubscriber *WebSocketDisasmInit(DebuggerEventHandlerMap &map);
@@ -28,7 +28,7 @@
#include "Core/Screenshot.h"
#include "GPU/Debugger/Stepping.h"
void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["gpu.buffer.screenshot"] = &WebSocketGPUBufferScreenshot;
map["gpu.buffer.renderColor"] = &WebSocketGPUBufferRenderColor;
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketGPUBufferInit(DebuggerEventHandlerMap &map);
void WebSocketGPUBufferScreenshot(DebuggerRequest &req);
void WebSocketGPUBufferRenderColor(DebuggerRequest &req);
@@ -0,0 +1,107 @@
// 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 "data/base64.h"
#include "Common/FileUtil.h"
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/System.h"
#include "GPU/Debugger/Record.h"
struct WebSocketGPURecordState : public DebuggerSubscriber {
~WebSocketGPURecordState() override;
void Dump(DebuggerRequest &req);
void Broadcast(net::WebSocketServer *ws) override;
protected:
bool pending_ = false;
std::string lastTicket_;
std::string lastFilename_;
};
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketGPURecordState();
map["gpu.record.dump"] = std::bind(&WebSocketGPURecordState::Dump, p, std::placeholders::_1);
return p;
}
WebSocketGPURecordState::~WebSocketGPURecordState() {
// Clear the callback to hopefully avoid a crash.
if (pending_)
GPURecord::SetCallback(nullptr);
}
// Begin recording (gpu.record.dump)
//
// No parameters.
//
// Response (same event name):
// - uri: data: URI containing debug dump data.
//
// Note: recording may take a moment.
void WebSocketGPURecordState::Dump(DebuggerRequest &req) {
if (!PSP_IsInited())
return req.Fail("CPU not started");
if (!GPURecord::Activate())
return req.Fail("Recording already in progress");
pending_ = true;
GPURecord::SetCallback([=](const std::string &filename) {
lastFilename_ = filename;
pending_ = false;
});
const JsonNode *value = req.data.get("ticket");
lastTicket_ = value ? json_stringify(value) : "";
}
// This handles the asynchronous gpu.record.dump response.
void WebSocketGPURecordState::Broadcast(net::WebSocketServer *ws) {
if (!lastFilename_.empty()) {
FILE *fp = File::OpenCFile(lastFilename_, "rb");
if (!fp) {
lastFilename_.clear();
return;
}
// We write directly to the stream since this is a large chunk of data.
ws->AddFragment(false, R"({"event":"gpu.record.dump")");
if (!lastTicket_.empty()) {
ws->AddFragment(false, R"(,"ticket":)");
ws->AddFragment(false, lastTicket_);
}
ws->AddFragment(false, R"(,"uri":"data:application/octet-stream;base64,)");
// Divisible by 3 for base64 reasons.
const size_t BUF_SIZE = 16383;
std::vector<uint8_t> buf;
buf.resize(BUF_SIZE);
while (!feof(fp)) {
size_t bytes = fread(&buf[0], 1, BUF_SIZE, fp);
ws->AddFragment(false, Base64Encode(&buf[0], bytes));
}
fclose(fp);
ws->AddFragment(true, R"("})");
lastFilename_.clear();
lastTicket_.clear();
}
}
@@ -0,0 +1,22 @@
// 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/Debugger/WebSocket/WebSocketUtils.h"
DebuggerSubscriber *WebSocketGPURecordInit(DebuggerEventHandlerMap &map);
@@ -20,7 +20,7 @@
#include "Core/ELF/ParamSFO.h"
#include "Core/System.h"
void *WebSocketGameInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map) {
map["game.status"] = &WebSocketGameStatus;
map["version"] = &WebSocketVersion;
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketGameInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketGameInit(DebuggerEventHandlerMap &map);
void WebSocketGameStatus(DebuggerRequest &req);
void WebSocketVersion(DebuggerRequest &req);
@@ -27,7 +27,7 @@
#include "Core/MIPS/MIPSStackWalk.h"
#include "Core/HLE/sceKernelThread.h"
void *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
map["hle.thread.list"] = &WebSocketHLEThreadList;
map["hle.thread.wake"] = &WebSocketHLEThreadWake;
map["hle.thread.stop"] = &WebSocketHLEThreadStop;
@@ -19,7 +19,7 @@
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
void *WebSocketHLEInit(DebuggerEventHandlerMap &map);
DebuggerSubscriber *WebSocketHLEInit(DebuggerEventHandlerMap &map);
void WebSocketHLEThreadList(DebuggerRequest &req);
void WebSocketHLEThreadWake(DebuggerRequest &req);
Oops, something went wrong.

0 comments on commit 3d1e0e0

Please sign in to comment.