From f161e7f9f959f35c475669206685f2f1d5ef237e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 9 Sep 2026 08:06:50 -0600 Subject: [PATCH] Revert "Expose the MIPSTracer over the WebSocket debugger" This reverts commit ccdaa94f538d111605e08fe68b7306dd20c6a02d. --- Core/CMakeLists.txt | 2 - Core/Core.vcxproj | 2 - Core/Core.vcxproj.filters | 6 - Core/Debugger/WebSocket.cpp | 2 - .../WebSocket/MIPSTracerSubscriber.cpp | 214 ------------------ .../Debugger/WebSocket/MIPSTracerSubscriber.h | 28 --- UWP/CoreUWP/CoreUWP.vcxproj | 2 - UWP/CoreUWP/CoreUWP.vcxproj.filters | 2 - android/jni/Android.mk | 1 - docs/WebSocketDebugger.md | 1 - 10 files changed, 260 deletions(-) delete mode 100644 Core/Debugger/WebSocket/MIPSTracerSubscriber.cpp delete mode 100644 Core/Debugger/WebSocket/MIPSTracerSubscriber.h diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 1cdc681e25..a283e7c935 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -197,9 +197,7 @@ if(NOT LIBRETRO) Debugger/WebSocket/LogBroadcaster.h Debugger/WebSocket/LogConfigSubscriber.cpp Debugger/WebSocket/LogConfigSubscriber.h - Debugger/WebSocket/MIPSTracerSubscriber.cpp Debugger/WebSocket/MemoryInfoSubscriber.cpp - Debugger/WebSocket/MIPSTracerSubscriber.h Debugger/WebSocket/MemoryInfoSubscriber.h Debugger/WebSocket/MemorySubscriber.cpp Debugger/WebSocket/MemorySubscriber.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c5b395d226..b1cbe3f35e 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -460,7 +460,6 @@ - @@ -1007,7 +1006,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 7d94c8a878..8bb8462455 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -988,9 +988,6 @@ Debugger - - Debugger\WebSocket - Debugger\WebSocket @@ -2157,9 +2154,6 @@ Debugger - - Debugger\WebSocket - Debugger\WebSocket diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 9780b13950..e0d9ecf999 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -66,7 +66,6 @@ #include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/InputSubscriber.h" #include "Core/Debugger/WebSocket/LogConfigSubscriber.h" -#include "Core/Debugger/WebSocket/MIPSTracerSubscriber.h" #include "Core/Debugger/WebSocket/MemoryInfoSubscriber.h" #include "Core/Debugger/WebSocket/MemorySubscriber.h" #include "Core/Debugger/WebSocket/ReplaySubscriber.h" @@ -87,7 +86,6 @@ static const std::vector subscribers({ &WebSocketHLEInit, &WebSocketInputInit, &WebSocketLogConfigInit, - &WebSocketMIPSTracerInit, &WebSocketMemoryInfoInit, &WebSocketMemoryInit, &WebSocketReplayInit, diff --git a/Core/Debugger/WebSocket/MIPSTracerSubscriber.cpp b/Core/Debugger/WebSocket/MIPSTracerSubscriber.cpp deleted file mode 100644 index f335faeee7..0000000000 --- a/Core/Debugger/WebSocket/MIPSTracerSubscriber.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) 2026- 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 "Common/StringUtils.h" -#include "Core/Config.h" -#include "Core/ConfigValues.h" -#include "Core/Core.h" -#include "Core/Debugger/WebSocket/MIPSTracerSubscriber.h" -#include "Core/Debugger/WebSocket/WebSocketUtils.h" -#include "Core/MIPS/MIPS.h" -#include "Core/MIPS/MIPSTracer.h" -#include "Core/System.h" - -DebuggerSubscriber *WebSocketMIPSTracerInit(DebuggerEventHandlerMap &map) { - map["cpu.tracer.status"] = &WebSocketMIPSTracerStatus; - map["cpu.tracer.start"] = &WebSocketMIPSTracerStart; - map["cpu.tracer.stop"] = &WebSocketMIPSTracerStop; - map["cpu.tracer.flush"] = &WebSocketMIPSTracerFlush; - map["cpu.tracer.clear"] = &WebSocketMIPSTracerClear; - - return nullptr; -} - -// The tracer records blocks from the IR frontend (IRFrontend::DoJit inserts the LogIRBlock -// instruction), so only the two IR-based cores feed it. Under the plain interpreter or the -// native JIT it stays silent, which from a client's side is indistinguishable from "nothing -// executed" - so say so up front instead. -static bool TracerCoreIsIR() { - const CPUCore core = (CPUCore)g_Config.iCpuCore; - return core == CPUCore::IR_INTERPRETER || core == CPUCore::JIT_IR; -} - -static const char *CPUCoreName(CPUCore core) { - switch (core) { - case CPUCore::INTERPRETER: return "interpreter"; - case CPUCore::JIT: return "jit"; - case CPUCore::IR_INTERPRETER: return "ir"; - case CPUCore::JIT_IR: return "jit-ir"; - default: return "unknown"; - } -} - -static void WriteStatus(JsonWriter &json) { - json.writeBool("tracing", mipsTracer.tracing_enabled); - json.writeString("path", mipsTracer.get_logging_path()); - json.writeUint("storageCapacity", (u32)mipsTracer.in_storage_capacity); - json.writeUint("maxTraceSize", (u32)mipsTracer.in_max_trace_size); - // How much of each ring has been used, so a client can tell "the trace covers everything - // since I started" from "the oldest entries have already been overwritten". - json.writeUint("blocksRecorded", mipsTracer.executed_blocks.current_index); - json.writeBool("traceOverflowed", mipsTracer.executed_blocks.overflow); - json.writeUint("distinctBlocks", (u32)mipsTracer.trace_info.size()); - json.writeUint("storageUsed", mipsTracer.storage.cur_index); - json.writeBool("supported", TracerCoreIsIR()); - json.writeString("cpuCore", CPUCoreName((CPUCore)g_Config.iCpuCore)); -} - -// Report tracer state (cpu.tracer.status) -// -// No parameters. -// -// Response (same event name): -// - tracing: boolean, whether recording is currently on. -// - path: string, file cpu.tracer.flush writes to ("" if never set). -// - storageCapacity: unsigned integer, instruction words the block storage holds. -// - maxTraceSize: unsigned integer, block entries the trace ring holds. -// - blocksRecorded: unsigned integer, entries written to the trace ring. -// - traceOverflowed: boolean, true once the ring wrapped and the oldest entries were lost. -// - distinctBlocks: unsigned integer, distinct basic blocks seen. -// - storageUsed: unsigned integer, instruction words of block storage used. -// - supported: boolean, whether the current CPU core feeds the tracer (IR cores only.) -// - cpuCore: string, one of 'interpreter', 'jit', 'ir', 'jit-ir'. -void WebSocketMIPSTracerStatus(DebuggerRequest &req) { - JsonWriter &json = req.Respond(); - WriteStatus(json); -} - -// Start recording a trace (cpu.tracer.start) -// -// Parameters: -// - storageCapacity: optional unsigned integer, instruction words to reserve for block storage. -// - maxTraceSize: optional unsigned integer, how many block entries the trace ring holds. The -// ring is cyclic, so once it fills the oldest entries are dropped and the trace covers the -// most recent maxTraceSize blocks - which is usually what you want when tracing up to a crash. -// - clearJit: optional boolean, default true. Blocks already compiled don't carry the tracer's -// LogIRBlock instruction, so without dropping them a hot loop that was compiled before this -// call never shows up. Pass false only if you know the code you care about isn't compiled yet. -// -// Response (same event name): the same fields as cpu.tracer.status. -// -// Note: starting resizes and clears the buffers, so any trace not yet flushed is lost. -void WebSocketMIPSTracerStart(DebuggerRequest &req) { - if (!PSP_IsInited()) - return req.Fail("CPU not started"); - if (!TracerCoreIsIR()) { - return req.Fail(StringFromFormat( - "The MIPS tracer only records under an IR core, but the current core is '%s' - " - "start PPSSPP with --cpu=ir (or --cpu=jit-ir) to use it", - CPUCoreName((CPUCore)g_Config.iCpuCore))); - } - - uint32_t storageCapacity = (uint32_t)mipsTracer.in_storage_capacity; - uint32_t maxTraceSize = (uint32_t)mipsTracer.in_max_trace_size; - if (!req.ParamU32("storageCapacity", &storageCapacity, false, DebuggerParamType::OPTIONAL)) - return; - if (!req.ParamU32("maxTraceSize", &maxTraceSize, false, DebuggerParamType::OPTIONAL)) - return; - if (storageCapacity == 0 || maxTraceSize == 0) - return req.Fail("Parameters 'storageCapacity' and 'maxTraceSize' must be non-zero"); - - bool clearJit = true; - if (!req.ParamBool("clearJit", &clearJit, DebuggerParamType::OPTIONAL)) - return; - - // Resizing the buffers and flipping the flag both race the CPU thread otherwise - see - // docs/DebuggerThreading.md. - Core_RunOnCPUThread([&] { - mipsTracer.in_storage_capacity = (int)storageCapacity; - mipsTracer.in_max_trace_size = (int)maxTraceSize; - mipsTracer.initialize(storageCapacity, maxTraceSize); - mipsTracer.start_tracing(); - if (clearJit) { - currentMIPS->ClearJitCacheDeferred(); - } - - JsonWriter &json = req.Respond(); - WriteStatus(json); - }); -} - -// Stop recording (cpu.tracer.stop) -// -// No parameters. Stopping keeps whatever has been recorded, so cpu.tracer.flush still works -// afterwards. -// -// Response (same event name): the same fields as cpu.tracer.status. -void WebSocketMIPSTracerStop(DebuggerRequest &req) { - Core_RunOnCPUThread([&] { - mipsTracer.stop_tracing(); - - JsonWriter &json = req.Respond(); - WriteStatus(json); - }); -} - -// Write the recorded trace to a file (cpu.tracer.flush) -// -// Parameters: -// - path: optional string, file to write to. Remembered for later flushes; required the first -// time, since the tracer has no default. -// -// Response (same event name): -// - path: string, the file written. -// - plus the same fields as cpu.tracer.status, as they stand after the flush. -// -// Note: a successful flush clears the tracer (MIPSTracer::flush_to_file does), so the next trace -// starts empty. Recording is not stopped - call cpu.tracer.stop first for a stable snapshot. -void WebSocketMIPSTracerFlush(DebuggerRequest &req) { - std::string path; - if (req.HasParam("path")) { - if (!req.ParamString("path", &path, DebuggerParamType::OPTIONAL)) - return; - if (path.empty()) - return req.Fail("Parameter 'path' must not be empty"); - } else if (mipsTracer.get_logging_path().empty()) { - return req.Fail("No trace path set - pass 'path' at least once"); - } - - std::string error; - Core_RunOnCPUThread([&] { - if (!path.empty()) - mipsTracer.set_logging_path(path); - if (!mipsTracer.flush_to_file()) { - // flush_to_file logs the specific reason (bad path, couldn't open) to Log::JIT. - error = "Couldn't write the trace to '" + mipsTracer.get_logging_path() + "'"; - return; - } - - JsonWriter &json = req.Respond(); - WriteStatus(json); - }); - - if (!error.empty()) - req.Fail(error); -} - -// Discard the recorded trace (cpu.tracer.clear) -// -// No parameters. Leaves recording on if it was on, so this is how to drop everything up to now -// and keep tracing from here. -// -// Response (same event name): the same fields as cpu.tracer.status. -void WebSocketMIPSTracerClear(DebuggerRequest &req) { - Core_RunOnCPUThread([&] { - mipsTracer.clear(); - - JsonWriter &json = req.Respond(); - WriteStatus(json); - }); -} diff --git a/Core/Debugger/WebSocket/MIPSTracerSubscriber.h b/Core/Debugger/WebSocket/MIPSTracerSubscriber.h deleted file mode 100644 index 8dc869d8ee..0000000000 --- a/Core/Debugger/WebSocket/MIPSTracerSubscriber.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2026- 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 *WebSocketMIPSTracerInit(DebuggerEventHandlerMap &map); - -void WebSocketMIPSTracerStatus(DebuggerRequest &req); -void WebSocketMIPSTracerStart(DebuggerRequest &req); -void WebSocketMIPSTracerStop(DebuggerRequest &req); -void WebSocketMIPSTracerFlush(DebuggerRequest &req); -void WebSocketMIPSTracerClear(DebuggerRequest &req); diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index f3e3aab116..b94187585b 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -119,7 +119,6 @@ - @@ -405,7 +404,6 @@ - diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 96576d9464..05b1873f8f 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -32,7 +32,6 @@ - @@ -461,7 +460,6 @@ - diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 5264150675..ef5b0c577a 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -678,7 +678,6 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/LogConfigSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/MemorySubscriber.cpp \ - $(SRC)/Core/Debugger/WebSocket/MIPSTracerSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/MemoryInfoSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/ReplaySubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ diff --git a/docs/WebSocketDebugger.md b/docs/WebSocketDebugger.md index c3200523b5..7ad9503a3d 100644 --- a/docs/WebSocketDebugger.md +++ b/docs/WebSocketDebugger.md @@ -273,7 +273,6 @@ file - this is just an index. | Game/version | `game.reset`, `game.status`, `game.speed.get/set` (emulation speed - unlimited fast-forward, or a percentage of 60 FPS; see below), `version` | `GameSubscriber.cpp` | | CPU core | `cpu.stepping`, `cpu.resume`, `cpu.status` (reports `ticks` plus `us`, emulated microseconds, and `clockHz` - use `us` to line up with wall-clock timings, since games change the clock frequency and the ticks-per-second ratio isn't fixed), `cpu.getAllRegs`, `cpu.getReg`, `cpu.setReg`, `cpu.evaluate` | `CPUCoreSubscriber.cpp` | | Stepping | `cpu.stepInto`, `cpu.stepOver`, `cpu.stepOut`, `cpu.runUntil`, `cpu.runUntilTime` (run until a point in emulated time - `us` absolute or `relativeUs` from now - and break there; this is how to get a scripted repro reproducibly "N seconds into the game" instead of polling `cpu.status` in a loop), `cpu.nextHLE` | `SteppingSubscriber.cpp` | -| MIPS tracer | `cpu.tracer.start`, `cpu.tracer.stop`, `cpu.tracer.flush`, `cpu.tracer.clear`, `cpu.tracer.status` - record every basic block the CPU executes and write the instruction stream to a file. The trace ring is cyclic, so a finished recording holds the *last* `maxTraceSize` blocks: start it, run into a crash, and the tail of the file is the instructions that led there. Only the IR cores feed the tracer (`--cpu=ir` / `--cpu=jit-ir`); `cpu.tracer.status` reports `supported` so a client can tell that apart from "nothing ran" | `MIPSTracerSubscriber.cpp` | | Breakpoints | `cpu.breakpoint.add/update/remove/list`, `memory.breakpoint.add/update/remove/list`, `cpu.regBreakpoint.add/update/remove/list` (break when a register is written to, by any instruction anywhere - currently GPRs only; interpreter-only, no effect under a JIT backend) | `BreakpointSubscriber.cpp` | | Memory read/write | `memory.read_u8/u16/u32`, `memory.read`, `memory.readString`, `memory.write_u8/u16/u32`, `memory.write`. The numeric ones report the result as both `value` and `uintValue` - the latter is what `cpu.getReg`/`cpu.getAllRegs` call it, so a client can read either without caring which event answered | `MemorySubscriber.cpp` | | Memory search | `memory.search` - scan a range for a `u8`/`u16`/`u32`/`float` value or a `bytes` pattern (with an optional wildcard mask), for narrowing down where an unknown value lives (Cheat Engine style) | `MemorySubscriber.cpp` |