Skip to content

Commit

Permalink
Debugger: Add function symbol add/remove API.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Jul 7, 2018
1 parent 2133b18 commit 8a7662a
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 2 deletions.
232 changes: 230 additions & 2 deletions Core/Debugger/WebSocket/HLESubscriber.cpp
Expand Up @@ -15,19 +15,44 @@
// Official git repository and contact information can be found at // Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. // 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/SymbolMap.h"
#include "Core/Debugger/WebSocket/HLESubscriber.h" #include "Core/Debugger/WebSocket/HLESubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelThread.h"


void *WebSocketHLEInit(DebuggerEventHandlerMap &map) { void *WebSocketHLEInit(DebuggerEventHandlerMap &map) {
map["hle.thread.list"] = &WebSocketHLEThreadList; map["hle.thread.list"] = &WebSocketHLEThreadList;
map["hle.func.list"] = &WebSocketHLEFuncList; 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; map["hle.module.list"] = &WebSocketHLEModuleList;


return nullptr; 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) { void WebSocketHLEThreadList(DebuggerRequest &req) {
// Will just return none of the CPU isn't ready yet. // Will just return none of the CPU isn't ready yet.
auto threads = GetThreadsInfo(); auto threads = GetThreadsInfo();
Expand Down Expand Up @@ -58,13 +83,22 @@ void WebSocketHLEThreadList(DebuggerRequest &req) {
json.writeUint("initialStackSize", th.initialStack); json.writeUint("initialStackSize", th.initialStack);
json.writeUint("currentStackSize", th.stackSize); json.writeUint("currentStackSize", th.stackSize);
json.writeInt("priority", th.priority); json.writeInt("priority", th.priority);
json.writeInt("priority", (int)th.waitType); json.writeInt("waitType", (int)th.waitType);
json.writeBool("isCurrent", th.isCurrent); json.writeBool("isCurrent", th.isCurrent);
json.pop(); json.pop();
} }
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) { void WebSocketHLEFuncList(DebuggerRequest &req) {
if (!g_symbolMap) if (!g_symbolMap)
return req.Fail("CPU not active"); return req.Fail("CPU not active");
Expand All @@ -83,14 +117,208 @@ void WebSocketHLEFuncList(DebuggerRequest &req) {
json.pop(); 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) { void WebSocketHLEModuleList(DebuggerRequest &req) {
if (!g_symbolMap) if (!g_symbolMap)
return req.Fail("CPU not active"); return req.Fail("CPU not active");


auto modules = g_symbolMap->getAllModules(); auto modules = g_symbolMap->getAllModules();


JsonWriter &json = req.Respond(); JsonWriter &json = req.Respond();
json.pushArray("functions"); json.pushArray("modules");
for (auto m : modules) { for (auto m : modules) {
json.pushDict(); json.pushDict();
json.writeString("name", m.name); json.writeString("name", m.name);
Expand Down
3 changes: 3 additions & 0 deletions Core/Debugger/WebSocket/HLESubscriber.h
Expand Up @@ -23,4 +23,7 @@ void *WebSocketHLEInit(DebuggerEventHandlerMap &map);


void WebSocketHLEThreadList(DebuggerRequest &req); void WebSocketHLEThreadList(DebuggerRequest &req);
void WebSocketHLEFuncList(DebuggerRequest &req); void WebSocketHLEFuncList(DebuggerRequest &req);
void WebSocketHLEFuncAdd(DebuggerRequest &req);
void WebSocketHLEFuncRemove(DebuggerRequest &req);
void WebSocketHLEFuncRename(DebuggerRequest &req);
void WebSocketHLEModuleList(DebuggerRequest &req); void WebSocketHLEModuleList(DebuggerRequest &req);

0 comments on commit 8a7662a

Please sign in to comment.