diff options
Diffstat (limited to 'Source/Core/DolphinQt/Debugger/JitBlockTableModel.cpp')
| -rw-r--r-- | Source/Core/DolphinQt/Debugger/JitBlockTableModel.cpp | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/Source/Core/DolphinQt/Debugger/JitBlockTableModel.cpp b/Source/Core/DolphinQt/Debugger/JitBlockTableModel.cpp new file mode 100644 index 0000000000..27bece81cd --- /dev/null +++ b/Source/Core/DolphinQt/Debugger/JitBlockTableModel.cpp @@ -0,0 +1,452 @@ +// Copyright 2024 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "DolphinQt/Debugger/JitBlockTableModel.h" + +#include <array> +#include <span> + +#include "Common/Assert.h" +#include "Common/Unreachable.h" +#include "Core/Core.h" +#include "Core/PowerPC/JitInterface.h" +#include "Core/PowerPC/PPCSymbolDB.h" +#include "DolphinQt/Host.h" +#include "DolphinQt/Settings.h" + +const JitBlock& JitBlockTableModel::GetJitBlock(const QModelIndex& index) const +{ + ASSERT(index.isValid()); + return m_jit_blocks[index.row()]; +} + +void JitBlockTableModel::SumOverallCosts() +{ + m_overall_cycles_spent = 0; + m_overall_time_spent = {}; + for (const JitBlock& block : m_jit_blocks) + { + if (block.profile_data == nullptr) + continue; + m_overall_cycles_spent += block.profile_data->cycles_spent; + m_overall_time_spent += block.profile_data->time_spent; + }; +} + +static QVariant GetSymbolNameQVariant(const Common::Symbol* symbol) +{ + return symbol ? QString::fromStdString(symbol->name) : QVariant{}; +} + +void JitBlockTableModel::PrefetchSymbols() +{ + m_symbol_list.clear(); + m_symbol_list.reserve(m_jit_blocks.size()); + // If the table viewing this model will be accessing every element, + // it would be a waste of effort to lazy-initialize the symbol list. + if (m_sorting_by_symbols || m_filtering_by_symbols) + { + for (const JitBlock& block : m_jit_blocks) + { + m_symbol_list.emplace_back( + GetSymbolNameQVariant(m_ppc_symbol_db.GetSymbolFromAddr(block.effectiveAddress))); + } + } + else + { + for (const JitBlock& block : m_jit_blocks) + { + m_symbol_list.emplace_back([this, &block]() { + return GetSymbolNameQVariant(m_ppc_symbol_db.GetSymbolFromAddr(block.effectiveAddress)); + }); + } + } +} + +void JitBlockTableModel::Clear() +{ + emit layoutAboutToBeChanged(); + m_jit_blocks.clear(); + m_symbol_list.clear(); + emit layoutChanged(); +} + +void JitBlockTableModel::Update(Core::State state) +{ + emit layoutAboutToBeChanged(); + m_jit_blocks.clear(); + if (state == Core::State::Paused) + { + m_jit_blocks.reserve(m_jit_interface.GetBlockCount()); + m_jit_interface.RunOnBlocks(Core::CPUThreadGuard{m_system}, [this](const JitBlock& block) { + m_jit_blocks.emplace_back(block); + }); + SumOverallCosts(); + } + PrefetchSymbols(); + emit layoutChanged(); +} + +void JitBlockTableModel::UpdateProfileData() +{ + const int row_count = rowCount(); + if (row_count <= 0) + return; + SumOverallCosts(); + static const QList<int> roles = {Qt::DisplayRole}; + const int last = row_count - 1; + emit dataChanged(createIndex(0, Column::RunCount), createIndex(last, Column::TimePercent), roles); +} + +void JitBlockTableModel::UpdateSymbols() +{ + const int row_count = rowCount(); + if (row_count <= 0) + return; + PrefetchSymbols(); + static const QList<int> roles = {Qt::DisplayRole}; + const int last = row_count - 1; + emit dataChanged(createIndex(0, Column::Symbol), createIndex(last, Column::Symbol), roles); +} + +void JitBlockTableModel::ConnectSlots() +{ + auto* const host = Host::GetInstance(); + connect(host, &Host::JitCacheCleared, this, &JitBlockTableModel::OnJitCacheCleared); + connect(host, &Host::JitProfileDataWiped, this, &JitBlockTableModel::OnJitProfileDataWiped); + connect(host, &Host::UpdateDisasmDialog, this, &JitBlockTableModel::OnUpdateDisasmDialog); + connect(host, &Host::PPCSymbolsChanged, this, &JitBlockTableModel::OnPPCSymbolsUpdated); + connect(host, &Host::PPCBreakpointsChanged, this, &JitBlockTableModel::OnPPCBreakpointsChanged); + auto* const settings = &Settings::Instance(); + connect(settings, &Settings::EmulationStateChanged, this, + &JitBlockTableModel::OnEmulationStateChanged); +} + +void JitBlockTableModel::DisconnectSlots() +{ + auto* const host = Host::GetInstance(); + disconnect(host, &Host::JitCacheCleared, this, &JitBlockTableModel::OnJitCacheCleared); + disconnect(host, &Host::JitProfileDataWiped, this, &JitBlockTableModel::OnJitProfileDataWiped); + disconnect(host, &Host::UpdateDisasmDialog, this, &JitBlockTableModel::OnUpdateDisasmDialog); + disconnect(host, &Host::PPCSymbolsChanged, this, &JitBlockTableModel::OnPPCSymbolsUpdated); + disconnect(host, &Host::PPCBreakpointsChanged, this, + &JitBlockTableModel::OnPPCBreakpointsChanged); + auto* const settings = &Settings::Instance(); + disconnect(settings, &Settings::EmulationStateChanged, this, + &JitBlockTableModel::OnEmulationStateChanged); +} + +void JitBlockTableModel::Show() +{ + ConnectSlots(); + // Every slot that may have missed a signal while this model was hidden can be handled by: + Update(Core::GetState(m_system)); +} + +void JitBlockTableModel::Hide() +{ + DisconnectSlots(); + Clear(); +} + +void JitBlockTableModel::OnShowSignal() +{ + Show(); +} + +void JitBlockTableModel::OnHideSignal() +{ + Hide(); +} + +void JitBlockTableModel::OnSortIndicatorChanged(int logicalIndex, Qt::SortOrder) +{ + m_sorting_by_symbols = logicalIndex == Column::Symbol; +} + +void JitBlockTableModel::OnFilterSymbolTextChanged(const QString& string) +{ + m_filtering_by_symbols = !string.isEmpty(); +} + +void JitBlockTableModel::OnJitCacheCleared() +{ + Update(Core::GetState(m_system)); +} + +void JitBlockTableModel::OnJitProfileDataWiped() +{ + UpdateProfileData(); +} + +void JitBlockTableModel::OnUpdateDisasmDialog() +{ + // This should hopefully catch all the little things that lead to stale JitBlock references. + Update(Core::GetState(m_system)); +} + +void JitBlockTableModel::OnPPCSymbolsUpdated() +{ + UpdateSymbols(); +} + +void JitBlockTableModel::OnPPCBreakpointsChanged() +{ + Update(Core::GetState(m_system)); +} + +void JitBlockTableModel::OnEmulationStateChanged(Core::State state) +{ + Update(state); +} + +static QString GetQStringDescription(const CPUEmuFeatureFlags flags) +{ + static const std::array<QString, (FEATURE_FLAG_END_OF_ENUMERATION - 1) << 1> descriptions = { + QStringLiteral(""), QStringLiteral("DR"), + QStringLiteral("IR"), QStringLiteral("DR|IR"), + QStringLiteral("PERFMON"), QStringLiteral("DR|PERFMON"), + QStringLiteral("IR|PERFMON"), QStringLiteral("DR|IR|PERFMON"), + }; + return descriptions[flags]; +} + +static QVariant GetValidSymbolStringVariant(const QVariant& symbol_name_v) +{ + if (symbol_name_v.isValid()) + return symbol_name_v; + return QStringLiteral(" --- "); +} + +QVariant JitBlockTableModel::DisplayRoleData(const QModelIndex& index) const +{ + const int column = index.column(); + if (column == Column::Symbol) + return GetValidSymbolStringVariant(*m_symbol_list[index.row()]); + + const JitBlock& jit_block = m_jit_blocks[index.row()]; + switch (column) + { + case Column::PPCFeatureFlags: + return GetQStringDescription(jit_block.feature_flags); + case Column::EffectiveAddress: + return QString::number(jit_block.effectiveAddress, 16); + case Column::CodeBufferSize: + return QString::number(jit_block.originalSize * sizeof(UGeckoInstruction)); + case Column::RepeatInstructions: + return QString::number(jit_block.originalSize - jit_block.physical_addresses.size()); + case Column::HostNearCodeSize: + return QString::number(jit_block.near_end - jit_block.near_begin); + case Column::HostFarCodeSize: + return QString::number(jit_block.far_end - jit_block.far_begin); + } + const JitBlock::ProfileData* const profile_data = jit_block.profile_data.get(); + if (profile_data == nullptr) + return QStringLiteral(" --- "); + switch (column) + { + case Column::RunCount: + return QString::number(profile_data->run_count); + case Column::CyclesSpent: + return QString::number(profile_data->cycles_spent); + case Column::CyclesAverage: + if (profile_data->run_count == 0) + return QStringLiteral(" --- "); + return QString::number( + static_cast<double>(profile_data->cycles_spent) / profile_data->run_count, 'f', 6); + case Column::CyclesPercent: + if (m_overall_cycles_spent == 0) + return QStringLiteral(" --- "); + return QStringLiteral("%1%").arg(100.0 * profile_data->cycles_spent / m_overall_cycles_spent, + 10, 'f', 6); + case Column::TimeSpent: + { + const auto cast_time = + std::chrono::duration_cast<std::chrono::nanoseconds>(profile_data->time_spent); + return QString::number(cast_time.count()); + } + case Column::TimeAverage: + { + if (profile_data->run_count == 0) + return QStringLiteral(" --- "); + const auto cast_time = std::chrono::duration_cast<std::chrono::duration<double, std::nano>>( + profile_data->time_spent); + return QString::number(cast_time.count() / profile_data->run_count, 'f', 6); + } + case Column::TimePercent: + { + if (m_overall_time_spent == JitBlock::ProfileData::Clock::duration{}) + return QStringLiteral(" --- "); + return QStringLiteral("%1%").arg( + 100.0 * profile_data->time_spent.count() / m_overall_time_spent.count(), 10, 'f', 6); + } + } + static_assert(Column::NumberOfColumns == 14); + Common::Unreachable(); +} + +QVariant JitBlockTableModel::TextAlignmentRoleData(const QModelIndex& index) const +{ + switch (index.column()) + { + case Column::PPCFeatureFlags: + case Column::EffectiveAddress: + return Qt::AlignCenter; + case Column::CodeBufferSize: + case Column::RepeatInstructions: + case Column::HostNearCodeSize: + case Column::HostFarCodeSize: + case Column::RunCount: + case Column::CyclesSpent: + case Column::CyclesAverage: + case Column::CyclesPercent: + case Column::TimeSpent: + case Column::TimeAverage: + case Column::TimePercent: + return QVariant::fromValue(Qt::AlignRight | Qt::AlignVCenter); + case Column::Symbol: + return QVariant::fromValue(Qt::AlignLeft | Qt::AlignVCenter); + } + static_assert(Column::NumberOfColumns == 14); + Common::Unreachable(); +} + +QVariant JitBlockTableModel::SortRoleData(const QModelIndex& index) const +{ + const int column = index.column(); + if (column == Column::Symbol) + return *m_symbol_list[index.row()]; + + const JitBlock& jit_block = m_jit_blocks[index.row()]; + switch (column) + { + case Column::PPCFeatureFlags: + return jit_block.feature_flags; + case Column::EffectiveAddress: + return jit_block.effectiveAddress; + case Column::CodeBufferSize: + return static_cast<qulonglong>(jit_block.originalSize); + case Column::RepeatInstructions: + return static_cast<qulonglong>(jit_block.originalSize - jit_block.physical_addresses.size()); + case Column::HostNearCodeSize: + return static_cast<qulonglong>(jit_block.near_end - jit_block.near_begin); + case Column::HostFarCodeSize: + return static_cast<qulonglong>(jit_block.far_end - jit_block.far_begin); + } + const JitBlock::ProfileData* const profile_data = jit_block.profile_data.get(); + if (profile_data == nullptr) + return QVariant(); + switch (column) + { + case Column::RunCount: + return static_cast<qulonglong>(profile_data->run_count); + case Column::CyclesSpent: + case Column::CyclesPercent: + return static_cast<qulonglong>(profile_data->cycles_spent); + case Column::CyclesAverage: + if (profile_data->run_count == 0) + return QVariant(); + return static_cast<double>(profile_data->cycles_spent) / profile_data->run_count; + case Column::TimeSpent: + case Column::TimePercent: + return static_cast<qulonglong>(profile_data->time_spent.count()); + case Column::TimeAverage: + if (profile_data->run_count == 0) + return QVariant(); + return static_cast<double>(profile_data->time_spent.count()) / profile_data->run_count; + } + static_assert(Column::NumberOfColumns == 14); + Common::Unreachable(); +} + +QVariant JitBlockTableModel::data(const QModelIndex& index, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + return DisplayRoleData(index); + case Qt::TextAlignmentRole: + return TextAlignmentRoleData(index); + case UserRole::SortRole: + return SortRoleData(index); + } + return QVariant(); +} + +QVariant JitBlockTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QVariant(); + + // These abbreviated table header display names have unabbreviated counterparts in JITWidget.cpp. + static constexpr std::array<const char*, Column::NumberOfColumns> headers = { + // i18n: PPC Feature Flags + QT_TR_NOOP("PPC Feat. Flags"), + // i18n: Effective Address + QT_TR_NOOP("Eff. Address"), + // i18n: Code Buffer Size + QT_TR_NOOP("Code Buff. Size"), + // i18n: Repeat Instructions + QT_TR_NOOP("Repeat Instr."), + // i18n: Host Near Code Size + QT_TR_NOOP("Host N. Size"), + // i18n: Host Far Code Size + QT_TR_NOOP("Host F. Size"), + QT_TR_NOOP("Run Count"), + QT_TR_NOOP("Cycles Spent"), + // i18n: Cycles Average + QT_TR_NOOP("Cycles Avg."), + // i18n: Cycles Percent + QT_TR_NOOP("Cycles %"), + QT_TR_NOOP("Time Spent (ns)"), + // i18n: Time Average (ns) + QT_TR_NOOP("Time Avg. (ns)"), + // i18n: Time Percent + QT_TR_NOOP("Time %"), + QT_TR_NOOP("Symbol"), + }; + + return tr(headers[section]); +} + +int JitBlockTableModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) [[unlikely]] + return 0; + return m_jit_blocks.size(); +} + +int JitBlockTableModel::columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) [[unlikely]] + return 0; + return Column::NumberOfColumns; +} + +bool JitBlockTableModel::removeRows(int row, int count, const QModelIndex& parent) +{ + if (parent.isValid() || row < 0) [[unlikely]] + return false; + if (count <= 0) [[unlikely]] + return true; + + beginRemoveRows(parent, row, row + count - 1); // Last is inclusive in Qt! + for (const JitBlock& block : + std::span{m_jit_blocks.data() + row, static_cast<std::size_t>(count)}) + { + m_jit_interface.EraseSingleBlock(block); + } + m_jit_blocks.remove(row, count); + m_symbol_list.remove(row, count); + endRemoveRows(); + return true; +} + +JitBlockTableModel::JitBlockTableModel(Core::System& system, JitInterface& jit_interface, + PPCSymbolDB& ppc_symbol_db, QObject* parent) + : QAbstractTableModel(parent), m_system(system), m_jit_interface(jit_interface), + m_ppc_symbol_db(ppc_symbol_db) +{ +} + +JitBlockTableModel::~JitBlockTableModel() = default; |
