Skip to content

Commit

Permalink
Debugger: Initial disasm API.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Apr 24, 2018
1 parent c587169 commit fbc75db
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 36 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/GameSubscriber.h
Core/Debugger/WebSocket/LogBroadcaster.cpp
Core/Debugger/WebSocket/LogBroadcaster.h
Core/Debugger/WebSocket/MemorySubscriber.cpp
Core/Debugger/WebSocket/MemorySubscriber.h
Core/Debugger/WebSocket/SteppingBroadcaster.cpp
Core/Debugger/WebSocket/SteppingBroadcaster.h
Core/Debugger/WebSocket/WebSocketUtils.cpp
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\GameSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\MemorySubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\SteppingBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\WebSocketUtils.cpp" />
<ClCompile Include="FileSystems\BlobFileSystem.cpp" />
Expand Down Expand Up @@ -541,6 +542,7 @@
<ClInclude Include="AVIDump.h" />
<ClInclude Include="Debugger\WebSocket.h" />
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\MemorySubscriber.h" />
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />
<ClInclude Include="Debugger\WebSocket\CPUCoreSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GameBroadcaster.h" />
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@
<ClCompile Include="Debugger\WebSocket\GameSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
<ClCompile Include="Debugger\WebSocket\MemorySubscriber.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1319,6 +1320,7 @@
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
<ClInclude Include="Debugger\WebSocket\MemorySubscriber.h" />
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
Expand Down
2 changes: 2 additions & 0 deletions Core/Debugger/WebSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

#include "Core/Debugger/WebSocket/CPUCoreSubscriber.h"
#include "Core/Debugger/WebSocket/GameSubscriber.h"
#include "Core/Debugger/WebSocket/MemorySubscriber.h"

typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map);
typedef void (*Subscribershutdown)(void *p);
Expand All @@ -60,6 +61,7 @@ struct SubscriberInfo {
static const std::vector<SubscriberInfo> subscribers({
{ &WebSocketCPUCoreInit, nullptr },
{ &WebSocketGameInit, nullptr },
{ &WebSocketMemoryInit, &WebSocketMemoryShutdown },
});

// To handle webserver restart, keep track of how many running.
Expand Down
2 changes: 1 addition & 1 deletion Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ void WebSocketCPUSetReg(DebuggerRequest &req) {
}

uint32_t val;
if (!req.ParamU32OrFloatBits("value", &val)) {
if (!req.ParamU32("value", &val, true)) {
// Already sent error.
return;
}
Expand Down
237 changes: 237 additions & 0 deletions Core/Debugger/WebSocket/MemorySubscriber.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Copyright (c) 2018- 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 "base/stringutil.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/DisassemblyManager.h"
#include "Core/Debugger/WebSocket/MemorySubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSDebugInterface.h"

struct WebSocketMemoryState {
void Base(DebuggerRequest &req);
void Disasm(DebuggerRequest &req);

protected:
void WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l);
void WriteBranchGuide(JsonWriter &json, const BranchLine &l);

DisassemblyManager disasm_;
};

void *WebSocketMemoryInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketMemoryState();
map["memory.base"] = std::bind(&WebSocketMemoryState::Base, p, std::placeholders::_1);
map["memory.disasm"] = std::bind(&WebSocketMemoryState::Disasm, p, std::placeholders::_1);

return p;
}

void WebSocketMemoryShutdown(void *p) {
delete static_cast<WebSocketMemoryState *>(p);
}

