mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-22 21:09:52 +00:00
Don't block the render thread while the CPU is paused. This is a prereq for imgui debuggers.
This commit is contained in:
parent
758faac445
commit
3a5968ba33
123
Core/Core.cpp
123
Core/Core.cpp
@ -35,6 +35,7 @@
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/MIPSDebugInterface.h"
|
||||
#include "Core/SaveState.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/MemFault.h"
|
||||
@ -51,16 +52,33 @@
|
||||
#include "Windows/InputDevice.h"
|
||||
#endif
|
||||
|
||||
static std::condition_variable m_StepCond;
|
||||
static std::mutex m_hStepMutex;
|
||||
// Step command to execute next
|
||||
static std::mutex g_stepMutex;
|
||||
struct StepCommand {
|
||||
CPUStepType type;
|
||||
int param;
|
||||
const char *reason;
|
||||
u32 relatedAddr;
|
||||
bool empty() const {
|
||||
return type == CPUStepType::None;
|
||||
}
|
||||
void clear() {
|
||||
type = CPUStepType::None;
|
||||
param = 0;
|
||||
reason = "";
|
||||
relatedAddr = 0;
|
||||
}
|
||||
};
|
||||
static StepCommand g_stepCommand;
|
||||
|
||||
// This is so that external threads can wait for the CPU to become inactive.
|
||||
static std::condition_variable m_InactiveCond;
|
||||
static std::mutex m_hInactiveMutex;
|
||||
static int g_singleStepsPending = 0;
|
||||
|
||||
static int steppingCounter = 0;
|
||||
static const char *steppingReason = "";
|
||||
static uint32_t steppingAddress = 0;
|
||||
static std::set<CoreLifecycleFunc> lifecycleFuncs;
|
||||
static std::set<CoreStopRequestFunc> stopFuncs;
|
||||
|
||||
static bool windowHidden = false;
|
||||
static bool powerSaving = false;
|
||||
|
||||
@ -126,14 +144,6 @@ 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() && !GPUStepping::IsStepping()) {
|
||||
std::unique_lock<std::mutex> guard(m_hInactiveMutex);
|
||||
@ -227,19 +237,23 @@ void Core_RunLoop(GraphicsContext *ctx) {
|
||||
NativeFrame(ctx);
|
||||
}
|
||||
|
||||
void Core_DoSingleStep() {
|
||||
std::lock_guard<std::mutex> guard(m_hStepMutex);
|
||||
g_singleStepsPending++;
|
||||
m_StepCond.notify_all();
|
||||
bool Core_RequestSingleStep(CPUStepType type, int stepSize) {
|
||||
std::lock_guard<std::mutex> guard(g_stepMutex);
|
||||
if (g_stepCommand.type != CPUStepType::None) {
|
||||
ERROR_LOG(Log::CPU, "Can't submit two steps in one frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
void Core_UpdateSingleStep() {
|
||||
std::lock_guard<std::mutex> guard(m_hStepMutex);
|
||||
m_StepCond.notify_all();
|
||||
g_stepCommand = { type, stepSize };
|
||||
return true;
|
||||
}
|
||||
|
||||
// See comment in header.
|
||||
void Core_PerformStep(DebugInterface *cpu, CPUStepType stepType, int stepSize) {
|
||||
// Handles more advanced step types (used by the debugger).
|
||||
// stepSize is to support stepping through compound instructions like fused lui+ladd (li).
|
||||
// Yes, our disassembler does support those.
|
||||
// Doesn't return the new address, as that's just mips->getPC().
|
||||
// Internal use.
|
||||
static void Core_PerformStep(MIPSDebugInterface *cpu, CPUStepType stepType, int stepSize) {
|
||||
switch (stepType) {
|
||||
case CPUStepType::Into:
|
||||
{
|
||||
@ -247,8 +261,8 @@ void Core_PerformStep(DebugInterface *cpu, CPUStepType stepType, int stepSize) {
|
||||
u32 newAddress = currentPc + stepSize;
|
||||
// If the current PC is on a breakpoint, the user still wants the step to happen.
|
||||
CBreakPoints::SetSkipFirst(currentPc);
|
||||
for (int i = 0; i < (newAddress - currentPc) / 4; i++) {
|
||||
Core_DoSingleStep();
|
||||
for (int i = 0; i < (int)(newAddress - currentPc) / 4; i++) {
|
||||
currentMIPS->SingleStep();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -316,22 +330,8 @@ void Core_PerformStep(DebugInterface *cpu, CPUStepType stepType, int stepSize) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline int Core_WaitStepping() {
|
||||
std::unique_lock<std::mutex> guard(m_hStepMutex);
|
||||
// We only wait 16ms so that we can still draw UI or react to events.
|
||||
double sleepStart = time_now_d();
|
||||
if (!g_singleStepsPending && coreState == CORE_STEPPING)
|
||||
m_StepCond.wait_for(guard, std::chrono::milliseconds(16));
|
||||
double sleepEnd = time_now_d();
|
||||
DisplayNotifySleep(sleepEnd - sleepStart);
|
||||
|
||||
int result = g_singleStepsPending;
|
||||
g_singleStepsPending = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Core_ProcessStepping() {
|
||||
Core_StateProcessed();
|
||||
void Core_ProcessStepping(MIPSDebugInterface *cpu) {
|
||||
coreStatePending = false;
|
||||
|
||||
// Check if there's any pending save state actions.
|
||||
SaveState::Process();
|
||||
@ -352,22 +352,27 @@ void Core_ProcessStepping() {
|
||||
}
|
||||
|
||||
// Need to check inside the lock to avoid races.
|
||||
int doSteps = Core_WaitStepping();
|
||||
std::lock_guard<std::mutex> guard(g_stepMutex);
|
||||
|
||||
if (coreState != CORE_STEPPING || g_stepCommand.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We may still be stepping without singleStepPending to process a save state.
|
||||
if (doSteps && coreState == CORE_STEPPING) {
|
||||
Core_ResetException();
|
||||
|
||||
for (int i = 0; i < doSteps; i++) {
|
||||
currentMIPS->SingleStep();
|
||||
if (!g_stepCommand.empty()) {
|
||||
Core_PerformStep(cpu, g_stepCommand.type, g_stepCommand.param);
|
||||
if (g_stepCommand.type == CPUStepType::Into) {
|
||||
// We're already done. The other step types will resume the CPU.
|
||||
System_Notify(SystemNotification::DISASSEMBLY_AFTERSTEP);
|
||||
}
|
||||
g_stepCommand.clear();
|
||||
steppingCounter++;
|
||||
}
|
||||
|
||||
// Update disasm dialog.
|
||||
System_Notify(SystemNotification::DISASSEMBLY_AFTERSTEP);
|
||||
System_Notify(SystemNotification::MEM_VIEW);
|
||||
}
|
||||
}
|
||||
|
||||
// Many platforms, like Android, do not call this function but handle things on their own.
|
||||
// Instead they simply call NativeFrame directly.
|
||||
@ -375,7 +380,6 @@ bool Core_Run(GraphicsContext *ctx) {
|
||||
System_Notify(SystemNotification::DISASSEMBLY);
|
||||
while (true) {
|
||||
if (GetUIState() != UISTATE_INGAME) {
|
||||
Core_StateProcessed();
|
||||
if (GetUIState() == UISTATE_EXIT) {
|
||||
// Not sure why we do a final frame here?
|
||||
NativeFrame(ctx);
|
||||
@ -388,11 +392,9 @@ bool Core_Run(GraphicsContext *ctx) {
|
||||
switch (coreState) {
|
||||
case CORE_RUNNING:
|
||||
case CORE_STEPPING:
|
||||
Core_StateProcessed();
|
||||
// enter a fast runloop
|
||||
Core_RunLoop(ctx);
|
||||
if (coreState == CORE_POWERDOWN) {
|
||||
Core_StateProcessed();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@ -402,7 +404,6 @@ bool Core_Run(GraphicsContext *ctx) {
|
||||
case CORE_BOOT_ERROR:
|
||||
case CORE_RUNTIME_ERROR:
|
||||
// Exit loop!!
|
||||
Core_StateProcessed();
|
||||
return true;
|
||||
|
||||
case CORE_NEXTFRAME:
|
||||
@ -411,27 +412,30 @@ bool Core_Run(GraphicsContext *ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Free-threaded (hm, possibly except tracing).
|
||||
void Core_Break(const char *reason, u32 relatedAddress) {
|
||||
// Stop the tracer
|
||||
mipsTracer.stop_tracing();
|
||||
|
||||
Core_UpdateState(CORE_STEPPING);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_stepMutex);
|
||||
steppingCounter++;
|
||||
_assert_msg_(reason != nullptr, "No reason specified for break");
|
||||
steppingReason = reason;
|
||||
steppingAddress = relatedAddress;
|
||||
|
||||
Core_UpdateState(CORE_STEPPING);
|
||||
}
|
||||
System_Notify(SystemNotification::DEBUG_MODE_CHANGE);
|
||||
}
|
||||
|
||||
// Free-threaded (or at least should be)
|
||||
void Core_Resume() {
|
||||
// Clear the exception if we resume.
|
||||
Core_ResetException();
|
||||
coreState = CORE_RUNNING;
|
||||
coreStatePending = false;
|
||||
m_StepCond.notify_all();
|
||||
System_Notify(SystemNotification::DEBUG_MODE_CHANGE);
|
||||
}
|
||||
|
||||
// Should be called from the EmuThread.
|
||||
bool Core_NextFrame() {
|
||||
if (coreState == CORE_RUNNING) {
|
||||
coreState = CORE_NEXTFRAME;
|
||||
@ -447,8 +451,11 @@ int Core_GetSteppingCounter() {
|
||||
|
||||
SteppingReason Core_GetSteppingReason() {
|
||||
SteppingReason r;
|
||||
r.reason = steppingReason;
|
||||
r.relatedAddress = steppingAddress;
|
||||
std::lock_guard<std::mutex> lock(g_stepMutex);
|
||||
if (!g_stepCommand.empty()) {
|
||||
r.reason = g_stepCommand.reason;
|
||||
r.relatedAddress = g_stepCommand.relatedAddr;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
14
Core/Core.h
14
Core/Core.h
@ -24,7 +24,6 @@
|
||||
#include "Core/CoreParameter.h"
|
||||
|
||||
class GraphicsContext;
|
||||
class DebugInterface;
|
||||
|
||||
// called from emu thread
|
||||
void UpdateRunLoop(GraphicsContext *ctx);
|
||||
@ -53,16 +52,9 @@ void Core_Break(const char *reason, u32 relatedAddress = 0);
|
||||
// void Core_Step(CPUStepType type); // CPUStepType::None not allowed
|
||||
void Core_Resume();
|
||||
|
||||
// Handles more advanced step types (used by the debugger).
|
||||
// stepSize is to support stepping through compound instructions like fused lui+ladd (li).
|
||||
// Yes, our disassembler does support those.
|
||||
// Doesn't return the new address, as that's just mips->getPC().
|
||||
void Core_PerformStep(DebugInterface *cpu, CPUStepType stepType, int stepSize);
|
||||
|
||||
// Refactor.
|
||||
void Core_DoSingleStep();
|
||||
void Core_UpdateSingleStep();
|
||||
void Core_ProcessStepping();
|
||||
// This should be called externally.
|
||||
// Can fail if another step type was requested this frame.
|
||||
bool Core_RequestSingleStep(CPUStepType stepType, int stepSize);
|
||||
|
||||
bool Core_ShouldRunBehind();
|
||||
bool Core_MustRunBehind();
|
||||
|
@ -90,7 +90,7 @@ void WebSocketCPUResume(DebuggerRequest &req) {
|
||||
|
||||
CBreakPoints::SetSkipFirst(currentMIPS->pc);
|
||||
if (currentMIPS->inDelaySlot) {
|
||||
Core_DoSingleStep();
|
||||
Core_RequestSingleStep(CPUStepType::Into, 1);
|
||||
}
|
||||
Core_Resume();
|
||||
}
|
||||
|
@ -107,9 +107,7 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) {
|
||||
CBreakPoints::SetSkipFirst(currentMIPS->pc);
|
||||
|
||||
int c = GetNextInstructionCount(cpuDebug);
|
||||
for (int i = 0; i < c; ++i) {
|
||||
Core_DoSingleStep();
|
||||
}
|
||||
Core_RequestSingleStep(CPUStepType::Into, c);
|
||||
} else {
|
||||
uint32_t breakpointAddress = cpuDebug->GetPC();
|
||||
PrepareResume();
|
||||
@ -278,7 +276,8 @@ int WebSocketSteppingState::GetNextInstructionCount(DebugInterface *cpuDebug) {
|
||||
|
||||
void WebSocketSteppingState::PrepareResume() {
|
||||
if (currentMIPS->inDelaySlot) {
|
||||
Core_DoSingleStep();
|
||||
// Delay slot instructions are never joined, so we pass 1.
|
||||
Core_RequestSingleStep(CPUStepType::Into, 1);
|
||||
} else {
|
||||
// If the current PC is on a breakpoint, the user doesn't want to do nothing.
|
||||
CBreakPoints::SetSkipFirst(currentMIPS->pc);
|
||||
|
@ -419,7 +419,6 @@ namespace SaveState
|
||||
// Don't actually run it until next frame.
|
||||
// It's possible there might be a duplicate but it won't hurt us.
|
||||
needsProcess = true;
|
||||
Core_UpdateSingleStep();
|
||||
}
|
||||
|
||||
void Load(const Path &filename, int slot, Callback callback, void *cbUserData)
|
||||
|
@ -118,6 +118,9 @@ static volatile bool pspIsIniting = false;
|
||||
static volatile bool pspIsQuitting = false;
|
||||
static volatile bool pspIsRebooting = false;
|
||||
|
||||
// This is called on EmuThread before RunLoop.
|
||||
void Core_ProcessStepping(MIPSDebugInterface *cpu);
|
||||
|
||||
void ResetUIState() {
|
||||
globalUIState = UISTATE_MENU;
|
||||
}
|
||||
@ -386,7 +389,6 @@ void Core_UpdateState(CoreState newState) {
|
||||
if ((coreState == CORE_RUNNING || coreState == CORE_NEXTFRAME) && newState != CORE_RUNNING)
|
||||
coreStatePending = true;
|
||||
coreState = newState;
|
||||
Core_UpdateSingleStep();
|
||||
}
|
||||
|
||||
void Core_UpdateDebugStats(bool collectStats) {
|
||||
@ -629,7 +631,7 @@ void PSP_RunLoopUntil(u64 globalticks) {
|
||||
if (coreState == CORE_POWERDOWN || coreState == CORE_BOOT_ERROR || coreState == CORE_RUNTIME_ERROR) {
|
||||
return;
|
||||
} else if (coreState == CORE_STEPPING) {
|
||||
Core_ProcessStepping();
|
||||
Core_ProcessStepping(currentDebugMIPS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -74,8 +74,8 @@ static void SetPauseAction(PauseAction act, bool waitComplete = true) {
|
||||
pauseAction = act;
|
||||
pauseLock.unlock();
|
||||
|
||||
if (coreState == CORE_STEPPING && act != PAUSE_CONTINUE)
|
||||
Core_UpdateSingleStep();
|
||||
// if (coreState == CORE_STEPPING && act != PAUSE_CONTINUE)
|
||||
// Core_UpdateSingleStep();
|
||||
|
||||
actionComplete = false;
|
||||
pauseWait.notify_all();
|
||||
|
@ -208,7 +208,7 @@ void CDisasm::step(CPUStepType stepType) {
|
||||
lastTicks_ = CoreTiming::GetTicks();
|
||||
|
||||
u32 stepSize = ptr->getInstructionSizeAt(cpu->GetPC());
|
||||
Core_PerformStep(cpu, stepType, stepSize);
|
||||
Core_RequestSingleStep(stepType, stepSize);
|
||||
}
|
||||
|
||||
void CDisasm::runToLine() {
|
||||
|
Loading…
Reference in New Issue
Block a user