Merge pull request #19837 from hrydgard/some-debugger-stuff

ImDebugger: Add a memory dump window
This commit is contained in:
Henrik Rydgård 2025-01-09 14:15:49 +01:00 committed by GitHub
commit 101d68d352
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 209 additions and 133 deletions

View file

@ -1092,3 +1092,87 @@ bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo &dest, boo
dest.totalSize = size;
return true;
}
bool GetDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData, bool displaySymbols) {
if (displaySymbols) {
const std::string addressSymbol = g_symbolMap->GetLabelString(address);
if (!addressSymbol.empty()) {
for (int k = 0; addressSymbol[k] != 0; k++) {
// abbreviate long names
if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0) {
*dest++ = '+';
break;
}
*dest++ = addressSymbol[k];
}
*dest++ = ':';
*dest = 0;
return true;
} else {
sprintf(dest," %08X",address);
return false;
}
} else {
if (showData) {
u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0;
sprintf(dest, "%08X %08X", address, encoding);
} else {
sprintf(dest, "%08X", address);
}
return false;
}
}
// Utilify function from the old debugger.
std::string DisassembleRange(u32 start, u32 size, bool displaySymbols, MIPSDebugInterface *debugger) {
auto memLock = Memory::Lock();
std::string result;
// gather all branch targets without labels
std::set<u32> branchAddresses;
for (u32 i = 0; i < size; i += debugger->getInstructionSize(0)) {
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger, start + i);
if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty()) {
if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) {
branchAddresses.insert(info.branchTarget);
}
}
}
u32 disAddress = start;
bool previousLabel = true;
DisassemblyLineInfo line;
while (disAddress < start + size) {
char addressText[64], buffer[512];
g_disassemblyManager.getLine(disAddress, displaySymbols, line, debugger);
bool isLabel = GetDisasmAddressText(disAddress, addressText, false, line.type == DISTYPE_OPCODE, displaySymbols);
if (isLabel) {
if (!previousLabel)
result += "\r\n";
sprintf(buffer, "%s\r\n\r\n", addressText);
result += buffer;
} else if (branchAddresses.find(disAddress) != branchAddresses.end()) {
if (!previousLabel)
result += "\r\n";
sprintf(buffer, "pos_%08X:\r\n\r\n", disAddress);
result += buffer;
}
if (line.info.isBranch && !line.info.isBranchToRegister
&& g_symbolMap->GetLabelString(line.info.branchTarget).empty()
&& branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) {
sprintf(buffer, "pos_%08X", line.info.branchTarget);
line.params = line.params.substr(0, line.params.find("0x")) + buffer;
}
sprintf(buffer, "\t%s\t%s\r\n", line.name.c_str(), line.params.c_str());
result += buffer;
previousLabel = isLabel;
disAddress += line.totalSize;
}
return result;
}

View file

@ -226,3 +226,6 @@ extern DisassemblyManager g_disassemblyManager;
bool isInInterval(u32 start, u32 size, u32 value);
bool IsLikelyStringAt(uint32_t addr);
std::string DisassembleRange(u32 start, u32 size, bool displaySymbols, MIPSDebugInterface *debugger);
bool GetDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData, bool displaySymbols);

View file

