mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-08-31 09:45:24 +02:00
GameBroadcaster and SteppingBroadcaster ran per connection on the WebSocket thread, so every connected debugger was reading pc, the tick count, coreState, the UI state and the param SFO out from under the CPU thread on every lap of its loop - up to 1000 times a second in high-activity mode. Inverted: the CPU thread notices the transition once in WebSocketDebuggerTick(), formats the event there, and drops it into a per-connection mailbox that the connection's own thread drains and sends. Same events, same conditions, no core reads off the CPU thread, and no per-connection polling of emulator state. The tick hangs off Core_ProcessCPUQueue(), the one function reliably called on the CPU thread both in game (Core_RunLoopUntil) and at the menu (NativeFrame). It polls even with nothing connected, since skipping would let the "previous state" go stale and fire a bogus event at whoever connects next. Behavior preserved including the awkward bit: a debugger that connects while the CPU is already stopped still gets an immediate cpu.stepping, which used to fall out of SteppingBroadcaster's counter starting at 0. That's now an explicit per-connection prime instead of an accident. Part of removing the WebSocket debugger's lifecycleLock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
89 lines
3.3 KiB
C++
89 lines
3.3 KiB
C++
// 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/Core.h"
|
|
#include "Core/CoreTiming.h"
|
|
#include "Core/Debugger/WebSocket/SteppingBroadcaster.h"
|
|
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
|
#include "Core/MIPS/MIPS.h"
|
|
#include "Core/System.h"
|
|
|
|
struct CPUSteppingEvent {
|
|
CPUSteppingEvent(const SteppingReason &reason) : reason_(reason) {
|
|
}
|
|
|
|
operator std::string() {
|
|
JsonWriter j;
|
|
j.begin();
|
|
j.writeString("event", "cpu.stepping");
|
|
j.writeUint("pc", currentMIPS->pc);
|
|
// A double ought to be good enough for a 156 day debug session.
|
|
j.writeFloat("ticks", CoreTiming::GetTicks(currentMIPS));
|
|
if (reason_.reason != BreakReason::None) {
|
|
j.writeString("reason", BreakReasonToString(reason_.reason));
|
|
j.writeUint("relatedAddress", reason_.relatedAddress);
|
|
}
|
|
j.end();
|
|
return j.str();
|
|
}
|
|
|
|
private:
|
|
const SteppingReason &reason_;
|
|
};
|
|
|
|
// CPU has begun stepping (cpu.stepping)
|
|
//
|
|
// Sent unexpectedly with these properties:
|
|
// - pc: number value of PC register (inaccurate unless stepping.)
|
|
// - ticks: number of CPU cycles into emulation.
|
|
// - reason: an optional property, if present, it's equal to the value submitted to Core_EnableStepping ("jit.branchdebug", "savestate.load", "ui.lost_focus", etc.)
|
|
// - relatedAddress: an optional address (often zero, but it can be a value of PC saved at some point, a related memory address, etc.), always present if 'reason' is present
|
|
|
|
// CPU has resumed from stepping (cpu.resume)
|
|
//
|
|
// Sent unexpectedly with no other properties.
|
|
// Tracked globally rather than per connection: this runs on the CPU thread, which owns the state
|
|
// being read, and the resulting event is then handed to every connected debugger.
|
|
static CoreState g_prevState = CORE_POWERDOWN;
|
|
static int g_lastCounter = 0;
|
|
|
|
std::string SteppingBroadcaster::PollChange() {
|
|
if (PSP_GetBootState() != BootState::Complete) {
|
|
g_lastCounter = -1;
|
|
g_prevState = CORE_POWERDOWN;
|
|
return std::string();
|
|
}
|
|
|
|
std::string result;
|
|
const int steppingCounter = Core_GetSteppingCounter();
|
|
// We ignore CORE_POWERDOWN as a stepping state.
|
|
if (coreState == CORE_STEPPING_CPU && steppingCounter != g_lastCounter) {
|
|
result = CPUSteppingEvent(Core_GetSteppingReason());
|
|
} else if (g_prevState == CORE_STEPPING_CPU && coreState != CORE_STEPPING_CPU && Core_IsActive()) {
|
|
result = R"({"event":"cpu.resume"})";
|
|
}
|
|
g_lastCounter = steppingCounter;
|
|
g_prevState = coreState;
|
|
return result;
|
|
}
|
|
|
|
std::string SteppingBroadcaster::CurrentState() {
|
|
if (PSP_GetBootState() != BootState::Complete || coreState != CORE_STEPPING_CPU)
|
|
return std::string();
|
|
return CPUSteppingEvent(Core_GetSteppingReason());
|
|
}
|