Files
FEX-Emu--FEX/Source/Tools/Debugger/IMGui_I.cpp
T
Lioncash 75b2f226f6 General: Migrate over to fmt where possible
Migrates lingering instances of the old logger over to fmt where
applicable. This allows removing some of the old defines and functions.

The only remaining usages of the printf-based variant of the logger is
in Tests/LinuxSyscalls/Syscalls.cpp for the strace handling.
2021-11-23 12:51:57 -05:00

1148 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();
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<char*>(&Header), sizeof(Header));
// IRFile.write(ir->GetOpAs<char>(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<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("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<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::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<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();
}
}
}
}