@ -56,6 +56,12 @@ void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control) {
}
}
void ShowInMemoryDumperMenuItem(uint32_t addr, uint32_t size, MemDumpMode mode, ImControl &control) {
if (ImGui::MenuItem(mode == MemDumpMode::Raw ? "Dump bytes to file..." : "Disassemble to file...")) {
control.command = { ImCmd::SHOW_IN_MEMORY_DUMPER, addr, size, (u32)mode};
}
}
void ShowInWindowMenuItems(uint32_t addr, ImControl &control) {
// Enable when we implement the memory viewer
ShowInMemoryViewerMenuItem(addr, control);
@ -1112,6 +1118,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
snprintf(title, sizeof(title), "Memory %d", i + 1);
ImGui::MenuItem(title, nullptr, &cfg_.memViewOpen[i]);
}
ImGui::MenuItem("Memory Dumper", nullptr, &cfg_.memDumpOpen);
ImGui::EndMenu();
}
if (ImGui::BeginMenu("HLE")) {
@ -1253,6 +1260,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
pixelViewer_.Draw(cfg_, control, gpuDebug, draw);
}
if (cfg_.memDumpOpen) {
memDumpWindow_.Draw(cfg_, mipsDebug);
}
for (int i = 0; i < 4; i++) {
if (cfg_.memViewOpen[i]) {
mem_[i].Draw(mipsDebug, cfg_, control, i);
@ -1288,6 +1299,13 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu
ImGui::SetWindowFocus(ImMemWindow::Title(index));
break;
}
case ImCmd::SHOW_IN_MEMORY_DUMPER:
{
cfg_.memDumpOpen = true;
memDumpWindow_.SetRange(control.command.param, control.command.param2, (MemDumpMode)control.command.param3);
ImGui::SetWindowFocus(memDumpWindow_.Title());
break;
}
case ImCmd::TRIGGER_FIND_POPUP:
// TODO
break;

View file

@ -143,6 +143,7 @@ struct ImConfig {
bool pixelViewerOpen;
bool npOpen;
bool socketsOpen;
bool memDumpOpen;
bool memViewOpen[4];
// HLE explorer settings
@ -175,6 +176,7 @@ enum class ImCmd {
SHOW_IN_GE_DISASM,
SHOW_IN_MEMORY_VIEWER, // param is address, param2 is viewer index
SHOW_IN_PIXEL_VIEWER, // param is address, param2 is stride, |0x80000000 if depth, param3 is w/h
SHOW_IN_MEMORY_DUMPER, // param is address, param2 is size, param3 is mode
};
struct ImCommand {
@ -211,6 +213,7 @@ private:
ImMemWindow mem_[4]; // We support 4 separate instances of the memory viewer.
ImStructViewer structViewer_;
ImGePixelViewerWindow pixelViewer_;
ImMemDumpWindow memDumpWindow_;
ImSnapshotState newSnapshot_;
ImSnapshotState snapshot_;
@ -226,4 +229,5 @@ private:
void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd);
void ShowInWindowMenuItems(uint32_t addr, ImControl &control);
void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control);
void ShowInMemoryDumperMenuItem(uint32_t addr, uint32_t size, MemDumpMode mode, ImControl &control);
void StatusBar(std::string_view str);

View file

@ -53,33 +53,7 @@ bool ImDisasmView::getDisasmAddressText(u32 address, char* dest, bool abbreviate
if (!PSP_IsInited())
return false;
if (displaySymbols_) {
const std::string addressSymbol = g_symbolMap->GetLabelString(address);
if (!addressSymbol.empty()) {
for (int k = 0; addressSymbol[k] != 0; k++) {
// abbreviate long names
if (abbreviateLabels && k == 16 && addressSymbol[k + 1] != 0) {
*dest++ = '+';
break;
}
*dest++ = addressSymbol[k];
}
*dest++ = ':';
*dest = 0;
return true;
} else {
sprintf(dest, " %08X", address);
return false;
}
} else {
if (showData) {
u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0;
sprintf(dest, "%08X %08X", address, encoding);
} else {
sprintf(dest, "%08X", address);
}
return false;
}
return GetDisasmAddressText(address, dest, abbreviateLabels, showData, displaySymbols_);
}
void ImDisasmView::assembleOpcode(u32 address, const std::string &defaultText) {
@ -830,9 +804,9 @@ void ImDisasmView::PopupMenu(ImControl &control) {
statusBarText_ = "WARNING: unable to find function symbol here";
}
}
u32 prevBegin = g_symbolMap->GetFunctionStart(curAddress_);
if (ImGui::MenuItem("Add function")) {
char statusBarTextBuff[256];
u32 prevBegin = g_symbolMap->GetFunctionStart(curAddress_);
if (prevBegin != -1) {
if (prevBegin == curAddress_) {
snprintf(statusBarTextBuff, 256, "WARNING: There's already a function entry point at this adress");
@ -861,8 +835,9 @@ void ImDisasmView::PopupMenu(ImControl &control) {
mapReloaded_ = true;
}
}
if (ImGui::MenuItem("Disassemble to file")) {
disassembleToFile();
if (prevBegin != -1) {
u32 prevSize = g_symbolMap->GetFunctionSize(prevBegin);
ShowInMemoryDumperMenuItem(prevBegin, prevSize, MemDumpMode::Disassembly, control);
}
ImGui::EndPopup();
}

View file

@ -1,6 +1,7 @@
#include <cctype>
#include <cmath>
#include <iomanip>
#include <cstdio>
#include <sstream>
#include "ext/imgui/imgui.h"
@ -9,6 +10,7 @@
#include "ext/xxhash.h"
#include "Common/StringUtils.h"
#include "Common/File/FileUtil.h"
#include "Core/Config.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
@ -838,3 +840,62 @@ void ImMemView::setHighlightType(MemBlockFlags flags) {
updateStatusBarText();
}
}
void ImMemDumpWindow::Draw(ImConfig &cfg, MIPSDebugInterface *debug) {
ImGui::SetNextWindowSize(ImVec2(200, 300), ImGuiCond_FirstUseEver);
if (!ImGui::Begin(Title(), &cfg.memDumpOpen)) {
ImGui::End();
return;
}
if (ImGui::Button("User RAM (0x08800000)")) {
address_ = 0x08800000;
size_ = 0x01800000; // 24MB
}
ImGui::InputScalar("Starting address", ImGuiDataType_U32, &address_, NULL, NULL, "%08X");
ImGui::InputScalar("Size", ImGuiDataType_U32, &size_, NULL, NULL, "%08X");
ImGui::InputText("Filename", filename_, ARRAY_SIZE(filename_));
const char* modes[] = { "Raw", "Disassembly" };
int modeIndex = static_cast<int>(mode_);
if (ImGui::Combo("Memory Dump Mode", &modeIndex, modes, IM_ARRAYSIZE(modes))) {
// Update the current mode if the user selects a new one
mode_ = static_cast<MemDumpMode>(modeIndex);
}
if (ImGui::Button(mode_ == MemDumpMode::Raw ? "Dump to file" : "Disassemble to file")) {
uint32_t validSize = Memory::ValidSize(address_, size_);
if (validSize != size_) {
errorMsg_ = "Address range out of bounds";
if (Memory::IsValidAddress(address_)) {
size_ = validSize;
}
} else if (strlen(filename_) == 0) {
errorMsg_ = "Please specify a valid filename";
} else {
FILE *file = File::OpenCFile(Path(filename_), "wb");
if (!file) {
errorMsg_ = "Couldn't open file for writing";
} else {
if (mode_ == MemDumpMode::Raw) {
const uint8_t *ptr = Memory::GetPointer(address_);
fwrite(ptr, 1, size_, file);
} else {
std::string disassembly = DisassembleRange(address_, size_, true, debug);
fprintf(file, "%s", disassembly.c_str());
}
errorMsg_.clear();
fclose(file);
}
}
}
if (!errorMsg_.empty()) {
ImGui::TextUnformatted(errorMsg_.data(), errorMsg_.data() + errorMsg_.size());
}
ImGui::End();
}

View file

@ -118,3 +118,33 @@ private:
std::string statusMessage_;
};
enum class MemDumpMode {
Raw = 0,
Disassembly = 1,
};
class ImMemDumpWindow {
public:
ImMemDumpWindow() {
filename_[0] = 0;
address_ = 0x08800000;
size_ = 0x01800000;
}
static const char *Title() {
return "Memory Dumper";
}
void Draw(ImConfig &cfg, MIPSDebugInterface *debug);
void SetRange(uint32_t addr, uint32_t size, MemDumpMode mode) {
address_ = addr;
size_ = size;
mode_ = mode;
}
private:
uint32_t address_;
uint32_t size_;
MemDumpMode mode_ = MemDumpMode::Raw;
char filename_[1024];
std::string errorMsg_;
};

View file

@ -218,44 +218,6 @@ static COLORREF scaleColor(COLORREF color, float factor)
return (color & 0xFF000000) | (b << 16) | (g << 8) | r;
}
bool CtrlDisAsmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData)
{
if (!PSP_IsInited())
return false;
if (displaySymbols)
{
const std::string addressSymbol = g_symbolMap->GetLabelString(address);
if (!addressSymbol.empty())
{
for (int k = 0; addressSymbol[k] != 0; k++)
{
// abbreviate long names
if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0)
{
*dest++ = '+';
break;
}
*dest++ = addressSymbol[k];
}
*dest++ = ':';
*dest = 0;
return true;
} else {
sprintf(dest," %08X",address);
return false;
}
} else {
if (showData) {
u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0;
sprintf(dest, "%08X %08X", address, encoding);
} else {
sprintf(dest, "%08X", address);
}
return false;
}
}
std::string trimString(std::string input)
{
size_t pos = input.find_first_not_of(" \t");
@ -567,7 +529,7 @@ void CtrlDisAsmView::onPaint(WPARAM wParam, LPARAM lParam)
SetTextColor(hdc,textColor);
char addressText[64];
getDisasmAddressText(address,addressText,true,line.type == DISTYPE_OPCODE);
GetDisasmAddressText(address,addressText,true,line.type == DISTYPE_OPCODE, displaySymbols);
TextOutA(hdc,pixelPositions.addressStart,rowY1+2,addressText,(int)strlen(addressText));
if (isInInterval(address,line.totalSize,debugger->GetPC()))
@ -935,7 +897,7 @@ void CtrlDisAsmView::CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructio
W32Util::CopyTextToClipboard(wnd, temp);
delete [] temp;
} else {
std::string disassembly = disassembleRange(startAddr,endAddr-startAddr);
std::string disassembly = DisassembleRange(startAddr,endAddr-startAddr, displaySymbols, debugger);
W32Util::CopyTextToClipboard(wnd, disassembly.c_str());
}
}
@ -1290,7 +1252,7 @@ void CtrlDisAsmView::search(bool continueSearch)
g_disassemblyManager.getLine(searchAddress,displaySymbols,lineInfo, debugger);
char addressText[64];
getDisasmAddressText(searchAddress,addressText,true,lineInfo.type == DISTYPE_OPCODE);
GetDisasmAddressText(searchAddress,addressText,true,lineInfo.type == DISTYPE_OPCODE, displaySymbols);
const char* opcode = lineInfo.name.c_str();
const char* arguments = lineInfo.params.c_str();
@ -1330,65 +1292,6 @@ void CtrlDisAsmView::search(bool continueSearch)
searching = false;
}
std::string CtrlDisAsmView::disassembleRange(u32 start, u32 size)
{
auto memLock = Memory::Lock();
std::string result;
// gather all branch targets without labels
std::set<u32> branchAddresses;
for (u32 i = 0; i < size; i += debugger->getInstructionSize(0))
{
MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger,start+i);
if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty())
{
if (branchAddresses.find(info.branchTarget) == branchAddresses.end())
{
branchAddresses.insert(info.branchTarget);
}
}
}
u32 disAddress = start;
bool previousLabel = true;
DisassemblyLineInfo line;
while (disAddress < start+size)
{
char addressText[64],buffer[512];
g_disassemblyManager.getLine(disAddress,displaySymbols,line, debugger);
bool isLabel = getDisasmAddressText(disAddress,addressText,false,line.type == DISTYPE_OPCODE);
if (isLabel)
{
if (!previousLabel) result += "\r\n";
sprintf(buffer,"%s\r\n\r\n",addressText);
result += buffer;
} else if (branchAddresses.find(disAddress) != branchAddresses.end())
{
if (!previousLabel) result += "\r\n";
sprintf(buffer,"pos_%08X:\r\n\r\n",disAddress);
result += buffer;
}
if (line.info.isBranch && !line.info.isBranchToRegister
&& g_symbolMap->GetLabelString(line.info.branchTarget).empty()
&& branchAddresses.find(line.info.branchTarget) != branchAddresses.end())
{
sprintf(buffer,"pos_%08X",line.info.branchTarget);
line.params = line.params.substr(0,line.params.find("0x")) + buffer;
}
sprintf(buffer,"\t%s\t%s\r\n",line.name.c_str(),line.params.c_str());
result += buffer;
previousLabel = isLabel;
disAddress += line.totalSize;
}
return result;
}
void CtrlDisAsmView::disassembleToFile() {
// get size
u32 size;
@ -1408,7 +1311,7 @@ void CtrlDisAsmView::disassembleToFile() {
return;
}
std::string disassembly = disassembleRange(curAddress, size);
std::string disassembly = DisassembleRange(curAddress, size, displaySymbols, debugger);
fprintf(output, "%s", disassembly.c_str());
fclose(output);

View file

@ -70,12 +70,10 @@ class CtrlDisAsmView
};
void assembleOpcode(u32 address, const std::string &defaultText);
std::string disassembleRange(u32 start, u32 size);
void disassembleToFile();
void search(bool continueSearch);
void followBranch();
void calculatePixelPositions();
bool getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData);
void updateStatusBarText();
void drawBranchLine(HDC hdc, std::map<u32, int> &addressPositions, const BranchLine &line);
void CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode);