Skip to content
Permalink
Browse files

Debugger: Add function symbol add/remove API.

  • Loading branch information...
unknownbrackets committed Jul 5, 2018
1 parent 2133b18 commit 8a7662adaee65543aa8beaad4dd50ff445394ef3
Showing with 233 additions and 2 deletions.
  1. +230 −2 Core/Debugger/WebSocket/HLESubscriber.cpp
  2. +3 −0 Core/Debugger/WebSocket/HLESubscriber.h
@@ -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,14 +117,208 @@ 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");

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);

0 comments on commit 8a7662a

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.