ImDebugger: Symbol deletion, select in symbol list, callstack thread choice

- Symbol list: "Delete symbol" in the context menu, which leaves the code
  without a function (useful to test stack walks with missing symbols).
- Disassembly context menu: "Select in symbol list" selects the function in
  the list without moving the disassembly, clearing a filter that hides it.
- Callstacks: a thread selector (following the current thread by default, with
  a Current button), selectable frames, double-click to show the frame in the
  disassembly, and a context menu to show or copy frames.
- Threads: "Show callstack", "Go to PC" and "Go to entry" in the context menu.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-19 10:56:05 +02:00
1 parent 1fb3d535d9
commit 2cfbbc001d
4 files changed
+175 -11

No files matched your search

+41
View File
@@ -844,6 +844,10 @@ void ImDisasmView::PopupMenu(MIPSState *mips, ImControl &control) {
}
}
ImGui::Separator();
const u32 funcStart = g_symbolMap->GetFunctionStart(curAddress_);
if (ImGui::MenuItem("Select in symbol list", nullptr, false, funcStart != -1)) {
symbolListRequest_ = funcStart;
}
if (ImGui::MenuItem("Rename function")) {
funcBegin_ = g_symbolMap->GetFunctionStart(curAddress_);
if (funcBegin_ != -1) {
@@ -1338,6 +1342,25 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
}
}
// From "Select in symbol list" in the disassembly's context menu. Only moves the list's
// selection, the disassembly stays where it is.
u32 requestedSymbol;
if (disasmView_.TakeSymbolListRequest(&requestedSymbol)) {
for (int i = 0; i < (int)symCache_.size(); i++) {
if (symCache_[i].address != requestedSymbol) {
continue;
}
selectedSymbol_ = i;
truncate_cpy(selectedSymbolName_, symCache_[i].name);
scrollToSelectedSymbol_ = true;
if (symFilter_[0] != '\0' && !containsNoCase(symCache_[i].name, symFilter_)) {
symFilter_[0] = '\0';
symMatchesDirty_ = true;
}
break;
}
}
if (selectedSymbol_ >= 0 && selectedSymbol_ < (int)symCache_.size()) {
auto &sym = symCache_[selectedSymbol_];
if (ImGui::TreeNode("Edit Symbol", "Edit %s", sym.name.c_str())) {
@@ -1377,6 +1400,15 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
}
if (ImGui::BeginListBox("##symbols", ImGui::GetContentRegionAvail())) {
if (scrollToSelectedSymbol_) {
scrollToSelectedSymbol_ = false;
auto it = std::find(symMatches_.begin(), symMatches_.end(), selectedSymbol_);
if (it != symMatches_.end()) {
// The clipper below only submits visible rows, so scroll by row height to center it.
const float rowHeight = ImGui::GetTextLineHeightWithSpacing();
ImGui::SetScrollY((float)(it - symMatches_.begin()) * rowHeight - (ImGui::GetWindowHeight() - rowHeight) * 0.5f);
}
}
ImGuiListClipper clipper;
clipper.Begin((int)symMatches_.size(), -1);
while (clipper.Step()) {
@@ -1398,6 +1430,15 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImContro
if (ImGui::MenuItem("Copy address to clipboard")) {
System_CopyStringToClipboard(StringFromFormat("%08x", symCache_[i].address));
}
ImGui::Separator();
// Leaves the code uncovered by any function, unlike "Remove function" in the
// disassembly, which merges it into the previous one. Handy for testing stack
// walks without symbols. The list rebuilds next frame, from the map's new version.
if (ImGui::MenuItem("Delete symbol")) {
g_symbolMap->RemoveFunction(symCache_[i].address, true);
g_symbolMap->SortSymbols();
g_disassemblyManager.clear();
}
ImGui::EndPopup();
}
ImGui::PopID();