Skip to content

Commit

Permalink
Debugger: Initial breakpoint APIs.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed May 4, 2018
1 parent 29f1134 commit 7273081
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/DisassemblyManager.h
Core/Debugger/WebSocket.cpp
Core/Debugger/WebSocket.h
Core/Debugger/WebSocket/BreakpointSubscriber.cpp
Core/Debugger/WebSocket/BreakpointSubscriber.h
Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
Core/Debugger/WebSocket/CPUCoreSubscriber.h
Core/Debugger/WebSocket/DisasmSubscriber.cpp
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
<ClCompile Include="..\ext\udis86\udis86.c" />
<ClCompile Include="AVIDump.cpp" />
<ClCompile Include="Debugger\WebSocket.cpp" />
<ClCompile Include="Debugger\WebSocket\BreakpointSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\CPUCoreSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\GameBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\GameSubscriber.cpp" />
Expand Down Expand Up @@ -542,6 +543,7 @@
<ClInclude Include="..\ext\udis86\udis86.h" />
<ClInclude Include="AVIDump.h" />
<ClInclude Include="Debugger\WebSocket.h" />
<ClInclude Include="Debugger\WebSocket\BreakpointSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.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 @@ -718,6 +718,7 @@
</ClCompile>
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\SteppingSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\BreakpointSubscriber.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1323,6 +1324,7 @@
</ClInclude>
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\BreakpointSubscriber.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 @@ -47,6 +47,7 @@
#include "Core/Debugger/WebSocket/LogBroadcaster.h"
#include "Core/Debugger/WebSocket/SteppingBroadcaster.h"

#include "Core/Debugger/WebSocket/BreakpointSubscriber.h"
#include "Core/Debugger/WebSocket/CPUCoreSubscriber.h"
#include "Core/Debugger/WebSocket/DisasmSubscriber.h"
#include "Core/Debugger/WebSocket/GameSubscriber.h"
Expand All @@ -60,6 +61,7 @@ struct SubscriberInfo {
};

static const std::vector<SubscriberInfo> subscribers({
{ &WebSocketBreakpointInit, nullptr },
{ &WebSocketCPUCoreInit, nullptr },
{ &WebSocketDisasmInit, &WebSocketDisasmShutdown },
{ &WebSocketGameInit, nullptr },
Expand Down
179 changes: 179 additions & 0 deletions Core/Debugger/WebSocket/BreakpointSubscriber.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// 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 "Common/StringUtils.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/WebSocket/BreakpointSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MIPS/MIPSDebugInterface.h"

void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
// No need to bind or alloc state, these are all global.
map["cpu.breakpoint.add"] = &WebSocketCPUBreakpointAdd;
map["cpu.breakpoint.update"] = &WebSocketCPUBreakpointUpdate;
map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove;
map["cpu.breakpoint.list"] = &WebSocketCPUBreakpointList;

return nullptr;
}

struct WebSocketCPUBreakpointParams {
uint32_t address = 0;
bool hasEnabled = false;
bool hasLog = false;
bool hasCondition = false;
bool hasLogFormat = false;

bool enabled;
bool log;
std::string condition;
PostfixExpression compiledCondition;
std::string logFormat;

bool Parse(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
req.Fail("CPU not started");
return false;
}

if (!req.ParamU32("address", &address))
return false;

hasEnabled = req.HasParam("enabled");
if (hasEnabled) {
if (!req.ParamBool("enabled", &enabled))
return false;
}
hasLog = req.HasParam("log");
if (hasLog) {
if (!req.ParamBool("log", &log))
return false;
}
hasCondition = req.HasParam("condition");
if (hasCondition) {
if (!req.ParamString("condition", &condition))
return false;
if (!currentDebugMIPS->initExpression(condition.c_str(), compiledCondition)) {
req.Fail(StringFromFormat("Could not parse expression syntax: %s", getExpressionError()));
return false;
}
}
hasLogFormat = req.HasParam("logFormat");
if (hasLogFormat) {
if (!req.ParamString("logFormat", &logFormat))
return false;
}

return true;
}

void Apply() {
if (hasCondition && !condition.empty()) {
BreakPointCond cond;
cond.debug = currentDebugMIPS;
cond.expressionString = condition;
cond.expression = compiledCondition;
CBreakPoints::ChangeBreakPointAddCond(address, cond);
} else if (hasCondition && condition.empty()) {
CBreakPoints::ChangeBreakPointRemoveCond(address);
}

if (hasLogFormat) {
CBreakPoints::ChangeBreakPointLogFormat(address, logFormat);
}

// TODO: Fix this interface.
if (hasLog && !hasEnabled) {
CBreakPoints::IsAddressBreakPoint(address, &enabled);
hasEnabled = true;
}
if (hasLog && hasEnabled) {
BreakAction result = BREAK_ACTION_IGNORE;
if (log)
result |= BREAK_ACTION_LOG;
if (enabled)
result |= BREAK_ACTION_PAUSE;
CBreakPoints::ChangeBreakPoint(address, result);
} else if (hasEnabled) {
CBreakPoints::ChangeBreakPoint(address, enabled);
}
}
};

void WebSocketCPUBreakpointAdd(DebuggerRequest &req) {
WebSocketCPUBreakpointParams params;
if (!params.Parse(req))
return;

CBreakPoints::AddBreakPoint(params.address);
params.Apply();
req.Respond();
}

void WebSocketCPUBreakpointUpdate(DebuggerRequest &req) {
WebSocketCPUBreakpointParams params;
if (!params.Parse(req))
return;
bool enabled;
if (!CBreakPoints::IsAddressBreakPoint(params.address, &enabled))
return req.Fail("Breakpoint not found");

params.Apply();
req.Respond();
}

void WebSocketCPUBreakpointRemove(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

uint32_t address;
if (!req.ParamU32("address", &address))
return;

CBreakPoints::RemoveBreakPoint(address);
req.Respond();
}

void WebSocketCPUBreakpointList(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

JsonWriter &json = req.Respond();
json.pushArray("breakpoints");
auto bps = CBreakPoints::GetBreakpoints();
for (const auto &bp : bps) {
if (bp.temporary)
continue;

json.pushDict();
json.writeUint("address", bp.addr);
json.writeBool("enabled", bp.IsEnabled());
json.writeBool("log", (bp.result & BREAK_ACTION_LOG) != 0);
if (bp.hasCond)
json.writeString("condition", bp.cond.expressionString);
else
json.writeNull("condition");
if (!bp.logFormat.empty())
json.writeString("logFormat", bp.logFormat);
else
json.writeNull("logFormat");
json.pop();
}
json.pop();
}
27 changes: 27 additions & 0 deletions Core/Debugger/WebSocket/BreakpointSubscriber.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 *WebSocketBreakpointInit(DebuggerEventHandlerMap &map);

void WebSocketCPUBreakpointAdd(DebuggerRequest &req);
void WebSocketCPUBreakpointUpdate(DebuggerRequest &req);
void WebSocketCPUBreakpointRemove(DebuggerRequest &req);
void WebSocketCPUBreakpointList(DebuggerRequest &req);
13 changes: 12 additions & 1 deletion Core/Debugger/WebSocket/WebSocketUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ static bool U32FromString(const char *str, uint32_t *out, bool allowFloat) {
return false;
}

bool DebuggerRequest::HasParam(const char *name, bool ignoreNull) {
const JsonNode *node = data.get(name);
if (!node) {
return false;
}
if (node->value.getTag() == JSON_NULL) {
return !ignoreNull;
}
return true;
}

bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatBits, DebuggerParamType type) {
bool allowLoose = type == DebuggerParamType::REQUIRED_LOOSE || type == DebuggerParamType::OPTIONAL_LOOSE;
bool required = type == DebuggerParamType::REQUIRED || type == DebuggerParamType::REQUIRED_LOOSE;
Expand Down Expand Up @@ -136,7 +147,7 @@ bool DebuggerRequest::ParamU32(const char *name, uint32_t *out, bool allowFloatB
return false;
}
if (tag != JSON_STRING) {
if (required || tag != JSON_NULL) {
if (type == DebuggerParamType::REQUIRED || tag != JSON_NULL) {
Fail(StringFromFormat("Invalid '%s' parameter type", name));
return false;
}
Expand Down
1 change: 1 addition & 0 deletions Core/Debugger/WebSocket/WebSocketUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ struct DebuggerRequest {
responseSent_ = true;
}

bool HasParam(const char *name, bool ignoreNull = false);
bool ParamU32(const char *name, uint32_t *out, bool allowFloatBits = false, DebuggerParamType type = DebuggerParamType::REQUIRED);
bool ParamBool(const char *name, bool *out, DebuggerParamType type = DebuggerParamType::REQUIRED);
bool ParamString(const char *name, std::string *out, DebuggerParamType type = DebuggerParamType::REQUIRED);
Expand Down
1 change: 1 addition & 0 deletions android/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Debugger/DisassemblyManager.cpp \
$(SRC)/Core/Debugger/SymbolMap.cpp \
$(SRC)/Core/Debugger/WebSocket.cpp \
$(SRC)/Core/Debugger/WebSocket/BreakpointSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/DisasmSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \
Expand Down

0 comments on commit 7273081

Please sign in to comment.