Add module filter to symbol list in ImDebugger

This commit is contained in:
Henrik Rydgård committed 2026-08-25 22:33:51 +02:00
1 parent a0c10a0ea6
commit 8cf77a49f3
13 files changed
+109 -16

No files matched your search

+1
View File
@@ -107,6 +107,7 @@ add_library(Common STATIC
Data/Text/Parsers.cpp
Data/Text/Parsers.h
Data/Text/StringWriter.h
Data/Text/StringWriter.cpp
Data/Text/WrapText.cpp
Data/Text/WrapText.h
Data/Random/Rng.h
+2 -1
View File
@@ -900,6 +900,7 @@
<ClCompile Include="Data\Hash\Hash.cpp" />
<ClCompile Include="Data\Text\I18n.cpp" />
<ClCompile Include="Data\Text\Parsers.cpp" />
<ClCompile Include="Data\Text\StringWriter.cpp" />
<ClCompile Include="Data\Text\WrapText.cpp" />
<ClCompile Include="File\AndroidContentURI.cpp" />
<ClCompile Include="File\AndroidStorage.cpp" />
@@ -1081,4 +1082,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+3
View File
@@ -1472,6 +1472,9 @@
<ClCompile Include="UI\ScreenManager.cpp">
<Filter>UI</Filter>
</ClCompile>
<ClCompile Include="Data\Text\StringWriter.cpp">
<Filter>Data\Text</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Crypto">
-13
View File
@@ -160,16 +160,3 @@ bool TryParse(const std::string &str, bool *const output) {
return true;
}
StringWriter &StringWriter::F(const char *format, ...) {
const size_t remainder = bufSize_ - (p_ - start_);
if (remainder < 3) {
return *this;
}
va_list args;
va_start(args, format);
int wouldHaveBeenWritten = vsnprintf(p_, remainder, format, args);
p_ += std::min((int)remainder, wouldHaveBeenWritten);
va_end(args);
return *this;
}
+14
View File
@@ -1,2 +1,16 @@
#include <cstdarg>
#include "Common/Data/Text/StringWriter.h"
StringWriter &StringWriter::F(const char *format, ...) {
const size_t remainder = bufSize_ - (p_ - start_);
if (remainder < 3) {
return *this;
}
va_list args;
va_start(args, format);
int wouldHaveBeenWritten = vsnprintf(p_, remainder, format, args);
p_ += std::min((int)remainder, wouldHaveBeenWritten);
va_end(args);
return *this;
}
+7 -1
View File
@@ -477,11 +477,14 @@ u32 SymbolMap::GetModuleCrc(int moduleIndex) const {
// per-game rather than per-module, hence GetGameSymbolsPath instead of GetModuleSymbolsPath.
bool SymbolMap::SaveModuleSymbols(int moduleIndex, const Path &filename, const std::string &gameID, const std::string &gameTitle) const {
u32 crc = 0;
// Only for logging - module 0 isn't a module, it's the symbols that aren't in one.
const char *moduleName = "(no module)";
if (moduleIndex != 0) {
bool found = false;
for (const auto &module : modules) {
if (module.index == moduleIndex) {
crc = module.crc;
moduleName = module.name;
found = true;
break;
}
@@ -529,8 +532,10 @@ bool SymbolMap::SaveModuleSymbols(int moduleIndex, const Path &filename, const s
if (count == 0) {
// Nothing worth keeping. Remove any previous file rather than leaving one behind that
// would restore symbols the user has since deleted.
if (File::Exists(filename))
if (File::Exists(filename)) {
INFO_LOG(Log::Loader, "SaveModuleSymbols: '%s' has no named symbols left, deleting %s", moduleName, filename.c_str());
File::Delete(filename);
}
return true;
}
@@ -552,6 +557,7 @@ bool SymbolMap::SaveModuleSymbols(int moduleIndex, const Path &filename, const s
fwrite(text.data(), 1, text.size(), f);
fclose(f);
INFO_LOG(Log::Loader, "SaveModuleSymbols: wrote %d symbol(s) for '%s' to %s", count, moduleName, filename.c_str());
return true;
}
+61
View File
@@ -1323,6 +1323,38 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
avail.y -= ImGui::GetTextLineHeightWithSpacing();
if (ImGui::BeginChild("left", ImVec2(150.0f, avail.y), ImGuiChildFlags_ResizeX)) {
// Modules load and unload as the game runs and nothing tells us about it, so poll. The
// list is a couple of dozen entries at most, and this only runs while the panel is open.
std::vector<LoadedModuleInfo> modules = g_symbolMap->getAllModules();
modules.erase(std::remove_if(modules.begin(), modules.end(), [](const LoadedModuleInfo &m) {
return !m.active;
}), modules.end());
if (modules.size() != symModules_.size() || !std::equal(modules.begin(), modules.end(), symModules_.begin(),
[](const LoadedModuleInfo &a, const LoadedModuleInfo &b) {
return a.address == b.address && a.size == b.size && a.name == b.name;
})) {
symModules_ = std::move(modules);
// symCache_ holds active symbols only, so it's stale now too.
symsDirty_ = true;
// Re-resolve the module filter against the new list. A module that reloaded lands at
// a different address, and one that's gone should drop back to All rather than leave
// the list mysteriously empty.
if (!symModuleFilter_.empty()) {
auto found = std::find_if(symModules_.begin(), symModules_.end(), [this](const LoadedModuleInfo &m) {
return m.name == symModuleFilter_;
});
if (found == symModules_.end()) {
symModuleFilter_.clear();
symModuleFilterStart_ = 0;
symModuleFilterSize_ = 0;
} else {
symModuleFilterStart_ = found->address;
symModuleFilterSize_ = found->size;
}
}
}
if (symCache_.empty() || symsDirty_) {
symCache_ = g_symbolMap->GetAllActiveSymbols(SymbolType::ST_FUNCTION);
symsDirty_ = false;
@@ -1346,6 +1378,30 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
symMatchesDirty_ = true;
}
ImGui::SetNextItemWidth(-1.0f);
if (ImGui::BeginCombo("##symmodule", symModuleFilter_.empty() ? "All modules" : symModuleFilter_.c_str())) {
if (ImGui::Selectable("All modules", symModuleFilter_.empty())) {
symModuleFilter_.clear();
symModuleFilterStart_ = 0;
symModuleFilterSize_ = 0;
symMatchesDirty_ = true;
}
for (int i = 0; i < (int)symModules_.size(); i++) {
const LoadedModuleInfo &module = symModules_[i];
// Module names are not unique - the same one can be loaded more than once.
ImGui::PushID(i);
const bool selected = symModuleFilter_ == module.name && symModuleFilterStart_ == module.address;
if (ImGui::Selectable(module.name.c_str(), selected)) {
symModuleFilter_ = module.name;
symModuleFilterStart_ = module.address;
symModuleFilterSize_ = module.size;
symMatchesDirty_ = true;
}
ImGui::PopID();
}
ImGui::EndCombo();
}
if (symMatchesDirty_) {
symMatchesDirty_ = false;
symMatches_.clear();
@@ -1353,6 +1409,11 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
// path to get wrong. These lists run to a few thousand entries, and this only reruns
// when the filter or the symbol map changes, not per frame.
for (int i = 0; i < (int)symCache_.size(); i++) {
if (!symModuleFilter_.empty()) {
const u32 addr = symCache_[i].address;
if (addr < symModuleFilterStart_ || addr >= symModuleFilterStart_ + symModuleFilterSize_)
continue;
}
if (symFilter_[0] == '\0' || containsNoCase(symCache_[i].name, symFilter_))
symMatches_.push_back(i);
}
+10
View File
@@ -230,6 +230,16 @@ private:
std::vector<int> symMatches_;
bool symMatchesDirty_ = true;
// Currently loaded modules, for the module filter dropdown. Refreshed every frame the
// symbol list is visible: modules come and go while a game runs, and nothing notifies us.
// A change here also invalidates symCache_, since it's built from active symbols only.
std::vector<LoadedModuleInfo> symModules_;
// The module filter is remembered as a name plus an address range rather than an index into
// symModules_, so a selection survives that list being rebuilt. Empty name means "All".
std::string symModuleFilter_;
u32 symModuleFilterStart_ = 0;
u32 symModuleFilterSize_ = 0;
ImDisasmView disasmView_;
char searchTerm_[64]{};
};
+2
View File
@@ -93,6 +93,7 @@
<ClInclude Include="..\..\Common\BitSet.h" />
<ClInclude Include="..\..\Common\Buffer.h" />
<ClInclude Include="..\..\Common\Data\Format\DDSLoad.h" />
<ClInclude Include="..\..\Common\Data\Text\StringWriter.h" />
<ClInclude Include="..\..\Common\File\AndroidContentURI.h" />
<ClInclude Include="..\..\Common\File\AndroidStorage.h" />
<ClInclude Include="..\..\Common\File\VFS\SevenZipFileReader.h" />
@@ -305,6 +306,7 @@
<ClCompile Include="..\..\Common\ArmEmitter.cpp" />
<ClCompile Include="..\..\Common\Buffer.cpp" />
<ClCompile Include="..\..\Common\Data\Format\DDSLoad.cpp" />
<ClCompile Include="..\..\Common\Data\Text\StringWriter.cpp" />
<ClCompile Include="..\..\Common\File\AndroidContentURI.cpp" />
<ClCompile Include="..\..\Common\File\AndroidStorage.cpp" />
<ClCompile Include="..\..\Common\File\VFS\SevenZipFileReader.cpp" />
+6
View File
@@ -591,6 +591,9 @@
<ClCompile Include="..\..\Common\UI\ScreenManager.cpp">
<Filter>UI</Filter>
</ClCompile>
<ClCompile Include="..\..\Common\Data\Text\StringWriter.cpp">
<Filter>Data\Text</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="targetver.h" />
@@ -1145,6 +1148,9 @@
<ClInclude Include="..\..\Common\UI\ScreenManager.h">
<Filter>UI</Filter>
</ClInclude>
<ClInclude Include="..\..\Common\Data\Text\StringWriter.h">
<Filter>Data\Text</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\ext\libpng17\CMakeLists.txt" />
+1
View File
@@ -334,6 +334,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Common/Data/Hash/Hash.cpp \
$(SRC)/Common/Data/Text/I18n.cpp \
$(SRC)/Common/Data/Text/Parsers.cpp \
$(SRC)/Common/Data/Text/StringWriter.cpp \
$(SRC)/Common/Data/Text/WrapText.cpp \
$(SRC)/Common/File/AndroidStorage.cpp \
$(SRC)/Common/File/AndroidContentURI.cpp \
+1 -1
View File
@@ -1,4 +1,4 @@
plugins {
id("com.android.application") version "9.3.1" apply false
id("com.android.application") version "9.3.2" apply false
id("com.google.protobuf") version "0.10.0" apply false
}
+1
View File
@@ -451,6 +451,7 @@ SOURCES_CXX += \
$(COMMONDIR)/Data/Hash/Hash.cpp \
$(COMMONDIR)/Data/Text/I18n.cpp \
$(COMMONDIR)/Data/Text/Parsers.cpp \
$(COMMONDIR)/Data/Text/StringWriter.cpp \
$(COMMONDIR)/Data/Text/WrapText.cpp \
$(COMMONDIR)/File/VFS/VFS.cpp \
$(COMMONDIR)/File/VFS/DirectoryReader.cpp \