duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

debuggermodels.cpp (13071B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "debuggermodels.h"
      5 
      6 #include "core/cpu_core.h"
      7 #include "core/cpu_core_private.h"
      8 #include "core/cpu_disasm.h"
      9 
     10 #include "common/small_string.h"
     11 
     12 #include <QtGui/QColor>
     13 #include <QtGui/QIcon>
     14 #include <QtGui/QPalette>
     15 #include <QtWidgets/QApplication>
     16 #include <QtWidgets/QMessageBox>
     17 #include <QtWidgets/QPushButton>
     18 
     19 static constexpr int NUM_COLUMNS = 5;
     20 static constexpr int STACK_RANGE = 128;
     21 static constexpr u32 STACK_VALUE_SIZE = sizeof(u32);
     22 
     23 DebuggerCodeModel::DebuggerCodeModel(QObject* parent /*= nullptr*/) : QAbstractTableModel(parent)
     24 {
     25   resetCodeView(0);
     26   m_pc_pixmap = QIcon(QStringLiteral(":/icons/debug-pc.png")).pixmap(QSize(12, 12));
     27   m_breakpoint_pixmap = QIcon(QStringLiteral(":/icons/media-record.png")).pixmap(QSize(12, 12));
     28 }
     29 
     30 DebuggerCodeModel::~DebuggerCodeModel()
     31 {
     32 }
     33 
     34 int DebuggerCodeModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const
     35 {
     36   return static_cast<int>((m_code_region_end - m_code_region_start) / CPU::INSTRUCTION_SIZE);
     37 }
     38 
     39 int DebuggerCodeModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const
     40 {
     41   return NUM_COLUMNS;
     42 }
     43 
     44 int DebuggerCodeModel::getRowForAddress(VirtualMemoryAddress address) const
     45 {
     46   return static_cast<int>((address - m_code_region_start) / CPU::INSTRUCTION_SIZE);
     47 }
     48 
     49 VirtualMemoryAddress DebuggerCodeModel::getAddressForRow(int row) const
     50 {
     51   return m_code_region_start + (static_cast<u32>(row) * CPU::INSTRUCTION_SIZE);
     52 }
     53 
     54 VirtualMemoryAddress DebuggerCodeModel::getAddressForIndex(QModelIndex index) const
     55 {
     56   return getAddressForRow(index.row());
     57 }
     58 
     59 int DebuggerCodeModel::getRowForPC() const
     60 {
     61   return getRowForAddress(m_last_pc);
     62 }
     63 
     64 QVariant DebuggerCodeModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const
     65 {
     66   if (index.column() < 0 || index.column() >= NUM_COLUMNS)
     67     return QVariant();
     68 
     69   if (role == Qt::DisplayRole)
     70   {
     71     const VirtualMemoryAddress address = getAddressForRow(index.row());
     72     switch (index.column())
     73     {
     74       case 0:
     75         // breakpoint
     76         return QVariant();
     77 
     78       case 1:
     79       {
     80         // Address
     81         return QVariant(QString::asprintf("0x%08X", address));
     82       }
     83 
     84       case 2:
     85       {
     86         // Bytes
     87         u32 instruction_bits;
     88         if (!CPU::SafeReadInstruction(address, &instruction_bits))
     89           return tr("<invalid>");
     90 
     91         return QString::asprintf("%08X", instruction_bits);
     92       }
     93 
     94       case 3:
     95       {
     96         // Instruction
     97         u32 instruction_bits;
     98         if (!CPU::SafeReadInstruction(address, &instruction_bits))
     99           return tr("<invalid>");
    100 
    101         SmallString str;
    102         CPU::DisassembleInstruction(&str, address, instruction_bits);
    103         return QString::fromUtf8(str.c_str(), static_cast<int>(str.length()));
    104       }
    105 
    106       case 4:
    107       {
    108         // Comment
    109         if (address != m_last_pc)
    110           return QVariant();
    111 
    112         u32 instruction_bits;
    113         if (!CPU::SafeReadInstruction(address, &instruction_bits))
    114           return tr("<invalid>");
    115 
    116         TinyString str;
    117         CPU::DisassembleInstructionComment(&str, address, instruction_bits);
    118         return QString::fromUtf8(str.c_str(), static_cast<int>(str.length()));
    119       }
    120 
    121       default:
    122         return QVariant();
    123     }
    124   }
    125   else if (role == Qt::DecorationRole)
    126   {
    127     if (index.column() == 0)
    128     {
    129       // breakpoint
    130       const VirtualMemoryAddress address = getAddressForRow(index.row());
    131       if (m_last_pc == address)
    132         return m_pc_pixmap;
    133       else if (hasBreakpointAtAddress(address))
    134         return m_breakpoint_pixmap;
    135     }
    136 
    137     return QVariant();
    138   }
    139   else if (role == Qt::BackgroundRole)
    140   {
    141     const VirtualMemoryAddress address = getAddressForRow(index.row());
    142 
    143     // breakpoint
    144     if (hasBreakpointAtAddress(address))
    145       return QVariant(QColor(171, 97, 107));
    146 
    147     //     if (address == m_last_pc)
    148     //       return QApplication::palette().toolTipBase();
    149     if (address == m_last_pc)
    150       return QColor(100, 100, 0);
    151     else
    152       return QVariant();
    153   }
    154   else if (role == Qt::ForegroundRole)
    155   {
    156     const VirtualMemoryAddress address = getAddressForRow(index.row());
    157 
    158     //     if (address == m_last_pc)
    159     //       return QApplication::palette().toolTipText();
    160     if (address == m_last_pc || hasBreakpointAtAddress(address))
    161       return QColor(Qt::white);
    162     else
    163       return QVariant();
    164   }
    165   else
    166   {
    167     return QVariant();
    168   }
    169 }
    170 
    171 QVariant DebuggerCodeModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
    172 {
    173   if (orientation != Qt::Horizontal)
    174     return QVariant();
    175 
    176   if (role != Qt::DisplayRole)
    177     return QVariant();
    178 
    179   switch (section)
    180   {
    181     case 1:
    182       return tr("Address");
    183     case 2:
    184       return tr("Bytes");
    185     case 3:
    186       return tr("Instruction");
    187     case 4:
    188       return tr("Comment");
    189     case 0:
    190     default:
    191       return QVariant();
    192   }
    193 }
    194 
    195 bool DebuggerCodeModel::updateRegion(VirtualMemoryAddress address)
    196 {
    197   CPU::Segment segment = CPU::GetSegmentForAddress(address);
    198   std::optional<Bus::MemoryRegion> region = Bus::GetMemoryRegionForAddress(CPU::VirtualAddressToPhysical(address));
    199   if (!region.has_value() || (address >= m_code_region_start && address < m_code_region_end))
    200     return false;
    201 
    202   static constexpr unsigned NUM_INSTRUCTIONS_BEFORE = 4096;
    203   static constexpr unsigned NUM_INSTRUCTIONS_AFTER = 4096;
    204   static constexpr unsigned NUM_BYTES_BEFORE = NUM_INSTRUCTIONS_BEFORE * sizeof(CPU::Instruction);
    205   static constexpr unsigned NUM_BYTES_AFTER = NUM_INSTRUCTIONS_AFTER * sizeof(CPU::Instruction);
    206 
    207   const VirtualMemoryAddress start_address =
    208     CPU::PhysicalAddressToVirtual(Bus::GetMemoryRegionStart(region.value()), segment);
    209   const VirtualMemoryAddress end_address =
    210     CPU::PhysicalAddressToVirtual(Bus::GetMemoryRegionEnd(region.value()), segment);
    211 
    212   beginResetModel();
    213   m_code_region_start = ((address - start_address) < NUM_BYTES_BEFORE) ? start_address : (address - NUM_BYTES_BEFORE);
    214   m_code_region_end = ((end_address - address) < NUM_BYTES_AFTER) ? end_address : (address + NUM_BYTES_AFTER);
    215   m_current_segment = segment;
    216   m_current_code_region = region.value();
    217   endResetModel();
    218   return true;
    219 }
    220 
    221 bool DebuggerCodeModel::emitDataChangedForAddress(VirtualMemoryAddress address)
    222 {
    223   CPU::Segment segment = CPU::GetSegmentForAddress(address);
    224   std::optional<Bus::MemoryRegion> region = Bus::GetMemoryRegionForAddress(CPU::VirtualAddressToPhysical(address));
    225   if (!region.has_value() || segment != m_current_segment || region != m_current_code_region)
    226     return false;
    227 
    228   const int row = getRowForAddress(address);
    229   emit dataChanged(index(row, 0), index(row, NUM_COLUMNS - 1));
    230   return true;
    231 }
    232 
    233 bool DebuggerCodeModel::hasBreakpointAtAddress(VirtualMemoryAddress address) const
    234 {
    235   return std::find(m_breakpoints.begin(), m_breakpoints.end(), address) != m_breakpoints.end();
    236 }
    237 
    238 void DebuggerCodeModel::resetCodeView(VirtualMemoryAddress start_address)
    239 {
    240   updateRegion(start_address);
    241 }
    242 
    243 void DebuggerCodeModel::setPC(VirtualMemoryAddress pc)
    244 {
    245   const VirtualMemoryAddress prev_pc = m_last_pc;
    246 
    247   m_last_pc = pc;
    248   if (!updateRegion(pc))
    249   {
    250     emitDataChangedForAddress(prev_pc);
    251     emitDataChangedForAddress(pc);
    252   }
    253 }
    254 
    255 void DebuggerCodeModel::ensureAddressVisible(VirtualMemoryAddress address)
    256 {
    257   updateRegion(address);
    258 }
    259 
    260 void DebuggerCodeModel::setBreakpointList(std::vector<VirtualMemoryAddress> bps)
    261 {
    262   clearBreakpoints();
    263 
    264   m_breakpoints = std::move(bps);
    265   for (VirtualMemoryAddress bp : m_breakpoints)
    266     emitDataChangedForAddress(bp);
    267 }
    268 
    269 void DebuggerCodeModel::clearBreakpoints()
    270 {
    271   std::vector<VirtualMemoryAddress> old_bps(std::move(m_breakpoints));
    272 
    273   for (VirtualMemoryAddress old_bp : old_bps)
    274     emitDataChangedForAddress(old_bp);
    275 }
    276 
    277 void DebuggerCodeModel::setBreakpointState(VirtualMemoryAddress address, bool enabled)
    278 {
    279   if (enabled)
    280   {
    281     if (std::find(m_breakpoints.begin(), m_breakpoints.end(), address) != m_breakpoints.end())
    282       return;
    283 
    284     m_breakpoints.push_back(address);
    285     emitDataChangedForAddress(address);
    286   }
    287   else
    288   {
    289     auto it = std::find(m_breakpoints.begin(), m_breakpoints.end(), address);
    290     if (it == m_breakpoints.end())
    291       return;
    292 
    293     m_breakpoints.erase(it);
    294     emitDataChangedForAddress(address);
    295   }
    296 }
    297 
    298 DebuggerRegistersModel::DebuggerRegistersModel(QObject* parent /*= nullptr*/) : QAbstractListModel(parent)
    299 {
    300 }
    301 
    302 DebuggerRegistersModel::~DebuggerRegistersModel()
    303 {
    304 }
    305 
    306 int DebuggerRegistersModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const
    307 {
    308   return static_cast<int>(CPU::NUM_DEBUGGER_REGISTER_LIST_ENTRIES);
    309 }
    310 
    311 int DebuggerRegistersModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const
    312 {
    313   return 2;
    314 }
    315 
    316 QVariant DebuggerRegistersModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const
    317 {
    318   u32 reg_index = static_cast<u32>(index.row());
    319   if (reg_index >= CPU::NUM_DEBUGGER_REGISTER_LIST_ENTRIES)
    320     return QVariant();
    321 
    322   if (index.column() < 0 || index.column() > 1)
    323     return QVariant();
    324 
    325   switch (index.column())
    326   {
    327     case 0: // address
    328     {
    329       if (role == Qt::DisplayRole)
    330         return QString::fromUtf8(CPU::g_debugger_register_list[reg_index].name);
    331     }
    332     break;
    333 
    334     case 1: // data
    335     {
    336       if (role == Qt::DisplayRole)
    337       {
    338         return QString::asprintf("0x%08X", m_reg_values[reg_index]);
    339       }
    340       else if (role == Qt::ForegroundRole)
    341       {
    342         if (m_reg_values[reg_index] != m_old_reg_values[reg_index])
    343           return QColor(255, 50, 50);
    344       }
    345     }
    346     break;
    347 
    348     default:
    349       break;
    350   }
    351 
    352   return QVariant();
    353 }
    354 
    355 QVariant DebuggerRegistersModel::headerData(int section, Qt::Orientation orientation,
    356                                             int role /*= Qt::DisplayRole*/) const
    357 {
    358   if (orientation != Qt::Horizontal)
    359     return QVariant();
    360 
    361   if (role != Qt::DisplayRole)
    362     return QVariant();
    363 
    364   switch (section)
    365   {
    366     case 0:
    367       return tr("Register");
    368     case 1:
    369       return tr("Value");
    370     default:
    371       return QVariant();
    372   }
    373 }
    374 
    375 void DebuggerRegistersModel::updateValues()
    376 {
    377   beginResetModel();
    378 
    379   for (u32 i = 0; i < CPU::NUM_DEBUGGER_REGISTER_LIST_ENTRIES; i++)
    380     m_reg_values[i] = *CPU::g_debugger_register_list[i].value_ptr;
    381 
    382   endResetModel();
    383 }
    384 
    385 void DebuggerRegistersModel::saveCurrentValues()
    386 {
    387   m_old_reg_values = m_reg_values;
    388 }
    389 
    390 DebuggerStackModel::DebuggerStackModel(QObject* parent /*= nullptr*/) : QAbstractListModel(parent)
    391 {
    392 }
    393 
    394 DebuggerStackModel::~DebuggerStackModel()
    395 {
    396 }
    397 
    398 int DebuggerStackModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const
    399 {
    400   return STACK_RANGE * 2;
    401 }
    402 
    403 int DebuggerStackModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const
    404 {
    405   return 2;
    406 }
    407 
    408 QVariant DebuggerStackModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const
    409 {
    410   if (index.column() < 0 || index.column() > 1)
    411     return QVariant();
    412 
    413   if (role != Qt::DisplayRole)
    414     return QVariant();
    415 
    416   const u32 sp = CPU::g_state.regs.sp;
    417   const VirtualMemoryAddress address =
    418     (sp - static_cast<u32>(STACK_RANGE * STACK_VALUE_SIZE)) + static_cast<u32>(index.row()) * STACK_VALUE_SIZE;
    419 
    420   if (index.column() == 0)
    421     return QString::asprintf("0x%08X", address);
    422 
    423   u32 value;
    424   if (!CPU::SafeReadMemoryWord(address, &value))
    425     return tr("<invalid>");
    426 
    427   return QString::asprintf("0x%08X", ZeroExtend32(value));
    428 }
    429 
    430 QVariant DebuggerStackModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
    431 {
    432   if (orientation != Qt::Horizontal)
    433     return QVariant();
    434 
    435   if (role != Qt::DisplayRole)
    436     return QVariant();
    437 
    438   switch (section)
    439   {
    440     case 0:
    441       return tr("Address");
    442     case 1:
    443       return tr("Value");
    444     default:
    445       return QVariant();
    446   }
    447 }
    448 
    449 void DebuggerStackModel::invalidateView()
    450 {
    451   beginResetModel();
    452   endResetModel();
    453 }
    454 
    455 DebuggerAddBreakpointDialog::DebuggerAddBreakpointDialog(QWidget* parent /*= nullptr*/) : QDialog(parent)
    456 {
    457   m_ui.setupUi(this);
    458   connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this,
    459           &DebuggerAddBreakpointDialog::okClicked);
    460 }
    461 
    462 DebuggerAddBreakpointDialog::~DebuggerAddBreakpointDialog() = default;
    463 
    464 void DebuggerAddBreakpointDialog::okClicked()
    465 {
    466   const QString address_str = m_ui.address->text();
    467   m_address = 0;
    468   bool ok = false;
    469 
    470   if (!address_str.isEmpty())
    471   {
    472     if (address_str.startsWith("0x"))
    473       m_address = address_str.mid(2).toUInt(&ok, 16);
    474     else
    475       m_address = address_str.toUInt(&ok, 16);
    476 
    477     if (!ok)
    478     {
    479       QMessageBox::critical(
    480         this, qApp->translate("DebuggerWindow", "Error"),
    481         qApp->translate("DebuggerWindow", "Invalid address. It should be in hex (0x12345678 or 12345678)"));
    482       return;
    483     }
    484 
    485     if (m_ui.read->isChecked())
    486       m_type = CPU::BreakpointType::Read;
    487     else if (m_ui.write->isChecked())
    488       m_type = CPU::BreakpointType::Write;
    489     else
    490       m_type = CPU::BreakpointType::Execute;
    491 
    492     accept();
    493   }
    494 }