Files
ppsspp/Windows/Debugger/DumpMemoryWindow.cpp
T
Henrik RydgårdandClaude Opus 5 75ff0d406c Rename Memory::Lock() to Core_LockAgainstShutdown(), move it to Core
It stopped being about memory when CPU_Shutdown started holding it across the
whole teardown - it's what keeps kernel objects, the symbol map and the memory
map from being freed while another thread reads them. The old name invited the
reading that it locks memory *access*, which it has never done.

Memory::Reinit() now holds it across both halves rather than relying on
Memory::Shutdown()'s own acquire: between Shutdown() and Init() there is no
memory map at all, and a reader could slip into that gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 13:11:16 +02:00

276 lines
7.9 KiB
C++

#include <algorithm>
#include <cstdio>
#include <mutex>
#include "Common/Data/Encoding/Utf8.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/MemMap.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Windows/Debugger/DumpMemoryWindow.h"
#include "Windows/resource.h"
#include "Windows/W32Util/ShellUtil.h"
DumpMemoryWindow* DumpMemoryWindow::bp;
void DumpMemoryWindow::HandleBrowseClick(HWND hwnd) {
std::wstring buffer;
HWND filenameWnd = GetDlgItem(hwnd, IDC_DUMP_FILENAME);
buffer.resize(GetWindowTextLengthW(filenameWnd) + 1);
GetWindowTextW(filenameWnd, &buffer[0], (int)buffer.size());
std::string fn = ConvertWStringToUTF8(buffer);
bool result = W32Util::BrowseForFileName(false, hwnd, L"Select filename", nullptr, L"dump.bin", nullptr, nullptr, fn);
if (result) {
filenameChosen_ = true;
buffer = ConvertUTF8ToWString(fn);
SetWindowTextW(filenameWnd, buffer.c_str());
}
}
INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
switch (iMsg)
{
case WM_INITDIALOG:
bp->changeMode(hwnd,bp->selectedMode);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_DUMP_USERMEMORY:
switch (HIWORD(wParam))
{
case BN_CLICKED:
bp->changeMode(hwnd,MODE_RAM);
break;
}
break;
case IDC_DUMP_VRAM:
switch (HIWORD(wParam))
{
case BN_CLICKED:
bp->changeMode(hwnd,MODE_VRAM);
break;
}
break;
case IDC_DUMP_SCRATCHPAD:
switch (HIWORD(wParam))
{
case BN_CLICKED:
bp->changeMode(hwnd,MODE_SCRATCHPAD);
break;
}
break;
case IDC_DUMP_CUSTOMRANGE:
switch (HIWORD(wParam))
{
case BN_CLICKED:
bp->changeMode(hwnd,MODE_CUSTOM);
break;
}
break;
case IDC_DUMP_BROWSEFILENAME:
switch (HIWORD(wParam))
{
case BN_CLICKED:
bp->HandleBrowseClick(hwnd);
break;
}
break;
case IDOK:
if (bp->fetchDialogData(hwnd)) {
bool includeReplacements = SendMessage(GetDlgItem(hwnd, IDC_DUMP_INCLUDEHACKS), BM_GETCHECK, 0, 0) != 0;
// Route the actual memory dump to the CPU thread instead of forcing the emulator
// to pause and poking at it directly from this GUI thread - see
// Core_RunOnCPUThread() in Core.h. MessageBoxA() is modal, so it stays outside the
// queued callback.
enum class Outcome { NotInited, OpenFailed, Success } outcome = Outcome::NotInited;
Core_RunOnCPUThread([&] {
CoreShutdownLock coreLock = Core_LockAgainstShutdown();
if (!PSP_IsInited())
return;
FILE *output = _wfopen(bp->fileName_.c_str(), L"wb");
if (output == nullptr) {
outcome = Outcome::OpenFailed;
return;
}
if (includeReplacements) {
fwrite(Memory::GetPointerOrException(bp->start), 1, bp->size, output);
} else {
auto savedReplacements = SaveAndClearReplacements();
if (MIPSComp::jit) {
auto savedBlocks = MIPSComp::jit->SaveAndClearEmuHackOps();
fwrite(Memory::GetPointerOrException(bp->start), 1, bp->size, output);
MIPSComp::jit->RestoreSavedEmuHackOps(savedBlocks);
} else {
fwrite(Memory::GetPointerOrException(bp->start), 1, bp->size, output);
}
RestoreSavedReplacements(savedReplacements);
}
fclose(output);
outcome = Outcome::Success;
});
if (outcome == Outcome::OpenFailed) {
char errorMessage[2048];
snprintf(errorMessage, sizeof(errorMessage), "Could not open file \"%S\".", bp->fileName_.c_str());
MessageBoxA(hwnd, errorMessage, "Error", MB_OK);
} else if (outcome == Outcome::Success) {
MessageBoxA(hwnd, "Done.", "Information", MB_OK);
EndDialog(hwnd, true);
}
}
break;
case IDCANCEL:
EndDialog(hwnd,false);
break;
}
break;
case WM_KEYDOWN:
break;
}
return FALSE;
}
static bool isInInterval(u32 start, u32 end, u32 value)
{
return start <= value && value < end;
}
bool DumpMemoryWindow::fetchDialogData(HWND hwnd)
{
char str[256],errorMessage[256];
PostfixExpression exp;
// parse start address
GetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_STARTADDRESS),str,256);
if (initExpression(cpu, str,exp) == false
|| parseExpression(cpu, exp,start) == false)
{
snprintf(errorMessage, sizeof(errorMessage), "Invalid address expression \"%s\".",str);
MessageBoxA(hwnd,errorMessage,"Error",MB_OK);
return false;
}
// parse size
GetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_SIZE),str,256);
if (initExpression(cpu, str,exp) == false
|| parseExpression(cpu, exp,size) == false)
{
snprintf(errorMessage, sizeof(errorMessage), "Invalid size expression \"%s\".",str);
MessageBoxA(hwnd,errorMessage,"Error",MB_OK);
return false;
}
if (size == 0)
{
MessageBoxA(hwnd,"Invalid size 0.","Error",MB_OK);
return false;
}
// get filename
fileName_.resize(GetWindowTextLengthW(GetDlgItem(hwnd, IDC_DUMP_FILENAME)) + 1);
GetWindowTextW(GetDlgItem(hwnd, IDC_DUMP_FILENAME), &fileName_[0], (int)fileName_.size());
if (fileName_.size() == 0)
return false;
// now check if data makes sense...
bool invalidSize = false;
bool invalidAddress = false;
if (isInInterval(PSP_GetScratchpadMemoryBase(),PSP_GetScratchpadMemoryEnd(),start))
{
invalidSize = !isInInterval(PSP_GetScratchpadMemoryBase(),PSP_GetScratchpadMemoryEnd(),start+size-1);
} else if (isInInterval(PSP_GetVidMemBase(),PSP_GetVidMemEnd(),start))
{
invalidSize = !isInInterval(PSP_GetVidMemBase(),PSP_GetVidMemEnd(),start+size-1);
} else if (isInInterval(PSP_GetKernelMemoryBase(),PSP_GetUserMemoryEnd(),start))
{
invalidSize = !isInInterval(PSP_GetKernelMemoryBase(),PSP_GetUserMemoryEnd(),start+size-1);
} else
{
invalidAddress = true;
}
if (invalidAddress)
{
snprintf(errorMessage, sizeof(errorMessage), "Invalid address 0x%08X.",start);
MessageBoxA(hwnd,errorMessage,"Error",MB_OK);
return false;
} else if (invalidSize)
{
snprintf(errorMessage, sizeof(errorMessage), "Invalid end address 0x%08X.",start+size);
MessageBoxA(hwnd,errorMessage,"Error",MB_OK);
return false;
}
return true;
}
void DumpMemoryWindow::changeMode(HWND hwnd, Mode newMode)
{
char buffer[128];
selectedMode = newMode;
SendMessage(GetDlgItem(hwnd,IDC_DUMP_USERMEMORY),BM_SETCHECK,selectedMode == MODE_RAM ? BST_CHECKED : BST_UNCHECKED,0);
SendMessage(GetDlgItem(hwnd,IDC_DUMP_VRAM),BM_SETCHECK,selectedMode == MODE_VRAM ? BST_CHECKED : BST_UNCHECKED,0);
SendMessage(GetDlgItem(hwnd,IDC_DUMP_SCRATCHPAD),BM_SETCHECK,selectedMode == MODE_SCRATCHPAD ? BST_CHECKED : BST_UNCHECKED,0);
SendMessage(GetDlgItem(hwnd,IDC_DUMP_CUSTOMRANGE),BM_SETCHECK,selectedMode == MODE_CUSTOM ? BST_CHECKED : BST_UNCHECKED,0);
if (selectedMode == MODE_CUSTOM)
{
EnableWindow(GetDlgItem(hwnd,IDC_DUMP_STARTADDRESS),TRUE);
EnableWindow(GetDlgItem(hwnd,IDC_DUMP_SIZE),TRUE);
if (filenameChosen_ == false)
SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),"Custom.dump");
} else {
u32 start = 0, size = 0;
const char *defaultFileName = "";
switch (selectedMode) {
case MODE_RAM:
start = PSP_GetUserMemoryBase();
size = PSP_GetUserMemoryEnd()-start;
defaultFileName = "RAM.dump";
break;
case MODE_VRAM:
start = PSP_GetVidMemBase();
size = PSP_GetVidMemEnd()-start;
defaultFileName = "VRAM.dump";
break;
case MODE_SCRATCHPAD:
start = PSP_GetScratchpadMemoryBase();
size = PSP_GetScratchpadMemoryEnd()-start;
defaultFileName = "Scratchpad.dump";
break;
case MODE_CUSTOM:
break;
}
snprintf(buffer, sizeof(buffer), "0x%08X", start);
SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_STARTADDRESS),buffer);
EnableWindow(GetDlgItem(hwnd,IDC_DUMP_STARTADDRESS),FALSE);
snprintf(buffer, sizeof(buffer), "0x%08X", size);
SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_SIZE),buffer);
EnableWindow(GetDlgItem(hwnd,IDC_DUMP_SIZE),FALSE);
if (filenameChosen_ == false)
SetWindowTextA(GetDlgItem(hwnd,IDC_DUMP_FILENAME),defaultFileName);
}
}
bool DumpMemoryWindow::exec()
{
bp = this;
bool result = DialogBoxParam(GetModuleHandle(0),MAKEINTRESOURCE(IDD_DUMPMEMORY),parentHwnd,dlgFunc,(LPARAM)this) != 0;
return result;
}