mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-25 19:30:53 +00:00
Debugger: Add function symbol add/remove API.
This commit is contained in:
parent
2133b18198
commit
8a7662adae
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user