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