Merge pull request #11148 from unknownbrackets/debugger-arm

Enable debugger / breakpoints / stepping on ARM / ARM64
This commit is contained in:
Henrik Rydgård
2018-06-07 18:44:40 +02:00
committed by GitHub
34 changed files with 360 additions and 174 deletions
+52 -42
View File
@@ -87,7 +87,6 @@ void Core_NotifyLifecycle(CoreLifecycle stage) {
void Core_Stop() {
Core_UpdateState(CORE_POWERDOWN);
m_StepCond.notify_all();
}
bool Core_IsStepping() {
@@ -102,6 +101,14 @@ bool Core_IsInactive() {
return coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && !coreStatePending;
}
static inline void Core_StateProcessed() {
if (coreStatePending) {
std::lock_guard<std::mutex> guard(m_hInactiveMutex);
coreStatePending = false;
m_InactiveCond.notify_all();
}
}
void Core_WaitInactive() {
while (Core_IsActive()) {
std::unique_lock<std::mutex> guard(m_hInactiveMutex);
@@ -173,6 +180,7 @@ bool UpdateScreenScale(int width, int height) {
return false;
}
// Note: not used on Android.
void UpdateRunLoop() {
if (windowHidden && g_Config.bPauseWhenMinimized) {
sleep_ms(16);
@@ -191,6 +199,8 @@ void KeepScreenAwake() {
void Core_RunLoop(GraphicsContext *ctx) {
graphicsContext = ctx;
while ((GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT) {
// In case it was pending, we're not in game anymore. We won't get to Core_Run().
Core_StateProcessed();
time_update();
double startTime = time_now_d();
UpdateRunLoop();
@@ -227,22 +237,55 @@ void Core_RunLoop(GraphicsContext *ctx) {
}
void Core_DoSingleStep() {
std::lock_guard<std::mutex> guard(m_hStepMutex);
singleStepPending = true;
m_StepCond.notify_all();
}
void Core_UpdateSingleStep() {
std::lock_guard<std::mutex> guard(m_hStepMutex);
m_StepCond.notify_all();
}
void Core_SingleStep() {
currentMIPS->SingleStep();
if (coreState == CORE_STEPPING)
steppingCounter++;
}
static inline void CoreStateProcessed() {
if (coreStatePending) {
coreStatePending = false;
m_InactiveCond.notify_all();
static inline bool Core_WaitStepping() {
std::unique_lock<std::mutex> guard(m_hStepMutex);
if (!singleStepPending && coreState == CORE_STEPPING)
m_StepCond.wait(guard);
bool result = singleStepPending;
singleStepPending = false;
return result;
}
void Core_ProcessStepping() {
Core_StateProcessed();
// Check if there's any pending save state actions.
SaveState::Process();
if (coreState != CORE_STEPPING) {
return;
}
// We're not inside jit now, so it's safe to clear the breakpoints.
CBreakPoints::ClearTemporaryBreakPoints();
host->UpdateDisassembly();
host->UpdateMemView();
// Need to check inside the lock to avoid races.
bool doStep = Core_WaitStepping();
// We may still be stepping without singleStepPending to process a save state.
if (doStep && coreState == CORE_STEPPING) {
Core_SingleStep();
// Update disasm dialog.
host->UpdateDisassembly();
host->UpdateMemView();
}
}
@@ -251,9 +294,8 @@ static inline void CoreStateProcessed() {
void Core_Run(GraphicsContext *ctx) {
host->UpdateDisassembly();
while (true) {
reswitch:
if (GetUIState() != UISTATE_INGAME) {
CoreStateProcessed();
Core_StateProcessed();
if (GetUIState() == UISTATE_EXIT) {
UpdateRunLoop();
return;
@@ -264,51 +306,20 @@ reswitch:
switch (coreState) {
case CORE_RUNNING:
case CORE_STEPPING:
// enter a fast runloop
Core_RunLoop(ctx);
break;
// We should never get here on Android.
case CORE_STEPPING:
singleStepPending = false;
CoreStateProcessed();
// Check if there's any pending savestate actions.
SaveState::Process();
if (coreState == CORE_POWERDOWN) {
Core_StateProcessed();
return;
}
// wait for step command..
host->UpdateDisassembly();
host->UpdateMemView();
host->SendCoreWait(true);
{
std::unique_lock<std::mutex> guard(m_hStepMutex);
m_StepCond.wait(guard);
}
host->SendCoreWait(false);
// No step pending? Let's go back to the wait.
if (!singleStepPending || coreState != CORE_STEPPING) {
if (coreState == CORE_POWERDOWN) {
return;
}
goto reswitch;
}
Core_SingleStep();
// update disasm dialog
host->UpdateDisassembly();
host->UpdateMemView();
break;
case CORE_POWERUP:
case CORE_POWERDOWN:
case CORE_ERROR:
// Exit loop!!
CoreStateProcessed();
Core_StateProcessed();
return;
@@ -320,7 +331,6 @@ reswitch:
void Core_EnableStepping(bool step) {
if (step) {
sleep_ms(1);
host->SetDebugMode(true);
Core_UpdateState(CORE_STEPPING);
steppingCounter++;
+2
View File
@@ -34,6 +34,7 @@ void Core_SetGraphicsContext(GraphicsContext *ctx);
void Core_EnableStepping(bool step);
void Core_DoSingleStep();
void Core_UpdateSingleStep();
void Core_ProcessStepping();
// Changes every time we enter stepping.
int Core_GetSteppingCounter();
@@ -54,6 +55,7 @@ bool Core_IsStepping();
bool Core_IsActive();
bool Core_IsInactive();
// Warning: these currently work only on Windows.
void Core_WaitInactive();
void Core_WaitInactive(int milliseconds);
+12 -14
View File
@@ -33,11 +33,6 @@ u64 CBreakPoints::breakSkipFirstTicks_ = 0;
std::vector<MemCheck> CBreakPoints::memChecks_;
std::vector<MemCheck *> CBreakPoints::cleanupMemChecks_;
MemCheck::MemCheck()
{
numHits = 0;
}
void MemCheck::Log(u32 addr, bool write, int size, u32 pc) {
if (result & BREAK_ACTION_LOG) {
if (logFormat.empty()) {
@@ -493,17 +488,20 @@ u32 CBreakPoints::CheckSkipFirst()
return 0;
}
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges()
{
const std::vector<MemCheck> CBreakPoints::GetMemCheckRanges(bool write) {
std::vector<MemCheck> ranges = memChecks_;
for (auto it = memChecks_.begin(), end = memChecks_.end(); it != end; ++it)
{
MemCheck check = *it;
for (const auto &check : memChecks_) {
if (!(check.cond & MEMCHECK_READ) && !write)
continue;
if (!(check.cond & MEMCHECK_WRITE) && write)
continue;
MemCheck copy = check;
// Toggle the cached part of the address.
check.start ^= 0x40000000;
if (check.end != 0)
check.end ^= 0x40000000;
ranges.push_back(check);
copy.start ^= 0x40000000;
if (copy.end != 0)
copy.end ^= 0x40000000;
ranges.push_back(copy);
}
return ranges;
+18 -30
View File
@@ -21,8 +21,7 @@
#include "Core/Debugger/DebugInterface.h"
enum BreakAction
{
enum BreakAction {
BREAK_ACTION_IGNORE = 0x00,
BREAK_ACTION_LOG = 0x01,
BREAK_ACTION_PAUSE = 0x02,
@@ -37,35 +36,27 @@ static inline BreakAction operator | (const BreakAction &lhs, const BreakAction
return BreakAction((u32)lhs | (u32)rhs);
}
struct BreakPointCond
{
DebugInterface *debug;
struct BreakPointCond {
DebugInterface *debug = nullptr;
PostfixExpression expression;
std::string expressionString;
BreakPointCond() : debug(nullptr)
{
}
u32 Evaluate()
{
u32 Evaluate() {
u32 result;
if (debug->parseExpression(expression,result) == false) return 0;
if (debug->parseExpression(expression, result) == false)
return 0;
return result;
}
};
struct BreakPoint
{
BreakPoint() : hasCond(false) {}
struct BreakPoint {
u32 addr;
bool temporary;
BreakAction result;
BreakAction result = BREAK_ACTION_IGNORE;
std::string logFormat;
bool hasCond;
bool hasCond = false;
BreakPointCond cond;
bool IsEnabled() const {
@@ -80,8 +71,7 @@ struct BreakPoint
}
};
enum MemCheckCondition
{
enum MemCheckCondition {
MEMCHECK_READ = 0x01,
MEMCHECK_WRITE = 0x02,
MEMCHECK_WRITE_ONCHANGE = 0x04,
@@ -89,21 +79,19 @@ enum MemCheckCondition
MEMCHECK_READWRITE = 0x03,
};
struct MemCheck
{
MemCheck();
struct MemCheck {
u32 start;
u32 end;
MemCheckCondition cond;
BreakAction result;
MemCheckCondition cond = MEMCHECK_READ;
BreakAction result = BREAK_ACTION_IGNORE;
std::string logFormat;
u32 numHits;
u32 numHits = 0;
u32 lastPC;
u32 lastAddr;
int lastSize;
u32 lastPC = 0;
u32 lastAddr = 0;
int lastSize = 0;
BreakAction Action(u32 addr, bool write, int size, u32 pc);
void JitBefore(u32 addr, bool write, int size, u32 pc);
@@ -168,7 +156,7 @@ public:
static u32 CheckSkipFirst();
// Includes uncached addresses.
static const std::vector<MemCheck> GetMemCheckRanges();
static const std::vector<MemCheck> GetMemCheckRanges(bool write);
static const std::vector<MemCheck> GetMemChecks();
static const std::vector<BreakPoint> GetBreakpoints();
+13 -16
View File
@@ -152,10 +152,10 @@ static int Replace_memcpy() {
}
}
RETURN(destPtr);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
#endif
return 10 + bytes / 4; // approximation
}
@@ -194,10 +194,10 @@ static int Replace_memcpy_jak() {
currentMIPS->r[MIPS_REG_A2] = 0;
currentMIPS->r[MIPS_REG_A3] = destPtr + bytes;
RETURN(destPtr);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
#endif
return 5 + bytes * 8 + 2; // approximation. This is a slow memcpy - a byte copy loop..
}
@@ -222,10 +222,10 @@ static int Replace_memcpy16() {
}
}
RETURN(destPtr);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
#endif
return 10 + bytes / 4; // approximation
}
@@ -260,10 +260,10 @@ static int Replace_memcpy_swizzled() {
}
RETURN(0);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcPtr, false, pitch * h, currentMIPS->pc);
CBreakPoints::ExecMemCheck(destPtr, true, pitch * h, currentMIPS->pc);
#endif
return 10 + (pitch * h) / 4; // approximation
}
@@ -288,10 +288,10 @@ static int Replace_memmove() {
}
}
RETURN(destPtr);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcPtr, false, bytes, currentMIPS->pc);
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
#endif
return 10 + bytes / 4; // approximation
}
@@ -310,9 +310,9 @@ static int Replace_memset() {
}
}
RETURN(destPtr);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
#endif
return 10 + bytes / 4; // approximation
}
@@ -342,9 +342,8 @@ static int Replace_memset_jak() {
currentMIPS->r[MIPS_REG_A3] = -1;
RETURN(destPtr);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(destPtr, true, bytes, currentMIPS->pc);
#endif
return 5 + bytes * 6 + 2; // approximation (hm, inspecting the disasm this should be 5 + 6 * bytes + 2, but this is what works..)
}
@@ -591,11 +590,9 @@ static int Replace_dl_write_matrix() {
#endif
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(PARAM(2), false, count * sizeof(float), currentMIPS->pc);
CBreakPoints::ExecMemCheck(PARAM(0) + 2 * sizeof(u32), true, sizeof(u32), currentMIPS->pc);
CBreakPoints::ExecMemCheck(dlStruct[2], true, (count + 1) * sizeof(u32), currentMIPS->pc);
#endif
dlStruct[2] += (1 + count) * 4;
RETURN(dlStruct[2]);
+2 -2
View File
@@ -654,10 +654,10 @@ static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size)
*dstp++ = *srcp++;
}
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(src, false, size, currentMIPS->pc);
CBreakPoints::ExecMemCheck(dst, true, size, currentMIPS->pc);
#endif
return dst;
}
+2 -12
View File
@@ -766,9 +766,8 @@ int MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMo
delete [] imgbuf;
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(bufferPtr, true, videoImageSize, currentMIPS->pc);
#endif
return videoImageSize;
#endif // USE_FFMPEG
return 0;
@@ -822,9 +821,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
writeVideoLineRGBA(imgbuf, data, width);
data += m_desWidth * sizeof(u32);
imgbuf += videoLineSize;
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u32), true, width * sizeof(u32), currentMIPS->pc);
#endif
}
break;
@@ -834,9 +831,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
writeVideoLineABGR5650(imgbuf, data, width);
data += m_desWidth * sizeof(u16);
imgbuf += videoLineSize;
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc);
#endif
}
break;
@@ -846,9 +841,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
writeVideoLineABGR5551(imgbuf, data, width);
data += m_desWidth * sizeof(u16);
imgbuf += videoLineSize;
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc);
#endif
}
break;
@@ -858,9 +851,7 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid
writeVideoLineABGR4444(imgbuf, data, width);
data += m_desWidth * sizeof(u16);
imgbuf += videoLineSize;
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(bufferPtr + y * frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc);
#endif
}
break;
@@ -954,9 +945,8 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) {
if (!m_audioContext->Decode(audioFrame, frameSize, buffer, &outbytes)) {
ERROR_LOG(ME, "Audio (%s) decode failed during video playback", GetCodecName(m_audioType));
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(bufferPtr, true, outbytes, currentMIPS->pc);
#endif
}
return 0x2000;
-2
View File
@@ -50,8 +50,6 @@ public:
virtual void SaveSymbolMap() {}
virtual void SetWindowTitle(const char *message) {}
virtual void SendCoreWait(bool) {}
// While debugging is active, it's perfectly fine for these to block.
virtual bool GPUDebuggingActive() { return false; }
virtual void GPUNotifyCommand(u32 pc) {}
-1
View File
@@ -238,7 +238,6 @@ void ArmJit::GenerateFixedCode() {
B_CC(CC_EQ, outerLoop);
SetJumpTarget(badCoreState);
breakpointBailout = GetCodePtr();
SaveDowncount();
RestoreRoundingMode(true);
+1
View File
@@ -93,6 +93,7 @@ extern int logBlocks;
void ArmJit::Comp_FPULS(MIPSOpcode op)
{
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
s32 offset = (s16)(op & 0xFFFF);
int ft = _FT;
+3
View File
@@ -112,6 +112,7 @@ namespace MIPSComp
void ArmJit::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op & 0xFFFF);
MIPSGPReg rt = _RT;
MIPSGPReg rs = _RS;
@@ -120,6 +121,7 @@ namespace MIPSComp
if (!js.inDelaySlot) {
// Optimisation: Combine to single unaligned load/store
bool isLeft = (o == 34 || o == 42);
CheckMemoryBreakpoint(1);
MIPSOpcode nextOp = GetOffsetInstruction(1);
// Find a matching shift in opposite direction with opposite offset.
if (nextOp == (isLeft ? (op.encoding + (4<<26) - 3)
@@ -259,6 +261,7 @@ namespace MIPSComp
void ArmJit::Comp_ITypeMem(MIPSOpcode op)
{
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op&0xFFFF);
bool load = false;
MIPSGPReg rt = _RT;
+2
View File
@@ -226,6 +226,7 @@ namespace MIPSComp
void ArmJit::Comp_SV(MIPSOpcode op) {
NEON_IF_AVAILABLE(CompNEON_SV);
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
s32 offset = (signed short)(op & 0xFFFC);
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
@@ -332,6 +333,7 @@ namespace MIPSComp
{
NEON_IF_AVAILABLE(CompNEON_SVQ);
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int imm = (signed short)(op&0xFFFC);
int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5);
+2
View File
@@ -159,6 +159,7 @@ void ArmJit::CompNEON_VecDo3(MIPSOpcode op) {
void ArmJit::CompNEON_SV(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
// Remember to use single lane stores here and not VLDR/VSTR - switching usage
// between NEON and VFPU can be expensive on some chips.
@@ -276,6 +277,7 @@ inline int MIPS_GET_VQVT(u32 op) {
void ArmJit::CompNEON_SVQ(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op & 0xFFFC);
int vt = MIPS_GET_VQVT(op.encoding);
+111 -4
View File
@@ -26,6 +26,7 @@
#include "Core/Config.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/MemMap.h"
@@ -45,7 +46,7 @@
using namespace ArmJitConstants;
void DisassembleArm(const u8 *data, int size) {
static void DisassembleArm(const u8 *data, int size) {
char temp[256];
for (int i = 0; i < size; i += 4) {
const u32 *codePtr = (const u32 *)(data + i);
@@ -69,6 +70,33 @@ void DisassembleArm(const u8 *data, int size) {
}
}
static u32 JitBreakpoint() {
// Should we skip this breakpoint?
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
return 0;
BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc);
if ((result & BREAK_ACTION_PAUSE) == 0)
return 0;
return 1;
}
static u32 JitMemCheck(u32 pc) {
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
return 0;
// Note: pc may be the delay slot.
const auto op = Memory::Read_Instruction(pc, true);
s32 offset = (s16)(op & 0xFFFF);
if (MIPSGetInfo(op) & IS_VFPU)
offset &= 0xFFFC;
u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset;
CBreakPoints::ExecOpMemCheck(addr, pc);
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1;
}
namespace MIPSComp
{
using namespace ArmGen;
@@ -86,6 +114,10 @@ ArmJit::ArmJit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), fpr(m
INFO_LOG(JIT, "ARM JIT initialized: %d MB of code space", GetSpaceLeft() / (1024 * 1024));
js.startDefaultPrefix = mips_->HasDefaultPrefix();
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
CBreakPoints::SetSkipFirst(0);
}
ArmJit::~ArmJit() {
@@ -104,6 +136,10 @@ void ArmJit::DoState(PointerWrap &p)
} else {
js.hasSetRounding = 1;
}
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
CBreakPoints::SetSkipFirst(0);
}
void ArmJit::UpdateFCR31() {
@@ -158,13 +194,16 @@ void ArmJit::EatInstruction(MIPSOpcode op) {
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
}
CheckJitBreakpoint(GetCompilerPC() + 4, 0);
js.numInstructions++;
js.compilerPC += 4;
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
}
void ArmJit::CompileDelaySlot(int flags)
{
void ArmJit::CompileDelaySlot(int flags) {
// Need to offset the downcount which was already incremented for the branch + delay slot.
CheckJitBreakpoint(GetCompilerPC() + 4, -2);
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
// delay slot, we're screwed.
@@ -295,6 +334,9 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b)
while (js.compiling)
{
gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages
// Jit breakpoints are quite fast, so let's do them in release too.
CheckJitBreakpoint(GetCompilerPC(), 0);
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
//MIPSInfo info = MIPSGetInfo(inst);
//if (info & IS_VFPU) {
@@ -492,7 +534,18 @@ void ArmJit::Comp_ReplacementFunc(MIPSOpcode op)
return;
}
if (entry->flags & REPFLAG_DISABLED) {
u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC());
bool disabled = (entry->flags & REPFLAG_DISABLED) != 0;
if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) {
// We don't need to disable hooks, the code will still run.
if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) {
// Any breakpoint at the func entry was already tripped, so we can still run the replacement.
// That's a common case - just to see how often the replacement hits.
disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32));
}
}
if (disabled) {
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
} else if (entry->jitReplaceFunc) {
MIPSReplaceFunc repl = entry->jitReplaceFunc;
@@ -686,6 +739,60 @@ void ArmJit::WriteSyscallExit()
B((const void *)dispatcherCheckCoreState);
}
bool ArmJit::CheckJitBreakpoint(u32 addr, int downcountOffset) {
if (CBreakPoints::IsAddressBreakPoint(addr)) {
MRS(R8);
FlushAll();
MOVI2R(SCRATCHREG1, GetCompilerPC());
MovToPC(SCRATCHREG1);
RestoreRoundingMode();
QuickCallFunction(SCRATCHREG1, &JitBreakpoint);
// If 0, the conditional breakpoint wasn't taken.
CMPI2R(R0, 0, SCRATCHREG2);
FixupBranch skip = B_CC(CC_EQ);
WriteDownCount(downcountOffset);
ApplyRoundingMode();
B((const void *)dispatcherCheckCoreState);
SetJumpTarget(skip);
ApplyRoundingMode();
_MSR(true, false, R8);
return true;
}
return false;
}
bool ArmJit::CheckMemoryBreakpoint(int instructionOffset) {
if (CBreakPoints::HasMemChecks()) {
int off = instructionOffset + (js.inDelaySlot ? 1 : 0);
MRS(R8);
FlushAll();
RestoreRoundingMode();
MOVI2R(R0, GetCompilerPC());
MovToPC(R0);
if (off != 0)
ADDI2R(R0, R0, off, SCRATCHREG2);
QuickCallFunction(SCRATCHREG2, &JitMemCheck);
// If 0, the breakpoint wasn't tripped.
CMPI2R(R0, 0, SCRATCHREG2);
FixupBranch skip = B_CC(CC_EQ);
WriteDownCount(-1 - off);
ApplyRoundingMode();
B((const void *)dispatcherCheckCoreState);
SetJumpTarget(skip);
ApplyRoundingMode();
_MSR(true, false, R8);
return true;
}
return false;
}
void ArmJit::Comp_DoNothing(MIPSOpcode op) { }
MIPSOpcode ArmJit::GetOriginalOp(MIPSOpcode op) {
+2 -2
View File
@@ -215,6 +215,8 @@ private:
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInR(ArmGen::ARMReg Reg);
void WriteSyscallExit();
bool CheckJitBreakpoint(u32 addr, int downcountOffset);
bool CheckMemoryBreakpoint(int instructionOffset = 0);
// Utility compilation functions
void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely);
@@ -311,8 +313,6 @@ public:
const u8 *restoreRoundingMode;
const u8 *applyRoundingMode;
const u8 *breakpointBailout;
};
} // namespace MIPSComp
-1
View File
@@ -275,7 +275,6 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) {
B(CC_EQ, outerLoop);
SetJumpTarget(badCoreState);
breakpointBailout = GetCodePtr();
SaveStaticRegisters();
RestoreRoundingMode(true);
+1 -1
View File
@@ -81,6 +81,7 @@ void Arm64Jit::Comp_FPU3op(MIPSOpcode op) {
void Arm64Jit::Comp_FPULS(MIPSOpcode op)
{
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
// Surprisingly, these work fine alraedy.
@@ -88,7 +89,6 @@ void Arm64Jit::Comp_FPULS(MIPSOpcode op)
int ft = _FT;
MIPSGPReg rs = _RS;
// u32 addr = R(rs) + offset;
// logBlocks = 1;
std::vector<FixupBranch> skips;
switch (op >> 26) {
case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1
+3
View File
@@ -111,6 +111,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_ITypeMemLR(MIPSOpcode op, bool load) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op & 0xFFFF);
MIPSGPReg rt = _RT;
MIPSGPReg rs = _RS;
@@ -119,6 +120,7 @@ namespace MIPSComp {
if (!js.inDelaySlot) {
// Optimisation: Combine to single unaligned load/store
bool isLeft = (o == 34 || o == 42);
CheckMemoryBreakpoint(1);
MIPSOpcode nextOp = GetOffsetInstruction(1);
// Find a matching shift in opposite direction with opposite offset.
if (nextOp == (isLeft ? (op.encoding + (4 << 26) - 3) : (op.encoding - (4 << 26) + 3))) {
@@ -255,6 +257,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_ITypeMem(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int offset = (signed short)(op & 0xFFFF);
bool load = false;
+2
View File
@@ -202,6 +202,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_SV(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
s32 offset = (signed short)(op & 0xFFFC);
int vt = ((op >> 16) & 0x1f) | ((op & 3) << 5);
@@ -275,6 +276,7 @@ namespace MIPSComp {
void Arm64Jit::Comp_SVQ(MIPSOpcode op) {
CONDITIONAL_DISABLE;
CheckMemoryBreakpoint();
int imm = (signed short)(op&0xFFFC);
int vt = (((op >> 16) & 0x1f)) | ((op&1) << 5);
+110 -3
View File
@@ -28,6 +28,7 @@
#include "Core/Config.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Breakpoints.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/MemMap.h"
@@ -44,7 +45,7 @@
using namespace Arm64JitConstants;
void DisassembleArm64Print(const u8 *data, int size) {
static void DisassembleArm64Print(const u8 *data, int size) {
std::vector<std::string> lines = DisassembleArm64(data, size);
for (auto s : lines) {
ILOG("%s", s.c_str());
@@ -60,6 +61,33 @@ void DisassembleArm64Print(const u8 *data, int size) {
ILOG("===");*/
}
static u32 JitBreakpoint() {
// Should we skip this breakpoint?
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
return 0;
BreakAction result = CBreakPoints::ExecBreakPoint(currentMIPS->pc);
if ((result & BREAK_ACTION_PAUSE) == 0)
return 0;
return 1;
}
static u32 JitMemCheck(u32 pc) {
if (CBreakPoints::CheckSkipFirst() == currentMIPS->pc)
return 0;
// Note: pc may be the delay slot.
const auto op = Memory::Read_Instruction(pc, true);
s32 offset = (s16)(op & 0xFFFF);
if (MIPSGetInfo(op) & IS_VFPU)
offset &= 0xFFFC;
u32 addr = currentMIPS->r[MIPS_GET_RS(op)] + offset;
CBreakPoints::ExecOpMemCheck(addr, pc);
return coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME ? 0 : 1;
}
namespace MIPSComp
{
using namespace Arm64Gen;
@@ -80,6 +108,10 @@ Arm64Jit::Arm64Jit(MIPSState *mips) : blocks(mips, this), gpr(mips, &js, &jo), f
GenerateFixedCode(jo);
js.startDefaultPrefix = mips_->HasDefaultPrefix();
js.currentRoundingFunc = convertS0ToSCRATCH1[mips_->fcr31 & 3];
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
CBreakPoints::SetSkipFirst(0);
}
Arm64Jit::~Arm64Jit() {
@@ -99,6 +131,10 @@ void Arm64Jit::DoState(PointerWrap &p) {
}
// Note: we can't update the currentRoundingFunc here because fcr31 wasn't loaded yet.
// The debugger sets this so that "go" on a breakpoint will actually... go.
// But if they reset, we can end up hitting it by mistake, since it's based on PC and ticks.
CBreakPoints::SetSkipFirst(0);
}
void Arm64Jit::UpdateFCR31() {
@@ -151,12 +187,16 @@ void Arm64Jit::EatInstruction(MIPSOpcode op) {
ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot.");
}
CheckJitBreakpoint(GetCompilerPC() + 4, 0);
js.numInstructions++;
js.compilerPC += 4;
js.downcountAmount += MIPSGetInstructionCycleEstimate(op);
}
void Arm64Jit::CompileDelaySlot(int flags) {
// Need to offset the downcount which was already incremented for the branch + delay slot.
CheckJitBreakpoint(GetCompilerPC() + 4, -2);
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
// delay slot, we're screwed.
@@ -285,8 +325,10 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
js.numInstructions = 0;
while (js.compiling) {
gpr.SetCompilerPC(GetCompilerPC()); // Let it know for log messages
// Jit breakpoints are quite fast, so let's do them in release too.
CheckJitBreakpoint(GetCompilerPC(), 0);
MIPSOpcode inst = Memory::Read_Opcode_JIT(GetCompilerPC());
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
MIPSCompileOp(inst, this);
@@ -473,7 +515,18 @@ void Arm64Jit::Comp_ReplacementFunc(MIPSOpcode op)
return;
}
if (entry->flags & REPFLAG_DISABLED) {
u32 funcSize = g_symbolMap->GetFunctionSize(GetCompilerPC());
bool disabled = (entry->flags & REPFLAG_DISABLED) != 0;
if (!disabled && funcSize != SymbolMap::INVALID_ADDRESS && funcSize > sizeof(u32)) {
// We don't need to disable hooks, the code will still run.
if ((entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) == 0) {
// Any breakpoint at the func entry was already tripped, so we can still run the replacement.
// That's a common case - just to see how often the replacement hits.
disabled = CBreakPoints::RangeContainsBreakPoint(GetCompilerPC() + sizeof(u32), funcSize - sizeof(u32));
}
}
if (disabled) {
MIPSCompileOp(Memory::Read_Instruction(GetCompilerPC(), true), this);
} else if (entry->jitReplaceFunc) {
MIPSReplaceFunc repl = entry->jitReplaceFunc;
@@ -646,6 +699,60 @@ void Arm64Jit::WriteSyscallExit() {
B((const void *)dispatcherCheckCoreState);
}
bool Arm64Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) {
if (CBreakPoints::IsAddressBreakPoint(addr)) {
MRS(FLAGTEMPREG, FIELD_NZCV);
FlushAll();
MOVI2R(SCRATCH1, GetCompilerPC());
MovToPC(SCRATCH1);
RestoreRoundingMode();
QuickCallFunction(SCRATCH1_64, &JitBreakpoint);
// If 0, the conditional breakpoint wasn't taken.
CMPI2R(W0, 0);
FixupBranch skip = B(CC_EQ);
WriteDownCount(downcountOffset);
ApplyRoundingMode();
B((const void *)dispatcherCheckCoreState);
SetJumpTarget(skip);
ApplyRoundingMode();
_MSR(FIELD_NZCV, FLAGTEMPREG);
return true;
}
return false;
}
bool Arm64Jit::CheckMemoryBreakpoint(int instructionOffset) {
if (CBreakPoints::HasMemChecks()) {
int off = instructionOffset + (js.inDelaySlot ? 1 : 0);
MRS(FLAGTEMPREG, FIELD_NZCV);
FlushAll();
RestoreRoundingMode();
MOVI2R(W0, GetCompilerPC());
MovToPC(W0);
if (off != 0)
ADDI2R(W0, W0, off * 4);
QuickCallFunction(SCRATCH2_64, &JitMemCheck);
// If 0, the breakpoint wasn't tripped.
CMPI2R(W0, 0);
FixupBranch skip = B(CC_EQ);
WriteDownCount(-1 - off);
ApplyRoundingMode();
B((const void *)dispatcherCheckCoreState);
SetJumpTarget(skip);
ApplyRoundingMode();
_MSR(FIELD_NZCV, FLAGTEMPREG);
return true;
}
return false;
}
void Arm64Jit::Comp_DoNothing(MIPSOpcode op) { }
MIPSOpcode Arm64Jit::GetOriginalOp(MIPSOpcode op) {
+2 -2
View File
@@ -214,6 +214,8 @@ private:
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInR(Arm64Gen::ARM64Reg Reg);
void WriteSyscallExit();
bool CheckJitBreakpoint(u32 addr, int downcountOffset);
bool CheckMemoryBreakpoint(int instructionOffset = 0);
// Utility compilation functions
void BranchFPFlag(MIPSOpcode op, CCFlags cc, bool likely);
@@ -272,8 +274,6 @@ public:
const u8 *dispatcher;
const u8 *dispatcherNoCheck;
const u8 *breakpointBailout;
const u8 *saveStaticRegisters;
const u8 *loadStaticRegisters;
-2
View File
@@ -181,8 +181,6 @@ public:
const u8 *dispatcherPCInR0;
const u8 *dispatcher;
const u8 *dispatcherNoCheck;
const u8 *breakpointBailout;
};
typedef void (MipsJit::*MIPSCompileFunc)(MIPSOpcode opcode);
-5
View File
@@ -211,11 +211,6 @@ void Jit::GenerateFixedCode(JitOptions &jo) {
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
breakpointBailout = GetCodePtr();
RestoreRoundingMode(true);
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
// Let's spare the pre-generated code from unprotect-reprotect.
endOfPregeneratedCode = AlignCodePage();
EndWrite();
-2
View File
@@ -320,8 +320,6 @@ private:
const u8 *dispatcherNoCheck;
const u8 *dispatcherInEAXNoCheck;
const u8 *breakpointBailout;
const u8 *restoreRoundingMode;
const u8 *applyRoundingMode;
+2 -9
View File
@@ -376,17 +376,10 @@ void JitSafeMem::MemCheckImm(MemoryOpType type)
void JitSafeMem::MemCheckAsm(MemoryOpType type)
{
const auto memchecks = CBreakPoints::GetMemCheckRanges();
bool possible = false;
const auto memchecks = CBreakPoints::GetMemCheckRanges(type == MEM_WRITE);
bool possible = !memchecks.empty();
for (auto it = memchecks.begin(), end = memchecks.end(); it != end; ++it)
{
if (!(it->cond & MEMCHECK_READ) && type == MEM_READ)
continue;
if (!(it->cond & MEMCHECK_WRITE) && type == MEM_WRITE)
continue;
possible = true;
FixupBranch skipNext, skipNextRange;
if (it->end != 0)
{
+1 -2
View File
@@ -446,9 +446,8 @@ void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) {
for (size_t i = 0; i < _iLength; i++)
Write_U8(_iValue, (u32)(_Address + i));
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(_Address, true, _iLength, currentMIPS->pc);
#endif
}
} // namespace
-6
View File
@@ -33,9 +33,7 @@ inline void Memcpy(const u32 to_address, const void *from_data, const u32 len)
u8 *to = GetPointer(to_address);
if (to) {
memcpy(to, from_data, len);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc);
#endif
}
// if not, GetPointer will log.
}
@@ -45,9 +43,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len)
const u8 *from = GetPointer(from_address);
if (from) {
memcpy(to_data, from, len);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(from_address, false, len, currentMIPS->pc);
#endif
}
// if not, GetPointer will log.
}
@@ -55,9 +51,7 @@ inline void Memcpy(void *to_data, const u32 from_address, const u32 len)
inline void Memcpy(const u32 to_address, const u32 from_address, const u32 len)
{
Memcpy(GetPointer(to_address), from_address, len);
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(to_address, true, len, currentMIPS->pc);
#endif
}
void Memset(const u32 _Address, const u8 _Data, const u32 _iLength);
+14
View File
@@ -430,10 +430,24 @@ void PSP_EndHostFrame() {
}
}
void PSP_RunLoopWhileState() {
// We just run the CPU until we get to vblank. This will quickly sync up pretty nicely.
// The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully...
int blockTicks = usToCycles(1000000 / 10);
// Run until CORE_NEXTFRAME
while (coreState == CORE_RUNNING || coreState == CORE_STEPPING) {
PSP_RunLoopFor(blockTicks);
}
}
void PSP_RunLoopUntil(u64 globalticks) {
SaveState::Process();
if (coreState == CORE_POWERDOWN || coreState == CORE_ERROR) {
return;
} else if (coreState == CORE_STEPPING) {
Core_ProcessStepping();
return;
}
mipsr4k.RunLoopUntil(globalticks);
+1
View File
@@ -70,6 +70,7 @@ void PSP_Shutdown();
void PSP_BeginHostFrame();
void PSP_EndHostFrame();
void PSP_RunLoopWhileState();
void PSP_RunLoopUntil(u64 globalticks);
void PSP_RunLoopFor(int cycles);
-2
View File
@@ -2576,10 +2576,8 @@ void GPUCommon::DoBlockTransfer(u32 skipDrawReason) {
framebufferManager_->NotifyBlockTransferAfter(dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, width, height, bpp, skipDrawReason);
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc);
CBreakPoints::ExecMemCheck(dstBasePtr + (dstY * dstStride + dstX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
#endif
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
cyclesExecuted += ((height * width * bpp) * 16) / 10;
-2
View File
@@ -344,10 +344,8 @@ void NullGPU::ExecuteOp(u32 op, u32 diff) {
memcpy(dst, src, width * bpp);
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc);
CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
#endif
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
cyclesExecuted += ((height * width * bpp) * 16) / 10;
-2
View File
@@ -648,10 +648,8 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff) {
memcpy(dst, src, width * bpp);
}
#ifndef MOBILE_DEVICE
CBreakPoints::ExecMemCheck(srcBasePtr + (srcY * srcStride + srcX) * bpp, false, height * srcStride * bpp, currentMIPS->pc);
CBreakPoints::ExecMemCheck(dstBasePtr + (srcY * dstStride + srcX) * bpp, true, height * dstStride * bpp, currentMIPS->pc);
#endif
// TODO: Correct timing appears to be 1.9, but erring a bit low since some of our other timing is inaccurate.
cyclesExecuted += ((height * width * bpp) * 16) / 10;
+1 -8
View File
@@ -1193,14 +1193,7 @@ void EmuScreen::render() {
PSP_BeginHostFrame();
// We just run the CPU until we get to vblank. This will quickly sync up pretty nicely.
// The actual number of cycles doesn't matter so much here as we will break due to CORE_NEXTFRAME, most of the time hopefully...
int blockTicks = usToCycles(1000000 / 10);
// Run until CORE_NEXTFRAME
while (coreState == CORE_RUNNING) {
PSP_RunLoopFor(blockTicks);
}
PSP_RunLoopWhileState();
// Hopefully coreState is now CORE_NEXTFRAME
if (coreState == CORE_NEXTFRAME) {
+1 -2
View File
@@ -809,7 +809,6 @@ void CDisasm::SetDebugMode(bool _bDebug, bool switchPC)
if (_bDebug && GetUIState() == UISTATE_INGAME && PSP_IsInited())
{
Core_WaitInactive(TEMP_BREAKPOINT_WAIT_MS);
CBreakPoints::ClearTemporaryBreakPoints();
breakpointList->reloadBreakpoints();
threadList->reloadThreads();
stackTraceView->loadStackTrace();
@@ -913,4 +912,4 @@ void CDisasm::UpdateDialog(bool _bComplete)
// redraw. all others are updated manually
InvalidateRect (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW), NULL, TRUE);
UpdateWindow (GetDlgItem(m_hDlg, IDC_DEBUGMEMVIEW));
}
}