mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-20 17:59:08 +02:00
Debugger: Route BreakpointSubscriber, DisasmSubscriber, and most of
CPUCoreSubscriber through the CPU thread Continues the pattern started with SteppingSubscriber: route breakpoint, disassembly, symbol, and register access through Core_RunOnCPUThread() instead of touching that state directly from the WebSocket handler thread. Two intentional exceptions, matching the reasoning already used for cpu.stepInto's "not currently stepping" branch: - cpu.stepping's Core_Break() call stays unqueued - it's what makes the CPU thread start reaching the queue drain point in the first place. - cpu.status stays unqueued - it's meant to be a cheap, frequently-pollable status check, and its "pc" field is already documented as inaccurate unless stepping. Matches how SteppingBroadcaster already reads the same state directly from the WebSocket thread. Where a handler's response doesn't depend on anything the queued lambda computed (plain add/remove endpoints), moved req.Respond() back out after the Core_RunOnCPUThread() call for readability - the JSON building and socket write happen later in Finish() regardless of where Respond() is called, so there's no thread-safety difference either way, just clarity about what actually needs to run on the CPU thread. memory.searchDisasm carries the same caveat flagged for memory.search: its scan range has no size cap, so if the CPU is stepping, a very large range will now block the CPU thread's own frame pump for the scan's duration rather than running unqueued on the WebSocket thread as before. Not fixed here - noted in a comment at the call site. Replaced remaining `auto` locals in these three files with concrete types. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
This commit is contained in:
1 parent
29825c1e61
commit
c32df3d931
3 files changed
+401
-307
No files matched your search
@@ -16,6 +16,7 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/Breakpoints.h"
|
||||
#include "Core/Debugger/DisassemblyManager.h"
|
||||
#include "Core/Debugger/SymbolMap.h"
|
||||
@@ -138,8 +139,12 @@ void WebSocketCPUBreakpointAdd(DebuggerRequest &req) {
|
||||
if (!params.Parse(req))
|
||||
return;
|
||||
|
||||
g_breakpoints.AddBreakPoint(params.address);
|
||||
params.Apply();
|
||||
// Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
Core_RunOnCPUThread([&] {
|
||||
g_breakpoints.AddBreakPoint(params.address);
|
||||
params.Apply();
|
||||
});
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -157,11 +162,19 @@ void WebSocketCPUBreakpointUpdate(DebuggerRequest &req) {
|
||||
WebSocketCPUBreakpointParams params;
|
||||
if (!params.Parse(req))
|
||||
return;
|
||||
bool enabled;
|
||||
if (!g_breakpoints.IsAddressBreakPoint(params.address, &enabled))
|
||||
return req.Fail("Breakpoint not found");
|
||||
|
||||
params.Apply();
|
||||
// Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
bool found = false;
|
||||
Core_RunOnCPUThread([&] {
|
||||
bool enabled;
|
||||
found = g_breakpoints.IsAddressBreakPoint(params.address, &enabled);
|
||||
if (found)
|
||||
params.Apply();
|
||||
});
|
||||
|
||||
if (!found)
|
||||
return req.Fail("Breakpoint not found");
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -180,7 +193,11 @@ void WebSocketCPUBreakpointRemove(DebuggerRequest &req) {
|
||||
if (!req.ParamU32("address", &address))
|
||||
return;
|
||||
|
||||
g_breakpoints.RemoveBreakPoint(address);
|
||||
// Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
Core_RunOnCPUThread([&] {
|
||||
g_breakpoints.RemoveBreakPoint(address);
|
||||
});
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -202,38 +219,42 @@ void WebSocketCPUBreakpointList(DebuggerRequest &req) {
|
||||
return req.Fail("CPU not started");
|
||||
}
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.pushArray("breakpoints");
|
||||
auto bps = g_breakpoints.GetBreakpoints();
|
||||
for (const auto &bp : bps) {
|
||||
if (bp.temporary)
|
||||
continue;
|
||||
// Route the breakpoint/symbol/disassembly reads to the CPU thread instead of poking at them directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
Core_RunOnCPUThread([&] {
|
||||
JsonWriter &json = req.Respond();
|
||||
json.pushArray("breakpoints");
|
||||
std::vector<BreakPoint> bps = g_breakpoints.GetBreakpoints();
|
||||
for (const BreakPoint &bp : bps) {
|
||||
if (bp.temporary)
|
||||
continue;
|
||||
|
||||
json.pushDict();
|
||||
json.writeUint("address", bp.addr);
|
||||
json.writeBool("enabled", bp.IsEnabled());
|
||||
json.writeBool("log", (bp.result & BREAK_ACTION_LOG) != 0);
|
||||
if (bp.hasCond)
|
||||
json.writeString("condition", bp.cond.expressionString);
|
||||
else
|
||||
json.writeNull("condition");
|
||||
if (!bp.logFormat.empty())
|
||||
json.writeString("logFormat", bp.logFormat);
|
||||
else
|
||||
json.writeNull("logFormat");
|
||||
std::string symbol = g_symbolMap->GetLabelString(bp.addr);
|
||||
if (symbol.empty())
|
||||
json.writeNull("symbol");
|
||||
else
|
||||
json.writeString("symbol", symbol);
|
||||
json.pushDict();
|
||||
json.writeUint("address", bp.addr);
|
||||
json.writeBool("enabled", bp.IsEnabled());
|
||||
json.writeBool("log", (bp.result & BREAK_ACTION_LOG) != 0);
|
||||
if (bp.hasCond)
|
||||
json.writeString("condition", bp.cond.expressionString);
|
||||
else
|
||||
json.writeNull("condition");
|
||||
if (!bp.logFormat.empty())
|
||||
json.writeString("logFormat", bp.logFormat);
|
||||
else
|
||||
json.writeNull("logFormat");
|
||||
std::string symbol = g_symbolMap->GetLabelString(bp.addr);
|
||||
if (symbol.empty())
|
||||
json.writeNull("symbol");
|
||||
else
|
||||
json.writeString("symbol", symbol);
|
||||
|
||||
DisassemblyLineInfo line;
|
||||
g_disassemblyManager.getLine(g_disassemblyManager.getStartAddress(bp.addr), true, line, currentDebugMIPS);
|
||||
json.writeString("code", line.name + " " + line.params);
|
||||
DisassemblyLineInfo line;
|
||||
g_disassemblyManager.getLine(g_disassemblyManager.getStartAddress(bp.addr), true, line, currentDebugMIPS);
|
||||
json.writeString("code", line.name + " " + line.params);
|
||||
|
||||
json.pop();
|
||||
}
|
||||
json.pop();
|
||||
}
|
||||
json.pop();
|
||||
});
|
||||
}
|
||||
|
||||
struct WebSocketMemoryBreakpointParams {
|
||||
@@ -361,8 +382,12 @@ void WebSocketMemoryBreakpointAdd(DebuggerRequest &req) {
|
||||
if (!params.Parse(req))
|
||||
return;
|
||||
|
||||
g_breakpoints.AddMemCheck(params.address, params.end, params.cond, params.Result(true));
|
||||
params.Apply();
|
||||
// Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
Core_RunOnCPUThread([&] {
|
||||
g_breakpoints.AddMemCheck(params.address, params.end, params.cond, params.Result(true));
|
||||
params.Apply();
|
||||
});
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -386,12 +411,20 @@ void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req) {
|
||||
if (!params.Parse(req))
|
||||
return;
|
||||
|
||||
MemCheck mc;
|
||||
if (!g_breakpoints.GetMemCheck(params.address, params.end, &mc))
|
||||
return req.Fail("Breakpoint not found");
|
||||
// Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
bool found = false;
|
||||
Core_RunOnCPUThread([&] {
|
||||
MemCheck mc;
|
||||
found = g_breakpoints.GetMemCheck(params.address, params.end, &mc);
|
||||
if (found) {
|
||||
g_breakpoints.ChangeMemCheck(params.address, params.end, params.cond, params.Result(true));
|
||||
params.Apply();
|
||||
}
|
||||
});
|
||||
|
||||
g_breakpoints.ChangeMemCheck(params.address, params.end, params.cond, params.Result(true));
|
||||
params.Apply();
|
||||
if (!found)
|
||||
return req.Fail("Breakpoint not found");
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -414,7 +447,11 @@ void WebSocketMemoryBreakpointRemove(DebuggerRequest &req) {
|
||||
if (!req.ParamU32("size", &size))
|
||||
return;
|
||||
|
||||
g_breakpoints.RemoveMemCheck(address, size == 0 ? 0 : address + size);
|
||||
// Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
Core_RunOnCPUThread([&] {
|
||||
g_breakpoints.RemoveMemCheck(address, size == 0 ? 0 : address + size);
|
||||
});
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
@@ -440,34 +477,38 @@ void WebSocketMemoryBreakpointList(DebuggerRequest &req) {
|
||||
return req.Fail("CPU not started");
|
||||
}
|
||||
|
||||
JsonWriter &json = req.Respond();
|
||||
json.pushArray("breakpoints");
|
||||
auto mcs = g_breakpoints.GetMemChecks();
|
||||
for (const auto &mc : mcs) {
|
||||
json.pushDict();
|
||||
json.writeUint("address", mc.start);
|
||||
json.writeUint("size", mc.end == 0 ? 0 : mc.end - mc.start);
|
||||
json.writeBool("enabled", mc.IsEnabled());
|
||||
json.writeBool("log", (mc.result & BREAK_ACTION_LOG) != 0);
|
||||
json.writeBool("read", (mc.cond & MEMCHECK_READ) != 0);
|
||||
json.writeBool("write", (mc.cond & MEMCHECK_WRITE) != 0);
|
||||
json.writeBool("change", (mc.cond & MEMCHECK_WRITE_ONCHANGE) != 0);
|
||||
json.writeUint("hits", mc.numHits);
|
||||
if (mc.hasCondition)
|
||||
json.writeString("condition", mc.condition.expressionString);
|
||||
else
|
||||
json.writeNull("condition");
|
||||
if (!mc.logFormat.empty())
|
||||
json.writeString("logFormat", mc.logFormat);
|
||||
else
|
||||
json.writeNull("logFormat");
|
||||
std::string symbol = g_symbolMap->GetLabelString(mc.start);
|
||||
if (symbol.empty())
|
||||
json.writeNull("symbol");
|
||||
else
|
||||
json.writeString("symbol", symbol);
|
||||
// Route the breakpoint/symbol reads to the CPU thread instead of poking at them directly
|
||||
// from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h.
|
||||
Core_RunOnCPUThread([&] {
|
||||
JsonWriter &json = req.Respond();
|
||||
json.pushArray("breakpoints");
|
||||
std::vector<MemCheck> mcs = g_breakpoints.GetMemChecks();
|
||||
for (const MemCheck &mc : mcs) {
|
||||
json.pushDict();
|
||||
json.writeUint("address", mc.start);
|
||||
json.writeUint("size", mc.end == 0 ? 0 : mc.end - mc.start);
|
||||
json.writeBool("enabled", mc.IsEnabled());
|
||||
json.writeBool("log", (mc.result & BREAK_ACTION_LOG) != 0);
|
||||
json.writeBool("read", (mc.cond & MEMCHECK_READ) != 0);
|
||||
json.writeBool("write", (mc.cond & MEMCHECK_WRITE) != 0);
|
||||
json.writeBool("change", (mc.cond & MEMCHECK_WRITE_ONCHANGE) != 0);
|
||||
json.writeUint("hits", mc.numHits);
|
||||
if (mc.hasCondition)
|
||||
json.writeString("condition", mc.condition.expressionString);
|
||||
else
|
||||
json.writeNull("condition");
|
||||
if (!mc.logFormat.empty())
|
||||
json.writeString("logFormat", mc.logFormat);
|
||||
else
|
||||
json.writeNull("logFormat");
|
||||
std::string symbol = g_symbolMap->GetLabelString(mc.start);
|
||||
if (symbol.empty())
|
||||
json.writeNull("symbol");
|
||||
else
|
||||
json.writeString("symbol", symbol);
|
||||
|
||||
json.pop();
|
||||
}
|
||||
json.pop();
|
||||
}
|
||||
json.pop();
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user