mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
250 lines
7.1 KiB
C++
250 lines
7.1 KiB
C++
#include "ppsspp_config.h"
|
|
|
|
#include "UI/ImDebugger/ImJitViewer.h"
|
|
#include "UI/ImDebugger/ImDebugger.h"
|
|
#include "Core/Config.h"
|
|
|
|
#include "Core/MIPS/JitCommon/JitBlockCache.h"
|
|
#include "Core/MIPS/JitCommon/JitCommon.h"
|
|
#include "Core/MIPS/JitCommon/JitState.h"
|
|
#include "Core/MIPS/IR/IRJit.h"
|
|
|
|
void ImJitViewerWindow::GoToBlockAtAddr(u32 addr) {
|
|
for (auto &block : blockList_) {
|
|
if (addr >= block.addr && addr < block.addr + (u32)block.sizeInBytes) {
|
|
curBlockNum_ = block.blockNum;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImJitViewerWindow::Draw(ImConfig &cfg, ImControl &control) {
|
|
ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
|
|
if (!ImGui::Begin("JitViewer", &cfg.jitViewerOpen)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const CPUCore core = (CPUCore)g_Config.iCpuCore;
|
|
|
|
if (core == CPUCore::INTERPRETER) {
|
|
ImGui::TextUnformatted("JIT Viewer is only available with JIT or IR-based CPU cores.");
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
// Options menu
|
|
if (ImGui::BeginPopup("Options")) {
|
|
// ImGui::Checkbox("Auto-scroll", &AutoScroll);
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
#if PPSSPP_ARCH(AMD64)
|
|
const char *TARGET = "X86-64";
|
|
#elif PPSSPP_ARCH(X86)
|
|
const char *TARGET = "X86";
|
|
#elif PPSSPP_ARCH(ARM64)
|
|
|
|
const char *TARGET = "ARM64";
|
|
#elif PPSSPP_ARCH(ARM)
|
|
const char *TARGET = "ARM32";
|
|
#else
|
|
const char *TARGET = "TARGET";
|
|
#endif
|
|
|
|
// Three or four columns: One table for blocks that can be sorted in various ways,
|
|
// then the remaining are either MIPS/IR/TARGET or MIPS/TARGET or MIPS/IR depending on the JIT type.
|
|
int numColumns = 3;
|
|
if (core == CPUCore::JIT_IR) {
|
|
numColumns = 4;
|
|
}
|
|
|
|
JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface();
|
|
|
|
if (core_ != (int)core) {
|
|
// Core changed, need to refresh.
|
|
refresh_ = true;
|
|
core_ = (int)core;
|
|
}
|
|
|
|
if (sortSpecs_ && sortSpecs_->SpecsDirty) {
|
|
refresh_ = true;
|
|
sortSpecs_->SpecsDirty = false;
|
|
}
|
|
|
|
// If the CPU moved, and we're not currently running, update the cached blocklist.
|
|
const CoreState coreStateCached = coreState;
|
|
if (coreStateCached == CORE_STEPPING_CPU || coreStateCached == CORE_NEXTFRAME || refresh_) {
|
|
const int cpuStepCount = Core_GetSteppingCounter();
|
|
if (lastCpuStepCount_ != cpuStepCount || refresh_) {
|
|
refresh_ = false;
|
|
lastCpuStepCount_ = cpuStepCount;
|
|
blockList_.clear();
|
|
curBlockNum_ = -1;
|
|
const int blockCount = blockCacheDebug->GetNumBlocks();
|
|
blockList_.reserve(blockCount);
|
|
INFO_LOG(Log::Debugger, "Updating JIT block list... %d blocks", blockCacheDebug->GetNumBlocks());
|
|
for (int i = 0; i < blockCount; i++) {
|
|
if (!blockCacheDebug->IsValidBlock(i)) {
|
|
continue;
|
|
}
|
|
JitBlockMeta meta = blockCacheDebug->GetBlockMeta(i);
|
|
if (!meta.valid) {
|
|
continue;
|
|
}
|
|
CachedBlock cb;
|
|
cb.addr = meta.addr;
|
|
cb.sizeInBytes = meta.sizeInBytes;
|
|
cb.blockNum = i;
|
|
#ifdef IR_PROFILING
|
|
JitBlockProfileStats stats = blockCacheDebug->GetBlockProfileStats(i);
|
|
cb.profileStats = stats;
|
|
#endif
|
|
blockList_.push_back(cb);
|
|
}
|
|
|
|
std::sort(blockList_.begin(), blockList_.end(), [this](const CachedBlock &a, const CachedBlock &b) {
|
|
if (!sortSpecs_ || sortSpecs_->SpecsCount <= 0) {
|
|
return a.addr < b.addr;
|
|
}
|
|
const ImGuiTableColumnSortSpecs *spec = &sortSpecs_->Specs[0];
|
|
int delta = 0;
|
|
switch (spec ? spec->ColumnUserID : 0) {
|
|
case 0: delta = (int)a.blockNum - (int)b.blockNum; break;
|
|
case 1: delta = (int)a.addr - (int)b.addr; break;
|
|
case 2: delta = a.sizeInBytes - b.sizeInBytes; break;
|
|
#ifdef IR_PROFILING
|
|
case 3: delta = a.profileStats.executions - b.profileStats.executions; break;
|
|
case 4: delta = a.profileStats.totalNanos - b.profileStats.totalNanos; break;
|
|
#endif
|
|
}
|
|
if (delta == 0) {
|
|
return a.addr < b.addr;
|
|
}
|
|
if (spec->SortDirection == ImGuiSortDirection_Ascending) {
|
|
return delta < 0;
|
|
} else {
|
|
return delta > 0;
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
ImGui::Text("Pause to update block list");
|
|
}
|
|
|
|
if (ImGui::BeginTable("columns", numColumns, ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersH | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Blocks");
|
|
ImGui::TableSetupColumn("MIPS");
|
|
if (core == CPUCore::JIT_IR || core == CPUCore::IR_INTERPRETER) {
|
|
ImGui::TableSetupColumn("IR");
|
|
}
|
|
if (core == CPUCore::JIT_IR || core == CPUCore::JIT) {
|
|
ImGui::TableSetupColumn(TARGET);
|
|
}
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
|
|
// For separate scrolling.
|
|
ImGui::BeginChild("LeftPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
#ifdef IR_PROFILING
|
|
const int numColumns = (core == CPUCore::IR_INTERPRETER) ? 5 : 3;
|
|
#else
|
|
const int numColumns = 3;
|
|
#endif
|
|
if (ImGui::BeginTable("blocks", numColumns, ImGuiTableFlags_Sortable | ImGuiTableFlags_Resizable)) {
|
|
ImGui::TableSetupColumn("Num", 0, 0, 0);
|
|
ImGui::TableSetupColumn("Addr", 0, 0, 1);
|
|
ImGui::TableSetupColumn("Size", 0, 0, 2);
|
|
#ifdef IR_PROFILING
|
|
if (numColumns == 5) {
|
|
ImGui::TableSetupColumn("Exec", 0, 0, 3);
|
|
ImGui::TableSetupColumn("Ns", 0, 0, 4);
|
|
}
|
|
#endif
|
|
ImGui::TableHeadersRow();
|
|
sortSpecs_ = ImGui::TableGetSortSpecs();
|
|
|
|
ImGuiListClipper clipper;
|
|
clipper.Begin((int)blockList_.size());
|
|
|
|
while (clipper.Step()) {
|
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
|
const auto &block = blockList_[i];
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
|
|
char label[32];
|
|
snprintf(label, sizeof(label), "%d", block.blockNum);
|
|
|
|
if (ImGui::Selectable(label, block.blockNum == curBlockNum_, ImGuiSelectableFlags_SpanAllColumns)) {
|
|
curBlockNum_ = block.blockNum;
|
|
if (curBlockNum_ >= 0 && curBlockNum_ < blockCacheDebug->GetNumBlocks()) {
|
|
debugInfo_ = blockCacheDebug->GetBlockDebugInfo(curBlockNum_);
|
|
}
|
|
}
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%08x", block.addr);
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", block.sizeInBytes);
|
|
|
|
#ifdef IR_PROFILING
|
|
if (numColumns == 5) {
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%d", block.profileStats.executions);
|
|
|
|
ImGui::TableNextColumn();
|
|
ImGui::Text("%0.3f ms", (double)block.profileStats.totalNanos * 0.000001);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
clipper.End();
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::BeginChild("MIPSPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
|
|
for (const auto &line : debugInfo_.origDisasm) {
|
|
ImGui::TextUnformatted(line.c_str());
|
|
}
|
|
ImGui::EndChild();
|
|
|
|
if (core == CPUCore::JIT_IR || core == CPUCore::IR_INTERPRETER) {
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::BeginChild("IRPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
// TODO : When we have both target and IR, need a third column.
|
|
for (const auto &line : debugInfo_.irDisasm) {
|
|
ImGui::TextUnformatted(line.c_str());
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
if (core == CPUCore::JIT_IR || core == CPUCore::JIT) {
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::BeginChild("TargetPane", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
|
|
for (const auto &line : debugInfo_.targetDisasm) {
|
|
ImGui::TextUnformatted(line.c_str());
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|