mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Replay and GPUDisasm subscribers: read core state on the CPU thread
replay.* mutates and reads replay/RTC state that the CPU thread consumes as it runs, and gpu.displaylist.disasm reads through the gpu pointer and emulated memory. Both did it straight from the WebSocket thread. Route through Core_RunOnCPUThread(), and fold the "is a game running" checks into the same trip rather than testing before it, where the answer could already be stale. Part of removing the WebSocket debugger's lifecycleLock. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
1 parent
a59b5c6dd5
commit
3a669891dc
2 files changed
+66
-26
No files matched your search
@@ -20,6 +20,7 @@
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Debugger/WebSocket/GPUDisasmSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "GPU/GPU.h"
|
||||
#include "GPU/GPUCommon.h"
|
||||
@@ -58,13 +59,6 @@ DebuggerSubscriber *WebSocketGPUDisasmInit(DebuggerEventHandlerMap &map) {
|
||||
// "AAAAAAAA desc" - meant for skimming a display list by eye instead of parsing full JSON,
|
||||
// same idea as memory.disasm's own compact mode.
|
||||
void WebSocketGPUDisplayListDisasm(DebuggerRequest &req) {
|
||||
if (!gpu) {
|
||||
return req.Fail("No GPU active (game not booted?)");
|
||||
}
|
||||
if (!Memory::IsActive()) {
|
||||
return req.Fail("Memory not active");
|
||||
}
|
||||
|
||||
// Mirrors memory.disasm's own limit - keeps a client typo (e.g. count=0xFFFFFFFF) from
|
||||
// blocking the debugger connection for an unreasonable amount of time.
|
||||
static const uint32_t MAX_RANGE = 10000;
|
||||
@@ -89,7 +83,17 @@ void WebSocketGPUDisplayListDisasm(DebuggerRequest &req) {
|
||||
if (!req.ParamBool("compact", &compact, DebuggerParamType::OPTIONAL))
|
||||
return;
|
||||
|
||||
std::vector<GPUDebugOp> ops = gpu->DisassembleOpRange(start, end);
|
||||
// gpu and the memory it disassembles from are CPU-thread-owned, so do the read over there
|
||||
// rather than from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
bool active = false;
|
||||
std::vector<GPUDebugOp> ops;
|
||||
Core_RunOnCPUThread([&] {
|
||||
active = gpu && Memory::IsActive();
|
||||
if (active)
|
||||
ops = gpu->DisassembleOpRange(start, end);
|
||||
});
|
||||
if (!active)
|
||||
return req.Fail("No GPU active (game not booted?)");
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.pushArray("lines");
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Common/Data/Encoding/Base64.h"
|
||||
#include "Common/Swap.h"
|
||||
#include "Core/HLE/sceRtc.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Replay.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Debugger/WebSocket/ReplaySubscriber.h"
|
||||
@@ -45,7 +46,12 @@ DebuggerSubscriber *WebSocketReplayInit(DebuggerEventHandlerMap &map) {
|
||||
//
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketReplayBegin(DebuggerRequest &req) {
|
||||
ReplayBeginSave();
|
||||
// Replay state is consumed by the CPU thread as it runs, so mutate it over there rather than
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h. Same for the rest
|
||||
// of the handlers below.
|
||||
Core_RunOnCPUThread([&] {
|
||||
ReplayBeginSave();
|
||||
});
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -57,7 +63,9 @@ void WebSocketReplayBegin(DebuggerRequest &req) {
|
||||
//
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketReplayAbort(DebuggerRequest &req) {
|
||||
ReplayAbort();
|
||||
Core_RunOnCPUThread([&] {
|
||||
ReplayAbort();
|
||||
});
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -71,11 +79,15 @@ void WebSocketReplayAbort(DebuggerRequest &req) {
|
||||
// - version: unsigned integer, version number of data.
|
||||
// - base64: base64 encode of binary data.
|
||||
void WebSocketReplayFlush(DebuggerRequest &req) {
|
||||
if (PSP_GetBootState() != BootState::Complete)
|
||||
return req.Fail("Game not running");
|
||||
|
||||
bool running = false;
|
||||
std::vector<uint8_t> data;
|
||||
ReplayFlushBlob(&data);
|
||||
Core_RunOnCPUThread([&] {
|
||||
running = PSP_GetBootState() == BootState::Complete;
|
||||
if (running)
|
||||
ReplayFlushBlob(&data);
|
||||
});
|
||||
if (!running)
|
||||
return req.Fail("Game not running");
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.writeInt("version", ReplayVersion());
|
||||
@@ -90,9 +102,6 @@ void WebSocketReplayFlush(DebuggerRequest &req) {
|
||||
//
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketReplayExecute(DebuggerRequest &req) {
|
||||
if (PSP_GetBootState() != BootState::Complete)
|
||||
return req.Fail("Game not running");
|
||||
|
||||
uint32_t version = -1;
|
||||
if (!req.ParamU32("version", &version))
|
||||
return;
|
||||
@@ -101,7 +110,16 @@ void WebSocketReplayExecute(DebuggerRequest &req) {
|
||||
return;
|
||||
|
||||
std::vector<uint8_t> data = Base64Decode(encoded.data(), encoded.size());
|
||||
if (!ReplayExecuteBlob(version, data))
|
||||
bool running = false;
|
||||
bool ok = false;
|
||||
Core_RunOnCPUThread([&] {
|
||||
running = PSP_GetBootState() == BootState::Complete;
|
||||
if (running)
|
||||
ok = ReplayExecuteBlob(version, data);
|
||||
});
|
||||
if (!running)
|
||||
return req.Fail("Game not running");
|
||||
if (!ok)
|
||||
return req.Fail("Invalid replay data or version");
|
||||
|
||||
req.Respond();
|
||||
@@ -115,9 +133,15 @@ void WebSocketReplayExecute(DebuggerRequest &req) {
|
||||
// - executing: boolean if a replay is being executed.
|
||||
// - saving: boolean if a replay is being recorded.
|
||||
void WebSocketReplayStatus(DebuggerRequest &req) {
|
||||
bool executing = false, saving = false;
|
||||
Core_RunOnCPUThread([&] {
|
||||
executing = ReplayIsExecuting();
|
||||
saving = ReplayIsSaving();
|
||||
});
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.writeBool("executing", ReplayIsExecuting());
|
||||
json.writeBool("saving", ReplayIsSaving());
|
||||
json.writeBool("executing", executing);
|
||||
json.writeBool("saving", saving);
|
||||
}
|
||||
|
||||
// Get the base RTC (real time clock) time for replay data (replay.time.get)
|
||||
@@ -130,11 +154,18 @@ void WebSocketReplayStatus(DebuggerRequest &req) {
|
||||
// Response (same event name):
|
||||
// - value: unsigned integer, may have more than 32 integer bits.
|
||||
void WebSocketReplayTimeGet(DebuggerRequest &req) {
|
||||
if (PSP_GetBootState() != BootState::Complete)
|
||||
bool running = false;
|
||||
uint32_t baseTime = 0;
|
||||
Core_RunOnCPUThread([&] {
|
||||
running = PSP_GetBootState() == BootState::Complete;
|
||||
if (running)
|
||||
baseTime = RtcBaseTime();
|
||||
});
|
||||
if (!running)
|
||||
return req.Fail("Game not running");
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.writeUint("value", RtcBaseTime());
|
||||
json.writeUint("value", baseTime);
|
||||
}
|
||||
|
||||
// Overwrite the base RTC time (replay.time.set)
|
||||
@@ -144,14 +175,19 @@ void WebSocketReplayTimeGet(DebuggerRequest &req) {
|
||||
//
|
||||
// Response (same event name) with no extra data.
|
||||
void WebSocketReplayTimeSet(DebuggerRequest &req) {
|
||||
if (PSP_GetBootState() != BootState::Complete)
|
||||
return req.Fail("Game not running");
|
||||
|
||||
uint32_t value;
|
||||
if (!req.ParamU32("value", &value, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RtcSetBaseTime((int32_t)value);
|
||||
bool running = false;
|
||||
Core_RunOnCPUThread([&] {
|
||||
running = PSP_GetBootState() == BootState::Complete;
|
||||
if (running)
|
||||
RtcSetBaseTime((int32_t)value);
|
||||
});
|
||||
if (!running)
|
||||
return req.Fail("Game not running");
|
||||
|
||||
req.Respond();
|
||||
}
|
||||
Reference in new issue
Block a user