Crash screen: Add a very dangerous and inaccurate "Resume" function.

Not sure if there's much point really but maybe..
This commit is contained in:
Henrik Rydgård 2020-12-19 20:26:02 +01:00
parent a550aacf96
commit 6c8ee35826
4 changed files with 56 additions and 7 deletions

View file

@ -17,6 +17,9 @@
#include "ppsspp_config.h"
#include <cstdint>
#include <unordered_set>
#include "Common/MachineContext.h"
#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)
@ -37,9 +40,13 @@
namespace Memory {
static int64_t g_numReportedBadAccesses = 0;
const uint8_t *g_lastCrashAddress;
std::unordered_set<const uint8_t *> g_ignoredAddresses;
void MemFault_Init() {
g_numReportedBadAccesses = 0;
g_lastCrashAddress = nullptr;
g_ignoredAddresses.clear();
}
#ifdef MACHINE_CONTEXT_SUPPORTED
@ -67,10 +74,21 @@ static bool DisassembleNativeAt(const uint8_t *codePtr, int instructionSize, std
return false;
}
bool MemFault_MayBeResumable() {
return g_lastCrashAddress != nullptr;
}
void MemFault_IgnoreLastCrash() {
g_ignoredAddresses.insert(g_lastCrashAddress);
}
bool HandleFault(uintptr_t hostAddress, void *ctx) {
SContext *context = (SContext *)ctx;
const uint8_t *codePtr = (uint8_t *)(context->CTX_PC);
// We set this later if we think it can be resumed from.
g_lastCrashAddress = nullptr;
// TODO: Check that codePtr is within the current JIT space.
bool inJitSpace = MIPSComp::jit && MIPSComp::jit->CodeInRange(codePtr);
if (!inJitSpace) {
@ -91,6 +109,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
return false;
}
// OK, a guest executable did a bad access. Take care of it.
uint32_t guestAddress = hostAddress - baseAddress;
@ -149,7 +168,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
type = MemoryExceptionType::UNKNOWN;
}
if (success && g_Config.bIgnoreBadMemAccess) {
if (success && (g_Config.bIgnoreBadMemAccess || g_ignoredAddresses.find(codePtr) != g_ignoredAddresses.end())) {
if (!info.isMemoryWrite) {
// It was a read. Fill the destination register with 0.
// TODO
@ -165,7 +184,10 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
uint32_t approximatePC = currentMIPS->pc;
Core_MemoryExceptionInfo(guestAddress, approximatePC, type, infoString);
// Redirect execution to a crash handler that will exit the game immediately.
// There's a small chance we can resume from this type of crash.
g_lastCrashAddress = codePtr;
// Redirect execution to a crash handler that will switch to CoreState::CORE_RUNTIME_ERROR immediately.
context->CTX_PC = (uintptr_t)MIPSComp::jit->GetCrashHandler();
ERROR_LOG(MEMMAP, "Bad memory access detected! %08x (%p) Stopping emulation. Info:\n%s", guestAddress, (void *)hostAddress, infoString.c_str());
}

View file

@ -6,6 +6,14 @@ namespace Memory {
void MemFault_Init();
// Used by the hacky "Resume" button.
//
// TODO: Add a way to actually resume to the next instruction,
// rather than just jumping into the dispatcher again and hoping for the best. That will be
// a little tricky though, with per-backend work.
bool MemFault_MayBeResumable();
void MemFault_IgnoreLastCrash();
// Called by exception handlers. We simply filter out accesses to PSP RAM and otherwise
// just leave it as-is.
bool HandleFault(uintptr_t hostAddress, void *context);

View file

@ -49,6 +49,7 @@
#include "Core/Core.h"
#include "Core/Host.h"
#include "Core/KeyMap.h"
#include "Core/MemFault.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "GPU/GPUState.h"
@ -1025,6 +1026,9 @@ void EmuScreen::CreateViews() {
if (g_Config.bShowDeveloperMenu) {
root_->Add(new Button(dev->T("DevMenu")))->OnClick.Handle(this, &EmuScreen::OnDevTools);
}
resumeButton_ = root_->Add(new Button(dev->T("Resume"), new AnchorLayoutParams(bounds.centerX(), NONE, NONE, 60, true)));
resumeButton_->OnClick.Handle(this, &EmuScreen::OnResume);
resumeButton_->SetVisibility(V_GONE);
cardboardDisableButton_ = root_->Add(new Button(sc->T("Cardboard VR OFF"), new AnchorLayoutParams(bounds.centerX(), NONE, NONE, 30, true)));
cardboardDisableButton_->OnClick.Handle(this, &EmuScreen::OnDisableCardboard);
@ -1135,15 +1139,29 @@ UI::EventReturn EmuScreen::OnDisableCardboard(UI::EventParams &params) {
return UI::EVENT_DONE;
}
UI::EventReturn EmuScreen::OnChat(UI::EventParams& params) {
if (chatButtons->GetVisibility() == UI::V_VISIBLE) chatButtons->SetVisibility(UI::V_GONE);
UI::EventReturn EmuScreen::OnChat(UI::EventParams &params) {
if (chatButtons->GetVisibility() == UI::V_VISIBLE) {
chatButtons->SetVisibility(UI::V_GONE);
}
screenManager()->push(new ChatMenu());
return UI::EVENT_DONE;
}
UI::EventReturn EmuScreen::OnResume(UI::EventParams &params) {
if (coreState == CoreState::CORE_RUNTIME_ERROR) {
// Force it!
Memory::MemFault_IgnoreLastCrash();
coreState = CoreState::CORE_RUNNING;
}
return UI::EVENT_DONE;
}
void EmuScreen::update() {
using namespace UI;
UIScreen::update();
onScreenMessagesView_->SetVisibility(g_Config.bShowOnScreenMessages ? UI::Visibility::V_VISIBLE : UI::Visibility::V_GONE);
onScreenMessagesView_->SetVisibility(g_Config.bShowOnScreenMessages ? V_VISIBLE : V_GONE);
resumeButton_->SetVisibility(coreState == CoreState::CORE_RUNTIME_ERROR && Memory::MemFault_MayBeResumable() ? V_VISIBLE : V_GONE);
if (bootPending_) {
bootGame(gamePath_);

View file

@ -49,13 +49,13 @@ public:
bool key(const KeyInput &key) override;
bool axis(const AxisInput &axis) override;
protected:
private:
void CreateViews() override;
UI::EventReturn OnDevTools(UI::EventParams &params);
UI::EventReturn OnDisableCardboard(UI::EventParams &params);
UI::EventReturn OnChat(UI::EventParams &params);
UI::EventReturn OnResume(UI::EventParams &params);
private:
void bootGame(const std::string &filename);
bool bootAllowStorage(const std::string &filename);
void bootComplete();
@ -105,6 +105,7 @@ private:
UI::VisibilityTween *loadingViewVisible_ = nullptr;
UI::Spinner *loadingSpinner_ = nullptr;
UI::TextView *loadingTextView_ = nullptr;
UI::Button *resumeButton_ = nullptr;
UI::Button *cardboardDisableButton_ = nullptr;
OnScreenMessagesView *onScreenMessagesView_ = nullptr;