#include #include "UI/JitCompareScreen.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/MIPS/JitCommon/JitState.h" JitCompareScreen::JitCompareScreen() : UIDialogScreenWithBackground() { FillBlockList(); } void JitCompareScreen::Flip() { using namespace UI; switch (viewMode_) { case ViewMode::DISASM: comparisonView_->SetVisibility(V_VISIBLE); blockListView_->SetVisibility(V_GONE); break; case ViewMode::BLOCK_LIST: comparisonView_->SetVisibility(V_GONE); blockListView_->SetVisibility(V_VISIBLE); break; } } // Three panes: Block chooser, MIPS view, ARM/x86 view void JitCompareScreen::CreateViews() { auto di = GetI18NCategory(I18NCat::DIALOG); auto dev = GetI18NCategory(I18NCat::DEVELOPER); using namespace UI; root_ = new LinearLayout(ORIENT_HORIZONTAL); ScrollView *leftColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT))); LinearLayout *leftColumn = leftColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL)); comparisonView_ = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); LinearLayout *blockTopBar = comparisonView_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); blockTopBar->Add(new Button(dev->T("Back to list")))->OnClick.Add([this](UI::EventParams &e) { viewMode_ = ViewMode::BLOCK_LIST; Flip(); return UI::EVENT_DONE; }); blockTopBar->Add(new Button(dev->T("Prev")))->OnClick.Add([=](UI::EventParams &e) { currentBlock_--; UpdateDisasm(); return UI::EVENT_DONE; }); blockTopBar->Add(new Button(dev->T("Next")))->OnClick.Add([=](UI::EventParams &e) { currentBlock_++; UpdateDisasm(); return UI::EVENT_DONE; }); blockTopBar->Add(new Button(dev->T("Random")))->OnClick.Add([=](UI::EventParams &e) { if (blockList_.empty()) { return UI::EVENT_DONE; } currentBlock_ = rand() % blockList_.size(); UpdateDisasm(); return UI::EVENT_DONE; }); blockAddr_ = blockTopBar->Add(new TextEdit("", dev->T("Block address"), "")); blockAddr_->OnEnter.Handle(this, &JitCompareScreen::OnAddressChange); blockName_ = blockTopBar->Add(new TextView(dev->T("No block"))); blockStats_ = blockTopBar->Add(new TextView("")); LinearLayout *columns = comparisonView_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f))); ScrollView *midColumnScroll = columns->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); LinearLayout *midColumn = midColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL)); midColumn->SetTag("JitCompareLeftDisasm"); leftDisasm_ = midColumn->Add(new LinearLayout(ORIENT_VERTICAL)); leftDisasm_->SetSpacing(0.0f); ScrollView *rightColumnScroll = columns->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); rightColumnScroll->SetTag("JitCompareRightDisasm"); LinearLayout *rightColumn = rightColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL)); rightDisasm_ = rightColumn->Add(new LinearLayout(ORIENT_VERTICAL)); rightDisasm_->SetSpacing(0.0f); blockListView_ = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f))); blockListView_->SetVisibility(V_GONE); blockListContainer_ = blockListView_->Add(new LinearLayout(ORIENT_VERTICAL)); // leftColumn->Add(new Choice(dev->T("By Address")))->OnClick.Handle(this, &JitCompareScreen::OnSelectBlock); leftColumn->Add(new Choice(dev->T("All")))->OnClick.Add([=](UI::EventParams &e) { listType_ = ListType::ALL_BLOCKS; viewMode_ = ViewMode::BLOCK_LIST; UpdateDisasm(); return UI::EVENT_DONE; }); leftColumn->Add(new Choice(dev->T("FPU")))->OnClick.Add([=](UI::EventParams &e) { listType_ = ListType::FPU_BLOCKS; viewMode_ = ViewMode::BLOCK_LIST; UpdateDisasm(); return UI::EVENT_DONE; }); leftColumn->Add(new Choice(dev->T("VFPU")))->OnClick.Add([=](UI::EventParams &e) { listType_ = ListType::VFPU_BLOCKS; viewMode_ = ViewMode::BLOCK_LIST; UpdateDisasm(); return UI::EVENT_DONE; }); leftColumn->Add(new Choice(dev->T("Stats")))->OnClick.Handle(this, &JitCompareScreen::OnShowStats); leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); UpdateDisasm(); } void JitCompareScreen::FillBlockList() { JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface(); blockList_.clear(); for (int i = 0; i < blockCacheDebug->GetNumBlocks(); i++) { if (!blockCacheDebug->IsValidBlock(i)) { continue; } switch (listType_) { case ListType::ALL_BLOCKS: blockList_.push_back(i); break; case ListType::FPU_BLOCKS: case ListType::VFPU_BLOCKS: { const int flags = listType_ == ListType::FPU_BLOCKS ? IS_FPU : IS_VFPU; JitBlockMeta meta = blockCacheDebug->GetBlockMeta(i); if (meta.valid) { for (u32 addr = meta.addr; addr < meta.addr + meta.sizeInBytes; addr += 4) { MIPSOpcode opcode = Memory::Read_Instruction(addr); if (MIPSGetInfo(opcode) & flags) { blockList_.push_back(i); break; } } } } default: break; } } if (listSort_ == ListSort::BLOCK_NUM) { // Already sorted, effectively. return; } if (listSort_ != ListSort::BLOCK_LENGTH_DESC) { return; } std::sort(blockList_.begin(), blockList_.end(), [=](const int &a_index, const int &b_index) { JitBlockMeta a_meta = blockCacheDebug->GetBlockMeta(a_index); JitBlockMeta b_meta = blockCacheDebug->GetBlockMeta(b_index); switch (listSort_) { case ListSort::BLOCK_LENGTH_DESC: return a_meta.sizeInBytes > b_meta.sizeInBytes; // reverse for descending default: return false; } }); } void JitCompareScreen::UpdateDisasm() { leftDisasm_->Clear(); rightDisasm_->Clear(); using namespace UI; if (!MIPSComp::jit) { return; } JitBlockCacheDebugInterface *blockCacheDebug = MIPSComp::jit->GetBlockCacheDebugInterface(); if (viewMode_ == ViewMode::DISASM && (currentBlock_ < 0 || currentBlock_ >= (int)blockList_.size())) { viewMode_ = ViewMode::BLOCK_LIST; } FillBlockList(); Flip(); if (viewMode_ == ViewMode::DISASM) { char temp[256]; snprintf(temp, sizeof(temp), "%d/%d", currentBlock_, (int)blockList_.size()); blockName_->SetText(temp); int blockNum = blockList_[currentBlock_]; if (!blockCacheDebug->IsValidBlock(blockNum)) { auto dev = GetI18NCategory(I18NCat::DEVELOPER); leftDisasm_->Add(new TextView(dev->T("No block"))); rightDisasm_->Add(new TextView(dev->T("No block"))); blockStats_->SetText(""); return; } JitBlockDebugInfo debugInfo = blockCacheDebug->GetBlockDebugInfo(blockNum); snprintf(temp, sizeof(temp), "%08x", debugInfo.originalAddress); blockAddr_->SetText(temp); // Alright. First generate the MIPS disassembly. // TODO: Need a way to communicate branch continuing. for (const auto &line : debugInfo.origDisasm) { leftDisasm_->Add(new TextView(line, FLAG_DYNAMIC_ASCII, false))->SetFocusable(true); } // TODO : When we have both target and IR, need a third column. if (debugInfo.targetDisasm.size()) { for (const auto &line : debugInfo.targetDisasm) { rightDisasm_->Add(new TextView(line, FLAG_DYNAMIC_ASCII, false))->SetFocusable(true); } } else { for (const auto &line : debugInfo.irDisasm) { rightDisasm_->Add(new TextView(line, FLAG_DYNAMIC_ASCII, false))->SetFocusable(true); } } int numMips = leftDisasm_->GetNumSubviews(); int numHost = rightDisasm_->GetNumSubviews(); snprintf(temp, sizeof(temp), "%d to %d : %d%%", numMips, numHost, 100 * numHost / numMips); blockStats_->SetText(temp); } else { blockListContainer_->Clear(); for (int i = 0; i < std::min(100, (int)blockList_.size()); i++) { JitBlockMeta meta = blockCacheDebug->GetBlockMeta(blockList_[i]); char temp[512], small[512]; snprintf(temp, sizeof(temp), "%08x: %d instrs", meta.addr, meta.sizeInBytes / 4); snprintf(small, sizeof(small), "Small text"); Choice *blockChoice = blockListContainer_->Add(new Choice(temp, small)); blockChoice->OnClick.Handle(this, &JitCompareScreen::OnBlockClick); } } } UI::EventReturn JitCompareScreen::OnBlockClick(UI::EventParams &e) { int blockIndex = blockListContainer_->IndexOfSubview(e.v); if (blockIndex >= 0) { viewMode_ = ViewMode::DISASM; currentBlock_ = blockIndex; UpdateDisasm(); } return UI::EVENT_DONE; } UI::EventReturn JitCompareScreen::OnAddressChange(UI::EventParams &e) { std::lock_guard guard(MIPSComp::jitLock); if (!MIPSComp::jit) { return UI::EVENT_DONE; } JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface(); if (!blockCache) return UI::EVENT_DONE; u32 addr; if (blockAddr_->GetText().size() > 8) return UI::EVENT_DONE; if (1 == sscanf(blockAddr_->GetText().c_str(), "%08x", &addr)) { if (Memory::IsValidAddress(addr)) { currentBlock_ = blockCache->GetBlockNumberFromStartAddress(addr); UpdateDisasm(); } } return UI::EVENT_DONE; } UI::EventReturn JitCompareScreen::OnShowStats(UI::EventParams &e) { std::lock_guard guard(MIPSComp::jitLock); if (!MIPSComp::jit) { return UI::EVENT_DONE; } JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface(); if (!blockCache) return UI::EVENT_DONE; BlockCacheStats bcStats; blockCache->ComputeStats(bcStats); NOTICE_LOG(JIT, "Num blocks: %i", bcStats.numBlocks); NOTICE_LOG(JIT, "Average Bloat: %0.2f%%", 100 * bcStats.avgBloat); NOTICE_LOG(JIT, "Min Bloat: %0.2f%% (%08x)", 100 * bcStats.minBloat, bcStats.minBloatBlock); NOTICE_LOG(JIT, "Max Bloat: %0.2f%% (%08x)", 100 * bcStats.maxBloat, bcStats.maxBloatBlock); int ctr = 0, sz = (int)bcStats.bloatMap.size(); for (auto iter : bcStats.bloatMap) { if (ctr < 10 || ctr > sz - 10) { NOTICE_LOG(JIT, "%08x: %f", iter.second, iter.first); } else if (ctr == 10) { NOTICE_LOG(JIT, "..."); } ctr++; } return UI::EVENT_DONE; } UI::EventReturn JitCompareScreen::OnSelectBlock(UI::EventParams &e) { auto dev = GetI18NCategory(I18NCat::DEVELOPER); auto addressPrompt = new AddressPromptScreen(dev->T("Block address")); addressPrompt->OnChoice.Handle(this, &JitCompareScreen::OnBlockAddress); screenManager()->push(addressPrompt); return UI::EVENT_DONE; } UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) { std::lock_guard guard(MIPSComp::jitLock); if (!MIPSComp::jit) { return UI::EVENT_DONE; } JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface(); if (!blockCache) return UI::EVENT_DONE; if (Memory::IsValidAddress(e.a)) { currentBlock_ = blockCache->GetBlockNumberFromStartAddress(e.a); } else { currentBlock_ = -1; } UpdateDisasm(); return UI::EVENT_DONE; } /* void JitCompareScreen::OnRandomBlock(int flag) { std::lock_guard guard(MIPSComp::jitLock); if (!MIPSComp::jit) { return; } JitBlockCacheDebugInterface *blockCache = MIPSComp::jit->GetBlockCacheDebugInterface(); if (!blockCache) return; int numBlocks = blockCache->GetNumBlocks(); if (numBlocks > 0) { bool anyWanted = false; int tries = 0; while (!anyWanted && tries < numBlocks) { currentBlock_ = rand() % numBlocks; if (blockCache->IsValidBlock(currentBlock_)) { JitBlockDebugInfo b = blockCache->GetBlockDebugInfo(currentBlock_); u32 mipsBytes = (u32)b.origDisasm.size() * 4; for (u32 addr = b.originalAddress; addr < b.originalAddress + mipsBytes; addr += 4) { MIPSOpcode opcode = Memory::Read_Instruction(addr); if (MIPSGetInfo(opcode) & flag) { char temp[256]; MIPSDisAsm(opcode, addr, temp, sizeof(temp)); // INFO_LOG(HLE, "Stopping at random instruction: %08x %s", addr, temp); anyWanted = true; break; } } } tries++; } if (!anyWanted) currentBlock_ = -1; } UpdateDisasm(); }*/ void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; auto dev = GetI18NCategory(I18NCat::DEVELOPER); addrView_ = new TextView(dev->T("Enter address"), ALIGN_HCENTER, false); parent->Add(addrView_); ViewGroup *grid = new GridLayout(GridLayoutSettings(60, 40)); parent->Add(grid); for (int i = 0; i < 16; ++i) { char temp[16]; snprintf(temp, 16, " %X ", i); buttons_[i] = new Button(temp); grid->Add(buttons_[i])->OnClick.Handle(this, &AddressPromptScreen::OnDigitButton); } parent->Add(new Button(dev->T("Backspace")))->OnClick.Handle(this, &AddressPromptScreen::OnBackspace); } void AddressPromptScreen::OnCompleted(DialogResult result) { if (result == DR_OK) { UI::EventParams e{}; e.v = root_; e.a = addr_; OnChoice.Trigger(e); } } UI::EventReturn AddressPromptScreen::OnDigitButton(UI::EventParams &e) { for (int i = 0; i < 16; ++i) { if (buttons_[i] == e.v) { AddDigit(i); } } return UI::EVENT_DONE; } UI::EventReturn AddressPromptScreen::OnBackspace(UI::EventParams &e) { BackspaceDigit(); return UI::EVENT_DONE; } void AddressPromptScreen::AddDigit(int n) { if ((addr_ & 0xF0000000) == 0) { addr_ = addr_ * 16 + n; } UpdatePreviewDigits(); } void AddressPromptScreen::BackspaceDigit() { addr_ /= 16; UpdatePreviewDigits(); } void AddressPromptScreen::UpdatePreviewDigits() { if (addr_ != 0) { char temp[32]; snprintf(temp, 32, "%8X", addr_); addrView_->SetText(temp); } else { auto dev = GetI18NCategory(I18NCat::DEVELOPER); addrView_->SetText(dev->T("Enter address")); } } bool AddressPromptScreen::key(const KeyInput &key) { if (key.flags & KEY_DOWN) { if (key.keyCode >= NKCODE_0 && key.keyCode <= NKCODE_9) { AddDigit(key.keyCode - NKCODE_0); } else if (key.keyCode >= NKCODE_A && key.keyCode <= NKCODE_F) { AddDigit(10 + key.keyCode - NKCODE_A); // NKCODE_DEL is backspace. } else if (key.keyCode == NKCODE_DEL) { BackspaceDigit(); } else if (key.keyCode == NKCODE_ENTER) { TriggerFinish(DR_OK); } else { return UIDialogScreen::key(key); } } else { return UIDialogScreen::key(key); } return true; }