Skip to content

Commit

Permalink
Debugger: Add stepping to WebSocket API.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Jun 8, 2018
1 parent f66738e commit e746a2d
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Expand Up @@ -1416,6 +1416,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/WebSocket/LogBroadcaster.h Core/Debugger/WebSocket/LogBroadcaster.h
Core/Debugger/WebSocket/SteppingBroadcaster.cpp Core/Debugger/WebSocket/SteppingBroadcaster.cpp
Core/Debugger/WebSocket/SteppingBroadcaster.h Core/Debugger/WebSocket/SteppingBroadcaster.h
Core/Debugger/WebSocket/SteppingSubscriber.cpp
Core/Debugger/WebSocket/SteppingSubscriber.h
Core/Debugger/WebSocket/WebSocketUtils.cpp Core/Debugger/WebSocket/WebSocketUtils.cpp
Core/Debugger/WebSocket/WebSocketUtils.h Core/Debugger/WebSocket/WebSocketUtils.h
Core/Dialog/PSPDialog.cpp Core/Dialog/PSPDialog.cpp
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj
Expand Up @@ -191,6 +191,7 @@
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" /> <ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" /> <ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\SteppingBroadcaster.cpp" /> <ClCompile Include="Debugger\WebSocket\SteppingBroadcaster.cpp" />
<ClCompile Include="Debugger\WebSocket\SteppingSubscriber.cpp" />
<ClCompile Include="Debugger\WebSocket\WebSocketUtils.cpp" /> <ClCompile Include="Debugger\WebSocket\WebSocketUtils.cpp" />
<ClCompile Include="FileSystems\BlobFileSystem.cpp" /> <ClCompile Include="FileSystems\BlobFileSystem.cpp" />
<ClCompile Include="HLE\KUBridge.cpp" /> <ClCompile Include="HLE\KUBridge.cpp" />
Expand Down Expand Up @@ -543,6 +544,7 @@
<ClInclude Include="Debugger\WebSocket.h" /> <ClInclude Include="Debugger\WebSocket.h" />
<ClInclude Include="Debugger\WebSocket\GameSubscriber.h" /> <ClInclude Include="Debugger\WebSocket\GameSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" /> <ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" /> <ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />
<ClInclude Include="Debugger\WebSocket\CPUCoreSubscriber.h" /> <ClInclude Include="Debugger\WebSocket\CPUCoreSubscriber.h" />
<ClInclude Include="Debugger\WebSocket\GameBroadcaster.h" /> <ClInclude Include="Debugger\WebSocket\GameBroadcaster.h" />
Expand Down
6 changes: 6 additions & 0 deletions Core/Core.vcxproj.filters
Expand Up @@ -719,6 +719,9 @@
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp"> <ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter> <Filter>Debugger\WebSocket</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Debugger\WebSocket\SteppingSubscriber.cpp">
<Filter>Debugger\WebSocket</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ELF\ElfReader.h"> <ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1325,6 +1328,9 @@
<ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h"> <ClInclude Include="Debugger\WebSocket\DisasmSubscriber.h">
<Filter>Debugger\WebSocket</Filter> <Filter>Debugger\WebSocket</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h">
<Filter>Debugger\WebSocket</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="CMakeLists.txt" /> <None Include="CMakeLists.txt" />
Expand Down
2 changes: 2 additions & 0 deletions Core/Debugger/WebSocket.cpp
Expand Up @@ -50,6 +50,7 @@
#include "Core/Debugger/WebSocket/CPUCoreSubscriber.h" #include "Core/Debugger/WebSocket/CPUCoreSubscriber.h"
#include "Core/Debugger/WebSocket/DisasmSubscriber.h" #include "Core/Debugger/WebSocket/DisasmSubscriber.h"
#include "Core/Debugger/WebSocket/GameSubscriber.h" #include "Core/Debugger/WebSocket/GameSubscriber.h"
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"


typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map); typedef void *(*SubscriberInit)(DebuggerEventHandlerMap &map);
typedef void (*Subscribershutdown)(void *p); typedef void (*Subscribershutdown)(void *p);
Expand All @@ -62,6 +63,7 @@ static const std::vector<SubscriberInfo> subscribers({
{ &WebSocketCPUCoreInit, nullptr }, { &WebSocketCPUCoreInit, nullptr },
{ &WebSocketDisasmInit, &WebSocketDisasmShutdown }, { &WebSocketDisasmInit, &WebSocketDisasmShutdown },
{ &WebSocketGameInit, nullptr }, { &WebSocketGameInit, nullptr },
{ &WebSocketSteppingInit, &WebSocketSteppingShutdown },
}); });


// To handle webserver restart, keep track of how many running. // To handle webserver restart, keep track of how many running.
Expand Down
3 changes: 3 additions & 0 deletions Core/Debugger/WebSocket/CPUCoreSubscriber.cpp
Expand Up @@ -71,6 +71,9 @@ void WebSocketCPUResume(DebuggerRequest &req) {
return req.Fail("CPU not stepping"); return req.Fail("CPU not stepping");
} }


if (currentMIPS->inDelaySlot) {
Core_DoSingleStep();
}
Core_EnableStepping(false); Core_EnableStepping(false);
} }


Expand Down
224 changes: 224 additions & 0 deletions Core/Debugger/WebSocket/SteppingSubscriber.cpp
@@ -0,0 +1,224 @@
// 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/SteppingSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/Core.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/MIPS/MIPSStackWalk.h"

using namespace MIPSAnalyst;

struct WebSocketSteppingState {
WebSocketSteppingState() {
disasm_.setCpu(currentDebugMIPS);
}

void Into(DebuggerRequest &req);
void Over(DebuggerRequest &req);
void Out(DebuggerRequest &req);
void RunUntil(DebuggerRequest &req);
void HLE(DebuggerRequest &req);

protected:
u32 GetNextAddress();
int GetNextInstructionCount();
void PrepareResume();

DisassemblyManager disasm_;
};

void *WebSocketSteppingInit(DebuggerEventHandlerMap &map) {
auto p = new WebSocketSteppingState();
map["cpu.stepInto"] = std::bind(&WebSocketSteppingState::Into, p, std::placeholders::_1);
map["cpu.stepOver"] = std::bind(&WebSocketSteppingState::Over, p, std::placeholders::_1);
map["cpu.stepOut"] = std::bind(&WebSocketSteppingState::Out, p, std::placeholders::_1);
map["cpu.runUntil"] = std::bind(&WebSocketSteppingState::RunUntil, p, std::placeholders::_1);
map["cpu.nextHLE"] = std::bind(&WebSocketSteppingState::HLE, p, std::placeholders::_1);

return p;
}

void WebSocketSteppingShutdown(void *p) {
delete static_cast<WebSocketSteppingState *>(p);
}

// Single step into the next instruction (cpu.stepInto)
//
// No parameters.
//
// No immediate response. A cpu.stepping event will be sent once complete.
void WebSocketSteppingState::Into(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

if (!Core_IsStepping()) {
Core_EnableStepping(true);
return;
}

// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);

int c = GetNextInstructionCount();
for (int i = 0; i < c; ++i) {
Core_DoSingleStep();
}
}

// Step over the next instruction (cpu.stepOver)
//
// Note: this jumps over function calls, but also delay slots.
//
// No parameters.
//
// No immediate response. A cpu.stepping event will be sent once complete.
void WebSocketSteppingState::Over(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

MipsOpcodeInfo info = GetOpcodeInfo(currentDebugMIPS, currentMIPS->pc);
u32 breakpointAddress = GetNextAddress();
if (info.isBranch) {
if (info.isConditional && !info.isLinkedBranch) {
if (info.conditionMet) {
breakpointAddress = info.branchTarget;
} else {
// Skip over the delay slot.
breakpointAddress += 4;
}
} else {
if (info.isLinkedBranch) {
// jal or jalr - a function call. Skip the delay slot.
breakpointAddress += 4;
} else {
// j - for absolute branches, set the breakpoint at the branch target.
breakpointAddress = info.branchTarget;
}
}
}

PrepareResume();
// Could have advanced to the breakpoint already in PrepareResume().
if (currentMIPS->pc != breakpointAddress) {
CBreakPoints::AddBreakPoint(breakpointAddress, true);
Core_EnableStepping(false);
}
}

// Step out of a function based on a stack walk (cpu.stepOut)
//
// No parameters.
//
// No immediate response. A cpu.stepping event will be sent once complete.
void WebSocketSteppingState::Out(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

auto threads = GetThreadsInfo();
u32 entry = currentMIPS->pc;
u32 stackTop = 0;
for (const DebugThreadInfo &th : threads) {
if (th.isCurrent) {
entry = th.entrypoint;
stackTop = th.initialStack;
break;
}
}

auto frames = MIPSStackWalk::Walk(currentMIPS->pc, currentMIPS->r[MIPS_REG_RA], currentMIPS->r[MIPS_REG_SP], entry, stackTop);
if (frames.size() < 2) {
// TODO: Respond in some way?
return;
}

u32 breakpointAddress = frames[1].pc;
PrepareResume();
// Could have advanced to the breakpoint already in PrepareResume().
if (currentMIPS->pc != breakpointAddress) {
CBreakPoints::AddBreakPoint(breakpointAddress, true);
Core_EnableStepping(false);
}
}

// Run until a certain address (cpu.stepOut)
//
// Parameters:
// - address: number parameter for destination.
//
// No immediate response. A cpu.stepping event will be sent once complete.
void WebSocketSteppingState::RunUntil(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

u32 address = 0;
if (!req.ParamU32("address", &address)) {
// Error already sent.
return;
}

bool wasAtAddress = currentMIPS->pc == address;
PrepareResume();
// We may have arrived already if PauseResume() stepped out of a delay slot.
if (currentMIPS->pc != address || wasAtAddress) {
CBreakPoints::AddBreakPoint(address, true);
Core_EnableStepping(false);
}
}

// Jump after the next HLE call (cpu.nextHLE)
//
// No parameters.
//
// No immediate response. A cpu.stepping event will be sent once complete.
void WebSocketSteppingState::HLE(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive()) {
return req.Fail("CPU not started");
}

PrepareResume();
hleDebugBreak();
Core_EnableStepping(false);
}

u32 WebSocketSteppingState::GetNextAddress() {
u32 current = disasm_.getStartAddress(currentMIPS->pc);
return disasm_.getNthNextAddress(current, 1);
}

int WebSocketSteppingState::GetNextInstructionCount() {
return (GetNextAddress() - currentMIPS->pc) / 4;
}

void WebSocketSteppingState::PrepareResume() {
if (currentMIPS->inDelaySlot) {
Core_DoSingleStep();
} else {
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
CBreakPoints::SetSkipFirst(currentMIPS->pc);
}
}

23 changes: 23 additions & 0 deletions Core/Debugger/WebSocket/SteppingSubscriber.h
@@ -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 *WebSocketSteppingInit(DebuggerEventHandlerMap &map);
void WebSocketSteppingShutdown(void *p);
1 change: 1 addition & 0 deletions android/jni/Android.mk
Expand Up @@ -308,6 +308,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/GameSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \
$(SRC)/Core/Debugger/WebSocket/SteppingSubscriber.cpp \
$(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \ $(SRC)/Core/Debugger/WebSocket/WebSocketUtils.cpp \
$(SRC)/Core/Dialog/PSPDialog.cpp \ $(SRC)/Core/Dialog/PSPDialog.cpp \
$(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \ $(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \
Expand Down

0 comments on commit e746a2d

Please sign in to comment.