From 8a7662adaee65543aa8beaad4dd50ff445394ef3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 4 Jul 2018 17:58:55 -0700 Subject: [PATCH] Debugger: Add function symbol add/remove API. --- Core/Debugger/WebSocket/HLESubscriber.cpp | 232 +++++++++++++++++++++- Core/Debugger/WebSocket/HLESubscriber.h | 3 + 2 files changed, 233 insertions(+), 2 deletions(-) diff --git a/Core/Debugger/WebSocket/HLESubscriber.cpp b/Core/Debugger/WebSocket/HLESubscriber.cpp index efb82c3b36..ae94993891 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.cpp +++ b/Core/Debugger/WebSocket/HLESubscriber.cpp @@ -15,19 +15,44 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "base/stringutil.h" +#include "Core/Config.h" +#include "Core/Core.h" +#include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/MIPS/MIPSAnalyst.h" #include "Core/HLE/sceKernelThread.h" void *WebSocketHLEInit(DebuggerEventHandlerMap &map) { map["hle.thread.list"] = &WebSocketHLEThreadList; map["hle.func.list"] = &WebSocketHLEFuncList; + map["hle.func.add"] = &WebSocketHLEFuncAdd; + map["hle.func.remove"] = &WebSocketHLEFuncRemove; + map["hle.func.rename"] = &WebSocketHLEFuncRename; map["hle.module.list"] = &WebSocketHLEModuleList; return nullptr; } +// List all current HLE threads (hle.thread.list) +// +// No parameters. +// +// Response (same event name): +// - threads: array of objects, each with properties: +// - id: unsigned integer unique id of thread. +// - name: name given to thread when created. +// - status: numeric status flags of thread. +// - statuses: array of string status names, e.g. 'running'. Typically only one set. +// - pc: unsigned integer address of next instruction on thread. +// - entry: unsigned integer address thread execution started at. +// - initialStackSize: unsigned integer, size of initial stack. +// - currentStackSize: unsigned integer, size of stack (e.g. if resized.) +// - priority: numeric priority level, lower values are better priority. +// - waitType: numeric wait type, if the thread is waiting, or 0 if not waiting. +// - isCurrent: boolean, true for the currently executing thread. void WebSocketHLEThreadList(DebuggerRequest &req) { // Will just return none of the CPU isn't ready yet. auto threads = GetThreadsInfo(); @@ -58,13 +83,22 @@ void WebSocketHLEThreadList(DebuggerRequest &req) { json.writeUint("initialStackSize", th.initialStack); json.writeUint("currentStackSize", th.stackSize); json.writeInt("priority", th.priority); - json.writeInt("priority", (int)th.waitType); + json.writeInt("waitType", (int)th.waitType); json.writeBool("isCurrent", th.isCurrent); json.pop(); } json.pop(); } +// List all current known function symbols (hle.func.list) +// +// No parameters. +// +// Response (same event name): +// - functions: array of objects, each with properties: +// - name: current name of function. +// - address: unsigned integer start address of function. +// - size: unsigned integer size in bytes. void WebSocketHLEFuncList(DebuggerRequest &req) { if (!g_symbolMap) return req.Fail("CPU not active"); @@ -83,6 +117,200 @@ void WebSocketHLEFuncList(DebuggerRequest &req) { json.pop(); } +// Add a new function symbols (hle.func.add) +// +// Parameters: +// - address: unsigned integer address for the start of the function. +// - size: unsigned integer size in bytes, optional. If 'address' is inside a function, +// defaults to that function's end, otherwise 4 bytes. +// - name: string to name the function, optional and defaults to an auto-generated name. +// +// Response (same event name): +// - address: the start address, repeated back. +// - size: the size of the function, whether autodetected or not. +// - name: name of the new function. +// +// Note: will fail if a function starts at that location already, or if size spans multiple +// existing functions. Remove those functions first if necessary. +void WebSocketHLEFuncAdd(DebuggerRequest &req) { + if (!g_symbolMap) + return req.Fail("CPU not active"); + if (!Core_IsStepping()) + return req.Fail("CPU currently running (cpu.stepping first)"); + + u32 addr; + if (!req.ParamU32("address", &addr)) + return; + u32 size = -1; + if (!req.ParamU32("size", &size, false, DebuggerParamType::OPTIONAL)) + return; + if (size == 0) + size = -1; + + std::string name; + if (!req.ParamString("name", &name, DebuggerParamType::OPTIONAL)) + return; + if (name.empty()) + name = StringFromFormat("z_un_%08x", addr); + + u32 prevBegin = g_symbolMap->GetFunctionStart(addr); + u32 endBegin = size == -1 ? prevBegin : g_symbolMap->GetFunctionStart(addr + size - 1); + if (prevBegin == addr) { + return req.Fail("Function already exists at 'address'"); + } else if (endBegin != prevBegin) { + return req.Fail("Function already exists between 'address' and 'address' + 'size'"); + } else if (prevBegin != -1) { + std::string prevName = g_symbolMap->GetLabelString(prevBegin); + u32 prevSize = g_symbolMap->GetFunctionSize(prevBegin); + u32 newPrevSize = addr - prevBegin; + + // The new function will be the remainder, unless otherwise specified. + if (size == -1) + size = prevSize - newPrevSize; + + // Make sure we register the new length for replacements too. + MIPSAnalyst::ForgetFunctions(prevBegin, prevBegin + newPrevSize - 1); + g_symbolMap->SetFunctionSize(prevBegin, newPrevSize); + MIPSAnalyst::RegisterFunction(prevBegin, newPrevSize, prevName.c_str()); + } else { + // There was no function there, so hopefully they specified a size. + if (size == -1) + size = 4; + } + + // To ensure we restore replacements. + MIPSAnalyst::ForgetFunctions(addr, addr + size - 1); + g_symbolMap->AddFunction(name.c_str(), addr, size); + g_symbolMap->SortSymbols(); + MIPSAnalyst::RegisterFunction(addr, size, name.c_str()); + + MIPSAnalyst::UpdateHashMap(); + MIPSAnalyst::ApplyHashMap(); + + if (g_Config.bFuncReplacements) { + MIPSAnalyst::ReplaceFunctions(); + } + + // Clear cache for branch lines and such. + DisassemblyManager manager; + manager.clear(); + + JsonWriter &json = req.Respond(); + json.writeUint("address", addr); + json.writeUint("size", size); + json.writeString("name", name); +} + +// Remove a function symbol (hle.func.remove) +// +// Parameters: +// - address: unsigned integer address within function to remove. +// +// Response (same event name): +// - address: the start address of the removed function. +// - size: the size in bytes of the removed function. +// +// Note: will expand any previous function automatically. +void WebSocketHLEFuncRemove(DebuggerRequest &req) { + if (!g_symbolMap) + return req.Fail("CPU not active"); + if (!Core_IsStepping()) + return req.Fail("CPU currently running (cpu.stepping first)"); + + u32 addr; + if (!req.ParamU32("address", &addr)) + return; + + u32 funcBegin = g_symbolMap->GetFunctionStart(addr); + if (funcBegin == -1) + return req.Fail("No function found at 'address'"); + u32 funcSize = g_symbolMap->GetFunctionSize(funcBegin); + + // Expand the previous function. + u32 prevBegin = g_symbolMap->GetFunctionStart(funcBegin - 1); + if (prevBegin != -1) { + std::string prevName = g_symbolMap->GetLabelString(prevBegin); + u32 expandedSize = g_symbolMap->GetFunctionSize(prevBegin) + funcSize; + g_symbolMap->SetFunctionSize(prevBegin, expandedSize); + MIPSAnalyst::ForgetFunctions(prevBegin, prevBegin + expandedSize - 1); + MIPSAnalyst::RegisterFunction(prevBegin, expandedSize, prevName.c_str()); + } else { + MIPSAnalyst::ForgetFunctions(funcBegin, funcBegin + funcSize - 1); + } + + g_symbolMap->RemoveFunction(funcBegin, true); + g_symbolMap->SortSymbols(); + + MIPSAnalyst::UpdateHashMap(); + MIPSAnalyst::ApplyHashMap(); + + if (g_Config.bFuncReplacements) { + MIPSAnalyst::ReplaceFunctions(); + } + + // Clear cache for branch lines and such. + DisassemblyManager manager; + manager.clear(); + + JsonWriter &json = req.Respond(); + json.writeUint("address", funcBegin); + json.writeUint("size", funcSize); +} + +// Rename a function symbol (hle.func.rename) +// +// Parameters: +// - address: unsigned integer address within function to rename. +// - name: string, new name for the function. +// +// Response (same event name): +// - address: the start address of the removed function. +// - size: the size in bytes of the removed function. +// - name: string, new name repeated back. +void WebSocketHLEFuncRename(DebuggerRequest &req) { + if (!g_symbolMap) + return req.Fail("CPU not active"); + if (!Core_IsStepping()) + return req.Fail("CPU currently running (cpu.stepping first)"); + + u32 addr; + if (!req.ParamU32("address", &addr)) + return; + std::string name; + if (!req.ParamString("name", &name)) + return; + + u32 funcBegin = g_symbolMap->GetFunctionStart(addr); + if (funcBegin == -1) + return req.Fail("No function found at 'address'"); + u32 funcSize = g_symbolMap->GetFunctionSize(funcBegin); + + g_symbolMap->SetLabelName(name.c_str(), funcBegin); + // To ensure we reapply replacements (in case we check name there.) + MIPSAnalyst::ForgetFunctions(funcBegin, funcBegin + funcSize - 1); + MIPSAnalyst::RegisterFunction(funcBegin, funcSize, name.c_str()); + MIPSAnalyst::UpdateHashMap(); + MIPSAnalyst::ApplyHashMap(); + if (g_Config.bFuncReplacements) { + MIPSAnalyst::ReplaceFunctions(); + } + + JsonWriter &json = req.Respond(); + json.writeUint("address", funcBegin); + json.writeUint("size", funcSize); + json.writeString("name", name); +} + +// List all known user modules (hle.module.list) +// +// No parameters. +// +// Response (same event name): +// - modules: array of objects, each with properties: +// - name: name of module when loaded. +// - address: unsigned integer start address. +// - size: unsigned integer size in bytes. +// - isActive: boolean, true if this module is active. void WebSocketHLEModuleList(DebuggerRequest &req) { if (!g_symbolMap) return req.Fail("CPU not active"); @@ -90,7 +318,7 @@ void WebSocketHLEModuleList(DebuggerRequest &req) { auto modules = g_symbolMap->getAllModules(); JsonWriter &json = req.Respond(); - json.pushArray("functions"); + json.pushArray("modules"); for (auto m : modules) { json.pushDict(); json.writeString("name", m.name); diff --git a/Core/Debugger/WebSocket/HLESubscriber.h b/Core/Debugger/WebSocket/HLESubscriber.h index 61af4dc80d..7188a5ef23 100644 --- a/Core/Debugger/WebSocket/HLESubscriber.h +++ b/Core/Debugger/WebSocket/HLESubscriber.h @@ -23,4 +23,7 @@ void *WebSocketHLEInit(DebuggerEventHandlerMap &map); void WebSocketHLEThreadList(DebuggerRequest &req); void WebSocketHLEFuncList(DebuggerRequest &req); +void WebSocketHLEFuncAdd(DebuggerRequest &req); +void WebSocketHLEFuncRemove(DebuggerRequest &req); +void WebSocketHLEFuncRename(DebuggerRequest &req); void WebSocketHLEModuleList(DebuggerRequest &req);