void WebSocketMemoryState::WriteDisasmLine(JsonWriter &json, const DisassemblyLineInfo &l) {
u32 addr = l.info.opcodeAddress;
json.pushDict();
if (l.type == DISTYPE_OPCODE)
json.writeString("type", "opcode");
else if (l.type == DISTYPE_MACRO)
json.writeString("type", "macro");
else if (l.type == DISTYPE_DATA)
json.writeString("type", "data");
else if (l.type == DISTYPE_OTHER)
json.writeString("type", "other");

json.writeFloat("address", addr);
json.writeInt("addressSize", l.totalSize);
json.writeFloat("encoding", l.info.encodedOpcode.encoding);
int c = currentDebugMIPS->getColor(addr) & 0x00FFFFFF;
json.writeString("backgroundColor", StringFromFormat("#%02x%02x%02x", c & 0xFF, (c >> 8) & 0xFF, c >> 16));
json.writeString("name", l.name);
json.writeString("params", l.params);

const std::string addressSymbol = g_symbolMap->GetLabelString(addr);
if (addressSymbol.empty())
json.writeRaw("symbol", "null");
else
json.writeString("symbol", addressSymbol);

bool enabled;
// TODO: Account for bp inside macro?
if (CBreakPoints::IsAddressBreakPoint(addr, &enabled)) {
json.pushDict("breakpoint");
json.writeBool("enabled", enabled);
auto cond = CBreakPoints::GetBreakPointCondition(addr);
if (cond)
json.writeString("expression", cond->expressionString);
else
json.writeRaw("expression", "null");
json.pop();
} else {
json.writeRaw("breakpoint", "null");
}

json.writeBool("isCurrentPC", currentDebugMIPS->GetPC() == addr);
if (l.info.isBranch) {
json.pushDict("branch");
if (!l.info.isBranchToRegister) {
json.writeFloat("targetAddress", l.info.branchTarget);
json.writeRaw("register", "null");
} else {
json.writeRaw("targetAddress", "null");
json.writeInt("register", l.info.branchRegisterNum);
}
json.writeBool("isLinked", l.info.isLinkedBranch);
json.writeBool("isLikely", l.info.isLikelyBranch);
json.pop();
} else {
json.writeRaw("branch", "null");
}

if (l.info.hasRelevantAddress) {
json.pushDict("relevantData");
json.writeFloat("address", l.info.relevantAddress);
if (Memory::IsValidRange(l.info.relevantAddress, 4))
json.writeFloat("uintValue", Memory::ReadUnchecked_U32(l.info.relevantAddress));
else
json.writeRaw("uintValue", "null");
json.pop();
} else {
json.writeRaw("relevantData", "null");
}

if (l.info.isConditional)
json.writeBool("conditionMet", l.info.conditionMet);
else
json.writeRaw("conditionMet", "null");

if (l.info.isDataAccess) {
json.pushDict("dataAccess");
json.writeFloat("address", l.info.dataAddress);
json.writeInt("size", l.info.dataSize);

std::string dataSymbol = g_symbolMap->GetLabelString(l.info.dataAddress);
std::string valueSymbol;
if (!Memory::IsValidRange(l.info.dataAddress, l.info.dataSize))
json.writeRaw("uintValue", "null");
else if (l.info.dataSize == 1)
json.writeFloat("uintValue", Memory::ReadUnchecked_U8(l.info.dataAddress));
else if (l.info.dataSize == 2)
json.writeFloat("uintValue", Memory::ReadUnchecked_U16(l.info.dataAddress));
else if (l.info.dataSize >= 4) {
u32 data = Memory::ReadUnchecked_U32(l.info.dataAddress);
valueSymbol = g_symbolMap->GetLabelString(data);
json.writeFloat("uintValue", data);
}

if (!dataSymbol.empty())
json.writeString("symbol", dataSymbol);
else
json.writeRaw("symbol", "null");
if (!valueSymbol.empty())
json.writeString("valueSymbol", valueSymbol);
else
json.writeRaw("valueSymbol", "null");
json.pop();
} else {
json.writeRaw("dataAccess", "null");
}

json.pop();
}

void WebSocketMemoryState::WriteBranchGuide(JsonWriter &json, const BranchLine &l) {
json.pushDict();
json.writeFloat("top", l.first);
json.writeFloat("bottom", l.second);
if (l.type == LINE_UP)
json.writeString("direction", "up");
else if (l.type == LINE_DOWN)
json.writeString("direction", "down");
else if (l.type == LINE_RIGHT)
json.writeString("direction", "right");
json.writeInt("lane", l.laneIndex);
json.pop();
}

void WebSocketMemoryState::Base(DebuggerRequest &req) {
JsonWriter &json = req.Respond();
json.writeString("addressHex", StringFromFormat("%016llx", Memory::base));
}

void WebSocketMemoryState::Disasm(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) {
return req.Fail("CPU not started");
}

uint32_t start, end, count;
if (!req.ParamU32("address", &start))
return;
if (req.ParamU32("count", &count)) {
// Let's assume everything is two instructions.
disasm_.analyze(start - 4, count * 8 + 8);
start = disasm_.getStartAddress(start);
if (start == -1)
req.ParamU32("address", &start);
end = disasm_.getNthNextAddress(start, count);
} else if (req.ParamU32("end", &end)) {
// Let's assume everything is two instructions.
disasm_.analyze(start - 4, end - start + 8);
start = disasm_.getStartAddress(start);
if (start == -1)
req.ParamU32("address", &start);

// Correct end and calculate count based on it.
// This accounts for macros as one line, although two instructions.
u32 stop = end;
count = 0;
for (end = start; end < stop; end = disasm_.getNthNextAddress(end, 1)) {
count++;
}
} else {
// Error message already sent.
return;
}

bool displaySymbols = true;
if (!req.ParamBool("displaySymbols", &displaySymbols, DebuggerParamType::OPTIONAL))
return;

JsonWriter &json = req.Respond();
json.pushDict("range");
json.writeFloat("start", start);
json.writeFloat("end", end);
json.pop();

json.pushArray("lines");
DisassemblyLineInfo line;
u32 addr = start;
for (u32 i = 0; i < count; ++i) {
disasm_.getLine(addr, displaySymbols, line);
WriteDisasmLine(json, line);
addr += line.totalSize;
}
json.pop();

json.pushArray("branchGuides");
auto branchGuides = disasm_.getBranchLines(start, end - start);
for (auto bl : branchGuides)
WriteBranchGuide(json, bl);
json.pop();
}
23 changes: 23 additions & 0 deletions Core/Debugger/WebSocket/MemorySubscriber.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2018- 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"

void *WebSocketMemoryInit(DebuggerEventHandlerMap &map);
void WebSocketMemoryShutdown(void *p);
Loading

0 comments on commit fbc75db

Please sign in to comment.