mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-09 00:00:17 +02:00
C++ no-op functions can't optimize out the predicate arguments in all cases. This was causing a problem where zero cost assertions weren't actually zero cost. The only way to resolve this is to actually use macros sadly enough. This will give a fairly hefty performance uplift with anything operating on IR.
1156 lines
36 KiB
C++
1156 lines
36 KiB
C++
#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 <chrono>
|
|
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Core/CoreState.h>
|
|
#include <FEXCore/Core/CPUBackend.h>
|
|
#include <FEXCore/Core/X86Enums.h>
|
|
#include <FEXCore/Debug/ContextDebug.h>
|
|
#include <FEXCore/Debug/InternalThreadState.h>
|
|
#include <FEXCore/IR/IR.h>
|
|
#include <FEXCore/IR/IntrusiveIRList.h>
|
|
#include <FEXCore/Utils/LogManager.h>
|
|
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
#define YES_IMGUIFILESYSTEM
|
|
#include <addons/imgui_user.h>
|
|
#include <GLFW/glfw3.h>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <list>
|
|
#include <vector>
|
|
#include <tiny-json.h>
|
|
#include <json-maker.h>
|
|
|
|
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<const char*> 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<uint32_t>(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<float> InstExecuted(60 * 10);
|
|
FEX::Debugger::Util::DataRingBuffer<float> 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<std::string> LogData;
|
|
std::vector<std::string> stdoutData;
|
|
std::vector<std::string> 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<std::string> 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<FEX::Debugger::Disassembler> HostDisassembler;
|
|
std::unique_ptr<FEX::Debugger::Disassembler> 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<char*>(GuestDisasmString.c_str()), GuestDisasmString.size(), ImVec2(-1, -1), ImGuiInputTextFlags_ReadOnly);
|
|
}
|
|
ImGui::End();
|
|
|
|
if (ImGui::Begin("#Host Disasm", &ShowCPUHostDisasm)) {
|
|
ImGui::InputTextMultiline("", const_cast<char*>(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 = "<No Backing>";
|
|
}
|
|
else {
|
|
HostDisasmString = HostDisassembler->Disassemble(static_cast<uint8_t*>(HostCodePtr), DebugData.HostCodeSize, -1U, reinterpret_cast<uint64_t>(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 = "<No Backing>";
|
|
}
|
|
else {
|
|
HostDisasmString = HostDisassembler->Disassemble(static_cast<uint8_t*>(HostCodePtr), DebugData.HostCodeSize, -1U, reinterpret_cast<uint64_t>(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<IRDebugData> IRListTexts;
|
|
int CPUIRListCurrentItem {};
|
|
|
|
struct IRFileHeader {
|
|
uint64_t RIP;
|
|
size_t IRSize;
|
|
};
|
|
|
|
bool HadSelectedSaveIR{};
|
|
ImGuiFs::Dialog Dialog;
|
|
FEX::Debugger::IR::Lexer Lexer;
|
|
std::vector<FEXImGui::IRLines> 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<uint64_t, uint32_t> MapFromTo{};
|
|
|
|
// // To->From: AlignmentType->Offset
|
|
// std::unordered_map<uint32_t, std::vector<uint64_t>> MapToFrom{};
|
|
|
|
// // Offset->Line
|
|
// std::unordered_map<uint32_t, uint64_t> 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<FEXCore::IR::IROp_Jump>();
|
|
// MapFromTo[i] = Op->Location.Off;
|
|
// MapToFrom[Op->Location.Off].emplace_back(i);
|
|
// break;
|
|
// }
|
|
// case FEXCore::IR::OP_CONDJUMP: {
|
|
// auto Op = IROp->C<FEXCore::IR::IROp_CondJump>();
|
|
// 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<uintptr_t>(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<void*>(1), IRListTexts.size());
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::NextColumn();
|
|
ImGui::PushItemWidth(-1.0);
|
|
UpdateSelection |= ImGui::ListBox("##CodeSize", &CPUIRListCurrentItem, IRListGetter, reinterpret_cast<void*>(2), IRListTexts.size());
|
|
ImGui::PopItemWidth();
|
|
|
|
ImGui::NextColumn();
|
|
ImGui::PushItemWidth(-1.0);
|
|
UpdateSelection |= ImGui::ListBox("##InstCount", &CPUIRListCurrentItem, IRListGetter, reinterpret_cast<void*>(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();
|
|
|
|
char const *InitialPath;
|
|
char const *File;
|
|
|
|
InitialPath = Dialog.saveFileDialog(HadSelectedSaveIR, "./");
|
|
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;
|
|
// IRFile.open(File, std::fstream::out | std::fstream::binary);
|
|
// LOGMAN_THROW_A(IRFile.is_open(), "Failed to open file");
|
|
|
|
// IRFile.write(reinterpret_cast<char*>(&Header), sizeof(Header));
|
|
// IRFile.write(ir->GetOpAs<char>(0), Header.IRSize);
|
|
// IRFile.close();
|
|
// }
|
|
}
|
|
}
|
|
|
|
void LoadIR(char const *Filename) {
|
|
IRFileHeader Header;
|
|
std::fstream IRFile;
|
|
IRFile.open(Filename, std::fstream::in | std::fstream::binary);
|
|
LOGMAN_THROW_A(IRFile.is_open(), "Failed to open file");
|
|
|
|
IRFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
|
|
|
|
// FEXCore::IR::IntrusiveIRList IR(Header.IRSize);
|
|
// char *Ptr = IR.SetupForLoad<char>(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<FEXCore::Core::InternalThreadState*>(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<std::pair<HistoryType, std::string>> 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;
|
|
HistoryFile.open("History.json", std::fstream::out);
|
|
LOGMAN_THROW_A(HistoryFile.is_open(), "Failed to open file");
|
|
|
|
HistoryFile.write(Buffer, strlen(Buffer));
|
|
HistoryFile.close();
|
|
}
|
|
|
|
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<char> 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<History::HistoryType>(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<Configs> 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<Configs>();
|
|
NewInstMax = Config->ConfigMaxInst();
|
|
Multiblock = Config->ConfigMultiblock();
|
|
FEX::DebuggerState::SetCoreType(static_cast<FEXCore::Config::ConfigCore>(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<uint8_t*>(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<int>(RIP - MappedRegions[MappedRegionsCurrentItem].Offset) / 16;
|
|
|
|
// // Now set the Current Address textbox and string to the correct address
|
|
// LogMan::Msg::D("RIP is 0x%lx", 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<int>(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<float>(current_time - g_Time) : static_cast<float>(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();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|