RegTest: Log state and RAM hashes on exit

Useful for checking determinism.
This commit is contained in:
Stenzek 2024-12-29 15:42:13 +10:00
parent 1ed9e609a5
commit 82a843c121
No known key found for this signature in database
3 changed files with 68 additions and 12 deletions

View File

@ -195,9 +195,6 @@ static void UpdatePerGameMemoryCards();
static std::unique_ptr<MemoryCard> GetMemoryCardForSlot(u32 slot, MemoryCardType type);
static void UpdateMultitaps();
/// Returns the maximum size of a save state, considering the current configuration.
static size_t GetMaxSaveStateSize();
static std::string GetMediaPathFromSaveState(const char* path);
static bool SaveUndoLoadState();
static void UpdateMemorySaveStateSettings();
@ -2929,7 +2926,18 @@ bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bo
// Updating game/loading settings can turn on hardcore mode. Catch this.
Achievements::DisableHardcoreMode();
StateWrapper sw(buffer.state_data.cspan(0, buffer.state_size), StateWrapper::Mode::Read, buffer.version);
return LoadStateDataFromBuffer(buffer.state_data.cspan(0, buffer.state_size), buffer.version, error, update_display);
}
bool System::LoadStateDataFromBuffer(std::span<const u8> data, u32 version, Error* error, bool update_display)
{
if (IsShutdown()) [[unlikely]]
{
Error::SetStringView(error, "System is invalid.");
return 0;
}
StateWrapper sw(data, StateWrapper::Mode::Read, version);
if (!DoState(sw, update_display))
{
Error::SetStringView(error, "Save state stream is corrupted.");
@ -3197,12 +3205,6 @@ bool System::SaveState(std::string path, Error* error, bool backup_existing_save
bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size /* = 256 */)
{
if (IsShutdown()) [[unlikely]]
{
Error::SetStringView(error, "System is invalid.");
return 0;
}
buffer->title = s_state.running_game_title;
buffer->serial = s_state.running_game_serial;
buffer->version = SAVE_STATE_VERSION;
@ -3250,14 +3252,25 @@ bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screen
if (buffer->state_data.empty())
buffer->state_data.resize(GetMaxSaveStateSize());
StateWrapper sw(buffer->state_data.span(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
return SaveStateDataToBuffer(buffer->state_data, &buffer->state_size, error);
}
bool System::SaveStateDataToBuffer(std::span<u8> data, size_t* data_size, Error* error)
{
if (IsShutdown()) [[unlikely]]
{
Error::SetStringView(error, "System is invalid.");
return 0;
}
StateWrapper sw(data, StateWrapper::Mode::Write, SAVE_STATE_VERSION);
if (!DoState(sw, false))
{
Error::SetStringView(error, "DoState() failed");
return false;
}
buffer->state_size = sw.GetPosition();
*data_size = sw.GetPosition();
return true;
}

View File

@ -260,11 +260,18 @@ bool BootSystem(SystemBootParameters parameters, Error* error);
void PauseSystem(bool paused);
void ResetSystem();
/// Returns the maximum size of a save state, considering the current configuration.
size_t GetMaxSaveStateSize();
/// Loads state from the specified path.
bool LoadState(const char* path, Error* error, bool save_undo_state);
bool SaveState(std::string path, Error* error, bool backup_existing_save, bool ignore_memcard_busy);
bool SaveResumeState(Error* error);
/// State data access, use with care as the media path is not updated.
bool LoadStateDataFromBuffer(std::span<const u8> data, u32 version, Error* error, bool update_display);
bool SaveStateDataToBuffer(std::span<u8> data, size_t* data_size, Error* error);
/// Runs the VM until the CPU execution is canceled.
void Execute();

View File

@ -2,12 +2,15 @@
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "core/achievements.h"
#include "core/bus.h"
#include "core/controller.h"
#include "core/fullscreen_ui.h"
#include "core/game_list.h"
#include "core/gpu.h"
#include "core/gpu_backend.h"
#include "core/gpu_thread.h"
#include "core/host.h"
#include "core/spu.h"
#include "core/system.h"
#include "core/system_private.h"
@ -27,6 +30,7 @@
#include "common/log.h"
#include "common/memory_settings_interface.h"
#include "common/path.h"
#include "common/sha256_digest.h"
#include "common/string_util.h"
#include "common/timer.h"
@ -38,6 +42,7 @@
LOG_CHANNEL(Host);
namespace RegTestHost {
static bool ParseCommandLineParameters(int argc, char* argv[], std::optional<SystemBootParameters>& autoboot);
static void PrintCommandLineVersion();
static void PrintCommandLineHelp(const char* progname);
@ -46,8 +51,10 @@ static void InitializeEarlyConsole();
static void HookSignals();
static bool SetFolders();
static bool SetNewDataRoot(const std::string& filename);
static void DumpSystemStateHashes();
static std::string GetFrameDumpFilename(u32 frame);
static void GPUThreadEntryPoint();
} // namespace RegTestHost
static std::unique_ptr<MemorySettingsInterface> s_base_settings_interface;
@ -320,7 +327,10 @@ void Host::PumpMessagesOnCPUThread()
{
s_frames_remaining--;
if (s_frames_remaining == 0)
{
RegTestHost::DumpSystemStateHashes();
System::ShutdownSystem(false);
}
}
void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */)
@ -528,6 +538,32 @@ void RegTestHost::GPUThreadEntryPoint()
GPUThread::Internal::GPUThreadEntryPoint();
}
void RegTestHost::DumpSystemStateHashes()
{
Error error;
// don't save full state on gpu dump, it's not going to be complete...
if (!System::IsReplayingGPUDump())
{
DynamicHeapArray<u8> state_data(System::GetMaxSaveStateSize());
size_t state_data_size;
if (!System::SaveStateDataToBuffer(state_data, &state_data_size, &error))
{
ERROR_LOG("Failed to save system state: {}", error.GetDescription());
return;
}
INFO_LOG("Save State Hash: {}",
SHA256Digest::DigestToString(SHA256Digest::GetDigest(state_data.cspan(0, state_data_size))));
INFO_LOG("RAM Hash: {}",
SHA256Digest::DigestToString(SHA256Digest::GetDigest(std::span<const u8>(Bus::g_ram, Bus::g_ram_size))));
INFO_LOG("SPU RAM Hash: {}", SHA256Digest::DigestToString(SHA256Digest::GetDigest(SPU::GetRAM())));
}
INFO_LOG("VRAM Hash: {}", SHA256Digest::DigestToString(SHA256Digest::GetDigest(
std::span<const u8>(reinterpret_cast<const u8*>(g_vram), VRAM_SIZE))));
}
void RegTestHost::InitializeEarlyConsole()
{
const bool was_console_enabled = Log::IsConsoleOutputEnabled();