ImDebugger: fix stale symbol list after a game is reloaded

The disasm window cached the flattened symbol list and only rebuilt it when one
of three menu items said so. Nothing marked it dirty when a game booted or
exited, and a new SymbolMap is allocated per boot, so the list kept showing the
previous game's functions.

Give SymbolMap a version counter that every mutator bumps, and let the window
compare against it instead. The counter is process-wide rather than per-map, so
a fresh map can't hand out a version a cached copy already holds.

Also re-find the selected symbol by address after a rebuild (the index means
something else afterwards), and drop the unused symbol cache members in
ImMemWindow.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01SfY7iFJEjmRXf1XGrTs4MF
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-08-27 10:43:08 +02:00
1 parent adccd302e5
commit daa18fc25a
7 files changed
+95 -23

No files matched your search

+2 -5
View File
@@ -2626,12 +2626,11 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw:
ImGui::Separator();
if (ImGui::MenuItem("Load .ppmap...")) {
System_BrowseForFile(reqToken_, "Load PPSSPP symbol map", BrowseFileType::SYMBOL_MAP, [this](std::string_view responseString, int) {
System_BrowseForFile(reqToken_, "Load PPSSPP symbol map", BrowseFileType::SYMBOL_MAP, [](std::string_view responseString, int) {
Path path(responseString);
if (!g_symbolMap->LoadSymbolMap(path)) {
ERROR_LOG(Log::Common, "Failed to load symbol map");
}
disasm_.DirtySymbolMap();
});
}
if (ImGui::MenuItem("Save .ppmap...")) {
@@ -2643,12 +2642,11 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw:
});
}
if (ImGui::MenuItem("Load No$ .sym...")) {
System_BrowseForFile(reqToken_, "Load No$ symbol map", BrowseFileType::SYMBOL_MAP, [this](std::string_view responseString, int) {
System_BrowseForFile(reqToken_, "Load No$ symbol map", BrowseFileType::SYMBOL_MAP, [](std::string_view responseString, int) {
Path path(responseString);
if (!g_symbolMap->LoadNocashSym(path)) {
ERROR_LOG(Log::Common, "Failed to load No$ symbol map");
}
disasm_.DirtySymbolMap();
});
}
if (ImGui::MenuItem("Save No$ .sym...")) {
@@ -2663,7 +2661,6 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUCommon *gpuDebug, Draw:
ImGui::MenuItem("Compress .ppmap files", nullptr, &g_Config.bCompressSymbols);
if (ImGui::MenuItem("Reset symbol map")) {
g_symbolMap->Clear();
disasm_.DirtySymbolMap();
// NotifyDebuggerMapLoaded();
}
ImGui::EndMenu();
+16 -4
View File
@@ -1323,18 +1323,30 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
avail.y -= ImGui::GetTextLineHeightWithSpacing();
if (ImGui::BeginChild("left", ImVec2(150.0f, avail.y), ImGuiChildFlags_ResizeX)) {
if (symCache_.empty() || symsDirty_) {
if (symCacheVersion_ != g_symbolMap->Version()) {
// The index into symCache_ means something different after a rebuild, and nothing at
// all if the map was replaced (which is what happens when a game exits), so re-find
// the selection by address instead of carrying the index over.
const u32 selectedAddr = (selectedSymbol_ >= 0 && selectedSymbol_ < (int)symCache_.size()) ? symCache_[selectedSymbol_].address : (u32)INVALID_ADDR;
symCache_ = g_symbolMap->GetAllActiveSymbols(SymbolType::ST_FUNCTION);
symsDirty_ = false;
symCacheVersion_ = g_symbolMap->Version();
symMatchesDirty_ = true;
selectedSymbol_ = -1;
if (selectedAddr != INVALID_ADDR) {
for (int i = 0; i < (int)symCache_.size(); i++) {
if (symCache_[i].address == selectedAddr) {
selectedSymbol_ = i;
break;
}
}
}
}
if (selectedSymbol_ >= 0 && selectedSymbol_ < symCache_.size()) {
if (selectedSymbol_ >= 0 && selectedSymbol_ < (int)symCache_.size()) {
auto &sym = symCache_[selectedSymbol_];
if (ImGui::TreeNode("Edit Symbol", "Edit %s", sym.name.c_str())) {
if (ImGui::InputText("Name", selectedSymbolName_, sizeof(selectedSymbolName_), ImGuiInputTextFlags_EnterReturnsTrue)) {
g_symbolMap->SetLabelName(selectedSymbolName_, sym.address);
symsDirty_ = true;
}
ImGui::Text("%08x (size: %0d)", sym.address, sym.size);
ImGui::TreePop();
+4 -5
View File
@@ -202,9 +202,6 @@ public:
void NotifyStep() {
disasmView_.NotifyStep();
}
void DirtySymbolMap() {
symsDirty_ = true;
}
const char *Title() const {
return "CPU Debugger";
}
@@ -218,9 +215,11 @@ private:
u32 gotoAddr_ = 0x08800000;
// Symbol cache
// Symbol cache. Rebuilt whenever the symbol map's version no longer matches the one the
// cache was built from - that covers the map being replaced wholesale when a game boots or
// exits, not just edits made from here.
std::vector<SymbolEntry> symCache_;
bool symsDirty_ = true;
uint32_t symCacheVersion_ = 0;
int selectedSymbol_ = -1;
char selectedSymbolName_[128];
-8
View File
@@ -180,9 +180,6 @@ public:
ImMemView &View() {
return memView_;
}
void DirtySymbolMap() {
symsDirty_ = true;
}
void GotoAddr(u32 addr) {
gotoAddr_ = addr;
memView_.gotoAddr(addr);
@@ -195,11 +192,6 @@ private:
INVALID_ADDR = 0xFFFFFFFF,
};
void ProcessKeyboardShortcuts();
// Symbol cache
std::vector<SymbolEntry> symCache_;
bool symsDirty_ = true;
int selectedSymbol_ = -1;
char selectedSymbolName_[128];
bool drawZeroDark_ = false;
bool editableMemory_ = false;