diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index ce27a29285..41b0645c51 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -1002,6 +1002,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 0daaae9977..4dfaa70ccb 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -2241,6 +2241,9 @@ Ext + + Debugger + @@ -2269,4 +2272,4 @@ Ext - + \ No newline at end of file diff --git a/Core/Debugger/Watch.h b/Core/Debugger/Watch.h new file mode 100644 index 0000000000..d576cef49b --- /dev/null +++ b/Core/Debugger/Watch.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "Common/Math/expression_parser.h" +#include "Core/MIPS/MIPSDebugInterface.h" + +enum class WatchFormat { + HEX, + INT, + FLOAT, + STR, +}; + +struct WatchInfo { + WatchInfo() = default; + WatchInfo(const std::string &name, const std::string &expr, MIPSDebugInterface *debug, WatchFormat format = WatchFormat::HEX) + : name(name), originalExpression(expr), format(format) { + initExpression(debug, expr.c_str(), expression); + } + void SetExpression(const std::string &expr, MIPSDebugInterface *debug) { + originalExpression = expr; + initExpression(debug, expr.c_str(), expression); + } + std::string name; + std::string originalExpression; + PostfixExpression expression; + WatchFormat format = WatchFormat::HEX; + uint32_t currentValue = 0; + uint32_t lastValue = 0; + int steppingCounter = -1; + bool evaluateFailed = false; +}; diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 980e56ba5c..946f5a31a7 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -1817,6 +1817,87 @@ static void DrawSymbols(const MIPSDebugInterface *debug, ImConfig &cfg, ImContro ImGui::End(); } +ImWatchWindow::ImWatchWindow() {} + +void ImWatchWindow::Draw(ImConfig &cfg, ImControl &control, MIPSDebugInterface *mipsDebug) { + if (!ImGui::Begin("Watch", &cfg.atracToolOpen) || !g_symbolMap) { + ImGui::End(); + return; + } + + // Refresh watches + int steppingCounter = Core_GetSteppingCounter(); + int changes = false; + for (auto &watch : watches_) { + if (watch.steppingCounter != steppingCounter) { + watch.lastValue = watch.currentValue; + watch.steppingCounter = steppingCounter; + } + + uint32_t prevValue = watch.currentValue; + watch.evaluateFailed = !parseExpression(mipsDebug, watch.expression, watch.currentValue); + } + + if (ImGui::Button("Add Watch")) { + watches_.push_back(WatchInfo("untitled", "[0x88000000]", mipsDebug)); + } + + if (ImGui::BeginTable("watches", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH)) { + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Expression", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableHeadersRow(); + + for (int i = 0; i < (int)watches_.size(); i++) { + auto &watch = watches_[i]; + ImGui::PushID(i); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(watch.name.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(watch.originalExpression.c_str()); + ImGui::TableNextColumn(); + if (watch.evaluateFailed) { + ImGui::TextUnformatted("(Error)"); + } else { + const uint32_t value = watch.currentValue; + float valuef = 0.0f; + switch (watch.format) { + case WatchFormat::HEX: + ImGui::Text("%08x", value); + break; + case WatchFormat::INT: + ImGui::Text("%d", value); + break; + case WatchFormat::FLOAT: + memcpy(&valuef, &value, sizeof(valuef)); + ImGui::Text("%f", value); + break; + case WatchFormat::STR: + if (Memory::IsValidAddress(value)) { + uint32_t len = Memory::ValidSize(value, 255); + ImGui::Text("%.*s", len, Memory::GetCharPointer(value)); + } else { + ImGui::Text("%08x", value); + } + break; + } + } + ImGui::TableNextColumn(); + if (ImGui::SmallButton("X")) { + watches_.erase(watches_.begin() + i); + } + ImGui::PopID(); + } + + ImGui::EndTable(); + } + + ImGui::End(); +} + void ImAtracToolWindow::Load() { if (File::ReadBinaryFileToString(Path(atracPath_), &data_)) { track_.reset(new Track()); @@ -2052,6 +2133,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::MenuItem("VFPU regs", nullptr, &cfg_.vfpuOpen); ImGui::MenuItem("Callstacks", nullptr, &cfg_.callstackOpen); ImGui::MenuItem("Breakpoints", nullptr, &cfg_.breakpointsOpen); + ImGui::MenuItem("Watch", nullptr, &cfg_.watchOpen); ImGui::EndMenu(); } if (ImGui::BeginMenu("Symbols")) { @@ -2305,6 +2387,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu memDumpWindow_.Draw(cfg_, mipsDebug); } + if (cfg_.watchOpen) { + watchWindow_.Draw(cfg_, control, mipsDebug); + } + for (int i = 0; i < 4; i++) { if (cfg_.memViewOpen[i]) { mem_[i].Draw(mipsDebug, cfg_, control, i); @@ -2475,6 +2561,8 @@ void ImConfig::SyncConfig(IniFile *ini, bool save) { sync.Sync("logConfigOpen", &logConfigOpen, false); sync.Sync("luaConsoleOpen", &luaConsoleOpen, false); sync.Sync("utilityModulesOpen", &utilityModulesOpen, false); + sync.Sync("memDumpOpen", &memDumpOpen, false); + sync.Sync("watchOpen", &watchOpen, false); sync.Sync("atracToolOpen", &atracToolOpen, false); for (int i = 0; i < 4; i++) { char name[64]; diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index 91830e0225..81ed7b507f 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -15,6 +15,7 @@ #include "Core/Debugger/DisassemblyManager.h" #include "Core/Debugger/DebugInterface.h" +#include "Core/Debugger/Watch.h" #include "UI/ImDebugger/ImDisasmView.h" #include "UI/ImDebugger/ImMemView.h" @@ -133,6 +134,17 @@ public: std::unique_ptr track_; std::string error_; std::string data_; + + int editingWatchIndex_ = -1; + int editingColumn_ = 0; +}; + +class ImWatchWindow { +public: + ImWatchWindow(); + void Draw(ImConfig &cfg, ImControl &control, MIPSDebugInterface *mipsDebug); +private: + std::vector watches_; }; enum class ImCmd { @@ -186,6 +198,7 @@ private: ImStructViewer structViewer_; ImGePixelViewerWindow pixelViewer_; ImMemDumpWindow memDumpWindow_; + ImWatchWindow watchWindow_; ImAtracToolWindow atracToolWindow_; ImConsole luaConsole_; diff --git a/Windows/Debugger/Debugger_Lists.h b/Windows/Debugger/Debugger_Lists.h index bda6c1209f..f5ac91eaff 100644 --- a/Windows/Debugger/Debugger_Lists.h +++ b/Windows/Debugger/Debugger_Lists.h @@ -1,19 +1,13 @@ #pragma once -#include "../../Core/Debugger/DebugInterface.h" -#include "../../Core/HLE/sceKernelThread.h" -#include "../../Core/Debugger/Breakpoints.h" -#include "../../Core/Debugger/SymbolMap.h" -#include "../../Core/MIPS/MIPSStackWalk.h" +#include "Core/Debugger/DebugInterface.h" +#include "Core/HLE/sceKernelThread.h" +#include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/SymbolMap.h" +#include "Core/Debugger/Watch.h" +#include "Core/MIPS/MIPSStackWalk.h" #include "Windows/W32Util/Misc.h" -enum class WatchFormat { - HEX, - INT, - FLOAT, - STR, -}; - class CtrlThreadList: public GenericListControl { public: @@ -111,16 +105,6 @@ private: void DeleteWatch(int pos); bool HasWatchChanged(int pos); - struct WatchInfo { - std::string name; - std::string originalExpression; - PostfixExpression expression; - WatchFormat format = WatchFormat::HEX; - uint32_t currentValue = 0; - uint32_t lastValue = 0; - int steppingCounter = -1; - bool evaluateFailed = false; - }; std::vector watches_; DebugInterface *cpu_; };