#include "DebuggerState.h" #include "Disassembler.h" #include "FEXImGui.h" #include "IMGui_I.h" #include "IRLexer.h" #include "Common/Config.h" #include "Common/StringUtil.h" #include "Util/DataRingBuffer.h" #include #include #include #include #include #include #include #include #include #include #include #include #define YES_IMGUIFILESYSTEM #include #include #include #include #include #include #include #include namespace FEX::Debugger { double g_Time = 0.0; constexpr size_t TmpSize = 512; char Tmp[TmpSize]; namespace CPUState { bool ShowCPUState {false}; bool ShowCPUThreads {true}; FEXCore::Core::CPUState PreviousState; FEXCore::Core::CPUState CurrentState; int CurrentThreadSelected {0}; bool ThreadNameGetter(void *data, int idx, const char** out_text) { if (FEX::DebuggerState::ActiveCore()) { snprintf(Tmp, TmpSize, "Thread %d", idx); *out_text = Tmp; return true; } return false; } void MenuItems() { ImGui::MenuItem("CPU State", nullptr, &ShowCPUState); ImGui::MenuItem("CPU Threads", nullptr, &ShowCPUThreads); if (ImGui::BeginMenu("Backend")) { if (ImGui::MenuItem("IR Interpreter", nullptr, FEX::DebuggerState::GetCoreType() == FEXCore::Config::ConfigCore::CONFIG_INTERPRETER, !FEX::DebuggerState::ActiveCore())) FEX::DebuggerState::SetCoreType(FEXCore::Config::ConfigCore::CONFIG_INTERPRETER); if (ImGui::MenuItem("IR JIT", nullptr, FEX::DebuggerState::GetCoreType() == FEXCore::Config::ConfigCore::CONFIG_IRJIT, !FEX::DebuggerState::ActiveCore())) FEX::DebuggerState::SetCoreType(FEXCore::Config::ConfigCore::CONFIG_IRJIT); ImGui::EndMenu(); } ImGui::Separator(); if (ImGui::MenuItem("Single Step", nullptr, FEX::DebuggerState::GetRunningMode() == 1, !FEX::DebuggerState::IsCoreRunning())) FEX::DebuggerState::SetRunningMode(1); if (ImGui::MenuItem("Regular Running", nullptr, FEX::DebuggerState::GetRunningMode() == 0, !FEX::DebuggerState::IsCoreRunning())) FEX::DebuggerState::SetRunningMode(0); } void Windows() { if (ImGui::Begin("#CPU State", &ShowCPUState)) { auto &State = CurrentState; bool PushedCol = false; #define DiffCol(X) do { if (PreviousState.X != State.X) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0, 0.0, 0.0, 1.0)); PushedCol = true;} } while (0) #define DiffPop(X) do { if (PushedCol) { ImGui::PopStyleColor(); PushedCol = false; if (ImGui::IsItemHovered()) ImGui::SetTooltip("Prev: 0x%lx", PreviousState.X);} } while (0) DiffCol(rip); ImGui::Text("RIP: 0x%lx", State.rip); DiffPop(rip); if (ImGui::CollapsingHeader("GPRs")) { const std::vector GPRNames = { "RAX", "RBX", "RCX", "RDX", "RSI", "RDI", "RBP", "RSP", " R8", " R9", "R10", "R11", "R12", "R13", "R14", "R15", }; for (unsigned i = 0; i < 16; ++i) { DiffCol(gregs[i]); ImGui::Text("%s: 0x%016lx", GPRNames[i], State.gregs[i]); DiffPop(gregs[i]); } } if (ImGui::CollapsingHeader("XMMs")) { for (unsigned i = 0; i < 16; ++i) { DiffCol(xmm[i][0]); ImGui::Text("%2d: 0x%016lx - 0x%016lx", i, State.xmm[i][0], State.xmm[i][1]); DiffPop(xmm[i][0]); } } if (ImGui::CollapsingHeader("FLAGs")) { // DiffCol(rflags); // ImGui::Text("EFLAG: 0x%x", static_cast(State.rflags)); // DiffPop(rflags); // #define PRINT_FLAG(X, Y) ImGui::Text(#X ": %d " #Y ": %d", !!(State.rflags & FEXCore::X86State::RFLAG_ ## X ## _LOC), !!(State.rflags & FEXCore::X86State::RFLAG_ ## Y ## _LOC)) // PRINT_FLAG(CF, PF); // PRINT_FLAG(AF, ZF); // PRINT_FLAG(SF, OF); // ImGui::Separator(); // PRINT_FLAG(TF, IF); // PRINT_FLAG(DF, IOPL); // PRINT_FLAG(NT, RF); // PRINT_FLAG(VM, AC); // PRINT_FLAG(VIF, VIP); // ImGui::Text("ID: %d", !!(State.rflags & FEXCore::X86State::RFLAG_ID_LOC)); // #undef PRINT_FLAG } ImGui::Separator(); DiffCol(fs); ImGui::Text("FS: 0x%lx", State.fs); DiffPop(fs); DiffCol(gs); ImGui::Text("GS: 0x%lx", State.gs); DiffPop(gs); #undef DiffCol #undef DiffPop FEX::DebuggerState::SetHasNewState(false); } ImGui::End(); if (ImGui::Begin("#CPU Threads", &ShowCPUThreads)) { size_t ThreadCount = 0; if (FEX::DebuggerState::ActiveCore()) { ThreadCount = FEXCore::Context::Debug::GetThreadCount(FEX::DebuggerState::GetContext()); } ImGui::Text("Threads: %ld", ThreadCount); ImGui::ListBox("##Threads", &CurrentThreadSelected, ThreadNameGetter, nullptr, ThreadCount); } ImGui::End(); } void Status() { ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFrameHeight()); bool PushedStyle = false; if (!FEX::DebuggerState::IsCoreRunning() || FEX::DebuggerState::GetCoreCurrentRunningMode() == 0) { ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(1.0, 0.0, 0.0, 1.0)); ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.0, 1.0, 0.0, 1.0)); PushedStyle = true; } else if (FEX::DebuggerState::IsCoreRunning() && FEX::DebuggerState::GetCoreCurrentRunningMode() == 1) { ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0, 1.0, 0.0, 1.0)); ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.0, 1.0, 0.0, 1.0)); PushedStyle = true; } ImGui::RadioButton("", FEX::DebuggerState::IsCoreRunning()); if (PushedStyle) { ImGui::PopStyleColor(2); } } void NewState() { PreviousState = CurrentState; CurrentState = FEX::DebuggerState::GetCPUState(); } } // CPU stats window namespace CPUStats { bool ShowCPUStats {true}; FEX::Debugger::Util::DataRingBuffer InstExecuted(60 * 10); FEX::Debugger::Util::DataRingBuffer BlocksCompiled(60 * 10); auto LastTime = std::chrono::high_resolution_clock::now(); void Window() { auto Now = std::chrono::high_resolution_clock::now(); if ((Now - LastTime) >= std::chrono::seconds(1)) { LastTime = Now; if (FEX::DebuggerState::ActiveCore()) { auto RuntimeStats = FEXCore::Context::Debug::GetRuntimeStatsForThread(FEX::DebuggerState::GetContext(), CPUState::CurrentThreadSelected); InstExecuted.push_back(RuntimeStats->InstructionsExecuted); BlocksCompiled.push_back(RuntimeStats->BlocksCompiled); RuntimeStats->InstructionsExecuted = 0; RuntimeStats->BlocksCompiled = 0; } } if (ImGui::Begin("#CPU Stats", &ShowCPUStats)) { if (!InstExecuted.empty()) { ImGui::PlotHistogram("MIPS", InstExecuted(), InstExecuted.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 50)); ImGui::SameLine(); ImGui::Text("%f", InstExecuted.back()); } if (!BlocksCompiled.empty()) { ImGui::PlotHistogram("Blocks Compiled", BlocksCompiled(), BlocksCompiled.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 50)); ImGui::SameLine(); ImGui::Text("%f", BlocksCompiled.back()); } } ImGui::End(); } } // Logging Windows namespace Logging { bool ShowLogOutput = true; bool ShowLogstdout = true; bool ShowLogstderr = true; bool ScrollLogWindow = false; std::vector LogData; std::vector stdoutData; std::vector stderrData; constexpr size_t MAX_LOG_SIZE = 10000; std::string IRData; void MsgHandler(LogMan::DebugLevels Level, char const *Message) { const char *CharLevel{nullptr}; switch (Level) { case LogMan::NONE: CharLevel = "NONE"; break; case LogMan::ASSERT: CharLevel = "ASSERT"; break; case LogMan::ERROR: CharLevel = "ERROR"; break; case LogMan::DEBUG: CharLevel = "DEBUG"; break; case LogMan::INFO: CharLevel = "Info"; break; case LogMan::STDOUT: CharLevel = "stdout"; break; case LogMan::STDERR: CharLevel = "stderr"; break; default: CharLevel = "???"; break; } auto AddTo = [](auto &Output, std::string &Log) { Output.emplace_back(Log); if (Output.size() > MAX_LOG_SIZE) { Output.erase(Output.begin(), Output.begin() + (Output.size() - MAX_LOG_SIZE)); } }; std::string Log; Log += "["; Log += CharLevel; Log += "] "; Log += Message; FEX::StringUtil::rtrim(Log); AddTo(LogData, Log); if (Level == LogMan::STDOUT) AddTo(stdoutData, Log); if (Level == LogMan::STDERR) AddTo(stderrData, Log); printf("[%s] %s\n", CharLevel, Message); ScrollLogWindow = true; } void AssertHandler(char const *Message) { std::string Log; Log += "[ASSERT] "; Log += Message; FEX::StringUtil::rtrim(Log); LogData.emplace_back(Log); if (LogData.size() > MAX_LOG_SIZE) { LogData.erase(LogData.begin(), LogData.begin() + (LogData.size() - MAX_LOG_SIZE)); } printf("[ASSERT] %s\n", Message); ScrollLogWindow = true; } void LogWindow(bool *LogBool, const char *Name, std::vector const &Log) { if (LogBool) { if (ImGui::Begin(Name, LogBool)) { ImGui::BeginChild("LogConsole"); for (auto &Text : Log) { bool pop_color = false; if (strstr(Text.c_str(), "ERROR")) { pop_color = true; ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.5f, 1.0f)); } if (strstr(Text.c_str(), "ASSERT")) { pop_color = true; ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); } if (strstr(Text.c_str(), "DEBUG")) { pop_color = true; ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f)); } ImGui::TextUnformatted(Text.c_str()); if (pop_color) { ImGui::PopStyleColor(); } } if (ScrollLogWindow) { ImGui::SetScrollHereY(1.0f); ScrollLogWindow = false; } ImGui::EndChild(); } ImGui::End(); } } void Windows() { LogWindow(&ShowLogOutput, "#Log", LogData); LogWindow(&ShowLogstderr, "#Log stderr", stderrData); LogWindow(&ShowLogstdout, "#Log stdout", stdoutData); } void Init() { LogMan::Throw::InstallHandler(AssertHandler); LogMan::Msg::InstallHandler(MsgHandler); } void MenuItems() { ImGui::MenuItem("Log", nullptr, &ShowLogOutput); ImGui::MenuItem("LogO", nullptr, &ShowLogstdout); ImGui::MenuItem("LogE", nullptr, &ShowLogstderr); } } namespace Disasm { bool ShowCPUHostDisasm = true; bool ShowCPUGuestDisasm = true; uint64_t CurrentDisasmRIP {}; std::string HostDisasmString; std::string GuestDisasmString; std::unique_ptr HostDisassembler; std::unique_ptr GuestDisassembler; void MenuItems() { ImGui::MenuItem("CPU Guest Disasm", nullptr, &ShowCPUGuestDisasm); ImGui::MenuItem("CPU Host Disasm", nullptr, &ShowCPUHostDisasm); } void Windows() { if (ImGui::Begin("#Guest Disasm", &ShowCPUGuestDisasm)) { ImGui::InputTextMultiline("", const_cast(GuestDisasmString.c_str()), GuestDisasmString.size(), ImVec2(-1, -1), ImGuiInputTextFlags_ReadOnly); } ImGui::End(); if (ImGui::Begin("#Host Disasm", &ShowCPUHostDisasm)) { ImGui::InputTextMultiline("", const_cast(HostDisasmString.c_str()), HostDisasmString.size(), ImVec2(-1, -1), ImGuiInputTextFlags_ReadOnly); } ImGui::End(); } uint8_t *GetPointerFromRegions(uint64_t PC) { return nullptr; } void DisasmGuest(uint64_t PC, int Size) { CurrentDisasmRIP = PC; uint8_t *GuestCode = GetPointerFromRegions(PC); uint32_t InstCount{}; GuestDisasmString = GuestDisassembler->Disassemble(GuestCode, Size, 255, PC, &InstCount); bool DebugBlockExists; bool HostCodeBlockExists; uint8_t *HostCodePtr; FEXCore::Core::DebugData DebugData; HostCodeBlockExists = FEXCore::Context::Debug::FindHostCodeForRIP(FEX::DebuggerState::GetContext(), PC, &HostCodePtr); DebugBlockExists = FEXCore::Context::Debug::GetDebugDataForRIP(FEX::DebuggerState::GetContext(), PC, &DebugData); if (!DebugBlockExists || !HostCodeBlockExists || DebugData.HostCodeSize == 0) { // Didn't have a block backing this PC. HostDisasmString = ""; } else { HostDisasmString = HostDisassembler->Disassemble(static_cast(HostCodePtr), DebugData.HostCodeSize, -1U, reinterpret_cast(HostCodePtr), &InstCount); } } void Disasm(uint64_t PC) { CurrentDisasmRIP = PC; uint8_t *GuestCode = GetPointerFromRegions(PC); uint32_t InstCount{}; bool DebugBlockExists; bool HostCodeBlockExists; uint8_t *HostCodePtr; FEXCore::Core::DebugData DebugData; HostCodeBlockExists = FEXCore::Context::Debug::FindHostCodeForRIP(FEX::DebuggerState::GetContext(), PC, &HostCodePtr); DebugBlockExists = FEXCore::Context::Debug::GetDebugDataForRIP(FEX::DebuggerState::GetContext(), PC, &DebugData); GuestDisasmString = GuestDisassembler->Disassemble(GuestCode, DebugData.GuestCodeSize, DebugData.GuestInstructionCount, PC, &InstCount); if (!DebugBlockExists || !HostCodeBlockExists || DebugData.HostCodeSize == 0) { // Didn't have a block backing this PC. HostDisasmString = ""; } else { HostDisasmString = HostDisassembler->Disassemble(static_cast(HostCodePtr), DebugData.HostCodeSize, -1U, reinterpret_cast(HostCodePtr), &InstCount); } } void Init() { HostDisassembler = FEX::Debugger::CreateHostDisassembler(); GuestDisassembler = FEX::Debugger::CreateGuestDisassembler(); } } namespace IR { bool ShowCPUIR = true; bool ShowCPUIRList = true; struct IRDebugData { FEXCore::Core::DebugData *Debug; uint64_t RIP; std::string RIPString; std::string GuestCodeSize; std::string GuestInstructionCount; }; std::vector IRListTexts; int CPUIRListCurrentItem {}; struct IRFileHeader { uint64_t RIP; size_t IRSize; }; bool HadSelectedSaveIR{}; ImGuiFs::Dialog Dialog; FEX::Debugger::IR::Lexer Lexer; std::vector IRLines; //void CalculateCFGLines(FEXCore::IR::IntrusiveIRList *IR) { // IRLines.clear(); // size_t Line {}; // size_t i {}; // size_t Size = IR->GetOffset(); // // From->To: Offset->AlignmentmentType // std::unordered_map MapFromTo{}; // // To->From: AlignmentType->Offset // std::unordered_map> MapToFrom{}; // // Offset->Line // std::unordered_map OffsetToLine{}; // while (i != Size) { // auto IROp = IR->GetOp(i); // auto OpSize = FEXCore::IR::GetSize(IROp->Op); // switch (IROp->Op) { // case FEXCore::IR::OP_JUMP: { // auto Op = IROp->C(); // MapFromTo[i] = Op->Location.Off; // MapToFrom[Op->Location.Off].emplace_back(i); // break; // } // case FEXCore::IR::OP_CONDJUMP: { // auto Op = IROp->C(); // MapFromTo[i] = Op->Location.Off; // MapToFrom[Op->Location.Off].emplace_back(i); // break; // } // default: break; // } // OffsetToLine[i] = Line; // i += OpSize; // ++Line; // } // for (auto From : MapFromTo) { // auto To = MapToFrom.find(From.second); // auto FromLine = OffsetToLine[From.first]; // auto ToLine = OffsetToLine[To->first]; // IRLines.emplace_back(FEXImGui::IRLines{FromLine, ToLine}); // } //} bool IRListGetter(void *data, int idx, const char** out_text) { int Type = reinterpret_cast(data); switch (Type) { case 1: *out_text = IRListTexts[idx].RIPString.c_str(); break; case 2: *out_text = IRListTexts[idx].GuestCodeSize.c_str(); break; case 3: *out_text = IRListTexts[idx].GuestInstructionCount.c_str(); break; } return true; } void MenuItems() { ImGui::MenuItem("IR Viewer", nullptr, &ShowCPUIR); ImGui::MenuItem("IR List", nullptr, &ShowCPUIRList); } void Windows() { auto SortByRIP = [](IRDebugData const &a, IRDebugData const &b) -> bool { return a.RIP < b.RIP; }; auto SortByCodeSize = [](IRDebugData const &a, IRDebugData const &b) -> bool { return a.Debug->GuestCodeSize < b.Debug->GuestCodeSize; }; auto SortByInstCount = [](IRDebugData const &a, IRDebugData const &b) -> bool { return a.Debug->GuestInstructionCount < b.Debug->GuestInstructionCount; }; if (ImGui::Begin("#CPU IR Entries", &ShowCPUIRList)) { if (ImGui::Button("Sort RIP")) { std::sort(IRListTexts.begin(), IRListTexts.end(), SortByRIP); } ImGui::SameLine(); if (ImGui::Button("Sort CodeSize")) { std::sort(IRListTexts.begin(), IRListTexts.end(), SortByCodeSize); } ImGui::SameLine(); if (ImGui::Button("Sort Inst Count")) { std::sort(IRListTexts.begin(), IRListTexts.end(), SortByInstCount); } ImGui::Columns(3); ImGui::Text("RIPS"); ImGui::NextColumn(); ImGui::Text("CodeSize"); ImGui::NextColumn(); ImGui::Text("InstructionCount"); ImGui::NextColumn(); ImGui::PushItemWidth(-1.0); bool UpdateSelection = false; UpdateSelection |= ImGui::ListBox("##RIP", &CPUIRListCurrentItem, IRListGetter, reinterpret_cast(1), IRListTexts.size()); ImGui::PopItemWidth(); ImGui::NextColumn(); ImGui::PushItemWidth(-1.0); UpdateSelection |= ImGui::ListBox("##CodeSize", &CPUIRListCurrentItem, IRListGetter, reinterpret_cast(2), IRListTexts.size()); ImGui::PopItemWidth(); ImGui::NextColumn(); ImGui::PushItemWidth(-1.0); UpdateSelection |= ImGui::ListBox("##InstCount", &CPUIRListCurrentItem, IRListGetter, reinterpret_cast(3), IRListTexts.size()); ImGui::PopItemWidth(); if (UpdateSelection) { uint64_t PC = IRListTexts[CPUIRListCurrentItem].RIP; std::stringstream out; FEX::DebuggerState::GetIR(&out, PC); Disasm::Disasm(PC); Logging::IRData = out.str(); // FEXCore::IR::IntrusiveIRList *ir; // bool HadIR = FEXCore::Context::Debug::FindIRForRIP(FEX::DebuggerState::GetContext(), Disasm::CurrentDisasmRIP, &ir); // if (HadIR) { // IR::CalculateCFGLines(ir); // } } } ImGui::End(); if (ImGui::Begin("#IR Viewer", &ShowCPUIR)) { HadSelectedSaveIR = ImGui::Button("Save IR"); FEXImGui::CustomIRViewer(Logging::IRData.c_str(), Logging::IRData.size(), &IRLines); } ImGui::End(); const char *InitialPath = Dialog.saveFileDialog(HadSelectedSaveIR, "./"); const char *File = Dialog.getChosenPath(); if (strlen(InitialPath) > 0 && strlen(File) > 0) { // FEXCore::IR::IntrusiveIRList *ir; // bool HadIR = FEXCore::Context::Debug::FindIRForRIP(FEX::DebuggerState::GetContext(), Disasm::CurrentDisasmRIP, &ir); // if (HadIR) { // IRFileHeader Header; // Header.RIP = Disasm::CurrentDisasmRIP; // Header.IRSize = ir->GetOffset(); // std::fstream IRFile(File, std::fstream::out | std::fstream::binary); // LOGMAN_THROW_A_FMT(IRFile.is_open(), "Failed to open file"); // // IRFile.write(reinterpret_cast(&Header), sizeof(Header)); // IRFile.write(ir->GetOpAs(0), Header.IRSize); // } } } void LoadIR(char const *Filename) { IRFileHeader Header; std::fstream IRFile(Filename, std::fstream::in | std::fstream::binary); LOGMAN_THROW_A_FMT(IRFile.is_open(), "Failed to open file"); IRFile.read(reinterpret_cast(&Header), sizeof(Header)); // FEXCore::IR::IntrusiveIRList IR(Header.IRSize); // char *Ptr = IR.SetupForLoad(Header.IRSize); // IRFile.read(Ptr, Header.IRSize); // IRFile.close(); // FEXCore::Context::Debug::SetIRForRIP(FEX::DebuggerState::GetContext(), Header.RIP, &IR); // FEX::DebuggerState::SetHasNewState(); // FEX::DebuggerState::CallNewState(); } void NewState() { CPUIRListCurrentItem = 0; IRListTexts.clear(); // CPU core could have been removed if (!FEX::DebuggerState::ActiveCore()) { return; } FEXCore::Core::ThreadState *State = FEXCore::Context::Debug::GetThreadState(FEX::DebuggerState::GetContext()); FEXCore::Core::InternalThreadState *TS = reinterpret_cast(State); auto Local = TS->LocalIRCache; auto &DebugData = TS->DebugData; for (auto &LocalEntry : TS->LocalIRCache) { std::ostringstream out; out << "0x" << std::hex << LocalEntry.first; IRDebugData DebugData; DebugData.Debug = LocalEntry.second.DebugData.get(); DebugData.RIP = IR.first; DebugData.RIPString = out.str(); DebugData.GuestCodeSize = std::to_string(DebugData.Debug->GuestCodeSize); DebugData.GuestInstructionCount = std::to_string(DebugData.Debug->GuestInstructionCount); IRListTexts.emplace_back(DebugData); } } } // History handling namespace History { enum HistoryType { TYPE_ELF, TYPE_TEST_HARNESS, TYPE_IR, TYPE_X86, }; std::list> HistoryItems; void Save() { // Save History char Buffer[512]; char *Dest; Dest = json_objOpen(Buffer, nullptr); Dest = json_objOpen(Dest, "History"); for (auto &it : HistoryItems) { Dest = json_int(Dest, it.second.c_str(), it.first); } Dest = json_objClose(Dest); Dest = json_objClose(Dest); json_end(Dest); std::fstream HistoryFile("History.json", std::fstream::out); LOGMAN_THROW_A_FMT(HistoryFile.is_open(), "Failed to open file"); HistoryFile.write(Buffer, strlen(Buffer)); } void Add(HistoryType Type, const char *Filename) { for (auto it = HistoryItems.begin(); it != HistoryItems.end(); ++it) { if (it->first == Type && it->second == Filename) { if (it != HistoryItems.begin()) { HistoryItems.splice(HistoryItems.begin(), HistoryItems, it, std::next(it)); Save(); } return; } } HistoryItems.emplace_front(std::make_pair(Type, Filename)); if (HistoryItems.size() > 5) { auto it = HistoryItems.begin(); std::advance(it, 5); HistoryItems.erase(it, HistoryItems.end()); } Save(); } void MenuItems() { for (auto &item : HistoryItems) { std::string Item{}; switch (item.first) { case TYPE_ELF: Item = "ELF: "; break; case TYPE_TEST_HARNESS: Item = "Test: "; break; case TYPE_IR: Item = "IR: "; break; case TYPE_X86: Item = "X86: "; break; } auto Last = item.second.find_last_of('/') + 1; std::string Filename = item.second.substr(Last); Item += Filename; if (ImGui::MenuItem(Item.c_str(), nullptr, nullptr, !FEX::DebuggerState::ActiveCore() || item.first == TYPE_IR)) { if (item.first == TYPE_IR) { IR::LoadIR(item.second.c_str()); } else { FEX::DebuggerState::Create(item.second.c_str(), item.first == TYPE_ELF); } History::Add(item.first, item.second.c_str()); } } } void Init() { // Load History std::fstream HistoryFile; std::vector Data; HistoryFile.open("History.json", std::fstream::in); if (HistoryFile.is_open()) { HistoryFile.seekg(0, std::fstream::end); size_t FileSize = HistoryFile.tellg(); HistoryFile.seekg(0, std::fstream::beg); Data.resize(FileSize); HistoryFile.read(&Data.at(0), FileSize); HistoryFile.close(); json_t elem[32]; json_t const* json = json_create(&Data.at(0), elem, std::size(elem)); json_t const* HistoryList = json_getProperty(json, "History"); for (json_t const* HistoryItem = json_getChild(HistoryList); HistoryItem != nullptr; HistoryItem = json_getSibling(HistoryItem)) { const char* HistoryName = json_getName(HistoryItem); const char* HistoryType = json_getValue(HistoryItem); History::Add(static_cast(atol(HistoryType)), HistoryName); } } } } // Config namespace Config { bool ShowConfig = true; struct Configs { FEX_CONFIG_OPT(ConfigMaxInst, MAXINST); FEX_CONFIG_OPT(ConfigCore, CORE); FEX_CONFIG_OPT(ConfigRunningMode, SINGLESTEP); FEX_CONFIG_OPT(ConfigMultiblock, MULTIBLOCK); }; std::unique_ptr Config; int NewInstMax {}; bool Multiblock; void Window() { if (ImGui::Begin("#Config", &ShowConfig)) { if (ImGui::SliderInt("Max Inst", &NewInstMax, -1, 255)) { } if (ImGui::Checkbox("Multiblock", &Multiblock)) { } } ImGui::End(); } void Init() { Config = std::make_unique(); NewInstMax = Config->ConfigMaxInst(); Multiblock = Config->ConfigMultiblock(); FEX::DebuggerState::SetCoreType(static_cast(Config::Config->ConfigCore())); FEX::DebuggerState::SetRunningMode(Config::Config->ConfigRunningMode()); } void Shutdown() { } } // Memory viewer window namespace MemoryViewer { bool ShowMemoryWindow = false; int MappedRegionsCurrentItem {}; int MappedRegionsMemoryCurrentItem {}; int CodeSize {128}; uint64_t CurrentAddress {}; char CurrentAddressString[32]{}; bool MappedRegionGetter(void *data, int idx, const char** out_text) { return false; //auto &Region = MappedRegions[idx]; //static char Buf[512]; //snprintf(Buf, 512, "[0x%lx, 0x%lx) - 0x%lx", Region.Offset, Region.Offset + Region.Size, Region.Size); //*out_text = Buf; //return true; } bool MappedRegionMemoryGetter(void *data, int idx, const char** out_text) { return false; //if (MappedRegions.empty()) // return false; //auto &Region = MappedRegions[MappedRegionsCurrentItem]; //static char Buf[512]{}; //char BufChars[17]{}; //uint8_t *Ptr = static_cast(Region.Ptr); //Ptr += idx * 16; //for (int i = 0; i < 16; i++) { // if (isprint(Ptr[i])) // BufChars[i] = Ptr[i]; // else // BufChars[i] = '.'; //} //snprintf(Buf, 512, // "%4lX > %02x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\t%s", // Region.Offset + idx * 16, // Ptr[0], Ptr[1], Ptr[2], Ptr[3], Ptr[4], Ptr[5], Ptr[6], Ptr[7], // Ptr[8], Ptr[9], Ptr[10], Ptr[11], Ptr[12], Ptr[13], Ptr[14], Ptr[15], // BufChars // ); //*out_text = Buf; //return true; } void Window() { //auto DisplayAddress = [&](uint64_t RIP) { // MappedRegionsCurrentItem = 0; // // First we need to find the RIP in the mapped regions // // Skip first entry because it is the full 64GB of vmem // for (size_t i = 1; i < MappedRegions.size(); ++i) { // if (MappedRegions[i].contains(RIP)) { // MappedRegionsCurrentItem = i; // break; // } // } // // Now we need to set our hex view listbox to the correct line // MappedRegionsMemoryCurrentItem = static_cast(RIP - MappedRegions[MappedRegionsCurrentItem].Offset) / 16; // // Now set the Current Address textbox and string to the correct address // LogMan::Msg::DFmt("RIP is 0x{:x}", RIP); // CurrentAddress = RIP; // std::ostringstream out{}; // out << "0x" << std::hex << CurrentAddress; // strncpy(CurrentAddressString, out.str().c_str(), 32); //}; //if (ImGui::Begin("#Memory Viewer", &ShowMemoryWindow)) { // if (FEX::DebuggerState::ActiveCore() && !FEX::DebuggerState::IsCoreRunning()) { // FEXCore::Context::Debug::GetMemoryRegions(FEX::DebuggerState::GetContext(), &MappedRegions); // } // if (!MappedRegions.empty()) { // ImGui::PushItemWidth(-1.0); // if (ImGui::ListBox("Mapped Regions", &MappedRegionsCurrentItem, MappedRegionGetter, nullptr, MappedRegions.size())) { // } // ImGui::PopItemWidth(); // if (ImGui::InputText("RIP:", CurrentAddressString, 32, ImGuiInputTextFlags_EnterReturnsTrue)) { // DisplayAddress(std::stoull(CurrentAddressString, nullptr, 0)); // } // ImGui::DragInt("Size", &CodeSize, 1.0f, 1, 255); // if (ImGui::Button("Disasm")) { // CurrentAddress = std::stoull(CurrentAddressString, nullptr, 0); // Disasm::DisasmGuest(CurrentAddress, CodeSize); // } // if (FEX::DebuggerState::IsCoreRunning()) { // ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); // ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); // } // if (ImGui::Button("Compile RIP")) { // FEXCore::Context::Debug::CompileRIP(FEX::DebuggerState::GetContext(), CurrentAddress); // std::stringstream out; // FEX::DebuggerState::GetIR(&out, CurrentAddress); // Disasm::Disasm(CurrentAddress); // Logging::IRData = out.str(); // // FEXCore::IR::IntrusiveIRList *ir; // // bool HadIR = FEXCore::Context::Debug::FindIRForRIP(FEX::DebuggerState::GetContext(), CurrentAddress, &ir); // // if (HadIR) { // // IR::CalculateCFGLines(ir); // // } // } // if (FEX::DebuggerState::IsCoreRunning()) { // ImGui::PopStyleVar(); // ImGui::PopItemFlag(); // } // if (ImGui::Button("Go to current RIP")) { // if (FEX::DebuggerState::ActiveCore()) { // DisplayAddress(CPUState::CurrentState.rip); // } // } // if (ImGui::BeginChildFrame(ImGui::GetID("Hex"), ImVec2(-1, -1))) { // ImGui::PushItemWidth(-1.0); // if (FEXImGui::ListBox("##Memory Hex", // &MappedRegionsMemoryCurrentItem, // MappedRegionMemoryGetter, // nullptr, // static_cast(MappedRegions[MappedRegionsCurrentItem].Size) / 16)) { // CurrentAddress = MappedRegions[MappedRegionsCurrentItem].Offset + MappedRegionsMemoryCurrentItem * 16; // std::ostringstream out{}; // out << "0x" << std::hex << CurrentAddress; // strncpy(CurrentAddressString, out.str().c_str(), 32); // } // ImGui::PopItemWidth(); // } // ImGui::EndChildFrame(); // } //} //ImGui::End(); } void Menu() { if (ImGui::BeginMenu("Memory")) { ImGui::MenuItem("Memory Viewer", nullptr, &ShowMemoryWindow); ImGui::EndMenu(); } } } namespace FileDialogs { bool HadSelectedLaunchHarness = false; bool HadSelectedLaunchELF = false; bool HadSelectedIR = false; bool HadSelectedX86 = false; ImGuiFs::Dialog DialogELF{}; ImGuiFs::Dialog DialogTest{}; ImGuiFs::Dialog DialogIR{}; ImGuiFs::Dialog DialogX86{}; void MenuItems() { if (ImGui::BeginMenu("Launch")) { HadSelectedLaunchELF = ImGui::MenuItem("ELF", nullptr, false, !FEX::DebuggerState::ActiveCore()); HadSelectedLaunchHarness = ImGui::MenuItem("TestHarness", nullptr, false, !FEX::DebuggerState::ActiveCore()); ImGui::EndMenu(); } if (ImGui::BeginMenu("Load")) { HadSelectedIR = ImGui::MenuItem("IR", nullptr, false, FEX::DebuggerState::ActiveCore()); HadSelectedX86 = ImGui::MenuItem("X86", nullptr, false, !FEX::DebuggerState::ActiveCore()); ImGui::EndMenu(); } //ImGui::MenuItem("Connect"); } void Windows() { char const *InitialPath; char const *File; InitialPath = DialogELF.chooseFileDialog(HadSelectedLaunchELF, "./"); File = DialogELF.getChosenPath(); if (strlen(InitialPath) > 0 && strlen(File) > 0) { FEX::DebuggerState::Create(File, true); History::Add(History::TYPE_ELF, File); } InitialPath = DialogTest.chooseFileDialog(HadSelectedLaunchHarness, "./"); File = DialogTest.getChosenPath(); if (strlen(InitialPath) > 0 && strlen(File) > 0) { FEX::DebuggerState::Create(File, false); History::Add(History::TYPE_TEST_HARNESS, File); } InitialPath = DialogIR.chooseFileDialog(HadSelectedIR, "./"); File = DialogIR.getChosenPath(); if (strlen(InitialPath) > 0 && strlen(File) > 0) { IR::LoadIR(File); History::Add(History::TYPE_IR, File); } InitialPath = DialogX86.chooseFileDialog(HadSelectedX86, "./"); File = DialogX86.getChosenPath(); if (strlen(InitialPath) > 0 && strlen(File) > 0) { FEX::DebuggerState::Create(File, false); History::Add(History::TYPE_X86, File); } HadSelectedLaunchHarness = false; HadSelectedLaunchELF = false; HadSelectedIR = false; HadSelectedX86 = false; } } void NewStateUpdate() { CPUState::NewState(); IR::NewState(); } void Init() { Logging::Init(); FEX::DebuggerState::RegisterNewStateCallback(NewStateUpdate); Disasm::Init(); Config::Init(); History::Init(); } void Shutdown() { FEX::DebuggerState::Close(); Config::Shutdown(); History::Save(); } void DrawDebugUI(GLContext::Context *Context) { ImGuiIO& io = ImGui::GetIO(); double current_time = glfwGetTime(); io.DeltaTime = g_Time > 0.0 ? static_cast(current_time - g_Time) : static_cast(1.0f/60.0f); g_Time = current_time; ImGui::NewFrame(); // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, // because it would be confusing to have two docking targets within each others. ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace", nullptr, window_flags); ImGui::PopStyleVar(3); ImGui::DockSpace(ImGui::GetID("DockSpace")); bool CanStep = FEX::DebuggerState::ActiveCore(); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) { FileDialogs::MenuItems(); if (ImGui::MenuItem("Close", nullptr, false, FEX::DebuggerState::ActiveCore())) { FEX::DebuggerState::Close(); } ImGui::Separator(); History::MenuItems(); ImGui::EndMenu(); } if (ImGui::BeginMenu("CPU")) { ImGui::MenuItem("CPU Stats", nullptr, &CPUStats::ShowCPUStats); CPUState::MenuItems(); ImGui::Separator(); Disasm::MenuItems(); ImGui::Separator(); IR::MenuItems(); ImGui::EndMenu(); } MemoryViewer::Menu(); if (ImGui::BeginMenu("Debug")) { if (ImGui::MenuItem("Step", "F11", false, CanStep)) { FEX::DebuggerState::Step(); } if (ImGui::MenuItem("Pause", "Shift+F5", false, CanStep)) { FEX::DebuggerState::Pause(); } if (ImGui::MenuItem("Continue", "F5", false, CanStep)) { FEX::DebuggerState::Continue(); } ImGui::EndMenu(); } if (ImGui::BeginMenu("Log")) { Logging::MenuItems(); ImGui::EndMenu(); } CPUState::Status(); ImGui::EndMenuBar(); } ImGui::End(); // End dockspace Disasm::Windows(); CPUState::Windows(); CPUStats::Window(); Config::Window(); IR::Windows(); MemoryViewer::Window(); FileDialogs::Windows(); Logging::Windows(); ImGui::Render(); if (CanStep) { if (ImGui::IsKeyPressed(GLFW_KEY_F11)) { FEX::DebuggerState::Step(); } if (ImGui::IsKeyPressed(GLFW_KEY_F5) && io.KeyShift) { FEX::DebuggerState::Pause(); } if (ImGui::IsKeyPressed(GLFW_KEY_F5) && !io.KeyShift) { FEX::DebuggerState::Continue(); } } } }