From 9bdd8a87286332245231bc87465038733b32b600 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 25 Jan 2018 00:34:15 -0500 Subject: [PATCH 01/26] Implement source view and highlight current line. --- gui/qt/CEmu.pro | 14 ++-- gui/qt/debugger.cpp | 1 + gui/qt/debugger/sourceswidget.cpp | 133 ++++++++++++++++++++++++++++++ gui/qt/debugger/sourceswidget.h | 29 +++++++ gui/qt/mainwindow.ui | 11 +++ gui/qt/settings.cpp | 2 +- 6 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 gui/qt/debugger/sourceswidget.cpp create mode 100644 gui/qt/debugger/sourceswidget.h diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index f31365b75..c47701dbb 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -145,13 +145,13 @@ if (!win32-msvc*) { } # Do we have a flag specifying use of libpng-apng from vcpkg? - equals(LIBPNG_APNG_FROM_VCPKG, 1) { - warning("Enabled using libpng-apng from vcpkg. Note that if you do not have vcpkg integrated into MSVC, and/or do not have libpng-apng installed within vcpkg, the build will likely fail.") - # This is a bad hack, but MOC kinda needs it to work correctly... - QMAKE_MOC_OPTIONS += -DPNG_WRITE_APNG_SUPPORTED - } + equals(LIBPNG_APNG_FROM_VCPKG, 1) { + warning("Enabled using libpng-apng from vcpkg. Note that if you do not have vcpkg integrated into MSVC, and/or do not have libpng-apng installed within vcpkg, the build will likely fail.") + # This is a bad hack, but MOC kinda needs it to work correctly... + QMAKE_MOC_OPTIONS += -DPNG_WRITE_APNG_SUPPORTED + } - # Otherwise... + # Otherwise... !equals(LIBPNG_APNG_FROM_VCPKG, 1) { # If we're not using vcpkg, we rely on manual variables to find needed # libpng-apng components. @@ -287,6 +287,7 @@ SOURCES += \ visualizerwidget.cpp \ debugger/visualizerdisplaywidget.cpp \ memorywidget.cpp \ + debugger/sourceswidget.cpp archive/extractor.c \ ../../core/bus.c \ keyhistorywidget.cpp \ @@ -387,6 +388,7 @@ HEADERS += \ vartablemodel.h \ visualizerwidget.h \ debugger/visualizerdisplaywidget.h \ + debugger/sourceswidget.h archive/extractor.h \ ../../core/bus.h \ keyhistorywidget.h \ diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 8699144ea..3ffabb2b1 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -855,6 +855,7 @@ void MainWindow::debugPopulate() { osUpdate(); stackUpdate(); disasmUpdateAddr(m_prevDisasmAddr = cpu.registers.PC, true); + ui->sources->updatePC(m_prevDisasmAddr); // Track step navigation: append on control-flow (branch taken), // replace on linear advance. Non-step stops do not modify history if (m_stepCtx.active) { diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp new file mode 100644 index 000000000..426410dee --- /dev/null +++ b/gui/qt/debugger/sourceswidget.cpp @@ -0,0 +1,133 @@ +#include "sourceswidget.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { +class DebugFile : public QFile { +public: + DebugFile(QString name) : QFile(name) { + open(QIODevice::ReadOnly); + } + quint32 readULEB128() { + quint32 result = 0, shift = 0; + char next; + do { + getChar(&next); + result |= (next & 0x7F) << shift; + shift += 7; + if (shift >= 32) { + setErrorString("shift overflow"); + return 0; + } + } while (next & 0x80); + return result; + } +}; +} + +SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { + m_button = new QPushButton(tr("Select Debug File")); + m_tabs = new QTabWidget; + m_tabs->setTabPosition(QTabWidget::South); + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(m_button); + layout->addWidget(m_tabs); + setLayout(layout); + connect(m_button, &QPushButton::clicked, this, &SourcesWidget::selectDebugFile); +} + +void SourcesWidget::selectDebugFile() { + QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "", tr("Debug File (*.dbg)")); + if (debugName.isNull()) { + return; + } + m_tabs->clear(); + m_addrToLoc.clear(); + m_locToAddr.clear(); + DebugFile debugFile(debugName); + quint8 index = 0; + while (!debugFile.atEnd()) { + bool success = true; + QString dir = QFileInfo(debugFile).dir().path(); + { + QStringList components = QString::fromLocal8Bit(debugFile.read(debugFile.readULEB128())).split('\\'); + for (QStringList::iterator i = components.begin(), e = components.end(); success && i != e; ) { + QStringList component(*i++); + QDirIterator matches(dir, component, (i != e ? QDir::Dirs : QDir::Files) | QDir::Readable); + if (matches.hasNext()) { + dir = matches.next(); + } else { + success = false; + } + } + } + { + QFile file(dir); + if (success && file.open(QIODevice::ReadOnly)) { + QPlainTextEdit *edit = new QPlainTextEdit(file.readAll()); + edit->setReadOnly(true); + edit->setFont(m_sourceFont); + edit->setCenterOnScroll(true); + m_tabs->addTab(edit, QFileInfo(file).baseName()); + } + } + { + quint32 addr = debugFile.readULEB128(), line = 0, lineOff = ~0; + while (true) { + if (success) { + quint32 loc = index << 24 | (lineOff ? line : s_lastLine); + m_addrToLoc[addr] = loc; + m_locToAddr[loc] = addr; + } + if (!lineOff || debugFile.atEnd()) { + break; + } + addr += debugFile.readULEB128(); + line += lineOff = debugFile.readULEB128(); + if (line >= s_lastLine) { + return; + } + } + } + if (!++index) { + return; + } + } +} + +void SourcesWidget::updatePC(quint32 pc) { + if (m_addrToLoc.empty()) { + return; + } + auto i = m_addrToLoc.upperBound(pc); + if (i == m_addrToLoc.begin() || i == m_addrToLoc.end()) { + return; + } + quint32 loc = *--i, line = loc & 0xFFFFFF; + quint8 index = loc >> 24; + QPlainTextEdit *edit = static_cast(m_tabs->widget(index)); + if (line) { + line--; + } + QTextCursor cursor(edit->document()->findBlockByLineNumber(line)); + QTextBlockFormat format; + format.setBackground(Qt::white); + edit->textCursor().mergeBlockFormat(format); + format.setBackground(Qt::red); + cursor.mergeBlockFormat(format); + edit->setTextCursor(cursor); + m_tabs->setCurrentIndex(index); +} + +void SourcesWidget::setSourceFont(const QFont &font) { + for (int i = 0; i != m_tabs->count(); i++) { + m_tabs->widget(i)->setFont(font); + } + m_sourceFont = font; +} diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h new file mode 100644 index 000000000..f5e4b4926 --- /dev/null +++ b/gui/qt/debugger/sourceswidget.h @@ -0,0 +1,29 @@ +#ifndef SOURCESWIDGET_H +#define SOURCESWIDGET_H + +#include +#include +#include +class QPushButton; +class QTabWidget; + +class SourcesWidget : public QWidget { + Q_OBJECT + +public: + SourcesWidget(QWidget * = Q_NULLPTR); + void setSourceFont(const QFont &font); + +public slots: + void selectDebugFile(); + void updatePC(quint32 pc); + +private: + QPushButton *m_button; + QTabWidget *m_tabs; + QFont m_sourceFont; + QMap m_addrToLoc, m_locToAddr; + static const quint32 s_lastLine = (1 << 24) - 1; +}; + +#endif diff --git a/gui/qt/mainwindow.ui b/gui/qt/mainwindow.ui index 9f8b5b9fc..dc878c68a 100644 --- a/gui/qt/mainwindow.ui +++ b/gui/qt/mainwindow.ui @@ -10396,6 +10396,11 @@ QPushButton:pressed { + + + Sources + + @@ -11005,6 +11010,12 @@ QPushButton:pressed {
keypad/keypadwidget.h
1 + + SourcesWidget + QWidget +
sourceswidget.h
+ 1 +
DataWidget QPlainTextEdit diff --git a/gui/qt/settings.cpp b/gui/qt/settings.cpp index 9ecb06da6..d8504994a 100644 --- a/gui/qt/settings.cpp +++ b/gui/qt/settings.cpp @@ -467,7 +467,7 @@ void MainWindow::setFont(int fontSize) const { monospace.setPointSize(fontSize); ui->console->setFont(monospace); m_disasm->setFont(monospace); - + ui->sources->setSourceFont(monospace); ui->stackView->setFont(monospace); ui->afregView->setFont(monospace); From 4986cb074a284bd8348413b912f40b1787144207 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 25 Jan 2018 02:13:56 -0500 Subject: [PATCH 02/26] Implement toggle breakpoint. --- gui/qt/debugger.cpp | 14 +++++---- gui/qt/debugger/sourceswidget.cpp | 50 +++++++++++++++++++++++-------- gui/qt/debugger/sourceswidget.h | 8 +++++ gui/qt/mainwindow.cpp | 1 + gui/qt/mainwindow.h | 1 + 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 3ffabb2b1..6b7d05d40 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -1022,17 +1022,21 @@ void MainWindow::breakAddSlot() { breakAdd(breakNextLabel(), 0, true, false, true); } +void MainWindow::breakToggle(quint32 address, bool gui) { + m_guiAdd = gui; + + breakAdd(breakNextLabel(), address, true, true, false); + + m_guiAdd = false; +} + void MainWindow::breakAddGui() { uint32_t address = static_cast(hex2int(m_disasm->getSelectedAddr())); QTextCursor c = m_disasm->textCursor(); c.setCharFormat(m_disasm->currentCharFormat()); - m_guiAdd = true; - - breakAdd(breakNextLabel(), address, true, true, false); - - m_guiAdd = false; + breakToggle(address); int32_t base = disasm.base; int32_t next = disasm.next; diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 426410dee..2bbb7da96 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -70,11 +71,13 @@ void SourcesWidget::selectDebugFile() { { QFile file(dir); if (success && file.open(QIODevice::ReadOnly)) { - QPlainTextEdit *edit = new QPlainTextEdit(file.readAll()); - edit->setReadOnly(true); - edit->setFont(m_sourceFont); - edit->setCenterOnScroll(true); - m_tabs->addTab(edit, QFileInfo(file).baseName()); + QPlainTextEdit *source = new QPlainTextEdit(file.readAll()); + source->setReadOnly(true); + source->setFont(m_sourceFont); + source->setCenterOnScroll(true); + source->setContextMenuPolicy(Qt::CustomContextMenu); + connect(source, &QWidget::customContextMenuRequested, this, &SourcesWidget::sourceContextMenu); + m_tabs->addTab(source, QFileInfo(file).baseName()); } } { @@ -102,6 +105,11 @@ void SourcesWidget::selectDebugFile() { } void SourcesWidget::updatePC(quint32 pc) { + QTextBlockFormat format; + if (!m_pcCursor.isNull()) { + format.setBackground(Qt::white); + m_pcCursor.mergeBlockFormat(format); + } if (m_addrToLoc.empty()) { return; } @@ -111,20 +119,38 @@ void SourcesWidget::updatePC(quint32 pc) { } quint32 loc = *--i, line = loc & 0xFFFFFF; quint8 index = loc >> 24; - QPlainTextEdit *edit = static_cast(m_tabs->widget(index)); + QPlainTextEdit *source = static_cast(m_tabs->widget(index)); if (line) { line--; } - QTextCursor cursor(edit->document()->findBlockByLineNumber(line)); - QTextBlockFormat format; - format.setBackground(Qt::white); - edit->textCursor().mergeBlockFormat(format); format.setBackground(Qt::red); - cursor.mergeBlockFormat(format); - edit->setTextCursor(cursor); + m_pcCursor = QTextCursor(source->document()->findBlockByLineNumber(line)); + m_pcCursor.mergeBlockFormat(format); + source->setTextCursor(m_pcCursor); m_tabs->setCurrentIndex(index); } +void SourcesWidget::sourceContextMenu(const QPoint &pos) { + QPlainTextEdit *source = static_cast(m_tabs->currentWidget()); + quint8 index = m_tabs->currentIndex(); + quint32 line = source->cursorForPosition(pos).block().blockNumber() + 1, + loc = index << 24 | line; + auto i = m_locToAddr.upperBound(loc); + if (i != m_addrToLoc.begin()) { + i--; + } + quint32 addr = *i; + + QMenu menu; + QAction toggleBreak(tr("Toggle Breakpoint")); + menu.addAction(&toggleBreak); + + QAction *action = menu.exec(source->mapToGlobal(pos)); + if (action == &toggleBreak) { + emit breakToggled(addr); + } +} + void SourcesWidget::setSourceFont(const QFont &font) { for (int i = 0; i != m_tabs->count(); i++) { m_tabs->widget(i)->setFont(font); diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h index f5e4b4926..34b2bae38 100644 --- a/gui/qt/debugger/sourceswidget.h +++ b/gui/qt/debugger/sourceswidget.h @@ -3,6 +3,7 @@ #include #include +#include #include class QPushButton; class QTabWidget; @@ -14,13 +15,20 @@ class SourcesWidget : public QWidget { SourcesWidget(QWidget * = Q_NULLPTR); void setSourceFont(const QFont &font); +signals: + void breakToggled(quint32 addr, bool gui = true); + public slots: void selectDebugFile(); void updatePC(quint32 pc); +private slots: + void sourceContextMenu(const QPoint &pos); + private: QPushButton *m_button; QTabWidget *m_tabs; + QTextCursor m_pcCursor; QFont m_sourceFont; QMap m_addrToLoc, m_locToAddr; static const quint32 s_lastLine = (1 << 24) - 1; diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index 8e44ae8d8..bac6559bf 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -195,6 +195,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U connect(ui->buttonCertID, &QPushButton::clicked, this, &MainWindow::setCalcId); connect(m_disasm, &DataWidget::gotoDisasmAddress, this, &MainWindow::gotoDisasmAddr); connect(m_disasm, &DataWidget::gotoMemoryAddress, this, &MainWindow::gotoMemAddr); + connect(ui->sources, &SourcesWidget::breakToggled, this, &MainWindow::breakToggle); #ifdef Q_OS_MACOS { diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index 81623a71c..f2fab0557 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -376,6 +376,7 @@ private slots: int breakGetMask(int row) const; // breakpoint additions + void breakToggle(quint32 address, bool gui = true); bool breakAdd(const QString &label, uint32_t address, bool enabled, bool toggle, bool unset); void breakAddGui(); void breakAddSlot(); From 1448709d4d5f91c9bd0df42aebb9609bc17d1691 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 26 Jan 2018 15:11:31 -0500 Subject: [PATCH 03/26] Added C syntax highlighting. --- gui/qt/CEmu.pro | 6 +- gui/qt/debugger.cpp | 33 ++-- gui/qt/debugger/cdebughighlighter.cpp | 274 ++++++++++++++++++++++++++ gui/qt/debugger/cdebughighlighter.h | 26 +++ gui/qt/debugger/sourceswidget.cpp | 72 +++++-- gui/qt/debugger/sourceswidget.h | 36 ++-- gui/qt/mainwindow.cpp | 1 + gui/qt/mainwindow.h | 3 + gui/qt/mainwindow.ui | 2 +- 9 files changed, 404 insertions(+), 49 deletions(-) create mode 100644 gui/qt/debugger/cdebughighlighter.cpp create mode 100644 gui/qt/debugger/cdebughighlighter.h diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index c47701dbb..f426db51d 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -287,7 +287,8 @@ SOURCES += \ visualizerwidget.cpp \ debugger/visualizerdisplaywidget.cpp \ memorywidget.cpp \ - debugger/sourceswidget.cpp + debugger/sourceswidget.cpp \ + debugger/cdebughighlighter.cpp \ archive/extractor.c \ ../../core/bus.c \ keyhistorywidget.cpp \ @@ -388,7 +389,8 @@ HEADERS += \ vartablemodel.h \ visualizerwidget.h \ debugger/visualizerdisplaywidget.h \ - debugger/sourceswidget.h + debugger/sourceswidget.h \ + debugger/cdebughighlighter.h \ archive/extractor.h \ ../../core/bus.h \ keyhistorywidget.h \ diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 6b7d05d40..87a569cdb 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -914,7 +914,7 @@ void MainWindow::breakSetPrev(QTableWidgetItem *current, [[maybe_unused]] QTable void MainWindow::breakRemoveRow(int row) { uint32_t address = static_cast(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text())); - debug_watch(address, DBG_MASK_EXEC, false); + changeDebugPoint(address, DBG_MASK_EXEC, false); if (!m_guiAdd && !m_useSoftCom) { disasmUpdate(); memUpdate(); @@ -1000,10 +1000,10 @@ void MainWindow::breakModified(QTableWidgetItem *item) { mask = breakGetMask(row); if (m_prevBreakAddr != DEBUG_UNSET_ADDR) { - debug_watch(hex2int(m_prevBreakAddr), DBG_MASK_EXEC, false); + changeDebugPoint(hex2int(m_prevBreakAddr), DBG_MASK_EXEC, false); } item->setText(addrStr); - debug_watch(addr, mask, true); + changeDebugPoint(addr, mask, true); m_breakpoints->blockSignals(false); } disasmUpdate(); @@ -1030,6 +1030,11 @@ void MainWindow::breakToggle(quint32 address, bool gui) { m_guiAdd = false; } +void MainWindow::changeDebugPoint(quint32 address, unsigned type, bool state) { + debug_watch(address, type, state); + emit debugPointChanged(address, type, state); +} + void MainWindow::breakAddGui() { uint32_t address = static_cast(hex2int(m_disasm->getSelectedAddr())); @@ -1111,7 +1116,7 @@ bool MainWindow::breakAdd(const QString &label, uint32_t addr, bool enabled, boo connect(btnEnable, &QToolButton::clicked, [this, btnEnable, itemAddr](bool checked) { uint32_t addr = static_cast(hex2int(itemAddr->text())); btnEnable->setIcon(checked ? m_iconCheck : m_iconCheckGray); - debug_watch(addr, DBG_MASK_EXEC, checked); + changeDebugPoint(addr, DBG_MASK_EXEC, checked); disasmUpdate(); memUpdate(); }); @@ -1126,7 +1131,7 @@ bool MainWindow::breakAdd(const QString &label, uint32_t addr, bool enabled, boo m_breakpoints->setCurrentCell(row, BREAK_REMOVE_COL); if (addrStr != DEBUG_UNSET_ADDR) { - debug_watch(addr, DBG_MASK_EXEC, enabled); + changeDebugPoint(addr, DBG_MASK_EXEC, enabled); } if (!m_guiAdd && !m_useSoftCom) { @@ -1367,7 +1372,7 @@ void MainWindow::watchRemoveRow(int row) { uint32_t high = static_cast(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); for (uint32_t addr = low; addr <= high; addr++) { - debug_watch(addr, DBG_MASK_READ | DBG_MASK_WRITE, false); + changeDebugPoint(addr, DBG_MASK_READ | DBG_MASK_WRITE, false); } if (!m_guiAdd && !m_useSoftCom) { @@ -1480,7 +1485,7 @@ void MainWindow::watchUpdate() { int mask = watchGetMask(row); for (uint32_t addr = low; addr <= high; addr++) { - debug_watch(addr, mask, true); + changeDebugPoint(addr, mask, true); } } } @@ -1499,7 +1504,7 @@ void MainWindow::watchUpdateRow(QTableWidgetItem *itemLow, QTableWidgetItem *ite uint32_t high = static_cast(hex2int(itemHigh->text())); for (uint32_t addr = low; addr <= high; addr++) { - debug_watch(addr, DBG_MASK_READ | DBG_MASK_WRITE, false); + changeDebugPoint(addr, DBG_MASK_READ | DBG_MASK_WRITE, false); } } @@ -1682,7 +1687,7 @@ void MainWindow::watchModified(QTableWidgetItem *item) { uint32_t high = static_cast(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); for (uint32_t watch_addr = low; watch_addr <= high; watch_addr++) { - debug_watch(watch_addr, DBG_MASK_READ | DBG_MASK_WRITE, false); + changeDebugPoint(watch_addr, DBG_MASK_READ | DBG_MASK_WRITE, false); } } @@ -1733,7 +1738,7 @@ void MainWindow::watchModified(QTableWidgetItem *item) { uint32_t high = static_cast(hex2int(m_prevWatchHigh)); for (uint32_t watch_addr = low; watch_addr <= high; watch_addr++) { - debug_watch(watch_addr, DBG_MASK_READ | DBG_MASK_WRITE, false); + changeDebugPoint(watch_addr, DBG_MASK_READ | DBG_MASK_WRITE, false); } } @@ -1802,9 +1807,9 @@ void MainWindow::updateLabels() { (m_watchpoints->item(row, WATCH_WRITE_COL)->checkState() == Qt::Checked ? DBG_MASK_WRITE : DBG_MASK_NONE); // remove old watchpoint and add new one m_watchpoints->blockSignals(true); - debug_watch(static_cast(hex2int(old)), mask, false); + changeDebugPoint(uint32_t(hex2int(old)), mask, false); m_watchpoints->item(row, WATCH_LOW_COL)->setText(next); - debug_watch(static_cast(hex2int(next)), mask, true); + changeDebugPoint(uint32_t(hex2int(next)), mask, true); m_watchpoints->blockSignals(true); } } @@ -1814,9 +1819,9 @@ void MainWindow::updateLabels() { if (!next.isEmpty() && next != old) { int mask = breakGetMask(row); m_breakpoints->blockSignals(true); - debug_watch(static_cast(hex2int(old)), mask, false); + changeDebugPoint(uint32_t(hex2int(old)), mask, false); m_breakpoints->item(row, BREAK_ADDR_COL)->setText(next); - debug_watch(static_cast(hex2int(next)), mask, true); + changeDebugPoint(uint32_t(hex2int(next)), mask, true); m_breakpoints->blockSignals(false); } } diff --git a/gui/qt/debugger/cdebughighlighter.cpp b/gui/qt/debugger/cdebughighlighter.cpp new file mode 100644 index 000000000..cb1c1586b --- /dev/null +++ b/gui/qt/debugger/cdebughighlighter.cpp @@ -0,0 +1,274 @@ +#include "cdebughighlighter.h" +#include "sourceswidget.h" + +#include + +#include + +const QSet CDebugHighlighter::s_keywords{ + QStringLiteral("_Align"), + QStringLiteral("_At"), + QStringLiteral("auto"), + QStringLiteral("break"), + QStringLiteral("case"), + QStringLiteral("char"), + QStringLiteral("const"), + QStringLiteral("continue"), + QStringLiteral("default"), + QStringLiteral("do"), + QStringLiteral("double"), + QStringLiteral("else"), + QStringLiteral("enum"), + QStringLiteral("extern"), + QStringLiteral("float"), + QStringLiteral("for"), + QStringLiteral("goto"), + QStringLiteral("if"), + QStringLiteral("int"), + QStringLiteral("interrupt"), + QStringLiteral("long"), + QStringLiteral("nested_interrupt"), + QStringLiteral("register"), + QStringLiteral("return"), + QStringLiteral("short"), + QStringLiteral("signed"), + QStringLiteral("sizeof"), + QStringLiteral("static"), + QStringLiteral("struct"), + QStringLiteral("switch"), + QStringLiteral("typedef"), + QStringLiteral("union"), + QStringLiteral("unsigned"), + QStringLiteral("void"), + QStringLiteral("volatile"), + QStringLiteral("while") +}; + +CDebugHighlighter::CDebugHighlighter(SourcesWidget *sources, QPlainTextEdit *source) + : QSyntaxHighlighter(source), m_sources(sources) { + setDocument(source->document()); +} + +void CDebugHighlighter::highlightBlock(const QString &text) { + enum class ParseState { + Default = -1, + StringLiteral, + PreprocessorString, + PreprocessorDirective, + PreprocessorInstruction, + PreprocessorInclude, + PreprocessorIf, + PreprocessorOther, + CharacterLiteral, + MultilineComment, + Comment, + NumberLiteral, + PreprocessorFilename, + Identifier, + Escape, + EscapeOctal, + EscapeHexadecimal + } state = static_cast(previousBlockState()), baseState = state; + assert(state == ParseState::Default || state == ParseState::MultilineComment); + int start = 0, literalStart = 0, characterLiteralCount = 0; + for (int i = 0; i <= text.length(); i++) { + QChar c = text.data()[i]; + switch (state) { + case ParseState::Default: + if (c == QChar('\"')) { + state = ParseState::StringLiteral; + literalStart = i; + } else if (c == QChar('#') && !i) { + state = ParseState::PreprocessorDirective; + } else if (c == QChar('\'')) { + state = ParseState::CharacterLiteral; + literalStart = i; + characterLiteralCount = 0; + } else if (c == QChar('/') && i + 1 < text.length()) { + c = text[i + 1]; + if (c == QChar('*')) { + state = ParseState::MultilineComment; + } else if (c == QChar('/')) { + state = ParseState::Comment; + } + } else if (c >= QChar('0') && c <= QChar('9')) { + state = ParseState::NumberLiteral; + } else if ((c >= QChar('A') && c <= QChar('Z')) || + c == QChar('_') || + (c >= QChar('a') && c <= QChar('z'))) { + state = ParseState::Identifier; + } + if (i == text.length() || state != ParseState::Default) { + setFormat(start, i - start, m_sources->m_defaultFormat); + start = i; + } + break; + case ParseState::StringLiteral: + case ParseState::PreprocessorString: + case ParseState::CharacterLiteral: + case ParseState::PreprocessorFilename: + if (i == text.length()) { + setFormat(literalStart, i - literalStart, m_sources->m_errorFormat); + } else if ((c == QChar('\"') && (state == ParseState::StringLiteral || + state == ParseState::PreprocessorString)) || + (c == QChar('\'') && state == ParseState::CharacterLiteral) || + (c == QChar('>') && state == ParseState::PreprocessorFilename)) { + setFormat(start, i + 1 - start, m_sources->m_literalFormat); + if (state == ParseState::CharacterLiteral && characterLiteralCount != 1) { + setFormat(literalStart, i + 1 - literalStart, m_sources->m_errorFormat); + } + start = i + 1; + if (state == ParseState::StringLiteral || state == ParseState::CharacterLiteral) { + state = ParseState::Default; + } else { + state = ParseState::PreprocessorOther; + } + } else { + if (state == ParseState::CharacterLiteral) { + characterLiteralCount++; + } + if (c == QChar('\\') && + state != ParseState::PreprocessorString && + state != ParseState::PreprocessorFilename) { + setFormat(start, i - start, m_sources->m_literalFormat); + start = i; + baseState = state; + state = ParseState::Escape; + } + } + break; + case ParseState::PreprocessorDirective: + if (i == text.length() || (c != QChar(' ') && c != QChar('\t'))) { + state = ParseState::PreprocessorInstruction; + start = i; + } else { + break; + } + // FALLTHOURGH + case ParseState::PreprocessorInstruction: + if (c == QChar(' ') || c == QChar('\t')) { + QStringRef instruction = text.midRef(start, i - start); + if (instruction == QStringLiteral("include")) { + state = ParseState::PreprocessorInclude; + } else if (instruction == QStringLiteral("if")) { + state = ParseState::PreprocessorIf; + } else { + state = ParseState::PreprocessorOther; + } + start = 0; + } + if (i != text.length()) { + break; + } + start = 0; + // FALLTHROUGH + case ParseState::PreprocessorInclude: + case ParseState::PreprocessorIf: + case ParseState::PreprocessorOther: + if (i == text.length() || c == QChar('\"') || + (c == QChar('<') && state == ParseState::PreprocessorInclude)) { + setFormat(start, i - start, m_sources->m_preprocessorFormat); + state = c == QChar('\"') ? + ParseState::PreprocessorString : ParseState::PreprocessorFilename; + start = literalStart = i; + } else if (c != QChar(' ') && c != QChar('\t')) { + state = ParseState::PreprocessorOther; + } + break; + case ParseState::MultilineComment: + if (i == text.length()) { + setFormat(start, i - start, m_sources->m_commentFormat); + } else if (i > start + 1 + (baseState != ParseState::MultilineComment) && + c == QChar('/') && text[i - 1] == QChar('*')) { + setFormat(start, i + 1 - start, m_sources->m_commentFormat); + start = i + 1; + state = baseState = ParseState::Default; + } + break; + case ParseState::Comment: + i = text.length(); + setFormat(start, i - start, m_sources->m_commentFormat); + break; + case ParseState::NumberLiteral: + case ParseState::Identifier: + if (i == text.length() || + ((c < QChar('0') || c > QChar('9')) && + (c < QChar('A') || c > QChar('Z')) && + c != QChar('_') && + (c < QChar('a') || c > QChar('z')))) { + QString token = text.mid(start, i - start); + bool number = state == ParseState::NumberLiteral, ok = true, keyword = false; + if (number) { + bool unsignedSuffix = false, longSuffix = false; + do { + if (!unsignedSuffix && token.endsWith(QChar('u'), Qt::CaseInsensitive)) { + token.chop(1); + unsignedSuffix = true; + continue; + } else if (!longSuffix && token.endsWith(QChar('l'), Qt::CaseInsensitive)) { + token.chop(1); + longSuffix = true; + continue; + } + } while (false); + if (unsignedSuffix) { + uint value = token.toUInt(&ok, 0); + ok &= longSuffix || value < 1u << 24; + } else { + int value = token.toInt(&ok, 0); + ok &= longSuffix || (value >= -(1 << 23) && value < 1 << 23); + } + } else { + keyword = s_keywords.contains(token); + } + setFormat(start, i - start, number ? ok ? m_sources->m_literalFormat : + m_sources->m_errorFormat : + keyword ? m_sources->m_keywordFormat : + m_sources->m_identifierFormat); + start = i--; + state = ParseState::Default; + } + break; + case ParseState::Escape: + if (i == text.length()) { + setFormat(literalStart, i - literalStart, m_sources->m_errorFormat); + } else if (c >= QChar('0') && c <= QChar('7')) { + state = ParseState::EscapeOctal; + } else if (c == QChar('x') || c == QChar('X')) { + state = ParseState::EscapeHexadecimal; + } else { + bool error = c != QChar('\'') && c != QChar('\"') && c != QChar('\?') && c != QChar('\\') && + c != QChar('a') && c != QChar('b') && c != QChar('e') && c != QChar('f') && + c != QChar('n') && c != QChar('r') && c != QChar('t') && c != QChar('v'); + setFormat(start, i + 1 - start, error ? m_sources->m_errorFormat : m_sources->m_escapeFormat); + start = i + 1; + state = baseState; + baseState = ParseState::Default; + } + break; + case ParseState::EscapeOctal: + if (i == text.length()) { + setFormat(literalStart, i - literalStart, m_sources->m_errorFormat); + } else if (i == start + 4 || c < QChar('0') || c > QChar('9')) { + setFormat(start, i - start, m_sources->m_escapeFormat); + start = i--; + state = baseState; + baseState = ParseState::Default; + } + break; + case ParseState::EscapeHexadecimal: + if (i == text.length()) { + setFormat(literalStart, i - literalStart, m_sources->m_errorFormat); + } else if ((c < QChar('0') || c > QChar('9')) && + (c < QChar('a') || c > QChar('f')) && + (c < QChar('A') || c > QChar('F'))) { + setFormat(start, i - start, i == start + 2 ? m_sources->m_errorFormat : m_sources->m_escapeFormat); + start = i--; + state = baseState; + baseState = ParseState::Default; + } + break; + } + } + setCurrentBlockState(static_cast(state == ParseState::MultilineComment ? state : ParseState::Default)); +} diff --git a/gui/qt/debugger/cdebughighlighter.h b/gui/qt/debugger/cdebughighlighter.h new file mode 100644 index 000000000..1553913f5 --- /dev/null +++ b/gui/qt/debugger/cdebughighlighter.h @@ -0,0 +1,26 @@ +#ifndef CHIGHLIGHTER_H +#define CHIGHLIGHTER_H + +class SourcesWidget; + +#include +#include +#include +#include +class QPlainTextEdit; + +class CDebugHighlighter : public QSyntaxHighlighter { + Q_OBJECT + +public: + CDebugHighlighter(SourcesWidget *sources, QPlainTextEdit *source = Q_NULLPTR); + +protected: + void highlightBlock(const QString &text) override; + +private: + SourcesWidget *m_sources; + const static QSet s_keywords; +}; + +#endif diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 2bbb7da96..15d1de345 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1,4 +1,5 @@ #include "sourceswidget.h" +#include "cdebughighlighter.h" #include #include @@ -41,14 +42,28 @@ SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { layout->addWidget(m_tabs); setLayout(layout); connect(m_button, &QPushButton::clicked, this, &SourcesWidget::selectDebugFile); + m_literalFormat.setForeground(QColor::fromRgb(0x008000)); + m_escapeFormat.setForeground(QColor::fromRgb(0x008000)); + m_escapeFormat.setFontWeight(QFont::Bold); + m_preprocessorFormat.setForeground(QColor::fromRgb(0x003F3F)); + m_preprocessorFormat.setFontWeight(QFont::Bold); + m_commentFormat.setForeground(QColor::fromRgb(0x808080)); + m_keywordFormat.setForeground(QColor::fromRgb(0x3F3FFF)); + m_keywordFormat.setFontWeight(QFont::Bold); + m_operatorFormat.setFontWeight(QFont::Bold); + m_errorFormat.setBackground(QColor::fromRgb(0xFF3F3F)); } void SourcesWidget::selectDebugFile() { - QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "", tr("Debug File (*.dbg)")); + QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "/home/jacob/Programming/calc/ez80/c/debug/bin", tr("Debug File (*.dbg)")); if (debugName.isNull()) { return; } + for (int i = 0; i < m_tabs->count(); i++) { + m_tabs->widget(i)->deleteLater(); + } m_tabs->clear(); + m_highlighters.clear(); m_addrToLoc.clear(); m_locToAddr.clear(); DebugFile debugFile(debugName); @@ -73,9 +88,9 @@ void SourcesWidget::selectDebugFile() { if (success && file.open(QIODevice::ReadOnly)) { QPlainTextEdit *source = new QPlainTextEdit(file.readAll()); source->setReadOnly(true); - source->setFont(m_sourceFont); source->setCenterOnScroll(true); source->setContextMenuPolicy(Qt::CustomContextMenu); + m_highlighters << new CDebugHighlighter(this, source); connect(source, &QWidget::customContextMenuRequested, this, &SourcesWidget::sourceContextMenu); m_tabs->addTab(source, QFileInfo(file).baseName()); } @@ -104,30 +119,46 @@ void SourcesWidget::selectDebugFile() { } } -void SourcesWidget::updatePC(quint32 pc) { - QTextBlockFormat format; - if (!m_pcCursor.isNull()) { - format.setBackground(Qt::white); - m_pcCursor.mergeBlockFormat(format); - } +void SourcesWidget::update(quint32 addr, bool pc) { if (m_addrToLoc.empty()) { return; } - auto i = m_addrToLoc.upperBound(pc); + auto i = m_addrToLoc.upperBound(addr); if (i == m_addrToLoc.begin() || i == m_addrToLoc.end()) { return; } quint32 loc = *--i, line = loc & 0xFFFFFF; quint8 index = loc >> 24; + if (index >= m_highlighters.size()) { + return; + } QPlainTextEdit *source = static_cast(m_tabs->widget(index)); - if (line) { - line--; + if (!line) { + return; + } + QTextBlock block = source->document()->findBlockByNumber(line); + if (!block.isValid()) { + return; + } + m_highlighters[index]->rehighlightBlock(block); + if (pc) { + m_tabs->setCurrentIndex(index); + source->setTextCursor(QTextCursor(block)); } - format.setBackground(Qt::red); - m_pcCursor = QTextCursor(source->document()->findBlockByLineNumber(line)); - m_pcCursor.mergeBlockFormat(format); - source->setTextCursor(m_pcCursor); - m_tabs->setCurrentIndex(index); +} + +void SourcesWidget::updateAll() { + for (auto *highlighter : m_highlighters) { + highlighter->rehighlight(); + } +} + +void SourcesWidget::updatePC(quint32 pc) { + update(pc, true); +} + +void SourcesWidget::updateAddr(quint32 addr) { + update(addr, false); } void SourcesWidget::sourceContextMenu(const QPoint &pos) { @@ -152,8 +183,11 @@ void SourcesWidget::sourceContextMenu(const QPoint &pos) { } void SourcesWidget::setSourceFont(const QFont &font) { - for (int i = 0; i != m_tabs->count(); i++) { - m_tabs->widget(i)->setFont(font); + for (auto *format : { &m_defaultFormat, &m_operatorFormat, &m_literalFormat, &m_escapeFormat, + &m_preprocessorFormat, &m_commentFormat, &m_keywordFormat, &m_identifierFormat, &m_errorFormat }) { + QFont::Weight weight = static_cast(format->fontWeight()); + format->setFont(font); + format->setFontWeight(weight); } - m_sourceFont = font; + updateAll(); } diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h index 34b2bae38..fbf9530a9 100644 --- a/gui/qt/debugger/sourceswidget.h +++ b/gui/qt/debugger/sourceswidget.h @@ -1,6 +1,9 @@ #ifndef SOURCESWIDGET_H #define SOURCESWIDGET_H +class CDebugHighlighter; + +#include #include #include #include @@ -9,29 +12,36 @@ class QPushButton; class QTabWidget; class SourcesWidget : public QWidget { - Q_OBJECT + Q_OBJECT public: - SourcesWidget(QWidget * = Q_NULLPTR); - void setSourceFont(const QFont &font); + SourcesWidget(QWidget * = Q_NULLPTR); + + void setSourceFont(const QFont &font); signals: - void breakToggled(quint32 addr, bool gui = true); + void breakToggled(quint32 addr, bool gui = true); public slots: - void selectDebugFile(); - void updatePC(quint32 pc); + void selectDebugFile(); + void updatePC(quint32 pc); + void updateAddr(quint32 addr); private slots: - void sourceContextMenu(const QPoint &pos); + void sourceContextMenu(const QPoint &pos); private: - QPushButton *m_button; - QTabWidget *m_tabs; - QTextCursor m_pcCursor; - QFont m_sourceFont; - QMap m_addrToLoc, m_locToAddr; - static const quint32 s_lastLine = (1 << 24) - 1; + void update(quint32 addr, bool pc); + void updateAll(); + + friend CDebugHighlighter; + QPushButton *m_button; + QTabWidget *m_tabs; + QList m_highlighters; + QTextCharFormat m_defaultFormat, m_operatorFormat, m_literalFormat, m_escapeFormat, + m_preprocessorFormat, m_commentFormat, m_keywordFormat, m_identifierFormat, m_errorFormat; + QMap m_addrToLoc, m_locToAddr; + static const quint32 s_lastLine = (1 << 24) - 1; }; #endif diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index bac6559bf..5954735e2 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -196,6 +196,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U connect(m_disasm, &DataWidget::gotoDisasmAddress, this, &MainWindow::gotoDisasmAddr); connect(m_disasm, &DataWidget::gotoMemoryAddress, this, &MainWindow::gotoMemAddr); connect(ui->sources, &SourcesWidget::breakToggled, this, &MainWindow::breakToggle); + connect(this, &MainWindow::debugPointChanged, ui->sources, &SourcesWidget::updateAddr); #ifdef Q_OS_MACOS { diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index f2fab0557..c7d25dd06 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -92,6 +92,8 @@ class MainWindow : public QMainWindow { void usbHotplug(libusb_device *device, bool attached); #endif void usbUnplug(); + // Debugging + void debugPointChanged(quint32 address, unsigned type, bool state); private slots: void usbUnplugged() const; @@ -364,6 +366,7 @@ private slots: void gotoDisasmAddr(uint32_t addr); QAction *gotoDisasmAction(QMenu *menu) const; void gotoMemAddr(uint32_t addr); + void changeDebugPoint(quint32 address, unsigned type, bool state); HexWidget *gotoMemAddrNoRaise(uint32_t addr); QAction *gotoMemAction(QMenu *menu, bool vat = false) const; diff --git a/gui/qt/mainwindow.ui b/gui/qt/mainwindow.ui index dc878c68a..c627ad59b 100644 --- a/gui/qt/mainwindow.ui +++ b/gui/qt/mainwindow.ui @@ -11013,7 +11013,7 @@ QPushButton:pressed { SourcesWidget QWidget -
sourceswidget.h
+
debugger/sourceswidget.h
1
From b444d6e5e6cafe985c60515ec488768102d7e687 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 20 Aug 2018 10:08:24 -0400 Subject: [PATCH 04/26] Parse and use new debug info format. --- gui/qt/debugger/sourceswidget.cpp | 523 +++++++++++++++++++++++++----- gui/qt/debugger/sourceswidget.h | 44 ++- gui/qt/mainwindow.h | 8 +- 3 files changed, 491 insertions(+), 84 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 15d1de345..d9955cb4c 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1,35 +1,143 @@ #include "sourceswidget.h" #include "cdebughighlighter.h" +#include "mainwindow.h" +#include "utils.h" #include -#include +#include #include #include +#include #include #include #include #include +#include +#include + +QDebug operator<<(QDebug debug, const SourcesWidget::Line &line) { + QDebugStateSaver saver(debug); + debug.nospace().noquote() << "Line(num=" << line.num << ", addr=" << QString::number(line.addr, 16) << ')'; + return debug.maybeSpace(); +} +QDebug operator<<(QDebug debug, const SourcesWidget::Symbol &symbol) { + QDebugStateSaver saver(debug); + debug.nospace() << "Symbol(value=" << symbol.value << ", type=" << symbol.type << ", tag=" << symbol.tag << ", kind=" << symbol.kind << ", length=" << symbol.length << ", dims=" << symbol.dims << ')'; + return debug.maybeSpace(); +} +QDebug operator<<(QDebug debug, const SourcesWidget::Record &source) { + QDebugStateSaver saver(debug); + debug << "Record(symbols=" << source.symbols << ", records=" << source.records << ", size=" << source.size << ')'; + return debug.maybeSpace(); +} +QDebug operator<<(QDebug debug, const SourcesWidget::Function &function) { + QDebugStateSaver saver(debug); + debug << "Function(symbols=" << function.symbols << ", records=" << function.records << ", lines=" << function.lines << ')'; + return debug.maybeSpace(); +} +QDebug operator<<(QDebug debug, const SourcesWidget::Source &source) { + QDebugStateSaver saver(debug); + debug << "Source(symbols=" << source.symbols << ", records=" << source.records << ", functionList=" << source.functionList << ", functionMap=" << source.functionMap << ')'; + return debug.maybeSpace(); +} + namespace { +enum class DebugTokenKind { + Eof, + Alias, + Base, + BegFunc, + BegRec, + Class, + Debug, + Define, + Dim, + End, + Endef, + EndFunc, + EndRec, + File, + Length, + Line, + Reg, + Size, + Space, + Strings, + Tag, + Type, + Value, +}; + class DebugFile : public QFile { public: - DebugFile(QString name) : QFile(name) { - open(QIODevice::ReadOnly); + bool success; + DebugFile(QString name) : QFile(name), success(open(QIODevice::ReadOnly)) {} + void setErrorString(const QString &errorString) { + if (success) { + success = false; + QFile::setErrorString(errorString); + } } quint32 readULEB128() { + if (!success) { + return 0; + } quint32 result = 0, shift = 0; char next; do { - getChar(&next); + if (!getChar(&next)) { + success = false; + return 0; + } result |= (next & 0x7F) << shift; - shift += 7; - if (shift >= 32) { - setErrorString("shift overflow"); + if (shift >= 25 && next >> (32 - shift)) { + setErrorString(tr("Shift overflow")); return 0; } + shift += 7; } while (next & 0x80); return result; } + qint32 readSLEB128() { + if (!success) { + return 0; + } + qint32 result = 0, shift = 0; + char next; + do { + if (!getChar(&next)) { + success = false; + return 0; + } + result |= (next & 0x7F) << shift; + if (shift >= 25 && (next ^ ((qint8)next << 1 >> 7 & 0x7F)) >> (32 - shift)) { + setErrorString(tr("Shift overflow")); + return 0; + } + shift += 7; + } while (next & 0x80); + if (shift >= 32) { + return result; + } + shift = 32 - shift; + return result << shift >> shift; + } + QString readString() { + if (!success) { + return QString(); + } + quint32 length = readULEB128(); + if (!length) { + return QString(""); + } + QByteArray result = read(length); + if (result.isEmpty()) { + setErrorString(tr("Invalid string")); + return QString(); + } + return QString::fromLocal8Bit(result); + } }; } @@ -54,101 +162,342 @@ SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { m_errorFormat.setBackground(QColor::fromRgb(0xFF3F3F)); } +#include +void SourcesWidget::clear() { + for (int i = 0; i < m_tabs->count(); i++) { + m_tabs->widget(i)->deleteLater(); + } + m_tabs->clear(); + m_sources.clear(); + m_stringList.clear(); + m_stringMap.clear(); + } + void SourcesWidget::selectDebugFile() { QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "/home/jacob/Programming/calc/ez80/c/debug/bin", tr("Debug File (*.dbg)")); if (debugName.isNull()) { return; } - for (int i = 0; i < m_tabs->count(); i++) { - m_tabs->widget(i)->deleteLater(); - } - m_tabs->clear(); - m_highlighters.clear(); - m_addrToLoc.clear(); - m_locToAddr.clear(); + clear(); DebugFile debugFile(debugName); - quint8 index = 0; - while (!debugFile.atEnd()) { - bool success = true; - QString dir = QFileInfo(debugFile).dir().path(); - { - QStringList components = QString::fromLocal8Bit(debugFile.read(debugFile.readULEB128())).split('\\'); - for (QStringList::iterator i = components.begin(), e = components.end(); success && i != e; ) { - QStringList component(*i++); - QDirIterator matches(dir, component, (i != e ? QDir::Dirs : QDir::Files) | QDir::Readable); - if (matches.hasNext()) { - dir = matches.next(); + QString dirPath = QFileInfo(debugFile).dir().path(); + Line prevLine{0, 0}; + bool eof = false; + quint32 lang = 0; + QStack scopes; + scopes.reserve(3); + bool inFunction = false; + Symbol *symbol = Q_NULLPTR; + quint32 alias = 0; + while (debugFile.success && !debugFile.atEnd()) { + switch (static_cast(debugFile.readULEB128())) { + case DebugTokenKind::Eof: { + if (!debugFile.atEnd() || !scopes.isEmpty()) { + debugFile.setErrorString(tr("Unexpected EOF token")); + break; + } + eof = true; + break; + } + case DebugTokenKind::Alias: { + if (!symbol || alias) { + debugFile.setErrorString(tr("Unexpected alias token")); + break; + } + alias = debugFile.readULEB128(); + break; + } + case DebugTokenKind::BegFunc: { + if (scopes.count() != 1 || symbol) { + debugFile.setErrorString(tr("Unexpected function token")); + break; + } + Source &source = m_sources.last(); + QVector &functionList = source.functionList; + QMultiHash &functionMap = source.functionMap; + int functionIndex = functionList.count(); + functionMap.insert(debugFile.readULEB128(), functionIndex); + functionMap.insert(debugFile.readULEB128(), functionIndex); + functionList.resize(functionIndex + 1); + Line line = prevLine; + line.num += debugFile.readULEB128(); + line.addr += debugFile.readULEB128(); + prevLine = line; + functionList.last().lines.append(line); + scopes.push(&functionList.last()); + inFunction = true; + break; + } + case DebugTokenKind::BegRec: { + if (scopes.isEmpty() || symbol) { + debugFile.setErrorString(tr("Unexpected record begin token")); + break; + } + quint32 name = debugFile.readULEB128(); + Record record; + record.size = debugFile.readULEB128(); + scopes.push(&*scopes.top()->records.insert(name, record)); + break; + } + case DebugTokenKind::Class: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected class begin token")); + break; + } + symbol->kind = debugFile.readULEB128(); + break; + } + case DebugTokenKind::Debug: { + if (lang ? lang != debugFile.readULEB128() + : !(lang = debugFile.readULEB128())) { + debugFile.setErrorString(tr("Mixed source languages")); + break; + } + break; + } + case DebugTokenKind::Define: { + if (scopes.isEmpty() || symbol) { + debugFile.setErrorString(tr("Unexpected define symbol begin token")); + break; + } + quint32 name = debugFile.readULEB128(); + symbol = &*scopes.top()->symbols.insert(name, Symbol()); + break; + } + case DebugTokenKind::Dim: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected dimension token")); + break; + } + symbol->dims.append(debugFile.readULEB128()); + break; + } + case DebugTokenKind::End: { + if (scopes.count() != 1 || symbol) { + debugFile.setErrorString(tr("Unexpected source file end token")); + break; + } + m_sources.last().endAddr = prevLine.addr += debugFile.readULEB128(); + scopes.pop(); + break; + } + case DebugTokenKind::Endef: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected define symbol end token")); + break; + } + if (alias) { + Symbol temp = *symbol; + scopes.top()->symbols.insert(alias, temp); + alias = 0; + } + symbol = Q_NULLPTR; + break; + } + case DebugTokenKind::EndFunc: { + if (scopes.count() != 2 || !inFunction || symbol) { + debugFile.setErrorString(tr("Unexpected function end token")); + break; + } + debugFile.readULEB128(); + debugFile.readULEB128(); + Line line = prevLine; + line.num += debugFile.readULEB128(); + line.addr += debugFile.readULEB128(); + prevLine = line; + m_sources.last().functionList.last().lines.append(line); + scopes.pop(); + inFunction = false; + break; + } + case DebugTokenKind::EndRec: { + if (scopes.count() <= 1 + inFunction || symbol) { + debugFile.setErrorString(tr("Unexpected record end token")); + break; + } + debugFile.readULEB128(); + scopes.pop(); + break; + } + case DebugTokenKind::File: { + if (!scopes.isEmpty()) { + debugFile.setErrorString(tr("Unexpected source file begin token")); + break; + } + QString sourcePath = dirPath; + QStringList components = debugFile.readString().split('\\'); + for (QStringList::iterator i = components.begin(), e = components.end(); debugFile.success && i != e; ) { + QStringList component(*i++); + QDirIterator matches(sourcePath, component, (i != e ? QDir::Dirs : QDir::Files) | QDir::Readable); + if (matches.hasNext()) { + sourcePath = matches.next(); + } else { + debugFile.setErrorString(tr("Unable to find source file \"%0\"").arg(components.join(QDir::separator()))); + } + } + QFile sourceFile(sourcePath); + if (debugFile.success && sourceFile.open(QIODevice::ReadOnly)) { + QPlainTextEdit *sourceView = new QPlainTextEdit(sourceFile.readAll()); + sourceView->setObjectName(QStringLiteral("sourceView%0").arg(m_sources.count())); + sourceView->setReadOnly(true); + sourceView->setCenterOnScroll(true); + sourceView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(sourceView, &QWidget::customContextMenuRequested, this, &SourcesWidget::sourceContextMenu); + m_tabs->addTab(sourceView, QFileInfo(sourceFile).baseName()); + new CDebugHighlighter(this, sourceView); + m_sources.resize(m_sources.count() + 1); + m_sources.last().startAddr = prevLine.addr += debugFile.readULEB128(); + prevLine.num = 0; + scopes.push(&m_sources.last()); } else { - success = false; + debugFile.setErrorString(tr("Error opening source file \"%0\"").arg(components.join(QDir::separator()))); } + break; } - } - { - QFile file(dir); - if (success && file.open(QIODevice::ReadOnly)) { - QPlainTextEdit *source = new QPlainTextEdit(file.readAll()); - source->setReadOnly(true); - source->setCenterOnScroll(true); - source->setContextMenuPolicy(Qt::CustomContextMenu); - m_highlighters << new CDebugHighlighter(this, source); - connect(source, &QWidget::customContextMenuRequested, this, &SourcesWidget::sourceContextMenu); - m_tabs->addTab(source, QFileInfo(file).baseName()); + case DebugTokenKind::Length: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected length token")); + break; + } + symbol->length = debugFile.readULEB128(); + break; } - } - { - quint32 addr = debugFile.readULEB128(), line = 0, lineOff = ~0; - while (true) { - if (success) { - quint32 loc = index << 24 | (lineOff ? line : s_lastLine); - m_addrToLoc[addr] = loc; - m_locToAddr[loc] = addr; + case DebugTokenKind::Line: { + if (scopes.count() != 2 || !inFunction || symbol) { + debugFile.setErrorString(tr("Unexpected line token")); + break; } - if (!lineOff || debugFile.atEnd()) { + Line line = prevLine; + line.num += debugFile.readULEB128(); + line.addr += debugFile.readULEB128(); + prevLine = line; + m_sources.last().functionList.last().lines.append(line); + break; + } + case DebugTokenKind::Strings: { + if (!scopes.isEmpty()) { + debugFile.setErrorString(tr("Unexpected strings token")); break; } - addr += debugFile.readULEB128(); - line += lineOff = debugFile.readULEB128(); - if (line >= s_lastLine) { - return; + quint32 count = debugFile.readULEB128(); + m_stringList.reserve(count); + m_stringMap.reserve(count); + for (quint32 index = 0; debugFile.success && index != count; ++index) { + QString string = debugFile.readString(); + if (string.isEmpty()) { + debugFile.setErrorString(tr("Invalid string in string table")); + } else { + m_stringList << string; + m_stringMap[string] = index; + } } + break; } + case DebugTokenKind::Tag: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected tag token")); + break; + } + symbol->tag = debugFile.readULEB128(); + break; + } + case DebugTokenKind::Type: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected value token")); + break; + } + symbol->type = debugFile.readSLEB128(); + break; + } + case DebugTokenKind::Value: { + if (!symbol) { + debugFile.setErrorString(tr("Unexpected value token")); + break; + } + symbol->value = debugFile.readSLEB128(); + break; + } + default: debugFile.setErrorString(tr("Unknown debug token kind")); } - if (!++index) { - return; - } + } + if (m_sources.isEmpty()) { + debugFile.setErrorString(tr("No source files")); + } + if (!eof) { + debugFile.setErrorString(tr("Missing EOF token")); + } + if (m_stringList.value(lang - 1) != QStringLiteral("C")) { + debugFile.setErrorString(tr("Unknown source language \"%0\"").arg(m_stringList.value(lang - 1))); + } + if (debugFile.success) { + qDebug() << m_sources; + qDebug() << m_stringList; + qDebug() << m_stringMap; + dumpObjectTree(); + } else { + clear(); + QMessageBox dialog(QMessageBox::Critical, findParent(this)->MSG_ERROR, tr("Error loading debug file:"), QMessageBox::Ok, this); + dialog.setInformativeText(debugFile.errorString()); + dialog.exec(); } } void SourcesWidget::update(quint32 addr, bool pc) { - if (m_addrToLoc.empty()) { - return; - } - auto i = m_addrToLoc.upperBound(addr); - if (i == m_addrToLoc.begin() || i == m_addrToLoc.end()) { + qDebug() << addr; + const QVector &sources = m_sources; + const SourceBase sourceKey{addr, addr}; + auto source = std::upper_bound(sources.begin(), sources.end(), sourceKey, + [](const SourceBase &first, + const SourceBase &second) { + return first.endAddr < second.startAddr; + }); + if (source == sources.begin() || + addr < (--source)->startAddr || addr >= source->endAddr) { return; } - quint32 loc = *--i, line = loc & 0xFFFFFF; - quint8 index = loc >> 24; - if (index >= m_highlighters.size()) { + qDebug() << *source; + auto sourceView = m_tabs->findChild(QStringLiteral("sourceView%0").arg(std::distance(sources.begin(), source))); + qDebug() << sourceView; + const QVector &functions = source->functionList; + const Line lineKey{0, addr}; + const FunctionBase functionKey{{lineKey}}; + auto function = std::upper_bound(functions.begin(), functions.end(), + functionKey, + [](const FunctionBase &first, + const FunctionBase &second) { + return first.lines.last().addr < + second.lines.first().addr; + }); + if (function == functions.begin()) { return; } - QPlainTextEdit *source = static_cast(m_tabs->widget(index)); - if (!line) { + const QVector &lines = (--function)->lines; + qDebug() << *function; + if (addr < lines.first().addr || addr >= lines.last().addr) { return; } - QTextBlock block = source->document()->findBlockByNumber(line); + auto line = + std::prev(std::upper_bound(std::next(lines.begin()), std::prev(lines.end()), + lineKey, + [](const Line &first, const Line &second) { + return first.addr < second.addr; + })); + qDebug() << *line; + QTextBlock block = sourceView->document()->findBlockByLineNumber(line->num - 1); if (!block.isValid()) { return; } - m_highlighters[index]->rehighlightBlock(block); + qDebug() << block.text(); if (pc) { - m_tabs->setCurrentIndex(index); - source->setTextCursor(QTextCursor(block)); + m_tabs->setCurrentWidget(sourceView); + QTextCursor cursor(block); + cursor.select(QTextCursor::LineUnderCursor); + sourceView->setTextCursor(cursor); } } void SourcesWidget::updateAll() { - for (auto *highlighter : m_highlighters) { + for (QSyntaxHighlighter *highlighter : m_tabs->findChildren()) { highlighter->rehighlight(); } } @@ -162,23 +511,41 @@ void SourcesWidget::updateAddr(quint32 addr) { } void SourcesWidget::sourceContextMenu(const QPoint &pos) { - QPlainTextEdit *source = static_cast(m_tabs->currentWidget()); - quint8 index = m_tabs->currentIndex(); - quint32 line = source->cursorForPosition(pos).block().blockNumber() + 1, - loc = index << 24 | line; - auto i = m_locToAddr.upperBound(loc); - if (i != m_addrToLoc.begin()) { - i--; + QPlainTextEdit *sourceView = static_cast(m_tabs->currentWidget()); + const QVector &functions = m_sources.at(sourceView->objectName().midRef(QStringLiteral("sourceView").count()).toInt()).functionList; + quint32 num = sourceView->cursorForPosition(pos).block().firstLineNumber() + 1; + const Line lineKey{num, 0}; + const FunctionBase functionKey{{lineKey}}; + auto function = std::upper_bound(functions.begin(), functions.end(), + functionKey, + [](const FunctionBase &first, + const FunctionBase &second) { + return first.lines.last().num < + second.lines.first().num; + }); + if (function == functions.begin()) { + return; + } + const QVector &lines = (--function)->lines; + qDebug() << *function; + if (lineKey.num < lines.first().num || lineKey.num >= lines.last().num) { + return; } - quint32 addr = *i; + auto line = + std::prev(std::upper_bound(std::next(lines.begin()), std::prev(lines.end()), + lineKey, + [](const Line &first, const Line &second) { + return first.num < second.num; + })); + qDebug() << *line; QMenu menu; QAction toggleBreak(tr("Toggle Breakpoint")); menu.addAction(&toggleBreak); - QAction *action = menu.exec(source->mapToGlobal(pos)); + QAction *action = menu.exec(sourceView->mapToGlobal(pos)); if (action == &toggleBreak) { - emit breakToggled(addr); + emit breakToggled(line->addr); } } diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h index fbf9530a9..2026af65c 100644 --- a/gui/qt/debugger/sourceswidget.h +++ b/gui/qt/debugger/sourceswidget.h @@ -31,17 +31,55 @@ private slots: void sourceContextMenu(const QPoint &pos); private: + void clear(); void update(quint32 addr, bool pc); void updateAll(); friend CDebugHighlighter; QPushButton *m_button; QTabWidget *m_tabs; - QList m_highlighters; QTextCharFormat m_defaultFormat, m_operatorFormat, m_literalFormat, m_escapeFormat, m_preprocessorFormat, m_commentFormat, m_keywordFormat, m_identifierFormat, m_errorFormat; - QMap m_addrToLoc, m_locToAddr; - static const quint32 s_lastLine = (1 << 24) - 1; + + struct Line { + quint32 num, addr; + }; + struct Symbol { + qint32 value = 0, type = 0; + quint32 tag = 0; + quint8 kind = 0, length = 0; + QVector dims; + }; + struct Record; + struct Scope { + QMultiHash symbols; + QMultiHash records; + }; + struct Record : Scope { + quint32 size = 0; + }; + struct FunctionBase { + QVector lines; + }; + struct Function : FunctionBase, Scope { + }; + struct SourceBase { + quint32 startAddr, endAddr; + }; + struct Source : SourceBase, Scope { + QVector functionList; + QMultiHash functionMap; + }; + QVector m_sources; + QStringList m_stringList; + QHash m_stringMap; +#ifndef NDEBUG + friend QDebug operator<<(QDebug debug, const Line &line); + friend QDebug operator<<(QDebug debug, const Symbol &line); + friend QDebug operator<<(QDebug debug, const Record &line); + friend QDebug operator<<(QDebug debug, const Function &line); + friend QDebug operator<<(QDebug debug, const Source &line); +#endif }; #endif diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index c7d25dd06..7af6bc48e 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -922,9 +922,6 @@ private slots: QString TXT_MISC; QString TXT_AUTOTESTER; - QString MSG_INFORMATION; - QString MSG_WARNING; - QString MSG_ERROR; QString MSG_ADD_MEMORY; QString MSG_ADD_VISUALIZER; QString MSG_EDIT_UI; @@ -980,6 +977,11 @@ private slots: QAction *actionToggleConsole; QString TXT_TOGGLE_CONSOLE; #endif + +public: + QString MSG_INFORMATION; + QString MSG_WARNING; + QString MSG_ERROR; }; #endif From b5cd7b7b7dc821f53f675ea863c8556dc31d95d6 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 20 Aug 2018 22:24:40 -0400 Subject: [PATCH 05/26] Implement variable views. --- gui/qt/CEmu.pro | 1 + gui/qt/debugger.cpp | 6 + gui/qt/debugger/cdebughighlighter.cpp | 137 +-- gui/qt/debugger/disasm.cpp | 14 +- gui/qt/debugger/sourceswidget.cpp | 1205 +++++++++++++++++++++---- gui/qt/debugger/sourceswidget.h | 79 +- gui/qt/mainwindow.cpp | 9 +- gui/qt/mainwindow.h | 1 + 8 files changed, 1205 insertions(+), 247 deletions(-) diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index f426db51d..0709b5b33 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -82,6 +82,7 @@ CONFIG(release, debug|release) { DEFINES += QT_NO_DEBUG_OUTPUT NDEBUG } else { #This is a debug build + QT += testlib GLOBAL_FLAGS += -g3 } diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 87a569cdb..fd85667cc 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -2832,6 +2832,12 @@ void MainWindow::stepOut() { debugStep(DBG_STEP_OUT); } +void MainWindow::runUntil(uint32_t addr) { + m_runUntilAddr = addr; + debugToggle(); + debugStep(DBG_RUN_UNTIL); +} + //------------------------------------------------ // Other Functions //------------------------------------------------ diff --git a/gui/qt/debugger/cdebughighlighter.cpp b/gui/qt/debugger/cdebughighlighter.cpp index cb1c1586b..913792594 100644 --- a/gui/qt/debugger/cdebughighlighter.cpp +++ b/gui/qt/debugger/cdebughighlighter.cpp @@ -70,32 +70,37 @@ void CDebugHighlighter::highlightBlock(const QString &text) { EscapeHexadecimal } state = static_cast(previousBlockState()), baseState = state; assert(state == ParseState::Default || state == ParseState::MultilineComment); - int start = 0, literalStart = 0, characterLiteralCount = 0; + int start = 0, literalStart = 0, literalInfo = 0; for (int i = 0; i <= text.length(); i++) { QChar c = text.data()[i]; switch (state) { case ParseState::Default: - if (c == QChar('\"')) { + if (c == '\"') { state = ParseState::StringLiteral; literalStart = i; - } else if (c == QChar('#') && !i) { + } else if (c == '#' && !i) { state = ParseState::PreprocessorDirective; - } else if (c == QChar('\'')) { + } else if (c == '\'') { state = ParseState::CharacterLiteral; literalStart = i; - characterLiteralCount = 0; - } else if (c == QChar('/') && i + 1 < text.length()) { + literalInfo = 0; + } else if (c == '.' && i < text.length() - 1) { c = text[i + 1]; - if (c == QChar('*')) { + if (c >= '0' && c <= '9') { + state = ParseState::NumberLiteral; + literalInfo = true; + } + } else if (c == '/' && i < text.length() - 1) { + c = text[i + 1]; + if (c == '*') { state = ParseState::MultilineComment; - } else if (c == QChar('/')) { + } else if (c == '/') { state = ParseState::Comment; } - } else if (c >= QChar('0') && c <= QChar('9')) { + } else if (c >= '0' && c <= '9') { state = ParseState::NumberLiteral; - } else if ((c >= QChar('A') && c <= QChar('Z')) || - c == QChar('_') || - (c >= QChar('a') && c <= QChar('z'))) { + literalInfo = false; + } else if ((c >='A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z')) { state = ParseState::Identifier; } if (i == text.length() || state != ParseState::Default) { @@ -109,13 +114,14 @@ void CDebugHighlighter::highlightBlock(const QString &text) { case ParseState::PreprocessorFilename: if (i == text.length()) { setFormat(literalStart, i - literalStart, m_sources->m_errorFormat); - } else if ((c == QChar('\"') && (state == ParseState::StringLiteral || - state == ParseState::PreprocessorString)) || - (c == QChar('\'') && state == ParseState::CharacterLiteral) || - (c == QChar('>') && state == ParseState::PreprocessorFilename)) { + } else if ((c == '\"' && (state == ParseState::StringLiteral || + state == ParseState::PreprocessorString)) || + (c == '\'' && state == ParseState::CharacterLiteral) || + (c == '>' && state == ParseState::PreprocessorFilename)) { setFormat(start, i + 1 - start, m_sources->m_literalFormat); - if (state == ParseState::CharacterLiteral && characterLiteralCount != 1) { - setFormat(literalStart, i + 1 - literalStart, m_sources->m_errorFormat); + if (state == ParseState::CharacterLiteral && literalInfo != 1) { + setFormat(literalStart, i + 1 - literalStart, + m_sources->m_errorFormat); } start = i + 1; if (state == ParseState::StringLiteral || state == ParseState::CharacterLiteral) { @@ -124,10 +130,8 @@ void CDebugHighlighter::highlightBlock(const QString &text) { state = ParseState::PreprocessorOther; } } else { - if (state == ParseState::CharacterLiteral) { - characterLiteralCount++; - } - if (c == QChar('\\') && + literalInfo++; + if (c == '\\' && state != ParseState::PreprocessorString && state != ParseState::PreprocessorFilename) { setFormat(start, i - start, m_sources->m_literalFormat); @@ -138,15 +142,15 @@ void CDebugHighlighter::highlightBlock(const QString &text) { } break; case ParseState::PreprocessorDirective: - if (i == text.length() || (c != QChar(' ') && c != QChar('\t'))) { + if (i == text.length() || (c != ' ' && c != '\t')) { state = ParseState::PreprocessorInstruction; start = i; } else { break; } - // FALLTHOURGH + [[gnu::fallthrough]]; case ParseState::PreprocessorInstruction: - if (c == QChar(' ') || c == QChar('\t')) { + if (c == ' ' || c == '\t') { QStringRef instruction = text.midRef(start, i - start); if (instruction == QStringLiteral("include")) { state = ParseState::PreprocessorInclude; @@ -161,17 +165,17 @@ void CDebugHighlighter::highlightBlock(const QString &text) { break; } start = 0; - // FALLTHROUGH + [[gnu::fallthrough]]; case ParseState::PreprocessorInclude: case ParseState::PreprocessorIf: case ParseState::PreprocessorOther: - if (i == text.length() || c == QChar('\"') || - (c == QChar('<') && state == ParseState::PreprocessorInclude)) { + if (i == text.length() || c == '\"' || + (c == '<' && state == ParseState::PreprocessorInclude)) { setFormat(start, i - start, m_sources->m_preprocessorFormat); - state = c == QChar('\"') ? - ParseState::PreprocessorString : ParseState::PreprocessorFilename; + state = c == '\"' ? ParseState::PreprocessorString + : ParseState::PreprocessorFilename; start = literalStart = i; - } else if (c != QChar(' ') && c != QChar('\t')) { + } else if (c != ' ' && c != '\t') { state = ParseState::PreprocessorOther; } break; @@ -179,7 +183,7 @@ void CDebugHighlighter::highlightBlock(const QString &text) { if (i == text.length()) { setFormat(start, i - start, m_sources->m_commentFormat); } else if (i > start + 1 + (baseState != ParseState::MultilineComment) && - c == QChar('/') && text[i - 1] == QChar('*')) { + c == '/' && text[i - 1] == '*') { setFormat(start, i + 1 - start, m_sources->m_commentFormat); start = i + 1; state = baseState = ParseState::Default; @@ -192,39 +196,54 @@ void CDebugHighlighter::highlightBlock(const QString &text) { case ParseState::NumberLiteral: case ParseState::Identifier: if (i == text.length() || - ((c < QChar('0') || c > QChar('9')) && - (c < QChar('A') || c > QChar('Z')) && - c != QChar('_') && - (c < QChar('a') || c > QChar('z')))) { - QString token = text.mid(start, i - start); - bool number = state == ParseState::NumberLiteral, ok = true, keyword = false; - if (number) { - bool unsignedSuffix = false, longSuffix = false; - do { - if (!unsignedSuffix && token.endsWith(QChar('u'), Qt::CaseInsensitive)) { - token.chop(1); - unsignedSuffix = true; - continue; - } else if (!longSuffix && token.endsWith(QChar('l'), Qt::CaseInsensitive)) { + ((c < '0' || c > '9') && (c < 'A' || c > 'Z') && + c != '_' && (c < 'a' || c > 'z'))) { + QStringRef token = text.midRef(start, i - start); + if (state == ParseState::NumberLiteral) { + if (c == '.' || ((c == '+' || c == '-') && i > start && + (text[i - 1] == 'E' || text[i - 1] == 'e'))) { + literalInfo = true; + break; + } + bool ok; + if (literalInfo) { + if (token.endsWith('f', Qt::CaseInsensitive) || + token.endsWith('l', Qt::CaseInsensitive)) { token.chop(1); - longSuffix = true; - continue; } - } while (false); - if (unsignedSuffix) { - uint value = token.toUInt(&ok, 0); - ok &= longSuffix || value < 1u << 24; + token.toFloat(&ok); } else { - int value = token.toInt(&ok, 0); - ok &= longSuffix || (value >= -(1 << 23) && value < 1 << 23); + bool unsignedSuffix = false, longSuffix = false; + do { + if (!unsignedSuffix && + token.endsWith('u', Qt::CaseInsensitive)) { + token.chop(1); + unsignedSuffix = true; + continue; + } else if (!longSuffix && + token.endsWith('l', Qt::CaseInsensitive)) { + token.chop(1); + longSuffix = true; + continue; + } + } while (false); + if (unsignedSuffix) { + uint value = token.toUInt(&ok, 0); + ok &= longSuffix || value < 1u << 24; + } else { + int value = token.toInt(&ok, 0); + ok &= longSuffix || (value >= -(1 << 23) && + value < 1 << 23); + } } + setFormat(start, i - start, ok ? m_sources->m_literalFormat + : m_sources->m_errorFormat); } else { - keyword = s_keywords.contains(token); + setFormat(start, i - start, + s_keywords.contains(token.toString()) ? + m_sources->m_keywordFormat : + m_sources->m_identifierFormat); } - setFormat(start, i - start, number ? ok ? m_sources->m_literalFormat : - m_sources->m_errorFormat : - keyword ? m_sources->m_keywordFormat : - m_sources->m_identifierFormat); start = i--; state = ParseState::Default; } diff --git a/gui/qt/debugger/disasm.cpp b/gui/qt/debugger/disasm.cpp index e28dd2fac..4357ee7af 100644 --- a/gui/qt/debugger/disasm.cpp +++ b/gui/qt/debugger/disasm.cpp @@ -146,7 +146,7 @@ static bool disasmPut([[maybe_unused]] struct zdis_ctx *ctx, enum zdis_put kind, *disasm.cur += strA(static_cast(val)); break; case ZDIS_PUT_CHAR: - *disasm.cur += static_cast(val); + *disasm.cur += char(val); break; case ZDIS_PUT_MNE_SEP: disasm.instr.operands = disasm.tab ? '\t' : ' '; @@ -170,7 +170,7 @@ void disasmInit() { void disasmGet(bool useCpuMode) { disasm.ctx.zdis_lowercase = !disasm.uppercase; disasm.ctx.zdis_implicit = !disasm.implicit; - disasm.ctx.zdis_end_addr = static_cast(disasm.base); + disasm.ctx.zdis_end_addr = uint32_t(disasm.base); disasm.ctx.zdis_adl = useCpuMode ? cpu.ADL : disasm.adl; disasm.cur = &disasm.instr.opcode; @@ -186,7 +186,7 @@ void disasmGet(bool useCpuMode) { zdis_put_inst(&disasm.ctx); - if (disasm.highlight.pc && cpu.registers.PC != static_cast(disasm.base)) { + if (disasm.highlight.pc && cpu.registers.PC != uint32_t(disasm.base)) { static char tmpbuf[20]; size_t size = cpu.registers.PC - static_cast(disasm.base); disasm.instr.data = disasm.instr.data.substr(0, size * 2); @@ -211,12 +211,12 @@ void disasmGet(bool useCpuMode) { disasm.instr.operands += ','; } } while (size); - disasm.base = static_cast(disasm.ctx.zdis_start_addr); - disasm.next = static_cast(cpu.registers.PC); + disasm.base = int32_t(disasm.ctx.zdis_start_addr); + disasm.next = int32_t(cpu.registers.PC); disasm.highlight.pc = false; } else { - disasm.base = static_cast(disasm.ctx.zdis_start_addr); - disasm.next = static_cast(disasm.ctx.zdis_end_addr); + disasm.base = int32_t(disasm.ctx.zdis_start_addr); + disasm.next = int32_t(disasm.ctx.zdis_end_addr); } disasm.instr.size = static_cast(disasm.next) - static_cast(disasm.base); diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index d9955cb4c..479d7c8a2 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -2,45 +2,168 @@ #include "cdebughighlighter.h" #include "mainwindow.h" #include "utils.h" +#include "../../core/mem.h" #include +#include +#include #include #include #include #include #include #include +#include #include +#include +#include #include #include #include +#include + +#ifdef QT_DEBUG +#include QDebug operator<<(QDebug debug, const SourcesWidget::Line &line) { QDebugStateSaver saver(debug); debug.nospace().noquote() << "Line(num=" << line.num << ", addr=" << QString::number(line.addr, 16) << ')'; return debug.maybeSpace(); } +QDebug operator<<(QDebug debug, const SourcesWidget::SymbolKind &kind) { + QDebugStateSaver saver(debug); + switch (kind) { + case SourcesWidget::SymbolKind::Function: + debug << "SymbolKind::Function"; + break; + case SourcesWidget::SymbolKind::StructField: + debug << "SymbolKind::StructField"; + break; + case SourcesWidget::SymbolKind::UnionField: + debug << "SymbolKind::UnionField"; + break; + case SourcesWidget::SymbolKind::BitField: + debug << "SymbolKind::BitField"; + break; + case SourcesWidget::SymbolKind::Constant: + debug << "SymbolKind::Constant"; + break; + case SourcesWidget::SymbolKind::Uninitialized: + debug << "SymbolKind::Uninitialized"; + break; + case SourcesWidget::SymbolKind::Variable: + debug << "SymbolKind::Variable"; + break; + default: + debug.nospace() << "SymbolKind(" << quint8(kind) << ')'; + break; + } + return debug.maybeSpace(); +} +QDebug operator<<(QDebug debug, const SourcesWidget::Context &context) { + QDebugStateSaver saver(debug); + debug.nospace() << "Context(local=" << context.local << ", global=" << context.global << ')'; + return debug.maybeSpace(); +} QDebug operator<<(QDebug debug, const SourcesWidget::Symbol &symbol) { QDebugStateSaver saver(debug); - debug.nospace() << "Symbol(value=" << symbol.value << ", type=" << symbol.type << ", tag=" << symbol.tag << ", kind=" << symbol.kind << ", length=" << symbol.length << ", dims=" << symbol.dims << ')'; + debug.nospace() << "Symbol(name=" << symbol.name << ", alias=" << symbol.alias << ", value=" << symbol.value << ", type=" << symbol.type << ", tag=" << symbol.tag << ", kind=" << symbol.kind << ", length=" << symbol.length << ", dims=" << symbol.dims << ')'; return debug.maybeSpace(); } -QDebug operator<<(QDebug debug, const SourcesWidget::Record &source) { +QDebug operator<<(QDebug debug, const SourcesWidget::Record &record) { QDebugStateSaver saver(debug); - debug << "Record(symbols=" << source.symbols << ", records=" << source.records << ", size=" << source.size << ')'; + debug << "Record(symbolList=" << record.symbolList << ", symbolMap=" << record.symbolMap << ", recordList=" << record.recordList << ", recordMap=" << record.recordMap << ", size=" << record.size << ')'; return debug.maybeSpace(); } QDebug operator<<(QDebug debug, const SourcesWidget::Function &function) { QDebugStateSaver saver(debug); - debug << "Function(symbols=" << function.symbols << ", records=" << function.records << ", lines=" << function.lines << ')'; + debug << "Function(symbolList=" << function.symbolList << ", symbolMap=" << function.symbolMap << ", recordList=" << function.recordList << ", recordMap=" << function.recordMap << ", lines=" << function.lines << ')'; return debug.maybeSpace(); } QDebug operator<<(QDebug debug, const SourcesWidget::Source &source) { QDebugStateSaver saver(debug); - debug << "Source(symbols=" << source.symbols << ", records=" << source.records << ", functionList=" << source.functionList << ", functionMap=" << source.functionMap << ')'; + debug << "Source(symbolList=" << source.symbolList << ", symbolMap=" << source.symbolMap << ", recordList=" << source.recordList << ", recordMap=" << source.recordMap << ", functionList=" << source.functionList << ", functionMap=" << source.functionMap << ')'; return debug.maybeSpace(); } +#endif + +class SourcesWidget::VariableModel : public QAbstractItemModel { + void deleteVariable(int id); +protected: + explicit VariableModel(SourcesWidget *parent) : QAbstractItemModel(parent) {} +#define using(const, field) const decltype(m_##field) &field() const { \ + return static_cast(QObject::parent())->m_##field; \ + } + using(const, sources) + using(const, stringList) + using(const, stringMap) +#undef using + enum class VariableFlag : quint8 { + None = 0, + Deleted = 1 << 0, + Changed = 1 << 1, + }; + Q_DECLARE_FLAGS(VariableFlags, VariableFlag) + struct Variable { + int parent, parentIndex, childIndex; + QList children; + Symbol symbol; + Context context; + QString data[2]; + VariableFlags flags; + }; + int m_freeVariable; + QVector m_variables; + QStringList m_topLevelData[2]; + QList> m_topLevelChildren; + bool lookupAddr(quint32 addr, int *sourceIndex = nullptr, + int *functionIndex = nullptr, int *lineIndex = nullptr) const { + return static_cast(QObject::parent())-> + lookupAddr(addr, sourceIndex, functionIndex, lineIndex); + } + static quint32 sizeOfSymbol(const Symbol &symbol, Context context); + QString symbolTypeToString(const QString &name, const Symbol &symbol, + Context context) const; + static QString symbolValueToString(const Symbol &symbol); + int createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, const QString &name, qint32 base = 0); + void createVariable(const QModelIndex &parent, const Symbol &symbol, + const QString &name, qint32 base = 0); + void removeTopLevels(int first, int last); + constexpr static quintptr s_topLevelId = ~quintptr(); + constexpr static int s_topLevelParent = int(s_topLevelId); +public: + virtual void update(); + QModelIndex parent(const QModelIndex &child) const override; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + int childCount(const QModelIndex &parent = QModelIndex()) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool canFetchMore(const QModelIndex &parent) const override; + void fetchMore(const QModelIndex &parent) override; +}; +class SourcesWidget::GlobalModel : public VariableModel { + static int commonPrefixLength(const QStringList &paths); +public: + explicit GlobalModel(SourcesWidget *parent) : VariableModel(parent) {} + void init(const QStringList &paths); +}; +class SourcesWidget::StackModel : public VariableModel { + struct StackEntry { + quint32 ix, pc, cookie[2]; + const Source *source; + const Function *function; + }; + QVector m_stack; +public: + explicit StackModel(SourcesWidget *parent) : VariableModel(parent) {} + void update() override; + void init(); +}; namespace { enum class DebugTokenKind { @@ -142,14 +265,36 @@ class DebugFile : public QFile { } SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { - m_button = new QPushButton(tr("Select Debug File")); + QPushButton *button = new QPushButton(tr("Select Debug File")); + connect(button, &QPushButton::clicked, this, &SourcesWidget::selectDebugFile); m_tabs = new QTabWidget; m_tabs->setTabPosition(QTabWidget::South); + m_global = new QTreeView; + m_globalModel = new GlobalModel(this); + m_global->setModel(m_globalModel); + m_stack = new QTreeView; + m_stackModel = new StackModel(this); +#ifdef QT_DEBUG + new QAbstractItemModelTester(m_globalModel, + QAbstractItemModelTester::FailureReportingMode::Fatal, + this); + new QAbstractItemModelTester(m_stackModel, + QAbstractItemModelTester::FailureReportingMode::Fatal, + this); +#endif + m_stack->setModel(m_stackModel); + QSplitter *treeSplitter = new QSplitter(Qt::Vertical); + treeSplitter->addWidget(m_global); + treeSplitter->addWidget(m_stack); + treeSplitter->setSizes({INT_MAX, INT_MAX}); + QSplitter *splitter = new QSplitter(Qt::Horizontal); + splitter->addWidget(m_tabs); + splitter->addWidget(treeSplitter); + splitter->setSizes({INT_MAX, INT_MAX}); QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(m_button); - layout->addWidget(m_tabs); + layout->addWidget(button); + layout->addWidget(splitter); setLayout(layout); - connect(m_button, &QPushButton::clicked, this, &SourcesWidget::selectDebugFile); m_literalFormat.setForeground(QColor::fromRgb(0x008000)); m_escapeFormat.setForeground(QColor::fromRgb(0x008000)); m_escapeFormat.setFontWeight(QFont::Bold); @@ -162,35 +307,28 @@ SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { m_errorFormat.setBackground(QColor::fromRgb(0xFF3F3F)); } -#include -void SourcesWidget::clear() { - for (int i = 0; i < m_tabs->count(); i++) { - m_tabs->widget(i)->deleteLater(); - } - m_tabs->clear(); - m_sources.clear(); - m_stringList.clear(); - m_stringMap.clear(); - } - void SourcesWidget::selectDebugFile() { QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "/home/jacob/Programming/calc/ez80/c/debug/bin", tr("Debug File (*.dbg)")); if (debugName.isNull()) { return; } - clear(); + + QStringList paths; + QVector sources; + QStringList stringList; + QHash stringMap; DebugFile debugFile(debugName); - QString dirPath = QFileInfo(debugFile).dir().path(); + QDir dir = QFileInfo(debugFile).dir(); + dir.cdUp(); Line prevLine{0, 0}; bool eof = false; quint32 lang = 0; QStack scopes; scopes.reserve(3); bool inFunction = false; - Symbol *symbol = Q_NULLPTR; - quint32 alias = 0; + Symbol *curSymbol = Q_NULLPTR; while (debugFile.success && !debugFile.atEnd()) { - switch (static_cast(debugFile.readULEB128())) { + switch (DebugTokenKind(debugFile.readULEB128())) { case DebugTokenKind::Eof: { if (!debugFile.atEnd() || !scopes.isEmpty()) { debugFile.setErrorString(tr("Unexpected EOF token")); @@ -200,51 +338,56 @@ void SourcesWidget::selectDebugFile() { break; } case DebugTokenKind::Alias: { - if (!symbol || alias) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected alias token")); break; } - alias = debugFile.readULEB128(); + Scope &scope = *scopes.top(); + curSymbol->alias = debugFile.readULEB128(); + scope.symbolMap.insert(curSymbol->alias, scope.symbolList.count() - 1); break; } case DebugTokenKind::BegFunc: { - if (scopes.count() != 1 || symbol) { + if (scopes.count() != 1 || curSymbol) { debugFile.setErrorString(tr("Unexpected function token")); break; } - Source &source = m_sources.last(); + Source &source = sources.last(); QVector &functionList = source.functionList; QMultiHash &functionMap = source.functionMap; int functionIndex = functionList.count(); + Function function; + function.name = debugFile.readULEB128(); + functionMap.insert(function.name, functionIndex); functionMap.insert(debugFile.readULEB128(), functionIndex); - functionMap.insert(debugFile.readULEB128(), functionIndex); - functionList.resize(functionIndex + 1); - Line line = prevLine; - line.num += debugFile.readULEB128(); - line.addr += debugFile.readULEB128(); - prevLine = line; - functionList.last().lines.append(line); + prevLine.num += debugFile.readULEB128(); + prevLine.addr += debugFile.readULEB128(); + function.lines.append(prevLine); + functionList.append(function); scopes.push(&functionList.last()); inFunction = true; break; } case DebugTokenKind::BegRec: { - if (scopes.isEmpty() || symbol) { + if (scopes.isEmpty() || curSymbol) { debugFile.setErrorString(tr("Unexpected record begin token")); break; } - quint32 name = debugFile.readULEB128(); + Scope &scope = *scopes.top(); Record record; + record.name = debugFile.readULEB128(); record.size = debugFile.readULEB128(); - scopes.push(&*scopes.top()->records.insert(name, record)); + scope.recordMap.insert(record.name, scope.recordList.count()); + scope.recordList.append(record); + scopes.push(&scope.recordList.last()); break; } case DebugTokenKind::Class: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected class begin token")); break; } - symbol->kind = debugFile.readULEB128(); + curSymbol->kind = SymbolKind(debugFile.readULEB128()); break; } case DebugTokenKind::Debug: { @@ -256,62 +399,59 @@ void SourcesWidget::selectDebugFile() { break; } case DebugTokenKind::Define: { - if (scopes.isEmpty() || symbol) { + if (scopes.isEmpty() || curSymbol) { debugFile.setErrorString(tr("Unexpected define symbol begin token")); break; } - quint32 name = debugFile.readULEB128(); - symbol = &*scopes.top()->symbols.insert(name, Symbol()); + Scope &scope = *scopes.top(); + Symbol symbol; + symbol.name = debugFile.readULEB128(); + scope.symbolMap.insert(symbol.name, scope.symbolList.count()); + scope.symbolList.append(symbol); + curSymbol = &scope.symbolList.last(); break; } case DebugTokenKind::Dim: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected dimension token")); break; } - symbol->dims.append(debugFile.readULEB128()); + curSymbol->dims.append(debugFile.readULEB128()); break; } case DebugTokenKind::End: { - if (scopes.count() != 1 || symbol) { + if (scopes.count() != 1 || curSymbol) { debugFile.setErrorString(tr("Unexpected source file end token")); break; } - m_sources.last().endAddr = prevLine.addr += debugFile.readULEB128(); + sources.last().endAddr = prevLine.addr += debugFile.readULEB128(); scopes.pop(); break; } case DebugTokenKind::Endef: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected define symbol end token")); break; } - if (alias) { - Symbol temp = *symbol; - scopes.top()->symbols.insert(alias, temp); - alias = 0; - } - symbol = Q_NULLPTR; + curSymbol = Q_NULLPTR; break; } case DebugTokenKind::EndFunc: { - if (scopes.count() != 2 || !inFunction || symbol) { + if (scopes.count() != 2 || !inFunction || curSymbol) { debugFile.setErrorString(tr("Unexpected function end token")); break; } debugFile.readULEB128(); debugFile.readULEB128(); - Line line = prevLine; - line.num += debugFile.readULEB128(); - line.addr += debugFile.readULEB128(); - prevLine = line; - m_sources.last().functionList.last().lines.append(line); + prevLine.num += debugFile.readULEB128(); + prevLine.addr += debugFile.readULEB128(); + sources.last().functionList.last().lines.append(prevLine); scopes.pop(); inFunction = false; break; } case DebugTokenKind::EndRec: { - if (scopes.count() <= 1 + inFunction || symbol) { + if (scopes.count() <= 1 + inFunction || curSymbol) { debugFile.setErrorString(tr("Unexpected record end token")); break; } @@ -324,54 +464,45 @@ void SourcesWidget::selectDebugFile() { debugFile.setErrorString(tr("Unexpected source file begin token")); break; } - QString sourcePath = dirPath; + paths << dir.path(); QStringList components = debugFile.readString().split('\\'); - for (QStringList::iterator i = components.begin(), e = components.end(); debugFile.success && i != e; ) { + for (QStringList::iterator i = components.begin(), e = components.end(); + debugFile.success && i != e; ) { QStringList component(*i++); - QDirIterator matches(sourcePath, component, (i != e ? QDir::Dirs : QDir::Files) | QDir::Readable); + QDirIterator matches(paths.last(), component, QDir::Readable | + (i != e ? QDir::Dirs : QDir::Files)); if (matches.hasNext()) { - sourcePath = matches.next(); + paths.last() = matches.next(); } else { - debugFile.setErrorString(tr("Unable to find source file \"%0\"").arg(components.join(QDir::separator()))); + debugFile.setErrorString(tr("Unable to find source file \"%0\""). + arg(components. + join(QDir::separator()))); } } - QFile sourceFile(sourcePath); - if (debugFile.success && sourceFile.open(QIODevice::ReadOnly)) { - QPlainTextEdit *sourceView = new QPlainTextEdit(sourceFile.readAll()); - sourceView->setObjectName(QStringLiteral("sourceView%0").arg(m_sources.count())); - sourceView->setReadOnly(true); - sourceView->setCenterOnScroll(true); - sourceView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(sourceView, &QWidget::customContextMenuRequested, this, &SourcesWidget::sourceContextMenu); - m_tabs->addTab(sourceView, QFileInfo(sourceFile).baseName()); - new CDebugHighlighter(this, sourceView); - m_sources.resize(m_sources.count() + 1); - m_sources.last().startAddr = prevLine.addr += debugFile.readULEB128(); + if (debugFile.success) { + sources.resize(sources.count() + 1); + sources.last().startAddr = prevLine.addr += debugFile.readULEB128(); prevLine.num = 0; - scopes.push(&m_sources.last()); - } else { - debugFile.setErrorString(tr("Error opening source file \"%0\"").arg(components.join(QDir::separator()))); + scopes.push(&sources.last()); } break; } case DebugTokenKind::Length: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected length token")); break; } - symbol->length = debugFile.readULEB128(); + curSymbol->length = debugFile.readULEB128(); break; } case DebugTokenKind::Line: { - if (scopes.count() != 2 || !inFunction || symbol) { + if (scopes.count() != 2 || !inFunction || curSymbol) { debugFile.setErrorString(tr("Unexpected line token")); break; } - Line line = prevLine; - line.num += debugFile.readULEB128(); - line.addr += debugFile.readULEB128(); - prevLine = line; - m_sources.last().functionList.last().lines.append(line); + prevLine.num += debugFile.readULEB128(); + prevLine.addr += debugFile.readULEB128(); + sources.last().functionList.last().lines.append(prevLine); break; } case DebugTokenKind::Strings: { @@ -380,140 +511,204 @@ void SourcesWidget::selectDebugFile() { break; } quint32 count = debugFile.readULEB128(); - m_stringList.reserve(count); - m_stringMap.reserve(count); + stringList.reserve(count); + stringMap.reserve(count); for (quint32 index = 0; debugFile.success && index != count; ++index) { QString string = debugFile.readString(); if (string.isEmpty()) { debugFile.setErrorString(tr("Invalid string in string table")); } else { - m_stringList << string; - m_stringMap[string] = index; + stringList << string; + stringMap[string] = index; } } break; } case DebugTokenKind::Tag: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected tag token")); break; } - symbol->tag = debugFile.readULEB128(); + curSymbol->tag = debugFile.readULEB128(); break; } case DebugTokenKind::Type: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected value token")); break; } - symbol->type = debugFile.readSLEB128(); + curSymbol->type = debugFile.readSLEB128(); break; } case DebugTokenKind::Value: { - if (!symbol) { + if (!curSymbol) { debugFile.setErrorString(tr("Unexpected value token")); break; } - symbol->value = debugFile.readSLEB128(); + curSymbol->value = debugFile.readSLEB128(); break; } default: debugFile.setErrorString(tr("Unknown debug token kind")); } } - if (m_sources.isEmpty()) { + if (sources.isEmpty()) { debugFile.setErrorString(tr("No source files")); } if (!eof) { debugFile.setErrorString(tr("Missing EOF token")); } - if (m_stringList.value(lang - 1) != QStringLiteral("C")) { - debugFile.setErrorString(tr("Unknown source language \"%0\"").arg(m_stringList.value(lang - 1))); + if (stringList.value(lang - 1) != QStringLiteral("C")) { + debugFile.setErrorString(tr("Unknown source language \"%0\""). + arg(stringList.value(lang - 1))); } if (debugFile.success) { - qDebug() << m_sources; - qDebug() << m_stringList; - qDebug() << m_stringMap; - dumpObjectTree(); + const int sourceCount = sources.count(); + for (int i = 0; i != m_tabs->count(); i++) { + m_tabs->widget(i)->deleteLater(); + } + m_tabs->clear(); + m_sources.clear(); + m_stringList.clear(); + m_stringMap.clear(); + for (int i = 0; i != sourceCount; i++) { + QFile sourceFile(paths.at(i)); + if (sourceFile.open(QIODevice::ReadOnly)) { + QPlainTextEdit *sourceView = new QPlainTextEdit(sourceFile.readAll()); + sourceView->setObjectName(QStringLiteral("sourceView%0").arg(i)); + sourceView->setReadOnly(true); + sourceView->setCenterOnScroll(true); + sourceView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(sourceView, &QWidget::customContextMenuRequested, + this, &SourcesWidget::sourceContextMenu); + m_tabs->addTab(sourceView, QFileInfo(sourceFile).baseName()); + new CDebugHighlighter(this, sourceView); + } + } + m_sources = std::move(sources); + m_stringList = std::move(stringList); + m_stringMap = std::move(stringMap); + m_globalModel->init(paths); + m_stackModel->init(); } else { - clear(); - QMessageBox dialog(QMessageBox::Critical, findParent(this)->MSG_ERROR, tr("Error loading debug file:"), QMessageBox::Ok, this); + QMessageBox dialog(QMessageBox::Critical, + findParent(this)->MSG_ERROR, + tr("Error loading debug file:"), QMessageBox::Ok, this); dialog.setInformativeText(debugFile.errorString()); dialog.exec(); } } -void SourcesWidget::update(quint32 addr, bool pc) { - qDebug() << addr; - const QVector &sources = m_sources; +bool SourcesWidget::lookupAddr(quint32 addr, int *sourceIndex, int *functionIndex, + int *lineIndex) const { + if (!sourceIndex && !functionIndex && !lineIndex) { + return true; + } const SourceBase sourceKey{addr, addr}; - auto source = std::upper_bound(sources.begin(), sources.end(), sourceKey, + auto source = std::upper_bound(m_sources.begin(), m_sources.end(), sourceKey, [](const SourceBase &first, const SourceBase &second) { return first.endAddr < second.startAddr; }); - if (source == sources.begin() || + if (source == m_sources.begin() || addr < (--source)->startAddr || addr >= source->endAddr) { - return; + return false; + } + if (sourceIndex) { + *sourceIndex = std::distance(m_sources.begin(), source); + if (!functionIndex && !lineIndex) { + return true; + } } - qDebug() << *source; - auto sourceView = m_tabs->findChild(QStringLiteral("sourceView%0").arg(std::distance(sources.begin(), source))); - qDebug() << sourceView; - const QVector &functions = source->functionList; const Line lineKey{0, addr}; const FunctionBase functionKey{{lineKey}}; - auto function = std::upper_bound(functions.begin(), functions.end(), - functionKey, + auto function = std::upper_bound(source->functionList.begin(), + source->functionList.end(), functionKey, [](const FunctionBase &first, const FunctionBase &second) { return first.lines.last().addr < second.lines.first().addr; }); - if (function == functions.begin()) { - return; + if (function == source->functionList.begin() || + addr < (--function)->lines.first().addr || + addr >= function->lines.last().addr) { + return false; } - const QVector &lines = (--function)->lines; - qDebug() << *function; - if (addr < lines.first().addr || addr >= lines.last().addr) { - return; + if (functionIndex) { + *functionIndex = std::distance(source->functionList.begin(), function); + if (!lineIndex) { + return true; + } } auto line = - std::prev(std::upper_bound(std::next(lines.begin()), std::prev(lines.end()), - lineKey, + std::prev(std::upper_bound(std::next(function->lines.begin()), + std::prev(function->lines.end()), lineKey, [](const Line &first, const Line &second) { return first.addr < second.addr; })); - qDebug() << *line; - QTextBlock block = sourceView->document()->findBlockByLineNumber(line->num - 1); - if (!block.isValid()) { - return; - } - qDebug() << block.text(); - if (pc) { - m_tabs->setCurrentWidget(sourceView); - QTextCursor cursor(block); - cursor.select(QTextCursor::LineUnderCursor); - sourceView->setTextCursor(cursor); - } + *lineIndex = std::distance(function->lines.begin(), line); + return true; } -void SourcesWidget::updateAll() { - for (QSyntaxHighlighter *highlighter : m_tabs->findChildren()) { +void SourcesWidget::updateFormats() { + for (QSyntaxHighlighter *highlighter : + m_tabs->findChildren()) { highlighter->rehighlight(); } } void SourcesWidget::updatePC(quint32 pc) { - update(pc, true); + m_globalModel->update(); + m_stackModel->update(); + for (auto &sourceView : m_tabs->findChildren()) { + sourceView->setExtraSelections({}); + } + int sourceIndex, functionIndex, lineIndex; + if (!lookupAddr(pc, &sourceIndex, &functionIndex, &lineIndex)) { + return; + } + QPlainTextEdit *sourceView = m_tabs->findChild(QStringLiteral("sourceView%0").arg(sourceIndex)); + QTextBlock block = sourceView->document()->findBlockByNumber(m_sources.at(sourceIndex).functionList.at(functionIndex).lines.at(lineIndex).num - 1); + if (!block.isValid()) { + return; + } + QTextEdit::ExtraSelection pcSelection; + pcSelection.cursor = QTextCursor(block); + if (pcSelection.cursor.movePosition(QTextCursor::EndOfWord)) { + pcSelection.cursor.movePosition(QTextCursor::StartOfBlock); + } else { + pcSelection.cursor.movePosition(QTextCursor::NextWord); + } + pcSelection.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + pcSelection.cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); + pcSelection.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + pcSelection.format.setBackground(QColor::fromRgb(0xFFFF99)); + sourceView->setExtraSelections({pcSelection}); + m_tabs->setCurrentWidget(sourceView); } -void SourcesWidget::updateAddr(quint32 addr) { - update(addr, false); +void SourcesWidget::updateAddr(quint32 addr, unsigned type, bool state) { + (void)type; // TODO: implement + int sourceIndex, functionIndex, lineIndex; + if (!lookupAddr(addr, &sourceIndex, &functionIndex, &lineIndex)) { + return; + } + auto sourceView = m_tabs->findChild(QStringLiteral("sourceView%0").arg(sourceIndex)); + QTextBlock block = sourceView->document()->findBlockByNumber(m_sources.at(sourceIndex).functionList.at(functionIndex).lines.at(lineIndex).num - 1); + if (!block.isValid()) { + return; + } + // TODO: This is obviously incorrect, but who cares for now. + QTextBlockFormat format; + if (state) { + format.setBackground(QColor::fromRgb(0xFF6050)); + } + QTextCursor(block).setBlockFormat(format); } void SourcesWidget::sourceContextMenu(const QPoint &pos) { QPlainTextEdit *sourceView = static_cast(m_tabs->currentWidget()); const QVector &functions = m_sources.at(sourceView->objectName().midRef(QStringLiteral("sourceView").count()).toInt()).functionList; - quint32 num = sourceView->cursorForPosition(pos).block().firstLineNumber() + 1; + quint32 num = sourceView->cursorForPosition(pos).block().blockNumber() + 1; const Line lineKey{num, 0}; const FunctionBase functionKey{{lineKey}}; auto function = std::upper_bound(functions.begin(), functions.end(), @@ -527,8 +722,7 @@ void SourcesWidget::sourceContextMenu(const QPoint &pos) { return; } const QVector &lines = (--function)->lines; - qDebug() << *function; - if (lineKey.num < lines.first().num || lineKey.num >= lines.last().num) { + if (lineKey.num < lines.first().num || lineKey.num > lines.last().num) { return; } auto line = @@ -537,24 +731,721 @@ void SourcesWidget::sourceContextMenu(const QPoint &pos) { [](const Line &first, const Line &second) { return first.num < second.num; })); - qDebug() << *line; QMenu menu; - QAction toggleBreak(tr("Toggle Breakpoint")); - menu.addAction(&toggleBreak); + QAction runUntilAction(tr("Run Until")); + menu.addAction(&runUntilAction); + menu.addSeparator(); + QAction toggleBreakAction(tr("Toggle Breakpoint")); + menu.addAction(&toggleBreakAction); QAction *action = menu.exec(sourceView->mapToGlobal(pos)); - if (action == &toggleBreak) { + if (action == &runUntilAction) { + emit runUntilTriggered(line->addr); + } else if (action == &toggleBreakAction) { emit breakToggled(line->addr); } } void SourcesWidget::setSourceFont(const QFont &font) { - for (auto *format : { &m_defaultFormat, &m_operatorFormat, &m_literalFormat, &m_escapeFormat, - &m_preprocessorFormat, &m_commentFormat, &m_keywordFormat, &m_identifierFormat, &m_errorFormat }) { - QFont::Weight weight = static_cast(format->fontWeight()); + for (auto format : { &m_defaultFormat, &m_operatorFormat, &m_literalFormat, &m_escapeFormat, + &m_preprocessorFormat, &m_commentFormat, &m_keywordFormat, &m_identifierFormat, &m_errorFormat }) { + QFont::Weight weight = QFont::Weight(format->fontWeight()); format->setFont(font); format->setFontWeight(weight); } - updateAll(); + updateFormats(); + for (auto child : { m_global, m_stack }) { + child->setFont(font); + } +} + +quint32 SourcesWidget::VariableModel::sizeOfSymbol(const Symbol &symbol, + Context context) { + quint32 size = 1, type = symbol.type; + if (type & 0xE0000000u) { + type |= 0xFFFFFFE0u; + } + if ((type & 0x1C) == 0xC) { + type -= 10; + } + for (int dim = 0; ; ++dim) { + switch (type >> 5 & 7) { + case 0: + switch (type) { + case 1: + case 2: + case 3: + case 4: + case 5: + return size * (type - 1); + case 6: + return size * 4; + case 8: + for (auto scope : { context.local, context.global }) { + auto record = scope->recordMap.find(symbol.tag); + if (record != scope->recordMap.end()) { + return size * scope->recordList.at(*record).size; + } + } + [[gnu::fallthrough]]; + default: + return 0; + } + case 1: + case 6: + return size * 3; + case 2: + default: + return 0; + case 3: + size *= symbol.dims.value(dim); + break; + } + type = (type >> 3 & ~0x1F) | (type & 0x1F); + } + return 0; +} +QString SourcesWidget::VariableModel::symbolTypeToString(const QString &name, + const Symbol &symbol, + Context context) const { + quint32 type = symbol.type; + QString prefix, tag = "struct ", base, suffix = name; + if (type & 0xE0000000u) { + type |= 0xFFFFFFE0u; + } + if ((type & 0x1C) == 0xC) { + base = "unsigned "; + type -= 10; + } + switch (type & 0x1F) { + case 1: base = "void"; break; + case 2: base += "char"; break; + case 3: base += "short"; break; + case 4: base += "int"; break; + case 5: base += "long"; break; + case 6: base = "float"; break; + case 8: + for (auto scope : { context.local, context.global }) { + auto record = scope->recordMap.find(symbol.tag); + if (record == scope->recordMap.end()) { + continue; + } + bool first = true; + for (auto &symbol : scope->recordList.at(*record).symbolList) { + switch (symbol.kind) { + case SymbolKind::BitField: + if (!first && symbol.length) { + if (!symbol.value) { + [[gnu::fallthrough]]; + case SymbolKind::UnionField: + tag = "union "; + } + [[gnu::fallthrough]]; + case SymbolKind::StructField: + break; + } + if (symbol.length) { + first = false; + } + [[gnu::fallthrough]]; + default: + continue; + } + break; + } + break; + } + base = tag + stringList().value(symbol.tag - 1); + break; + default: base = ""; break; + } + base += ' '; + int dim = 0; + for (type >>= 5; ; type >>= 3) { + switch (type & 7) { + default: + base += ""; + [[gnu::fallthrough]]; + case 0: + return prefix + base + suffix; + case 1: + suffix = '*' + prefix + suffix; + prefix = ""; + break; + case 2: + if (suffix.startsWith('*')) { + suffix = '(' + prefix + suffix + ')'; + prefix = ""; + } + suffix += "(...)"; + break; + case 3: + if (suffix.startsWith('*')) { + suffix = '(' + prefix + suffix + ')'; + prefix = ""; + } + suffix += '[' + QString::number(symbol.dims.value(dim++)) + ']'; + break; + case 6: + suffix = '*' + prefix + suffix; + prefix = "const "; + break; + } + } +} +QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol) { + qint32 addr = symbol.value; + bool isFloat = false, isSigned = true; + quint32 type = symbol.type; + if (type & 0xE0000000u) { + return {}; + } + switch (type >> 5 & 7) { + case 0: + if (type == 6) { + isFloat = true; + type -= 1; + } else if ((type & ~3) == 0xC) { + isSigned = false; + type -= 10; + } + if (type >= 2 && type <= 5) { + union { + quint32 u; + qint32 s; + float f; + } value = { 0 }; + for (unsigned offset = 0; offset != type - 1; ++offset) { + value.u |= mem_peek_byte(addr++) << 8*offset; + } + if (isFloat) { + return QString::number(value.f); + } + if (isSigned) { + if (type == 2) { + QString valueStr; + switch (value.u) { + case '\'': valueStr = "'\\''"; break; + case '\"': valueStr = "'\\\"'"; break; + case '\\': valueStr = "'\\\\'"; break; + case '\a': valueStr = "'\\a'"; break; + case '\b': valueStr = "'\\b'"; break; + case '\e': valueStr = "'\\e'"; break; + case '\f': valueStr = "'\\f'"; break; + case '\n': valueStr = "'\\n'"; break; + case '\r': valueStr = "'\\r'"; break; + case '\t': valueStr = "'\\t'"; break; + case '\v': valueStr = "'\\v'"; break; + default: + valueStr += '\''; + if (value.u >= ' ' && value.u <= '~') { + valueStr += char(value.u); + } else { + valueStr += '\\' + QString::number(value.u, 8) + .rightJustified(3, '0'); + } + valueStr += '\''; + break; + } + return valueStr; + } + int extend = 32 - 8*(type - 1); + return QString::number(value.s << extend >> extend); + } + return QString::number(value.u); + } + [[gnu::fallthrough]]; + default: + return {}; + case 1: + case 6: + if (quint32 value = mem_peek_long(addr)) { + return "0x" + QString::number(value, 16).rightJustified(6, '0'); + } + return "NULL"; + } +} + +int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, + int childIndex, const Symbol &symbol, + Context context, const QString &name, + qint32 base) { + int id = m_freeVariable; + if (id == -1) { + id = m_variables.count(); + m_variables.resize(id + 1); + } else { + m_freeVariable = m_variables[id].parent; + } + auto &variable = m_variables[id]; + variable.parent = parent; + variable.parentIndex = parentIndex; + variable.childIndex = childIndex; + variable.symbol = symbol; + variable.symbol.value += base; + variable.context = context; + variable.data[0] = name; + QString valueStr = symbolValueToString(variable.symbol); + if (!valueStr.isEmpty()) { + variable.data[1] = valueStr; + } + variable.flags = VariableFlag::None; + return id; +} +void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, + const Symbol &symbol, + const QString &name, qint32 base) { + Q_ASSERT(parent.isValid() && parent.model() == this); + auto &variable = m_variables.at(parent.internalId()); + int child = createVariable(parent.internalId(), parent.row(), variable.children. + count(), symbol, variable.context, name, base); + m_variables[parent.internalId()].children << child; +} +void SourcesWidget::VariableModel::deleteVariable(int id) { + for (int child : m_variables.at(id).children) { + deleteVariable(child); + } + m_variables[id].parent = m_freeVariable; + m_variables[id].children.clear(); + m_variables[id].flags |= VariableFlag::Deleted; + m_freeVariable = id; +} +void SourcesWidget::VariableModel::removeTopLevels(int first, int last) { + if (first > last) { + return; + } + beginRemoveRows(QModelIndex(), first, last); + for (int topLevel = first; topLevel <= last; ++topLevel) { + for (int id : m_topLevelChildren.at(topLevel)) { + deleteVariable(id); + } + } + m_topLevelData[0].erase(m_topLevelData[0].begin() + first, + m_topLevelData[0].begin() + last + 1); + m_topLevelData[1].erase(m_topLevelData[1].begin() + first, + m_topLevelData[1].begin() + last + 1); + m_topLevelChildren.erase(m_topLevelChildren.begin() + first, + m_topLevelChildren.begin() + last + 1); + endRemoveRows(); +} +void SourcesWidget::VariableModel::update() { + for (int id = 0; id < m_variables.count(); id++) { + auto &variable = m_variables[id]; + if (variable.flags.testFlag(VariableFlag::Deleted)) { + continue; + } + QString valueStr = symbolValueToString(variable.symbol); + bool valueChanged = variable.data[1] != valueStr; + int firstChangedColumn = 1; + QVector changedRoles; + changedRoles.reserve(2); + if (valueChanged) { + variable.data[1] = valueStr; + changedRoles << Qt::DisplayRole; + } + if (valueChanged != variable.flags.testFlag(VariableFlag::Changed)) { + variable.flags.setFlag(VariableFlag::Changed, valueChanged); + firstChangedColumn = 0; + changedRoles << Qt::BackgroundRole; + } + if (!changedRoles.isEmpty()) { + emit dataChanged(createIndex(variable.childIndex, firstChangedColumn, id), + createIndex(variable.childIndex, 1, id), changedRoles); + } + } +} +QModelIndex SourcesWidget::VariableModel::parent(const QModelIndex &child) const { + Q_ASSERT(!child.isValid() || child.model() == this); + if (!child.isValid() || child.internalId() == s_topLevelId) { + return QModelIndex(); + } + auto &variable = m_variables.at(child.internalId()); + Q_ASSERT(variable.parent >= s_topLevelParent && + variable.parent < m_variables.count()); + int index = variable.parentIndex; + if (index < 0) { + Q_ASSERT(variable.parent == s_topLevelParent); + index += m_topLevelChildren.count(); + } + return createIndex(index, 0, variable.parent); +} +QModelIndex SourcesWidget::VariableModel::index(int row, int column, + const QModelIndex &parent) const { + Q_ASSERT(!parent.isValid() || parent.model() == this); + if (row < 0 || row >= rowCount(parent) || + column < 0 || column >= columnCount(parent)) { + return {}; + } + if (!parent.isValid()) { + return createIndex(row, column, s_topLevelId); + } + if (parent.column()) { + return {}; + } + if (parent.internalId() == s_topLevelId) { + if (parent.row() < 0 || parent.row() >= m_topLevelChildren.count()) { + return {}; + } + return createIndex(row, column, m_topLevelChildren.at(parent.row()).at(row)); + } + if (parent.internalId() >= unsigned(m_variables.count())) { + return {}; + } + return createIndex(row, column, + m_variables.at(parent.internalId()).children.at(row)); +} +int SourcesWidget::VariableModel::childCount(const QModelIndex &parent) const { + Q_ASSERT(!parent.isValid() || parent.model() == this); + if (parent.column() || parent.internalId() == s_topLevelId) { + return rowCount(parent); + } + auto &variable = m_variables.at(parent.internalId()); + switch (variable.symbol.type >> 5 & 7) { + case 1: + case 6: + return variable.data[1] != "NULL"; + case 3: + return variable.symbol.dims.value(0); + case 0: + if (variable.symbol.type == 8) { + for (auto scope : { variable.context.local, + variable.context.global }) { + auto record = scope->recordMap.find(variable.symbol.tag); + if (record != scope->recordMap.end()) { + return scope->recordList.at(*record).symbolList.count(); + } + } + } + [[gnu::fallthrough]]; + default: + return 0; + } +} +bool SourcesWidget::VariableModel::hasChildren(const QModelIndex &parent) const { + return childCount(parent); +} +int SourcesWidget::VariableModel::rowCount(const QModelIndex &parent) const { + Q_ASSERT(!parent.isValid() || parent.model() == this); + if (!parent.isValid()) { + return m_topLevelChildren.count(); + } + if (parent.column()) { + return {}; + } + if (parent.internalId() == s_topLevelId) { + return m_topLevelChildren.value(parent.row()).count(); + } + if (parent.internalId() >= unsigned(m_variables.count())) { + return {}; + } + return m_variables.at(parent.internalId()).children.count(); +} +int SourcesWidget::VariableModel::columnCount(const QModelIndex &parent) const { + Q_ASSERT(!parent.isValid() || parent.model() == this); + return 2; +} +QVariant SourcesWidget::VariableModel::data(const QModelIndex &index, int role) const { + Q_ASSERT(!index.isValid() || index.model() == this); + if (!index.isValid() || index.column() < 0 || index.column() >= columnCount(index)) { + return {}; + } + switch (role) { + case Qt::DisplayRole: + if (index.internalId() == s_topLevelId) { + return m_topLevelData[index.column()].at(index.row()); + } + return m_variables.at(index.internalId()).data[index.column()]; + case Qt::TextAlignmentRole: + return int((index.column() ? Qt::AlignLeft : Qt::AlignRight) | + Qt::AlignVCenter); + case Qt::BackgroundRole: + if (index.internalId() != s_topLevelId && m_variables. + at(index.internalId()).flags.testFlag(VariableFlag::Changed)) { + return QColor::fromRgb(0xFFFF99); + } + break; + } + return {}; +} +Qt::ItemFlags SourcesWidget::VariableModel::flags(const QModelIndex &index) const { + Q_ASSERT(!index.isValid() || index.model() == this); + if (!index.isValid()) { + return Qt::NoItemFlags; + } + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (index.column()) { + flags |= Qt::ItemNeverHasChildren; + } else if (index.isValid() && index.internalId() != s_topLevelId) { + qint32 type = m_variables.at(index.internalId()).symbol.type; + if (!(type & ~0x1F) && type != 8) { + flags |= Qt::ItemNeverHasChildren; + } + } + return flags; +} +bool SourcesWidget::VariableModel::canFetchMore(const QModelIndex &parent) const { + return rowCount(parent) < childCount(parent); +} +void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { + Q_ASSERT(!parent.isValid() || parent.model() == this); + if (!parent.isValid()) { + return; + } + auto &variable = m_variables[parent.internalId()]; + auto symbol = variable.symbol; + if (symbol.type & 0xE0000000u) { + symbol.type |= 0xFFFFFFE0u; + } + QString name = variable.data[0]; + if (variable.parent == s_topLevelParent) { + name = stringList().value(variable.symbol.name - 1); + } + if (symbol.type & ~0x1F) { + quint8 mod = symbol.type >> 5 & 7; + symbol.type = (symbol.type >> 3 & ~0x1F) | (symbol.type & 0x1F); + if (mod == 1 || mod == 6) { + if ((symbol.value = mem_peek_long(symbol.value))) { + createVariable(parent, symbol, '*' + name); + } + } else if (mod == 3 && !symbol.dims.isEmpty()) { + quint32 elements = symbol.dims.takeFirst(), + elementSize = sizeOfSymbol(symbol, variable.context); + if (name.startsWith('*')) { + name = '(' + name + ')'; + } + for (quint32 index = 0; index != elements; ++index) { + createVariable(parent, symbol, + name + '[' + QString::number(index) + ']'); + symbol.value += elementSize; + } + } + } else if (symbol.type == 8) { + for (auto scope : { variable.context.local, variable.context.global }) { + auto record = scope->recordMap.find(variable.symbol.tag); + if (record == scope->recordMap.end()) { + continue; + } + if (name.startsWith('*')) { + name = name.midRef(1) + "->"; + } else { + name = name + '.'; + } + for (auto member : scope->recordList.at(*record).symbolList) { + createVariable(parent, member, + name + stringList().value(member.name - 1), symbol.value); + } + break; + } + } +} + +int SourcesWidget::GlobalModel::commonPrefixLength(const QStringList &paths) { + int maxPrefixLength = 0; + if (!paths.count()) { + return maxPrefixLength; + } + forever { + int prefixLength = paths.first().indexOf('/', maxPrefixLength) + 1; + if (!prefixLength) { + return maxPrefixLength; + } + QStringRef prefix = paths.first().leftRef(prefixLength); + for (int i = 1; i < paths.count(); i++) { + if (!paths.at(i).startsWith(prefix)) { + return maxPrefixLength; + } + } + maxPrefixLength = prefixLength; + } +} +void SourcesWidget::GlobalModel::init(const QStringList &paths) { + beginResetModel(); + m_freeVariable = -1; + m_variables.clear(); + m_topLevelData[0].clear(); + m_topLevelData[1].clear(); + m_topLevelChildren.clear(); + int prefixLength = commonPrefixLength(paths); + for (int parent = 0; parent < sources().count(); ++parent) { + QStringRef path = paths.at(parent).midRef(prefixLength); + int lastSeparator = path.lastIndexOf('/') + 1; + m_topLevelData[0] << path.left(lastSeparator).toString(); + m_topLevelData[1] << path.mid(lastSeparator).toString(); + m_topLevelChildren.append(QList()); + + auto &source = sources().at(parent); + auto &symbols = source.symbolList; + Context context = { &source, &source }; + for (int child = 0; child < symbols.count(); ++child) { + auto &symbol = symbols.at(child); + if (symbol.kind == SymbolKind::Function) { + continue; + } + QString name = stringList().value(symbol.name - 1); + m_topLevelChildren.last() << createVariable(s_topLevelParent, parent, child, + symbol, context, + symbolTypeToString(name, symbol, + context)); + } + } + endResetModel(); +} + +void SourcesWidget::StackModel::update() { + QVector stack; + quint32 ix = cpu.registers.IX, pc = cpu.registers.PC; + bool top = true; + forever { + int sourceIndex, functionIndex; + if (!lookupAddr(pc, &sourceIndex, &functionIndex, nullptr)) { + break; + } + const Source &source = sources().at(sourceIndex); + const Function &function = source.functionList.at(functionIndex); + static const quint8 prolog[2 + 5 + 2] = {0xDD,0xE5, 0xDD,0x21,0,0,0, 0xDD,0x39}, + epilog[2 + 2 + 1] = {0xDD,0xF9, 0xDD,0xE1, 0xC9}; + static const quint32 stackTop = 0xD1A87E, stackBot = 0xD1987E; + quint32 prologStartAddr = function.lines.first().addr, + prologEndAddr = function.lines.at(1).addr, + epilogStartAddr = function.lines.last().addr - sizeof(epilog); + void *prologPtr = phys_mem_ptr(prologStartAddr, sizeof(prolog)), + *epilogPtr = phys_mem_ptr(epilogStartAddr, sizeof(epilog)); + if (!prologPtr || memcmp(prologPtr, prolog, sizeof(prolog)) || + !epilogPtr || memcmp(epilogPtr, epilog, sizeof(epilog))) { + break; + } + if (pc >= prologEndAddr && pc <= epilogStartAddr) { + stack << StackEntry{ ix, pc, { mem_peek_long(ix), mem_peek_long(ix + 3) }, + &source, &function }; + } + quint32 ixAddr, pcAddr; + if (pc < prologStartAddr + sizeof(prolog)) { + if (!top) { + break; + } + if (pc < prologStartAddr + 2) { // PUSH IX + ixAddr = ~0u; + pcAddr = cpu.registers.SPL; + } else { // LD IX,0 \ ADD IX,SP + ixAddr = cpu.registers.SPL; + pcAddr = cpu.registers.SPL + 3; + } + } else if (pc >= epilogStartAddr + 2 + 2) { // LD SP,IX \ POP IX + if (!top) { + break; + } + ixAddr = ~0u; + pcAddr = cpu.registers.SPL; + } else { + ixAddr = ix; + pcAddr = ix + 3; + } + if (ixAddr > stackBot + 3 && ixAddr <= stackTop - 3) { + ix = mem_peek_long(ixAddr); + } else if (ixAddr != ~0u) { + break; + } + if (pcAddr > stackBot + 3 && pcAddr <= stackTop - 3) { + pc = mem_peek_long(pcAddr); + } else { + break; + } + top = false; + } + if (stack.isEmpty()) { + ix = cpu.registers.SPL; + } else { + ix = stack.last().ix; + } + int aliveIndex = m_stack.count(); + while (aliveIndex--) { + auto &entry = m_stack.at(aliveIndex); + if (entry.ix <= ix || + mem_peek_long(entry.ix) != entry.cookie[0] || + mem_peek_long(entry.ix + 3) != entry.cookie[1]) { + break; + } + } + int commonSuffix = m_stack.count() - aliveIndex++; + stack << m_stack.mid(aliveIndex); + for (; commonSuffix <= stack.count() && commonSuffix <= m_stack.count() && + stack.at(stack.count() - commonSuffix).function == + m_stack.at(m_stack.count() - commonSuffix).function; ++commonSuffix); + removeTopLevels(0, m_stack.count() - commonSuffix); + VariableModel::update(); + if (commonSuffix <= stack.count()) { + beginInsertRows(QModelIndex(), 0, stack.count() - commonSuffix); + for (int parent = stack.count() - commonSuffix; parent >= 0; --parent) { + auto &entry = stack.at(parent); + m_topLevelData[0].prepend(stringList().value(entry.function->name - 1)); + m_topLevelData[1].prepend("()"); + m_topLevelChildren.prepend({}); + Context context = { entry.function, entry.source }; + auto &symbols = entry.function->symbolList; + for (int child = 0; child < symbols.count(); ++child) { + auto &symbol = symbols.at(child); + if (symbol.kind != SymbolKind::StackSlot) { + continue; + } + QString name = stringList().value(symbol.name - 1); + m_topLevelChildren.first() << createVariable(s_topLevelParent, + parent - stack.count(), + child, symbol, context, + symbolTypeToString(name, + symbol, + context), + entry.ix); + } + } + endInsertRows(); + } + int firstParentChanged = -1; + const QVector changedRoles{ Qt::DisplayRole }; + for (int parent = 0; parent < stack.count(); ++parent) { + auto &symbols = stack.at(parent).function->symbolList; + QStringList argumentList; + for (int child = 0; child < symbols.count(); ++child) { + auto &symbol = symbols.at(child); + if (symbol.kind != SymbolKind::StackSlot) { + continue; + } + if (symbol.value < 0) { + break; + } + argumentList << + m_variables.at(m_topLevelChildren.at(parent).at(child)).data[1]; + if (argumentList.last().isEmpty()) { + argumentList.last() = '?'; + } + } + QString arguments = '(' + argumentList.join(", ") + ')'; + bool argumentsChanged = m_topLevelData[1].at(parent) != arguments; + m_topLevelData[1][parent] = arguments; + if (argumentsChanged ^ (firstParentChanged == -1)) { + if (argumentsChanged) { + emit dataChanged(createIndex(firstParentChanged, 1, s_topLevelId), + createIndex(parent - 1, 1, s_topLevelId), changedRoles); + firstParentChanged = -1; + } else { + firstParentChanged = parent; + } + } + } + if (firstParentChanged != -1) { + emit dataChanged(createIndex(firstParentChanged, 1, s_topLevelId), + createIndex(stack.count() - 1, 1, s_topLevelId), changedRoles); + } + m_stack = stack; +} +void SourcesWidget::StackModel::init() { + beginResetModel(); + m_freeVariable = -1; + m_variables.clear(); + m_topLevelData[0].clear(); + m_topLevelData[1].clear(); + m_topLevelChildren.clear(); + m_stack.clear(); + endResetModel(); + update(); } diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h index 2026af65c..d963a90c1 100644 --- a/gui/qt/debugger/sourceswidget.h +++ b/gui/qt/debugger/sourceswidget.h @@ -1,15 +1,24 @@ #ifndef SOURCESWIDGET_H #define SOURCESWIDGET_H +#include "../../core/cpu.h" + class CDebugHighlighter; -#include -#include -#include -#include +#include +#include +#include +#include +#include #include +QT_BEGIN_NAMESPACE +class QFont; class QPushButton; class QTabWidget; +class QTreeView; +class QTreeWidget; +class QTreeWidgetItem; +QT_END_NAMESPACE class SourcesWidget : public QWidget { Q_OBJECT @@ -20,48 +29,75 @@ class SourcesWidget : public QWidget { void setSourceFont(const QFont &font); signals: + void runUntilTriggered(quint32 addr); void breakToggled(quint32 addr, bool gui = true); public slots: void selectDebugFile(); void updatePC(quint32 pc); - void updateAddr(quint32 addr); + void updateAddr(quint32 addr, unsigned type, bool state); private slots: void sourceContextMenu(const QPoint &pos); private: - void clear(); - void update(quint32 addr, bool pc); - void updateAll(); + void updateFormats(); friend CDebugHighlighter; - QPushButton *m_button; + class VariableModel; + class GlobalModel; + class StackModel; + QTabWidget *m_tabs; + //QTextCursor m_pcCursor; + //QBlockFormat m_pcFormat; + QTreeView *m_global, *m_stack; + GlobalModel *m_globalModel; + StackModel *m_stackModel; QTextCharFormat m_defaultFormat, m_operatorFormat, m_literalFormat, m_escapeFormat, m_preprocessorFormat, m_commentFormat, m_keywordFormat, m_identifierFormat, m_errorFormat; + constexpr static quint32 s_unnamed = 0; struct Line { quint32 num, addr; }; + enum class SymbolKind : quint8 { + Unknown = 0, + Function = 2, + StructField = 8, + UnionField = 11, + BitField = 18, + StackSlot = 65, // can be Constant, Uninitialized or Variable + Constant = 69, + Uninitialized = 83, // can be Constant or Variable + Variable = 84, + }; struct Symbol { + quint32 name = s_unnamed, alias = s_unnamed; qint32 value = 0, type = 0; quint32 tag = 0; - quint8 kind = 0, length = 0; - QVector dims; + SymbolKind kind = SymbolKind::Unknown; + quint8 length = 0; + QList dims; }; struct Record; struct Scope { - QMultiHash symbols; - QMultiHash records; + QVector symbolList; + QVector recordList; + QMultiHash symbolMap; + QMultiHash recordMap; + }; + struct Context { + const Scope *local, *global; }; struct Record : Scope { - quint32 size = 0; + quint32 name, size; }; struct FunctionBase { QVector lines; }; struct Function : FunctionBase, Scope { + quint32 name; }; struct SourceBase { quint32 startAddr, endAddr; @@ -73,13 +109,18 @@ private slots: QVector m_sources; QStringList m_stringList; QHash m_stringMap; -#ifndef NDEBUG +#ifdef QT_DEBUG friend QDebug operator<<(QDebug debug, const Line &line); - friend QDebug operator<<(QDebug debug, const Symbol &line); - friend QDebug operator<<(QDebug debug, const Record &line); - friend QDebug operator<<(QDebug debug, const Function &line); - friend QDebug operator<<(QDebug debug, const Source &line); + friend QDebug operator<<(QDebug debug, const SymbolKind &kind); + friend QDebug operator<<(QDebug debug, const Context &context); + friend QDebug operator<<(QDebug debug, const Symbol &symbol); + friend QDebug operator<<(QDebug debug, const Record &record); + friend QDebug operator<<(QDebug debug, const Function &function); + friend QDebug operator<<(QDebug debug, const Source &sourc); #endif + + bool lookupAddr(quint32 addr, int *sourceIndex = nullptr, + int *functionIndex = nullptr, int *lineIndex = nullptr) const; }; #endif diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index 5954735e2..31b4733cf 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -195,6 +195,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U connect(ui->buttonCertID, &QPushButton::clicked, this, &MainWindow::setCalcId); connect(m_disasm, &DataWidget::gotoDisasmAddress, this, &MainWindow::gotoDisasmAddr); connect(m_disasm, &DataWidget::gotoMemoryAddress, this, &MainWindow::gotoMemAddr); + connect(ui->sources, &SourcesWidget::runUntilTriggered, this, &MainWindow::runUntil); connect(ui->sources, &SourcesWidget::breakToggled, this, &MainWindow::breakToggle); connect(this, &MainWindow::debugPointChanged, ui->sources, &SourcesWidget::updateAddr); @@ -2755,7 +2756,7 @@ void MainWindow::contextDisasm(const QPoint &posa) { backAct->setEnabled(m_disasmNavIndex > 0); fwdAct->setEnabled(m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size()); menu.addSeparator(); - QAction *runUntil = menu.addAction(ACTION_RUN_UNTIL); + QAction *runUntilStr = menu.addAction(ACTION_RUN_UNTIL); menu.addSeparator(); QAction *toggleBreak = menu.addAction(ACTION_TOGGLE_BREAK); QAction *toggleRead = menu.addAction(ACTION_TOGGLE_READ); @@ -2786,10 +2787,8 @@ void MainWindow::contextDisasm(const QPoint &posa) { } else if (item == toggleRw) { watchAddGuiRW(); memUpdate(); - } else if (item == runUntil) { - m_runUntilAddr = addr; - debugToggle(); - debugStep(DBG_RUN_UNTIL); + } else if (item == runUntilStr) { + runUntil(addr); } else if (item == gotoMem) { gotoMemAddr(addr); } diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index 7af6bc48e..ae1d78c4c 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -355,6 +355,7 @@ private slots: void stepOver(); void stepNext(); void stepOut(); + void runUntil(uint32_t addr); // os view void osUpdate(); From 46810f0af17c95380560c808b13c164507a278b0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 21 Sep 2018 14:32:25 -0400 Subject: [PATCH 06/26] Supported displaying bitfields. --- gui/qt/debugger/sourceswidget.cpp | 48 ++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 479d7c8a2..f612180ca 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -109,6 +109,7 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { int parent, parentIndex, childIndex; QList children; Symbol symbol; + qint32 base; Context context; QString data[2]; VariableFlags flags; @@ -125,7 +126,7 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { static quint32 sizeOfSymbol(const Symbol &symbol, Context context); QString symbolTypeToString(const QString &name, const Symbol &symbol, Context context) const; - static QString symbolValueToString(const Symbol &symbol); + static QString symbolValueToString(const Symbol &symbol, qint32 base = 0); int createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, const QString &name, qint32 base = 0); void createVariable(const QModelIndex &parent, const Symbol &symbol, const QString &name, qint32 base = 0); @@ -894,9 +895,10 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const QString &name, } } } -QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol) { - qint32 addr = symbol.value; - bool isFloat = false, isSigned = true; +QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, + qint32 base) { + qint32 addr = base + symbol.value; + bool isFloat = false, isBitField = false, isSigned = true; quint32 type = symbol.type; if (type & 0xE0000000u) { return {}; @@ -905,7 +907,12 @@ QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol) case 0: if (type == 6) { isFloat = true; - type -= 1; + type = 5; + } else if (type == 16) { + isBitField = true; + isSigned = false; // We have no way of knowing if it is signed :( + type = 4; + addr = base + 3*(symbol.value / 24); } else if ((type & ~3) == 0xC) { isSigned = false; type -= 10; @@ -922,6 +929,17 @@ QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol) if (isFloat) { return QString::number(value.f); } + if (isBitField) { + int shift = symbol.value % 24, extend = 32 - symbol.length; + if (shift < 0) { + shift += 24; + } + value.u <<= 8 + shift; + if (isSigned) { + return QString::number(value.s >> extend); + } + return QString::number(value.u >> unsigned(extend)); + } if (isSigned) { if (type == 2) { QString valueStr; @@ -983,13 +1001,10 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, variable.parentIndex = parentIndex; variable.childIndex = childIndex; variable.symbol = symbol; - variable.symbol.value += base; + variable.base = base; variable.context = context; variable.data[0] = name; - QString valueStr = symbolValueToString(variable.symbol); - if (!valueStr.isEmpty()) { - variable.data[1] = valueStr; - } + variable.data[1] = symbolValueToString(symbol, base); variable.flags = VariableFlag::None; return id; } @@ -1035,7 +1050,7 @@ void SourcesWidget::VariableModel::update() { if (variable.flags.testFlag(VariableFlag::Deleted)) { continue; } - QString valueStr = symbolValueToString(variable.symbol); + QString valueStr = symbolValueToString(variable.symbol, variable.base); bool valueChanged = variable.data[1] != valueStr; int firstChangedColumn = 1; QVector changedRoles; @@ -1194,6 +1209,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { } auto &variable = m_variables[parent.internalId()]; auto symbol = variable.symbol; + qint32 addr = variable.base + symbol.value; if (symbol.type & 0xE0000000u) { symbol.type |= 0xFFFFFFE0u; } @@ -1205,8 +1221,8 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { quint8 mod = symbol.type >> 5 & 7; symbol.type = (symbol.type >> 3 & ~0x1F) | (symbol.type & 0x1F); if (mod == 1 || mod == 6) { - if ((symbol.value = mem_peek_long(symbol.value))) { - createVariable(parent, symbol, '*' + name); + if ((addr = mem_peek_long(addr))) { + createVariable(parent, symbol, '*' + name, addr); } } else if (mod == 3 && !symbol.dims.isEmpty()) { quint32 elements = symbol.dims.takeFirst(), @@ -1216,8 +1232,8 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { } for (quint32 index = 0; index != elements; ++index) { createVariable(parent, symbol, - name + '[' + QString::number(index) + ']'); - symbol.value += elementSize; + name + '[' + QString::number(index) + ']', addr); + addr += elementSize; } } } else if (symbol.type == 8) { @@ -1233,7 +1249,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { } for (auto member : scope->recordList.at(*record).symbolList) { createVariable(parent, member, - name + stringList().value(member.name - 1), symbol.value); + name + stringList().value(member.name - 1), addr); } break; } From a2efccfa82cc07414300b495348cec71afdc9566 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 20 Feb 2019 15:33:43 -0500 Subject: [PATCH 07/26] Fix doubled addresses and general cleanup. --- gui/qt/debugger/sourceswidget.cpp | 186 +++++++++++++++++++----------- gui/qt/debugger/sourceswidget.h | 23 ++-- 2 files changed, 127 insertions(+), 82 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index f612180ca..f9aeca4a4 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -23,6 +23,58 @@ #include #include +struct SourcesWidget::Context { + const Function *local; + const Source *global; + + typedef Scope value_type; + typedef const Scope &reference; + typedef const Scope *pointer; + typedef qint8 difference_type; + typedef quint8 size_type; + struct const_iterator { + typedef Scope value_type; + typedef const Scope &reference; + typedef const Scope *pointer; + typedef qint8 difference_type; + typedef std::random_access_iterator_tag iterator_category; + + const_iterator(const Context *context = nullptr, difference_type i = 0) : context(context), i(i) {} + reference operator*() const { switch (i) { case 0: return *context->local; default: return *context->global; } } + pointer operator->() const { return &**this; } + reference operator[](difference_type n) { return *(*this + n); } + const_iterator &operator++() { ++i; return *this; } + const_iterator operator++(int) { const_iterator temp(*this); ++*this; return temp; } + const_iterator &operator--() { ++i; return *this; } + const_iterator operator--(int) { const_iterator temp(*this); ++*this; return temp; } + const_iterator &operator+=(difference_type n) { i += n; return *this; } + friend const_iterator operator+(const_iterator iter, difference_type n) { return iter += n; } + friend const_iterator operator+(difference_type n, const_iterator iter) { return iter += n; } + const_iterator &operator-=(difference_type n) { i -= n; return *this; } + friend const_iterator operator-(const_iterator iter, difference_type n) { return iter -= n; } + difference_type operator-(const_iterator iter) const { return i - iter.i; } + bool operator==(const_iterator iter) const { return context == iter.context && i == iter.i; } + bool operator!=(const_iterator iter) const { return !(*this == iter); } + bool operator<(const_iterator iter) const { return i < iter.i; } + bool operator>(const_iterator iter) const { return i > iter.i; } + bool operator<=(const_iterator iter) const { return i <= iter.i; } + bool operator>=(const_iterator iter) const { return i >= iter.i; } + + private: + const Context *context; + difference_type i; + }; + typedef const_iterator iterator; + + size_type size() const { return !!local + !!global; } + static size_type max_size() { return 2; } + const_iterator cbegin() const { return const_iterator(this, max_size() - size()); } + const_iterator cend() const { return const_iterator(this, max_size()); } + iterator begin() const { return cbegin(); } + iterator end() const { return cend(); } +}; + + #ifdef QT_DEBUG #include @@ -37,6 +89,9 @@ QDebug operator<<(QDebug debug, const SourcesWidget::SymbolKind &kind) { case SourcesWidget::SymbolKind::Function: debug << "SymbolKind::Function"; break; + case SourcesWidget::SymbolKind::StaticFunction: + debug << "SymbolKind::StaticFunction"; + break; case SourcesWidget::SymbolKind::StructField: debug << "SymbolKind::StructField"; break; @@ -46,6 +101,9 @@ QDebug operator<<(QDebug debug, const SourcesWidget::SymbolKind &kind) { case SourcesWidget::SymbolKind::BitField: debug << "SymbolKind::BitField"; break; + case SourcesWidget::SymbolKind::StackSlot: + debug << "SymbolKind::StackSlot"; + break; case SourcesWidget::SymbolKind::Constant: debug << "SymbolKind::Constant"; break; @@ -124,12 +182,10 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { lookupAddr(addr, sourceIndex, functionIndex, lineIndex); } static quint32 sizeOfSymbol(const Symbol &symbol, Context context); - QString symbolTypeToString(const QString &name, const Symbol &symbol, - Context context) const; - static QString symbolValueToString(const Symbol &symbol, qint32 base = 0); - int createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, const QString &name, qint32 base = 0); - void createVariable(const QModelIndex &parent, const Symbol &symbol, - const QString &name, qint32 base = 0); + QString symbolTypeToString(const Symbol &symbol, Context context) const; + QString symbolValueToString(const Symbol &symbol, qint32 base = 0) const; + int createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, qint32 base = 0, const QString &name = {}); + void createVariable(const QModelIndex &parent, const Symbol &symbol, qint32 base = 0, const QString &name = {}); void removeTopLevels(int first, int last); constexpr static quintptr s_topLevelId = ~quintptr(); constexpr static int s_topLevelParent = int(s_topLevelId); @@ -137,11 +193,11 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { virtual void update(); QModelIndex parent(const QModelIndex &child) const override; QModelIndex index(int row, int column, - const QModelIndex &parent = QModelIndex()) const override; - int childCount(const QModelIndex &parent = QModelIndex()) const; - bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; + const QModelIndex &parent = {}) const override; + int childCount(const QModelIndex &parent = {}) const; + bool hasChildren(const QModelIndex &parent = {}) const override; + int rowCount(const QModelIndex &parent = {}) const override; + int columnCount(const QModelIndex &parent = {}) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool canFetchMore(const QModelIndex &parent) const override; @@ -558,20 +614,16 @@ void SourcesWidget::selectDebugFile() { if (!eof) { debugFile.setErrorString(tr("Missing EOF token")); } - if (stringList.value(lang - 1) != QStringLiteral("C")) { + if (stringList.value(lang - 1) != "C") { debugFile.setErrorString(tr("Unknown source language \"%0\""). arg(stringList.value(lang - 1))); } if (debugFile.success) { - const int sourceCount = sources.count(); for (int i = 0; i != m_tabs->count(); i++) { m_tabs->widget(i)->deleteLater(); } m_tabs->clear(); - m_sources.clear(); - m_stringList.clear(); - m_stringMap.clear(); - for (int i = 0; i != sourceCount; i++) { + for (int i = 0; i != sources.count(); i++) { QFile sourceFile(paths.at(i)); if (sourceFile.open(QIODevice::ReadOnly)) { QPlainTextEdit *sourceView = new QPlainTextEdit(sourceFile.readAll()); @@ -749,20 +801,19 @@ void SourcesWidget::sourceContextMenu(const QPoint &pos) { } void SourcesWidget::setSourceFont(const QFont &font) { - for (auto format : { &m_defaultFormat, &m_operatorFormat, &m_literalFormat, &m_escapeFormat, - &m_preprocessorFormat, &m_commentFormat, &m_keywordFormat, &m_identifierFormat, &m_errorFormat }) { + for (auto *format : { &m_defaultFormat, &m_operatorFormat, &m_literalFormat, &m_escapeFormat, + &m_preprocessorFormat, &m_commentFormat, &m_keywordFormat, &m_identifierFormat, &m_errorFormat }) { QFont::Weight weight = QFont::Weight(format->fontWeight()); format->setFont(font); format->setFontWeight(weight); } updateFormats(); - for (auto child : { m_global, m_stack }) { + for (auto *child : { m_global, m_stack }) { child->setFont(font); } } -quint32 SourcesWidget::VariableModel::sizeOfSymbol(const Symbol &symbol, - Context context) { +quint32 SourcesWidget::VariableModel::sizeOfSymbol(const Symbol &symbol, Context context) { quint32 size = 1, type = symbol.type; if (type & 0xE0000000u) { type |= 0xFFFFFFE0u; @@ -783,10 +834,10 @@ quint32 SourcesWidget::VariableModel::sizeOfSymbol(const Symbol &symbol, case 6: return size * 4; case 8: - for (auto scope : { context.local, context.global }) { - auto record = scope->recordMap.find(symbol.tag); - if (record != scope->recordMap.end()) { - return size * scope->recordList.at(*record).size; + for (auto &scope : context) { + auto record = scope.recordMap.find(symbol.tag); + if (record != scope.recordMap.end()) { + return size * scope.recordList.at(*record).size; } } [[gnu::fallthrough]]; @@ -807,9 +858,8 @@ quint32 SourcesWidget::VariableModel::sizeOfSymbol(const Symbol &symbol, } return 0; } -QString SourcesWidget::VariableModel::symbolTypeToString(const QString &name, - const Symbol &symbol, - Context context) const { +QString SourcesWidget::VariableModel::symbolTypeToString(const Symbol &symbol, Context context) const { + QString name = stringList().value(symbol.name - 1); quint32 type = symbol.type; QString prefix, tag = "struct ", base, suffix = name; if (type & 0xE0000000u) { @@ -827,13 +877,13 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const QString &name, case 5: base += "long"; break; case 6: base = "float"; break; case 8: - for (auto scope : { context.local, context.global }) { - auto record = scope->recordMap.find(symbol.tag); - if (record == scope->recordMap.end()) { + for (auto &scope : context) { + auto record = scope.recordMap.find(symbol.tag); + if (record == scope.recordMap.end()) { continue; } bool first = true; - for (auto &symbol : scope->recordList.at(*record).symbolList) { + for (auto &symbol : scope.recordList.at(*record).symbolList) { switch (symbol.kind) { case SymbolKind::BitField: if (!first && symbol.length) { @@ -895,8 +945,7 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const QString &name, } } } -QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, - qint32 base) { +QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, qint32 base) const { qint32 addr = base + symbol.value; bool isFloat = false, isBitField = false, isSigned = true; quint32 type = symbol.type; @@ -979,16 +1028,20 @@ QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, case 1: case 6: if (quint32 value = mem_peek_long(addr)) { - return "0x" + QString::number(value, 16).rightJustified(6, '0'); + int sourceIndex, functionIndex; + if (lookupAddr(value, &sourceIndex, &functionIndex)) { + return '&' + stringList().value(sources().at(sourceIndex).functionList.at(functionIndex).name - 1); + } else { + return "0x" + QString::number(value, 16).rightJustified(6, '0'); + } } return "NULL"; } } -int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, - int childIndex, const Symbol &symbol, - Context context, const QString &name, - qint32 base) { +int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, int childIndex, + const Symbol &symbol, Context context, qint32 base, + const QString &name) { int id = m_freeVariable; if (id == -1) { id = m_variables.count(); @@ -1003,18 +1056,17 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, variable.symbol = symbol; variable.base = base; variable.context = context; - variable.data[0] = name; + variable.data[0] = name.isNull() ? symbolTypeToString(symbol, context) : name; variable.data[1] = symbolValueToString(symbol, base); variable.flags = VariableFlag::None; return id; } -void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, - const Symbol &symbol, - const QString &name, qint32 base) { +void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, const Symbol &symbol, + qint32 base, const QString &name) { Q_ASSERT(parent.isValid() && parent.model() == this); auto &variable = m_variables.at(parent.internalId()); int child = createVariable(parent.internalId(), parent.row(), variable.children. - count(), symbol, variable.context, name, base); + count(), symbol, variable.context, base, name); m_variables[parent.internalId()].children << child; } void SourcesWidget::VariableModel::deleteVariable(int id) { @@ -1124,11 +1176,10 @@ int SourcesWidget::VariableModel::childCount(const QModelIndex &parent) const { return variable.symbol.dims.value(0); case 0: if (variable.symbol.type == 8) { - for (auto scope : { variable.context.local, - variable.context.global }) { - auto record = scope->recordMap.find(variable.symbol.tag); - if (record != scope->recordMap.end()) { - return scope->recordList.at(*record).symbolList.count(); + for (auto &scope : variable.context) { + auto record = scope.recordMap.find(variable.symbol.tag); + if (record != scope.recordMap.end()) { + return scope.recordList.at(*record).symbolList.count(); } } } @@ -1210,6 +1261,9 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { auto &variable = m_variables[parent.internalId()]; auto symbol = variable.symbol; qint32 addr = variable.base + symbol.value; + if (symbol.kind != SymbolKind::StackSlot) { + symbol.value = 0; + } if (symbol.type & 0xE0000000u) { symbol.type |= 0xFFFFFFE0u; } @@ -1222,7 +1276,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { symbol.type = (symbol.type >> 3 & ~0x1F) | (symbol.type & 0x1F); if (mod == 1 || mod == 6) { if ((addr = mem_peek_long(addr))) { - createVariable(parent, symbol, '*' + name, addr); + createVariable(parent, symbol, addr, '*' + name); } } else if (mod == 3 && !symbol.dims.isEmpty()) { quint32 elements = symbol.dims.takeFirst(), @@ -1231,15 +1285,15 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { name = '(' + name + ')'; } for (quint32 index = 0; index != elements; ++index) { - createVariable(parent, symbol, - name + '[' + QString::number(index) + ']', addr); + createVariable(parent, symbol, addr, + name + '[' + QString::number(index) + ']'); addr += elementSize; } } } else if (symbol.type == 8) { - for (auto scope : { variable.context.local, variable.context.global }) { - auto record = scope->recordMap.find(variable.symbol.tag); - if (record == scope->recordMap.end()) { + for (auto &scope : variable.context) { + auto record = scope.recordMap.find(variable.symbol.tag); + if (record == scope.recordMap.end()) { continue; } if (name.startsWith('*')) { @@ -1247,9 +1301,8 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { } else { name = name + '.'; } - for (auto member : scope->recordList.at(*record).symbolList) { - createVariable(parent, member, - name + stringList().value(member.name - 1), addr); + for (auto &member : scope.recordList.at(*record).symbolList) { + createVariable(parent, member, addr); } break; } @@ -1292,7 +1345,7 @@ void SourcesWidget::GlobalModel::init(const QStringList &paths) { auto &source = sources().at(parent); auto &symbols = source.symbolList; - Context context = { &source, &source }; + Context context = { nullptr, &source }; for (int child = 0; child < symbols.count(); ++child) { auto &symbol = symbols.at(child); if (symbol.kind == SymbolKind::Function) { @@ -1300,9 +1353,7 @@ void SourcesWidget::GlobalModel::init(const QStringList &paths) { } QString name = stringList().value(symbol.name - 1); m_topLevelChildren.last() << createVariable(s_topLevelParent, parent, child, - symbol, context, - symbolTypeToString(name, symbol, - context)); + symbol, context); } } endResetModel(); @@ -1405,13 +1456,8 @@ void SourcesWidget::StackModel::update() { continue; } QString name = stringList().value(symbol.name - 1); - m_topLevelChildren.first() << createVariable(s_topLevelParent, - parent - stack.count(), - child, symbol, context, - symbolTypeToString(name, - symbol, - context), - entry.ix); + m_topLevelChildren.first() << createVariable(s_topLevelParent, parent - stack.count(), + child, symbol, context, entry.ix); } } endInsertRows(); diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h index d963a90c1..cb99f1ca5 100644 --- a/gui/qt/debugger/sourceswidget.h +++ b/gui/qt/debugger/sourceswidget.h @@ -62,15 +62,16 @@ private slots: quint32 num, addr; }; enum class SymbolKind : quint8 { - Unknown = 0, - Function = 2, - StructField = 8, - UnionField = 11, - BitField = 18, - StackSlot = 65, // can be Constant, Uninitialized or Variable - Constant = 69, - Uninitialized = 83, // can be Constant or Variable - Variable = 84, + Unknown = 0, + Function = 2, + StaticFunction = 3, + StructField = 8, + UnionField = 11, + BitField = 18, + StackSlot = 65, // can be Constant, Uninitialized or Variable + Constant = 69, + Uninitialized = 83, // can be Constant or Variable + Variable = 84, }; struct Symbol { quint32 name = s_unnamed, alias = s_unnamed; @@ -87,9 +88,7 @@ private slots: QMultiHash symbolMap; QMultiHash recordMap; }; - struct Context { - const Scope *local, *global; - }; + struct Context; struct Record : Scope { quint32 name, size; }; From 39a6b412161ff00cd7768d36a449d1be05678ed0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 20 Feb 2019 15:35:30 -0500 Subject: [PATCH 08/26] Show static function-local variables in the globals view. --- gui/qt/debugger/sourceswidget.cpp | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index f9aeca4a4..b96ce325b 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1171,7 +1171,21 @@ int SourcesWidget::VariableModel::childCount(const QModelIndex &parent) const { switch (variable.symbol.type >> 5 & 7) { case 1: case 6: - return variable.data[1] != "NULL"; + return (variable.symbol.type >> 5 >> 3 & 7) != 2 && variable.data[1] != "NULL"; + case 2: { + int count = 0; + auto &source = *variable.context.global; + auto function = source.functionMap.find(variable.symbol.name); + if (function != source.functionMap.end()) { + for (auto &symbol : source.functionList.at(*function).symbolList) { + if (symbol.kind == SymbolKind::StackSlot) { + continue; + } + ++count; + } + } + return count; + } case 3: return variable.symbol.dims.value(0); case 0: @@ -1278,6 +1292,17 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { if ((addr = mem_peek_long(addr))) { createVariable(parent, symbol, addr, '*' + name); } + } else if (mod == 2) { + auto &source = *variable.context.global; + auto function = source.functionMap.find(variable.symbol.name); + if (function != source.functionMap.end()) { + for (auto &symbol : source.functionList.at(*function).symbolList) { + if (symbol.kind == SymbolKind::StackSlot) { + continue; + } + createVariable(parent, symbol); + } + } } else if (mod == 3 && !symbol.dims.isEmpty()) { quint32 elements = symbol.dims.takeFirst(), elementSize = sizeOfSymbol(symbol, variable.context); @@ -1348,9 +1373,6 @@ void SourcesWidget::GlobalModel::init(const QStringList &paths) { Context context = { nullptr, &source }; for (int child = 0; child < symbols.count(); ++child) { auto &symbol = symbols.at(child); - if (symbol.kind == SymbolKind::Function) { - continue; - } QString name = stringList().value(symbol.name - 1); m_topLevelChildren.last() << createVariable(s_topLevelParent, parent, child, symbol, context); From 7685a4bf54216413a7e4916187de8d9b21b5c285 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 20 Feb 2019 14:29:32 -0500 Subject: [PATCH 09/26] Only highlight the value of changed variables. --- gui/qt/debugger/sourceswidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index b96ce325b..707701f76 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1240,8 +1240,8 @@ QVariant SourcesWidget::VariableModel::data(const QModelIndex &index, int role) return int((index.column() ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignVCenter); case Qt::BackgroundRole: - if (index.internalId() != s_topLevelId && m_variables. - at(index.internalId()).flags.testFlag(VariableFlag::Changed)) { + if (index.internalId() != s_topLevelId && index.column() == 1 && + m_variables.at(index.internalId()).flags.testFlag(VariableFlag::Changed)) { return QColor::fromRgb(0xFFFF99); } break; From eb721009404a0bdffbadb89b103e09e367f1a02f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 20 Feb 2019 14:29:49 -0500 Subject: [PATCH 10/26] Show static function-local variables in the stack view. --- gui/qt/debugger/sourceswidget.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 707701f76..f8a4c662d 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1065,8 +1065,8 @@ void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, con qint32 base, const QString &name) { Q_ASSERT(parent.isValid() && parent.model() == this); auto &variable = m_variables.at(parent.internalId()); - int child = createVariable(parent.internalId(), parent.row(), variable.children. - count(), symbol, variable.context, base, name); + int child = createVariable(parent.internalId(), parent.row(), variable.children.count(), + symbol, variable.context, base, name); m_variables[parent.internalId()].children << child; } void SourcesWidget::VariableModel::deleteVariable(int id) { @@ -1374,8 +1374,8 @@ void SourcesWidget::GlobalModel::init(const QStringList &paths) { for (int child = 0; child < symbols.count(); ++child) { auto &symbol = symbols.at(child); QString name = stringList().value(symbol.name - 1); - m_topLevelChildren.last() << createVariable(s_topLevelParent, parent, child, - symbol, context); + m_topLevelChildren.last() << + createVariable(s_topLevelParent, parent, child, symbol, context); } } endResetModel(); @@ -1474,12 +1474,10 @@ void SourcesWidget::StackModel::update() { auto &symbols = entry.function->symbolList; for (int child = 0; child < symbols.count(); ++child) { auto &symbol = symbols.at(child); - if (symbol.kind != SymbolKind::StackSlot) { - continue; - } QString name = stringList().value(symbol.name - 1); - m_topLevelChildren.first() << createVariable(s_topLevelParent, parent - stack.count(), - child, symbol, context, entry.ix); + m_topLevelChildren.first() << + createVariable(s_topLevelParent, parent - stack.count(), child, symbol, + context, symbol.kind == SymbolKind::StackSlot ? entry.ix : 0); } } endInsertRows(); From f409308b28306445b672ebbd36a87ac3f0c92f00 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 20 Feb 2019 16:21:55 -0500 Subject: [PATCH 11/26] Fix invalid stack variable offests and field names. --- gui/qt/debugger/sourceswidget.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index f8a4c662d..7b293a583 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1275,12 +1275,13 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { auto &variable = m_variables[parent.internalId()]; auto symbol = variable.symbol; qint32 addr = variable.base + symbol.value; - if (symbol.kind != SymbolKind::StackSlot) { - symbol.value = 0; - } + symbol.value = 0; if (symbol.type & 0xE0000000u) { symbol.type |= 0xFFFFFFE0u; } + if (symbol.kind == SymbolKind::StackSlot) { + symbol.kind = SymbolKind::Uninitialized; + } QString name = variable.data[0]; if (variable.parent == s_topLevelParent) { name = stringList().value(variable.symbol.name - 1); @@ -1327,7 +1328,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { name = name + '.'; } for (auto &member : scope.recordList.at(*record).symbolList) { - createVariable(parent, member, addr); + createVariable(parent, member, addr, name + stringList().value(member.name - 1)); } break; } From e617dc5c0506081e1db6a322d3722f03fcf6d3c3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 21 Feb 2019 08:43:39 -0500 Subject: [PATCH 12/26] Update variable values when parent pointer value changes. --- gui/qt/debugger/sourceswidget.cpp | 44 ++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 7b293a583..9a9e6bf59 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -182,8 +182,8 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { lookupAddr(addr, sourceIndex, functionIndex, lineIndex); } static quint32 sizeOfSymbol(const Symbol &symbol, Context context); - QString symbolTypeToString(const Symbol &symbol, Context context) const; - QString symbolValueToString(const Symbol &symbol, qint32 base = 0) const; + QString variableTypeToString(const Variable &variable) const; + QString variableValueToString(const Variable &variable) const; int createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, qint32 base = 0, const QString &name = {}); void createVariable(const QModelIndex &parent, const Symbol &symbol, qint32 base = 0, const QString &name = {}); void removeTopLevels(int first, int last); @@ -858,9 +858,9 @@ quint32 SourcesWidget::VariableModel::sizeOfSymbol(const Symbol &symbol, Context } return 0; } -QString SourcesWidget::VariableModel::symbolTypeToString(const Symbol &symbol, Context context) const { - QString name = stringList().value(symbol.name - 1); - quint32 type = symbol.type; +QString SourcesWidget::VariableModel::variableTypeToString(const Variable &variable) const { + QString name = stringList().value(variable.symbol.name - 1); + quint32 type = variable.symbol.type; QString prefix, tag = "struct ", base, suffix = name; if (type & 0xE0000000u) { type |= 0xFFFFFFE0u; @@ -877,8 +877,8 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const Symbol &symbol, C case 5: base += "long"; break; case 6: base = "float"; break; case 8: - for (auto &scope : context) { - auto record = scope.recordMap.find(symbol.tag); + for (auto &scope : variable.context) { + auto record = scope.recordMap.find(variable.symbol.tag); if (record == scope.recordMap.end()) { continue; } @@ -907,7 +907,7 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const Symbol &symbol, C } break; } - base = tag + stringList().value(symbol.tag - 1); + base = tag + stringList().value(variable.symbol.tag - 1); break; default: base = ""; break; } @@ -936,7 +936,7 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const Symbol &symbol, C suffix = '(' + prefix + suffix + ')'; prefix = ""; } - suffix += '[' + QString::number(symbol.dims.value(dim++)) + ']'; + suffix += '[' + QString::number(variable.symbol.dims.value(dim++)) + ']'; break; case 6: suffix = '*' + prefix + suffix; @@ -945,10 +945,10 @@ QString SourcesWidget::VariableModel::symbolTypeToString(const Symbol &symbol, C } } } -QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, qint32 base) const { - qint32 addr = base + symbol.value; +QString SourcesWidget::VariableModel::variableValueToString(const Variable &variable) const { + qint32 addr = variable.base + variable.symbol.value; bool isFloat = false, isBitField = false, isSigned = true; - quint32 type = symbol.type; + quint32 type = variable.symbol.type; if (type & 0xE0000000u) { return {}; } @@ -961,7 +961,7 @@ QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, isBitField = true; isSigned = false; // We have no way of knowing if it is signed :( type = 4; - addr = base + 3*(symbol.value / 24); + addr = variable.base + 3*(variable.symbol.value / 24); } else if ((type & ~3) == 0xC) { isSigned = false; type -= 10; @@ -979,7 +979,7 @@ QString SourcesWidget::VariableModel::symbolValueToString(const Symbol &symbol, return QString::number(value.f); } if (isBitField) { - int shift = symbol.value % 24, extend = 32 - symbol.length; + int shift = variable.symbol.value % 24, extend = 32 - variable.symbol.length; if (shift < 0) { shift += 24; } @@ -1056,9 +1056,9 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, in variable.symbol = symbol; variable.base = base; variable.context = context; - variable.data[0] = name.isNull() ? symbolTypeToString(symbol, context) : name; - variable.data[1] = symbolValueToString(symbol, base); variable.flags = VariableFlag::None; + variable.data[0] = name.isNull() ? variableTypeToString(variable) : name; + variable.data[1] = variableValueToString(variable); return id; } void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, const Symbol &symbol, @@ -1097,12 +1097,20 @@ void SourcesWidget::VariableModel::removeTopLevels(int first, int last) { endRemoveRows(); } void SourcesWidget::VariableModel::update() { - for (int id = 0; id < m_variables.count(); id++) { + for (int id = 0; id < m_variables.count(); ++id) { auto &variable = m_variables[id]; if (variable.flags.testFlag(VariableFlag::Deleted)) { continue; } - QString valueStr = symbolValueToString(variable.symbol, variable.base); + if (variable.parent != s_topLevelParent) { + auto &parent = m_variables.at(variable.parent); + if ((parent.symbol.type >> 5 & 7) == 1) { + variable.base = mem_peek_long(parent.base + parent.symbol.value); + } else { + variable.base = parent.base; + } + } + QString valueStr = variableValueToString(variable); bool valueChanged = variable.data[1] != valueStr; int firstChangedColumn = 1; QVector changedRoles; From 5e242d23a28e63380949267e0a31d38776954885 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 21 Feb 2019 10:35:22 -0500 Subject: [PATCH 13/26] Fix showing of arrays. --- gui/qt/debugger/sourceswidget.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 9a9e6bf59..fac342bae 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1104,10 +1104,11 @@ void SourcesWidget::VariableModel::update() { } if (variable.parent != s_topLevelParent) { auto &parent = m_variables.at(variable.parent); - if ((parent.symbol.type >> 5 & 7) == 1) { - variable.base = mem_peek_long(parent.base + parent.symbol.value); - } else { - variable.base = parent.base; + if (parent.symbol.kind > SymbolKind::StaticFunction) { + variable.base = parent.base + parent.symbol.value; + if ((parent.symbol.type >> 5 & 7) == 1) { + variable.base = mem_peek_long(variable.base); + } } } QString valueStr = variableValueToString(variable); @@ -1291,7 +1292,8 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { symbol.kind = SymbolKind::Uninitialized; } QString name = variable.data[0]; - if (variable.parent == s_topLevelParent) { + if (variable.parent == s_topLevelParent || + m_variables.at(variable.parent).symbol.kind <= SymbolKind::StaticFunction) { name = stringList().value(variable.symbol.name - 1); } if (symbol.type & ~0x1F) { @@ -1321,7 +1323,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { for (quint32 index = 0; index != elements; ++index) { createVariable(parent, symbol, addr, name + '[' + QString::number(index) + ']'); - addr += elementSize; + symbol.value += elementSize; } } } else if (symbol.type == 8) { From bdc41e6e53966d608516894b901e3fa47fd36306 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 21 Feb 2019 08:44:13 -0500 Subject: [PATCH 14/26] Temporary debugger debugging. --- gui/qt/debugger/sourceswidget.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index fac342bae..f82089077 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -126,7 +126,7 @@ QDebug operator<<(QDebug debug, const SourcesWidget::Context &context) { } QDebug operator<<(QDebug debug, const SourcesWidget::Symbol &symbol) { QDebugStateSaver saver(debug); - debug.nospace() << "Symbol(name=" << symbol.name << ", alias=" << symbol.alias << ", value=" << symbol.value << ", type=" << symbol.type << ", tag=" << symbol.tag << ", kind=" << symbol.kind << ", length=" << symbol.length << ", dims=" << symbol.dims << ')'; + debug.nospace() << "Symbol(name=" << symbol.name << ", alias=" << symbol.alias << ", value=" << QString::number(symbol.value, symbol.kind == SourcesWidget::SymbolKind::StackSlot ? 10 : 16) << ", type=" << symbol.type << ", tag=" << symbol.tag << ", kind=" << symbol.kind << ", length=" << symbol.length << ", dims=" << symbol.dims << ')'; return debug.maybeSpace(); } QDebug operator<<(QDebug debug, const SourcesWidget::Record &record) { @@ -1059,6 +1059,7 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, in variable.flags = VariableFlag::None; variable.data[0] = name.isNull() ? variableTypeToString(variable) : name; variable.data[1] = variableValueToString(variable); + qDebug().noquote() << '\t' << stringList().value(m_variables.value(variable.parent).symbol.name - 1) << "→" << stringList().value(variable.symbol.name - 1) << variable.symbol << QString::number(variable.base, 16); return id; } void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, const Symbol &symbol, @@ -1129,6 +1130,7 @@ void SourcesWidget::VariableModel::update() { emit dataChanged(createIndex(variable.childIndex, firstChangedColumn, id), createIndex(variable.childIndex, 1, id), changedRoles); } + qDebug().noquote() << '\t' << stringList().value(m_variables.value(variable.parent).symbol.name - 1) << "→" << stringList().value(variable.symbol.name - 1) << variable.symbol << QString::number(variable.base, 16); } } QModelIndex SourcesWidget::VariableModel::parent(const QModelIndex &child) const { @@ -1478,6 +1480,7 @@ void SourcesWidget::StackModel::update() { beginInsertRows(QModelIndex(), 0, stack.count() - commonSuffix); for (int parent = stack.count() - commonSuffix; parent >= 0; --parent) { auto &entry = stack.at(parent); + qDebug().nospace().noquote() << stringList().at(entry.function->name - 1) << ':'; m_topLevelData[0].prepend(stringList().value(entry.function->name - 1)); m_topLevelData[1].prepend("()"); m_topLevelChildren.prepend({}); @@ -1491,6 +1494,7 @@ void SourcesWidget::StackModel::update() { context, symbol.kind == SymbolKind::StackSlot ? entry.ix : 0); } } + qDebug() << ""; endInsertRows(); } int firstParentChanged = -1; From d6045285c0cd2d921d7a8d5d1830e65acdb7da37 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 28 Feb 2019 07:49:04 -0500 Subject: [PATCH 15/26] Update variables top-down to avoid update delay. --- gui/qt/CEmu.pro | 2 +- gui/qt/debugger/sourceswidget.cpp | 109 +++++++++++++++++------------- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index 0709b5b33..08175a135 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -88,7 +88,7 @@ CONFIG(release, debug|release) { # GCC/clang flags if (!win32-msvc*) { - GLOBAL_FLAGS += -W -Wall -Wextra -Wunused-function -Werror=write-strings -Werror=redundant-decls -Werror=format -Werror=format-security -Werror=return-type -Werror=pointer-arith -Winit-self -Wimplicit-fallthrough + GLOBAL_FLAGS += -W -Wall -Wextra -Wunused-function -Werror=write-strings -Werror=redundant-decls -Werror=format -Werror=format-security -Werror=return-type -Werror=pointer-arith -Winit-self -Wimplicit-fallthrough -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled GLOBAL_FLAGS += -ffunction-sections -fdata-sections -fno-strict-overflow QMAKE_CFLAGS += -std=gnu11 -Werror=implicit-function-declaration -Werror=missing-prototypes diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index f82089077..876e1a2be 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -157,22 +157,24 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { using(const, stringList) using(const, stringMap) #undef using - enum class VariableFlag : quint8 { - None = 0, - Deleted = 1 << 0, - Changed = 1 << 1, - }; - Q_DECLARE_FLAGS(VariableFlags, VariableFlag) struct Variable { + enum class Flag : quint8 { + Deleted = 1 << 0, + Changed = 1 << 1, + Visited = 1 << 2, + }; + Q_DECLARE_FLAGS(Flags, Flag) + int parent, parentIndex, childIndex; QList children; Symbol symbol; qint32 base; Context context; QString data[2]; - VariableFlags flags; + Flags flags; }; - int m_freeVariable; + bool m_visited = false; + int m_freeVariable = -1; QVector m_variables; QStringList m_topLevelData[2]; QList> m_topLevelChildren; @@ -189,6 +191,8 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { void removeTopLevels(int first, int last); constexpr static quintptr s_topLevelId = ~quintptr(); constexpr static int s_topLevelParent = int(s_topLevelId); +private: + const Variable &updateVariable(int id); public: virtual void update(); QModelIndex parent(const QModelIndex &child) const override; @@ -1047,7 +1051,7 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, in id = m_variables.count(); m_variables.resize(id + 1); } else { - m_freeVariable = m_variables[id].parent; + m_freeVariable = m_variables.at(id).parent; } auto &variable = m_variables[id]; variable.parent = parent; @@ -1056,7 +1060,8 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, in variable.symbol = symbol; variable.base = base; variable.context = context; - variable.flags = VariableFlag::None; + variable.flags = 0; + variable.flags.setFlag(Variable::Flag::Visited, m_visited); variable.data[0] = name.isNull() ? variableTypeToString(variable) : name; variable.data[1] = variableValueToString(variable); qDebug().noquote() << '\t' << stringList().value(m_variables.value(variable.parent).symbol.name - 1) << "→" << stringList().value(variable.symbol.name - 1) << variable.symbol << QString::number(variable.base, 16); @@ -1071,12 +1076,13 @@ void SourcesWidget::VariableModel::createVariable(const QModelIndex &parent, con m_variables[parent.internalId()].children << child; } void SourcesWidget::VariableModel::deleteVariable(int id) { - for (int child : m_variables.at(id).children) { + auto &variable = m_variables[id]; + for (int child : variable.children) { deleteVariable(child); } - m_variables[id].parent = m_freeVariable; - m_variables[id].children.clear(); - m_variables[id].flags |= VariableFlag::Deleted; + variable.parent = m_freeVariable; + variable.children.clear(); + variable.flags.setFlag(Variable::Flag::Deleted); m_freeVariable = id; } void SourcesWidget::VariableModel::removeTopLevels(int first, int last) { @@ -1097,40 +1103,47 @@ void SourcesWidget::VariableModel::removeTopLevels(int first, int last) { m_topLevelChildren.begin() + last + 1); endRemoveRows(); } -void SourcesWidget::VariableModel::update() { - for (int id = 0; id < m_variables.count(); ++id) { - auto &variable = m_variables[id]; - if (variable.flags.testFlag(VariableFlag::Deleted)) { - continue; - } - if (variable.parent != s_topLevelParent) { - auto &parent = m_variables.at(variable.parent); - if (parent.symbol.kind > SymbolKind::StaticFunction) { - variable.base = parent.base + parent.symbol.value; - if ((parent.symbol.type >> 5 & 7) == 1) { - variable.base = mem_peek_long(variable.base); - } +auto SourcesWidget::VariableModel::updateVariable(int id) -> const Variable & { + auto &variable = m_variables[id]; + if (variable.flags.testFlag(Variable::Flag::Deleted) || + variable.flags.testFlag(Variable::Flag::Visited) == m_visited) { + return variable; + } + if (variable.parent != s_topLevelParent) { + auto &parent = updateVariable(variable.parent); + if (parent.symbol.kind > SymbolKind::StaticFunction) { + variable.base = parent.base + parent.symbol.value; + if ((parent.symbol.type >> 5 & 7) == 1) { + variable.base = mem_peek_long(variable.base); } } - QString valueStr = variableValueToString(variable); - bool valueChanged = variable.data[1] != valueStr; - int firstChangedColumn = 1; - QVector changedRoles; - changedRoles.reserve(2); - if (valueChanged) { - variable.data[1] = valueStr; - changedRoles << Qt::DisplayRole; - } - if (valueChanged != variable.flags.testFlag(VariableFlag::Changed)) { - variable.flags.setFlag(VariableFlag::Changed, valueChanged); - firstChangedColumn = 0; - changedRoles << Qt::BackgroundRole; - } - if (!changedRoles.isEmpty()) { - emit dataChanged(createIndex(variable.childIndex, firstChangedColumn, id), - createIndex(variable.childIndex, 1, id), changedRoles); - } - qDebug().noquote() << '\t' << stringList().value(m_variables.value(variable.parent).symbol.name - 1) << "→" << stringList().value(variable.symbol.name - 1) << variable.symbol << QString::number(variable.base, 16); + } + QString valueStr = variableValueToString(variable); + bool valueChanged = variable.data[1] != valueStr; + int firstChangedColumn = 1; + QVector changedRoles; + changedRoles.reserve(2); + if (valueChanged) { + variable.data[1] = valueStr; + changedRoles << Qt::DisplayRole; + } + if (valueChanged != variable.flags.testFlag(Variable::Flag::Changed)) { + variable.flags.setFlag(Variable::Flag::Changed, valueChanged); + firstChangedColumn = 0; + changedRoles << Qt::BackgroundRole; + } + if (!changedRoles.isEmpty()) { + emit dataChanged(createIndex(variable.childIndex, firstChangedColumn, id), + createIndex(variable.childIndex, 1, id), changedRoles); + } + qDebug().noquote() << '\t' << stringList().value(m_variables.value(variable.parent).symbol.name - 1) << "→" << stringList().value(variable.symbol.name - 1) << variable.symbol << QString::number(variable.base, 16); + variable.flags.setFlag(Variable::Flag::Visited, m_visited); + return variable; +} +void SourcesWidget::VariableModel::update() { + m_visited = !m_visited; + for (int id = 0; id < m_variables.count(); ++id) { + updateVariable(id); } } QModelIndex SourcesWidget::VariableModel::parent(const QModelIndex &child) const { @@ -1252,7 +1265,7 @@ QVariant SourcesWidget::VariableModel::data(const QModelIndex &index, int role) Qt::AlignVCenter); case Qt::BackgroundRole: if (index.internalId() != s_topLevelId && index.column() == 1 && - m_variables.at(index.internalId()).flags.testFlag(VariableFlag::Changed)) { + m_variables.at(index.internalId()).flags.testFlag(Variable::Flag::Changed)) { return QColor::fromRgb(0xFFFF99); } break; @@ -1283,7 +1296,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { if (!parent.isValid()) { return; } - auto &variable = m_variables[parent.internalId()]; + auto &variable = m_variables.at(parent.internalId()); auto symbol = variable.symbol; qint32 addr = variable.base + symbol.value; symbol.value = 0; From 5961e8702422b831cbb7e6f3b4a651a15d387807 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 28 Feb 2019 11:57:14 -0500 Subject: [PATCH 16/26] Fix bug when parsing unsigned long literals. --- gui/qt/debugger/cdebughighlighter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gui/qt/debugger/cdebughighlighter.cpp b/gui/qt/debugger/cdebughighlighter.cpp index 913792594..c37eaf39d 100644 --- a/gui/qt/debugger/cdebughighlighter.cpp +++ b/gui/qt/debugger/cdebughighlighter.cpp @@ -214,19 +214,19 @@ void CDebugHighlighter::highlightBlock(const QString &text) { token.toFloat(&ok); } else { bool unsignedSuffix = false, longSuffix = false; - do { + forever { if (!unsignedSuffix && token.endsWith('u', Qt::CaseInsensitive)) { token.chop(1); unsignedSuffix = true; - continue; } else if (!longSuffix && token.endsWith('l', Qt::CaseInsensitive)) { token.chop(1); longSuffix = true; - continue; + } else { + break; } - } while (false); + } if (unsignedSuffix) { uint value = token.toUInt(&ok, 0); ok &= longSuffix || value < 1u << 24; From 0ecac9826a419da884ec6d9b7cb4cba10ac6f1f4 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 1 Mar 2019 02:45:09 -0500 Subject: [PATCH 17/26] Scroll to corresponding source line when PC changes. --- gui/qt/debugger/sourceswidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 876e1a2be..6c586239d 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -735,11 +735,13 @@ void SourcesWidget::updatePC(quint32 pc) { } else { pcSelection.cursor.movePosition(QTextCursor::NextWord); } + sourceView->setTextCursor(pcSelection.cursor); pcSelection.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); pcSelection.cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); pcSelection.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); pcSelection.format.setBackground(QColor::fromRgb(0xFFFF99)); sourceView->setExtraSelections({pcSelection}); + sourceView->centerCursor(); m_tabs->setCurrentWidget(sourceView); } From 16954e947a02ee456df7cecdf70950ba2ffa2e61 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 27 Sep 2020 00:30:03 -0400 Subject: [PATCH 18/26] Debug info stuff. --- core/cpu.c | 29 +- core/debug/debug.c | 50 +- core/debug/debug.h | 7 +- gui/qt/CEmu.pro | 20 +- gui/qt/debugger.cpp | 205 +-- gui/qt/debugger/cdebughighlighter.cpp | 2 +- gui/qt/debugger/debuginfo.cpp | 1904 +++++++++++++++++++++++++ gui/qt/debugger/debuginfo.h | 1687 ++++++++++++++++++++++ gui/qt/debugger/sourceswidget.cpp | 986 ++++++------- gui/qt/debugger/sourceswidget.h | 30 +- gui/qt/mainwindow.h | 4 +- gui/qt/mainwindow.ui | 29 + gui/qt/resources.qrc | 1 + gui/qt/resources/icons/C.png | Bin 0 -> 1762 bytes 14 files changed, 4317 insertions(+), 637 deletions(-) create mode 100644 gui/qt/debugger/debuginfo.cpp create mode 100644 gui/qt/debugger/debuginfo.h create mode 100644 gui/qt/resources/icons/C.png diff --git a/core/cpu.c b/core/cpu.c index 026cbe464..b435e04f8 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -459,10 +459,15 @@ static void cpu_trap(void) { cpu_trap_rewind(1); } -static void cpu_jump(uint32_t address, bool mode) { +static void cpu_jump(uint32_t address, bool mode, bool absolute) { cpu_prefetch(address, mode); #ifdef DEBUG_SUPPORT - debug_record_ret(cpu.registers.PC, mode); + debug_record_jump(address); + if (absolute) { + debug_record_ret(cpu.registers.PC, mode); + } +#else + (void)absolute; #endif } @@ -480,7 +485,7 @@ static void cpu_return(void) { } else { address = cpu_pop_word(); } - cpu_jump(address, mode); + cpu_jump(address, mode, true); } static void cpu_execute_alu(int i, uint8_t v) { @@ -654,10 +659,14 @@ static void cpu_execute_bli() { uint_fast8_t xp = cpu.context.x << 2 | cpu.context.p; int_fast8_t delta = cpu.context.q ? -1 : 1; bool repeat = (cpu.context.x | cpu.context.p) & 1; +#ifdef DEBUG_SUPPORT + bool first = true; +#endif do { #ifdef DEBUG_SUPPORT if (cpu.inBlock) { - debug_inst_repeat(); + debug_inst_repeat(first); + first = false; } #endif switch (cpu.context.z) { @@ -973,12 +982,12 @@ void cpu_execute(void) { s = cpu_fetch_offset(); if (--r->B) { cpu.cycles++; - cpu_prefetch(cpu_mask_mode(r->PC + s, cpu.L), cpu.ADL); + cpu_jump(cpu_mask_mode(r->PC + s, cpu.L), cpu.ADL, false); } break; case 3: /* JR d */ s = cpu_fetch_offset(); - cpu_prefetch(cpu_mask_mode(r->PC + s, cpu.L), cpu.ADL); + cpu_jump(cpu_mask_mode(r->PC + s, cpu.L), cpu.ADL, false); break; case 4: case 5: @@ -987,7 +996,7 @@ void cpu_execute(void) { s = cpu_fetch_offset(); if (cpu_read_cc(context.y - 4)) { cpu.cycles++; - cpu_prefetch(cpu_mask_mode(r->PC + s, cpu.L), cpu.ADL); + cpu_jump(cpu_mask_mode(r->PC + s, cpu.L), cpu.ADL, false); } break; } @@ -1208,7 +1217,7 @@ void cpu_execute(void) { break; case 2: /* JP (rr) */ cpu_prefetch_discard(); - cpu_jump(cpu_read_index(), cpu.L); + cpu_jump(cpu_read_index(), cpu.L, true); break; case 3: /* LD SP, HL */ cpu_write_sp(cpu_read_index()); @@ -1220,7 +1229,7 @@ void cpu_execute(void) { case 2: /* JP cc[y], nn */ if (cpu_read_cc(context.y)) { cpu.cycles++; - cpu_jump(cpu_fetch_word_no_prefetch(), cpu.L); + cpu_jump(cpu_fetch_word_no_prefetch(), cpu.L, true); } else { cpu_fetch_word(); } @@ -1233,7 +1242,7 @@ void cpu_execute(void) { switch (context.y) { case 0: /* JP nn */ cpu.cycles++; - cpu_jump(cpu_fetch_word_no_prefetch(), cpu.L); + cpu_jump(cpu_fetch_word_no_prefetch(), cpu.L, true); break; case 1: /* 0xCB prefixed opcodes */ w = cpu_index_address(); diff --git a/core/debug/debug.c b/core/debug/debug.c index a41b6507e..1713a4312 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -171,19 +171,38 @@ void debug_flag(int mask, bool set) { void debug_step(int mode, uint32_t addr) { switch (mode) { case DBG_STEP_IN: + case DBG_RUN_IN: debug.step = true; + debug.stepOver = false; + debug.stepOut = ~0u; break; case DBG_STEP_OVER: + case DBG_RUN_OVER: debug.step = true; debug.stepOver = true; + debug.stepOut = ~0u; + break; + case DBG_RUN_AGAIN: + gui_debug_close(); + debug.step = true; + debug.stepOut = ~0u; break; case DBG_STEP_OUT: + debug.step = debug.stepOver = false; + /* fallthrough */ + case DBG_RUN_OUT: gui_debug_close(); debug.stepOut = debug.stackIndex; break; + } + switch (mode) { case DBG_STEP_NEXT: case DBG_RUN_UNTIL: gui_debug_close(); + /* fallthrough */ + case DBG_RUN_IN: + case DBG_RUN_OVER: + case DBG_RUN_OUT: debug.tempExec = addr; break; case DBG_BASIC_STEP_IN: @@ -196,6 +215,9 @@ void debug_step(int mode, uint32_t addr) { debug.stepBasicEnd = addr >> 16; debug_get_executing_basic_prgm(debug.stepBasicPrgm); break; + default: + debug.tempExec = ~0u; + break; } } @@ -212,7 +234,7 @@ void debug_clear_basic_step(void) { void debug_inst_start(void) { uint32_t pc = cpu.registers.PC; debug.addr[pc] |= DBG_INST_START_MARKER; - if (debug.step && !(debug.addr[pc] & DBG_MASK_EXEC) && pc != debug.tempExec) { + if (debug.step && !(debug.addr[pc] & DBG_MASK_EXEC) && debug.tempExec == ~0u) { debug.step = debug.stepOver = false; debug_open(DBG_STEP, cpu.registers.PC); } @@ -228,10 +250,12 @@ void debug_inst_fetch(void) { } } -void debug_inst_repeat(void) { +void debug_inst_repeat(bool first) { if (debug.step) { - if (debug.stepOver) { - gui_debug_close(); + if (debug.stepOver || debug.tempExec != ~0u) { + if (first) { + gui_debug_close(); + } } else { debug.step = false; debug_open(DBG_STEP, cpu.registers.PC); @@ -239,6 +263,12 @@ void debug_inst_repeat(void) { } } +void debug_record_jump(uint32_t addr) { + if (debug.step) { + debug.tempExec = ~0u; + } +} + void debug_record_call(uint32_t retAddr, bool mode) { uint32_t stack = cpu_address_mode(cpu.registers.stack[mode].hl, mode); uint32_t index = (debug.stackIndex + 1) & DBG_STACK_MASK; @@ -252,10 +282,14 @@ void debug_record_call(uint32_t retAddr, bool mode) { if (debug.stackSize < DBG_STACK_SIZE) { debug.stackSize++; } - if (debug.stepOver) { - gui_debug_close(); - debug.step = debug.stepOver = false; - debug.stepOut = index; + if (debug.step) { + if (debug.stepOver) { + gui_debug_close(); + debug.step = false; + debug.stepOut = index; + } else { + debug.tempExec = ~0u; + } } } diff --git a/core/debug/debug.h b/core/debug/debug.h index 3a4fd1cba..6dc0544a0 100644 --- a/core/debug/debug.h +++ b/core/debug/debug.h @@ -68,7 +68,8 @@ void debug_free(void); /* call after emulation end void debug_set_pc(uint32_t addr); /* when in gui debug set program counter */ void debug_inst_start(void); void debug_inst_fetch(void); -void debug_inst_repeat(void); +void debug_inst_repeat(bool first); +void debug_record_jump(uint32_t addr); void debug_record_call(uint32_t retAddr, bool stack); void debug_record_ret(uint32_t retAddr, bool stack); void debug_watch(uint32_t addr, int mask, bool set); /* set a breakpoint or a watchpoint */ @@ -175,6 +176,10 @@ enum { DBG_STEP_OVER, DBG_STEP_NEXT, DBG_RUN_UNTIL, + DBG_RUN_IN, + DBG_RUN_OVER, + DBG_RUN_AGAIN, + DBG_RUN_OUT, DBG_BASIC_STEP_IN, DBG_BASIC_STEP_NEXT, }; diff --git a/gui/qt/CEmu.pro b/gui/qt/CEmu.pro index 08175a135..29ffe8677 100644 --- a/gui/qt/CEmu.pro +++ b/gui/qt/CEmu.pro @@ -252,6 +252,12 @@ SOURCES += \ basiccodeviewerwindow.cpp \ sendinghandler.cpp \ debugger.cpp \ + debugger/cdebughighlighter.cpp \ + debugger/debuginfo.cpp \ + debugger/disasm.cpp \ + debugger/hexwidget.cpp \ + debugger/sourceswidget.cpp \ + debugger/visualizerdisplaywidget.cpp \ settings.cpp \ capture/animated-png.c \ keypad/qtkeypadbridge.cpp \ @@ -259,8 +265,6 @@ SOURCES += \ keypad/keypadwidget.cpp \ keypad/rectkey.cpp \ keypad/arrowkey.cpp \ - debugger/hexwidget.cpp \ - debugger/disasm.cpp \ tivars_lib_cpp/src/tivarslib_utils.cpp \ tivars_lib_cpp/src/BinaryFile.cpp \ tivars_lib_cpp/src/TIVarFile.cpp \ @@ -286,10 +290,7 @@ SOURCES += \ tivars_lib_cpp/src/TypeHandlers/STH_FP.cpp \ vartablemodel.cpp \ visualizerwidget.cpp \ - debugger/visualizerdisplaywidget.cpp \ memorywidget.cpp \ - debugger/sourceswidget.cpp \ - debugger/cdebughighlighter.cpp \ archive/extractor.c \ ../../core/bus.c \ keyhistorywidget.cpp \ @@ -376,8 +377,12 @@ HEADERS += \ keypad/operkey.h \ keypad/arrowkey.h \ capture/animated-png.h \ - debugger/hexwidget.h \ + debugger/cdebughighlighter.h \ + debugger/debuginfo.h \ debugger/disasm.h \ + debugger/hexwidget.h \ + debugger/sourceswidget.h \ + debugger/visualizerdisplaywidget.h \ tivars_lib_cpp/src/tivarslib_utils.h \ tivars_lib_cpp/src/CommonTypes.h \ tivars_lib_cpp/src/BinaryFile.h \ @@ -389,9 +394,6 @@ HEADERS += \ tivars_lib_cpp/src/TypeHandlers/TypeHandlers.h \ vartablemodel.h \ visualizerwidget.h \ - debugger/visualizerdisplaywidget.h \ - debugger/sourceswidget.h \ - debugger/cdebughighlighter.h \ archive/extractor.h \ ../../core/bus.h \ keyhistorywidget.h \ diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index fd85667cc..bcd9d7f63 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -105,16 +105,35 @@ void MainWindow::debugEnable() { } void MainWindow::debugStep(int mode) { - if (mode == DBG_RUN_UNTIL) { - debug_step(mode, m_runUntilAddr); - } else { - disasm.base = static_cast(cpu.registers.PC); - disasmGet(true); + uint32_t addr; + switch (mode) { + default: + disasm.base = int32_t(cpu.registers.PC); - m_stepCtx.active = true; - m_stepCtx.seqNext = static_cast(disasm.next); - debug_step(mode, static_cast(disasm.next)); + disasmGet(true); + addr = uint32_t(disasm.next); + m_runLoc = {}; + break; + case DBG_RUN_UNTIL: + addr = m_runUntilAddr; + m_runLoc = {}; + break; + case DBG_RUN_IN: + case DBG_RUN_OVER: + m_runLoc = ui->sources->getLocInfo(cpu.registers.PC); + if (m_runLoc.invalid()) { + debugStep(DBG_STEP_OUT); + return; + } + [[gnu::fallthrough]]; + case DBG_RUN_AGAIN: + case DBG_RUN_OUT: + addr = m_runLoc.end(); + break; } + m_stepCtx.active = true; + m_stepCtx.seqNext = static_cast(disasm.next); + debug_step(mode, addr); emu.resume(); } @@ -145,7 +164,7 @@ void MainWindow::debugImportFile(const QString &file) { goto error; } for (i = 0; i < breakLabel.size(); i++) { - breakAdd(breakLabel.at(i), static_cast(hex2int(breakAddr.at(i))), breakSet.at(i) == TXT_YES, false, false); + breakAdd(breakLabel.at(i), uint32_t(hex2int(breakAddr.at(i))), breakSet.at(i) == TXT_YES, false, false); } // load the watchpoint information @@ -160,8 +179,8 @@ void MainWindow::debugImportFile(const QString &file) { for (i = 0; i < watchLabel.size(); i++) { int mask = (watchR.at(i) == TXT_YES ? DBG_MASK_READ : DBG_MASK_NONE) | (watchW.at(i) == TXT_YES ? DBG_MASK_WRITE : DBG_MASK_NONE); - watchAdd(watchLabel.at(i), static_cast(hex2int(watchLow.at(i))), - static_cast(hex2int(watchHigh.at(i))), mask, false, false); + watchAdd(watchLabel.at(i), uint32_t(hex2int(watchLow.at(i))), + uint32_t(hex2int(watchHigh.at(i))), mask, false, false); } // load the port monitor information @@ -176,7 +195,7 @@ void MainWindow::debugImportFile(const QString &file) { int mask = (portR.at(i) == TXT_YES ? DBG_MASK_PORT_READ : DBG_MASK_NONE) | (portW.at(i) == TXT_YES ? DBG_MASK_PORT_WRITE : DBG_MASK_NONE) | (portF.at(i) == TXT_YES ? DBG_MASK_PORT_FREEZE : DBG_MASK_NONE); - portAdd(static_cast(hex2int(portAddr.at(i))), mask, false); + portAdd(uint16_t(hex2int(portAddr.at(i))), mask, false); } // add all the equate files and load them in @@ -410,7 +429,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) { // This means the program is trying to send us a debug command. Let's see what we can do with that information if (reason >= DBG_NUMBER) { - debugExecute(static_cast(reason - DBG_PORT_RANGE), static_cast(data)); + debugExecute(uint32_t(reason - DBG_PORT_RANGE), uint8_t(data)); return; } @@ -439,13 +458,13 @@ void MainWindow::debugCommand(int reason, uint32_t data) { case DBG_WATCHPOINT_READ: case DBG_WATCHPOINT_WRITE: input = int2hex(data, 6); - addr = static_cast(hex2int(input)); + addr = uint32_t(hex2int(input)); type = (reason == DBG_WATCHPOINT_READ) ? tr("read") : tr("write"); text = tr("Hit ") + type + tr(" watchpoint ") + input; for (int i = 0; i < m_watchpoints->rowCount(); i++) { - uint32_t low = static_cast(hex2int(m_watchpoints->item(i, WATCH_LOW_COL)->text())); - uint32_t high = static_cast(hex2int(m_watchpoints->item(i, WATCH_HIGH_COL)->text())); + uint32_t low = uint32_t(hex2int(m_watchpoints->item(i, WATCH_LOW_COL)->text())); + uint32_t high = uint32_t(hex2int(m_watchpoints->item(i, WATCH_HIGH_COL)->text())); if (addr >= low && addr <= high) { label = m_watchpoints->item(row, WATCH_NAME_COL)->text(); valid = true; @@ -461,7 +480,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) { // Raise the debugger before going to the memory address so the memory docks are updated ui->debuggerLabel->setText(text); debugRaise(); - gotoMemAddrNoRaise(static_cast(hex2int(input))); + gotoMemAddrNoRaise(uint32_t(hex2int(input))); return; case DBG_PORT_READ: case DBG_PORT_WRITE: @@ -491,6 +510,20 @@ void MainWindow::debugCommand(int reason, uint32_t data) { } debugBasicRaise(); return; + case DBG_STEP: + if (!m_runLoc.invalid()) { + auto info = ui->sources->getLocInfo(data); + if (info.invalid()) { + debugStep(DBG_RUN_OUT); + return; + } + if (info.unknown() || info.sameSourceLoc(m_runLoc)) { + m_runLoc.end(info.end()); + debugStep(DBG_RUN_AGAIN); + return; + } + } + [[gnu::fallthrough]]; case DBG_USER: default: debugRaise(); @@ -510,26 +543,26 @@ void MainWindow::debugSync() const { } // Update all the changes in the core - cpu.registers.AF = static_cast(hex2int(ui->afregView->text())); - cpu.registers.HL = static_cast(hex2int(ui->hlregView->text())); - cpu.registers.DE = static_cast(hex2int(ui->deregView->text())); - cpu.registers.BC = static_cast(hex2int(ui->bcregView->text())); - cpu.registers.IX = static_cast(hex2int(ui->ixregView->text())); - cpu.registers.IY = static_cast(hex2int(ui->iyregView->text())); - - cpu.registers._AF = static_cast(hex2int(ui->af_regView->text())); - cpu.registers._HL = static_cast(hex2int(ui->hl_regView->text())); - cpu.registers._DE = static_cast(hex2int(ui->de_regView->text())); - cpu.registers._BC = static_cast(hex2int(ui->bc_regView->text())); - - cpu.registers.SPL = static_cast(hex2int(ui->splregView->text())); - cpu.registers.SPS = static_cast(hex2int(ui->spsregView->text())); - - cpu.registers.MBASE = static_cast(hex2int(ui->mbregView->text())); - cpu.registers.I = static_cast(hex2int(ui->iregView->text())); + cpu.registers.AF = uint16_t(hex2int(ui->afregView->text())); + cpu.registers.HL = uint32_t(hex2int(ui->hlregView->text())); + cpu.registers.DE = uint32_t(hex2int(ui->deregView->text())); + cpu.registers.BC = uint32_t(hex2int(ui->bcregView->text())); + cpu.registers.IX = uint32_t(hex2int(ui->ixregView->text())); + cpu.registers.IY = uint32_t(hex2int(ui->iyregView->text())); + + cpu.registers._AF = uint16_t(hex2int(ui->af_regView->text())); + cpu.registers._HL = uint32_t(hex2int(ui->hl_regView->text())); + cpu.registers._DE = uint32_t(hex2int(ui->de_regView->text())); + cpu.registers._BC = uint32_t(hex2int(ui->bc_regView->text())); + + cpu.registers.SPL = uint32_t(hex2int(ui->splregView->text())); + cpu.registers.SPS = uint16_t(hex2int(ui->spsregView->text())); + + cpu.registers.MBASE = uint8_t(hex2int(ui->mbregView->text())); + cpu.registers.I = uint16_t(hex2int(ui->iregView->text())); uint8_t r = hex2int(ui->rregView->text()); cpu.registers.R = uint8_t(r << 1 | r >> 7); - cpu.IM = static_cast(hex2int(ui->imregView->text())); + cpu.IM = uint8_t(hex2int(ui->imregView->text())); cpu.IM += !!cpu.IM; cpu.registers.flags.Z = ui->checkZ->isChecked(); @@ -551,7 +584,7 @@ void MainWindow::debugSync() const { debug.totalCycles = ui->cycleView->text().toLongLong() + (m_ignoreDmaCycles ? debug.dmaCycles : 0); debug.flashCacheMisses = ui->flashMissesView->text().toUInt(); - uint32_t uiPC = static_cast(hex2int(ui->pcregView->text())); + uint32_t uiPC = uint32_t(hex2int(ui->pcregView->text())); if (cpu.registers.PC != uiPC) { cpu_flush(uiPC, ui->checkADL->isChecked()); } @@ -560,11 +593,11 @@ void MainWindow::debugSync() const { backlight.factor = (310u - backlight.brightness) / 160.0f; panel.gammaDirty = true; - lcd.upbase = static_cast(hex2int(ui->lcdbaseView->text())); - lcd.upcurr = static_cast(hex2int(ui->lcdcurrView->text())); + lcd.upbase = uint32_t(hex2int(ui->lcdbaseView->text())); + lcd.upcurr = uint32_t(hex2int(ui->lcdcurrView->text())); lcd.control &= ~14u; - lcd.control |= static_cast(ui->bppView->currentIndex() << 1); + lcd.control |= unsigned(ui->bppView->currentIndex() << 1); set_reset(ui->checkPowered->isChecked(), 0x800u, lcd.control); set_reset(ui->checkBEPO->isChecked(), 0x400u, lcd.control); @@ -582,7 +615,7 @@ void MainWindow::debugSync() const { panel_update_clock_rate(); } - set_cpu_clock(static_cast(ui->freqView->text().toDouble() * 1e6)); + set_cpu_clock(uint32_t(ui->freqView->text().toDouble() * 1e6)); lcd_update(); @@ -854,8 +887,8 @@ void MainWindow::debugPopulate() { osUpdate(); stackUpdate(); - disasmUpdateAddr(m_prevDisasmAddr = cpu.registers.PC, true); - ui->sources->updatePC(m_prevDisasmAddr); + disasmUpdateAddr(cpu.registers.PC, true); + ui->sources->updatePC(); // Track step navigation: append on control-flow (branch taken), // replace on linear advance. Non-step stops do not modify history if (m_stepCtx.active) { @@ -912,7 +945,7 @@ void MainWindow::breakSetPrev(QTableWidgetItem *current, [[maybe_unused]] QTable } void MainWindow::breakRemoveRow(int row) { - uint32_t address = static_cast(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text())); + uint32_t address = uint32_t(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text())); changeDebugPoint(address, DBG_MASK_EXEC, false); if (!m_guiAdd && !m_useSoftCom) { @@ -933,7 +966,7 @@ void MainWindow::breakRemoveSelected() { void MainWindow::breakRemove(uint32_t address) { for (int row = 0; row < m_breakpoints->rowCount(); row++) { - uint32_t test = static_cast(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text())); + uint32_t test = uint32_t(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text())); if (address == test) { breakRemoveRow(row); break; @@ -983,7 +1016,7 @@ void MainWindow::breakModified(QTableWidgetItem *item) { return; } - addr = static_cast(hex2int(QString::fromStdString(s))); + addr = uint32_t(hex2int(QString::fromStdString(s))); addrStr = int2hex(addr, 6); m_breakpoints->blockSignals(true); @@ -1036,7 +1069,7 @@ void MainWindow::changeDebugPoint(quint32 address, unsigned type, bool state) { } void MainWindow::breakAddGui() { - uint32_t address = static_cast(hex2int(m_disasm->getSelectedAddr())); + uint32_t address = uint32_t(hex2int(m_disasm->getSelectedAddr())); QTextCursor c = m_disasm->textCursor(); c.setCharFormat(m_disasm->currentCharFormat()); @@ -1114,7 +1147,7 @@ bool MainWindow::breakAdd(const QString &label, uint32_t addr, bool enabled, boo connect(btnRemove, &QToolButton::clicked, this, &MainWindow::breakRemoveSelected); connect(btnEnable, &QToolButton::clicked, [this, btnEnable, itemAddr](bool checked) { - uint32_t addr = static_cast(hex2int(itemAddr->text())); + uint32_t addr = uint32_t(hex2int(itemAddr->text())); btnEnable->setIcon(checked ? m_iconCheck : m_iconCheckGray); changeDebugPoint(addr, DBG_MASK_EXEC, checked); disasmUpdate(); @@ -1167,7 +1200,7 @@ void MainWindow::portSetPrev(QTableWidgetItem *current, [[maybe_unused]] QTableW } void MainWindow::portRemoveRow(int row) const { - uint16_t port = static_cast(hex2int(m_ports->item(row, PORT_ADDR_COL)->text())); + uint16_t port = uint16_t(hex2int(m_ports->item(row, PORT_ADDR_COL)->text())); debug_ports(port, ~DBG_MASK_NONE, false); m_ports->removeRow(row); } @@ -1182,8 +1215,8 @@ void MainWindow::portRemoveSelected() { } void MainWindow::portPopulate(int currRow) const { - uint16_t port = static_cast(hex2int(m_ports->item(currRow, PORT_ADDR_COL)->text())); - uint8_t read = static_cast(port_peek_byte(port)); + uint16_t port = uint16_t(hex2int(m_ports->item(currRow, PORT_ADDR_COL)->text())); + uint8_t read = uint8_t(port_peek_byte(port)); m_ports->item(currRow, PORT_VALUE_COL)->setText(int2hex(read, 2)); } @@ -1291,7 +1324,7 @@ void MainWindow::portModified(QTableWidgetItem *item) { int col = item->column(); if (col == PORT_READ_COL || col == PORT_WRITE_COL || col == PORT_FREEZE_COL) { - uint16_t port = static_cast(hex2int(m_ports->item(row, PORT_ADDR_COL)->text())); + uint16_t port = uint16_t(hex2int(m_ports->item(row, PORT_ADDR_COL)->text())); unsigned int mask = DBG_MASK_NONE; if (col == PORT_READ_COL) { // Break on read @@ -1313,7 +1346,7 @@ void MainWindow::portModified(QTableWidgetItem *item) { return; } - uint16_t port = static_cast(hex2int(QString::fromStdString(s))); + uint16_t port = uint16_t(hex2int(QString::fromStdString(s))); uint8_t data = port_peek_byte(port); QString portStr = int2hex(port, 4); @@ -1338,8 +1371,8 @@ void MainWindow::portModified(QTableWidgetItem *item) { m_ports->item(row, PORT_VALUE_COL)->setText(int2hex(data, 2)); } else if (col == PORT_VALUE_COL) { if (m_ports->item(row, PORT_ADDR_COL)->text() != DEBUG_UNSET_PORT) { - uint8_t pdata = static_cast(hex2int(item->text())); - uint16_t port = static_cast(hex2int(m_ports->item(row, PORT_ADDR_COL)->text())); + uint8_t pdata = uint8_t(hex2int(item->text())); + uint16_t port = uint16_t(hex2int(m_ports->item(row, PORT_ADDR_COL)->text())); port_poke_byte(port, pdata); item->setText(int2hex(port_peek_byte(port), 2)); @@ -1368,8 +1401,8 @@ void MainWindow::watchSetPrev(QTableWidgetItem *current, [[maybe_unused]] QTable void MainWindow::watchRemoveRow(int row) { if (m_watchpoints->item(row, WATCH_LOW_COL)->text() != DEBUG_UNSET_ADDR && m_watchpoints->item(row, WATCH_HIGH_COL)->text() != DEBUG_UNSET_ADDR) { - uint32_t low = static_cast(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); - uint32_t high = static_cast(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); + uint32_t low = uint32_t(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); + uint32_t high = uint32_t(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); for (uint32_t addr = low; addr <= high; addr++) { changeDebugPoint(addr, DBG_MASK_READ | DBG_MASK_WRITE, false); @@ -1395,7 +1428,7 @@ void MainWindow::watchRemoveSelected() { void MainWindow::watchRemove(uint32_t address) { for (int row = 0; row < m_watchpoints->rowCount(); row++) { - uint32_t test = static_cast(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); + uint32_t test = uint32_t(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); if (address == test) { watchRemoveRow(row); break; @@ -1420,7 +1453,7 @@ void MainWindow::watchAddGuiRW() { void MainWindow::watchAddGui() { int mask = m_watchGUIMask; - uint32_t addr = static_cast(hex2int(m_disasm->getSelectedAddr())); + uint32_t addr = uint32_t(hex2int(m_disasm->getSelectedAddr())); QTextCursor c = m_disasm->textCursor(); c.setCharFormat(m_disasm->currentCharFormat()); @@ -1434,7 +1467,7 @@ void MainWindow::watchAddGui() { int32_t base = disasm.base; int32_t next = disasm.next; - disasm.base = static_cast(addr); + disasm.base = int32_t(addr); disasm.highlight.watchR = false; disasm.highlight.watchW = false; disasmGet(); @@ -1480,8 +1513,8 @@ void MainWindow::watchUpdate() { for (int row = 0; row < m_watchpoints->rowCount(); row++) { if (m_watchpoints->item(row, WATCH_LOW_COL)->text() != DEBUG_UNSET_ADDR && m_watchpoints->item(row, WATCH_HIGH_COL)->text() != DEBUG_UNSET_ADDR) { - uint32_t low = static_cast(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); - uint32_t high = static_cast(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); + uint32_t low = uint32_t(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); + uint32_t high = uint32_t(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); int mask = watchGetMask(row); for (uint32_t addr = low; addr <= high; addr++) { @@ -1500,8 +1533,8 @@ void MainWindow::watchUpdateRow(QTableWidgetItem *itemLow, QTableWidgetItem *ite // this is needed in the case of overlapping address spaces if (itemLow->text() != DEBUG_UNSET_ADDR && itemHigh->text() != DEBUG_UNSET_ADDR) { - uint32_t low = static_cast(hex2int(itemLow->text())); - uint32_t high = static_cast(hex2int(itemHigh->text())); + uint32_t low = uint32_t(hex2int(itemLow->text())); + uint32_t high = uint32_t(hex2int(itemHigh->text())); for (uint32_t addr = low; addr <= high; addr++) { changeDebugPoint(addr, DBG_MASK_READ | DBG_MASK_WRITE, false); @@ -1659,11 +1692,11 @@ void MainWindow::watchModified(QTableWidgetItem *item) { m_watchpoints->blockSignals(false); } - addr = static_cast(hex2int(QString::fromStdString(s))); + addr = uint32_t(hex2int(QString::fromStdString(s))); highStr = m_watchpoints->item(row, WATCH_HIGH_COL)->text(); if (isNotValidHex(s) || s.length() > 6 || - (highStr != DEBUG_UNSET_ADDR && addr > static_cast(hex2int(highStr)))) { + (highStr != DEBUG_UNSET_ADDR && addr > uint32_t(hex2int(highStr)))) { item->setText(m_prevWatchLow); m_watchpoints->blockSignals(false); return; @@ -1683,8 +1716,8 @@ void MainWindow::watchModified(QTableWidgetItem *item) { } if (m_prevWatchLow != DEBUG_UNSET_ADDR) { - uint32_t low = static_cast(hex2int(m_prevWatchLow)); - uint32_t high = static_cast(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); + uint32_t low = uint32_t(hex2int(m_prevWatchLow)); + uint32_t high = uint32_t(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text())); for (uint32_t watch_addr = low; watch_addr <= high; watch_addr++) { changeDebugPoint(watch_addr, DBG_MASK_READ | DBG_MASK_WRITE, false); @@ -1710,11 +1743,11 @@ void MainWindow::watchModified(QTableWidgetItem *item) { m_watchpoints->blockSignals(false); } - addr = static_cast(hex2int(QString::fromStdString(s))); + addr = uint32_t(hex2int(QString::fromStdString(s))); lowStr = m_watchpoints->item(row, WATCH_LOW_COL)->text(); if (isNotValidHex(s) || s.length() > 6 || - (lowStr != DEBUG_UNSET_ADDR && addr < static_cast(hex2int(lowStr)))) { + (lowStr != DEBUG_UNSET_ADDR && addr < uint32_t(hex2int(lowStr)))) { item->setText(m_prevWatchLow); m_watchpoints->blockSignals(false); return; @@ -1734,8 +1767,8 @@ void MainWindow::watchModified(QTableWidgetItem *item) { } if (m_prevWatchHigh != DEBUG_UNSET_ADDR) { - uint32_t low = static_cast(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); - uint32_t high = static_cast(hex2int(m_prevWatchHigh)); + uint32_t low = uint32_t(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text())); + uint32_t high = uint32_t(hex2int(m_prevWatchHigh)); for (uint32_t watch_addr = low; watch_addr <= high; watch_addr++) { changeDebugPoint(watch_addr, DBG_MASK_READ | DBG_MASK_WRITE, false); @@ -1762,7 +1795,7 @@ void MainWindow::batterySetCharging(bool checked) { } void MainWindow::batterySet(int value) const { - control.setBatteryStatus = static_cast(value); + control.setBatteryStatus = uint8_t(value); ui->sliderBattery->setValue(value); ui->labelBattery->setText(QString::number(value * 20) + "%"); } @@ -2278,16 +2311,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { QString val2 = QString::number(num2); if (num > 0x7FFFFF) { - val += QStringLiteral("\n\t") + QString::number(static_cast(num | 0xFF000000u)); + val += QStringLiteral("\n\t") + QString::number(int32_t(num | 0xFF000000u)); } if (num0 > 0x7F) { - val0 += QStringLiteral("\t") + QString::number(static_cast(num0)); + val0 += QStringLiteral("\t") + QString::number(int8_t(num0)); } if (num1 > 0x7F) { - val1 += QStringLiteral("\t") + QString::number(static_cast(num1)); + val1 += QStringLiteral("\t") + QString::number(int8_t(num1)); } if (num2 > 0x7F) { - val2 += QStringLiteral("\t") + QString::number(static_cast(num2)); + val2 += QStringLiteral("\t") + QString::number(int8_t(num2)); } if (name == QStringLiteral("afregView")) @@ -2413,7 +2446,7 @@ void MainWindow::osUpdate() { for (uint32_t j = i; j < i+11; j++) { uint8_t ch = mem_peek_byte(j); - array.append(static_cast(ch)); + array.append(char(ch)); if ((ch < 0x20) || (ch > 0x7e)) { ch = '.'; } @@ -2591,7 +2624,7 @@ void MainWindow::opModified(QTableWidgetItem *item) const { QString txt = item->text(); QString data; QByteArray array; - uint32_t addr = static_cast(hex2int(ui->opView->item(row, OP_ADDR_COL)->text())); + uint32_t addr = uint32_t(hex2int(ui->opView->item(row, OP_ADDR_COL)->text())); array.resize(11); sender()->blockSignals(true); @@ -2610,7 +2643,7 @@ void MainWindow::opModified(QTableWidgetItem *item) const { array.fill(0); try { data_t value = tivars::TypeHandlers::TH_GenericReal::makeDataFromString(txt.toStdString()); - for (int i = 0; i < 11 && i < static_cast(value.size()); i++) { + for (int i = 0; i < 11 && i < int(value.size()); i++) { array[i] = value[i]; } } catch(...) {} @@ -2625,7 +2658,7 @@ void MainWindow::opModified(QTableWidgetItem *item) const { for (uint32_t j = addr; j < addr + 11; j++) { uint8_t ch = mem_peek_byte(j); - array.append(static_cast(ch)); + array.append(char(ch)); if ((ch < 0x20) || (ch > 0x7e)) { ch = '.'; } @@ -2656,7 +2689,7 @@ void MainWindow::fpModified(QTableWidgetItem *item) const { QString txt = item->text(); QString data; QByteArray array; - uint32_t addr = static_cast(hex2int(ui->fpStack->item(row, FP_ADDR_COL)->text())); + uint32_t addr = uint32_t(hex2int(ui->fpStack->item(row, FP_ADDR_COL)->text())); array.resize(11); sender()->blockSignals(true); @@ -2675,7 +2708,7 @@ void MainWindow::fpModified(QTableWidgetItem *item) const { array.fill(0); try { data_t value = tivars::TypeHandlers::TH_GenericReal::makeDataFromString(txt.toStdString()); - for (int i = 0; i < 9 && i < static_cast(value.size()); i++) { + for (int i = 0; i < 9 && i < int(value.size()); i++) { array[i] = value[i]; } } catch(...) {} @@ -2690,7 +2723,7 @@ void MainWindow::fpModified(QTableWidgetItem *item) const { for (uint32_t j = addr; j < addr + 9; j++) { uint8_t ch = mem_peek_byte(j); - array.append(static_cast(ch)); + array.append(char(ch)); if ((ch < 0x20) || (ch > 0x7e)) { ch = '.'; } @@ -2729,7 +2762,7 @@ void MainWindow::contextOp(const QPoint &posa) { QAction *item = menu.exec(globalPos); if (item == gotoMem) { - gotoMemAddr(static_cast(hex2int(addr))); + gotoMemAddr(uint32_t(hex2int(addr))); } else if (item == copyAddr) { qApp->clipboard()->setText(addr); } else if (item == copyData) { @@ -2796,7 +2829,7 @@ void MainWindow::stepIn() { disconnect(m_shortcutStepIn, &QShortcut::activated, this, &MainWindow::stepIn); debugSync(); - debugStep(DBG_STEP_IN); + debugStep(ui->buttonToggleCStepping->isChecked() ? DBG_RUN_IN : DBG_STEP_IN); } void MainWindow::stepOver() { @@ -2807,7 +2840,7 @@ void MainWindow::stepOver() { disconnect(m_shortcutStepOver, &QShortcut::activated, this, &MainWindow::stepOver); debugSync(); - debugStep(DBG_STEP_OVER); + debugStep(ui->buttonToggleCStepping->isChecked() ? DBG_RUN_OVER : DBG_STEP_OVER); } void MainWindow::stepNext() { diff --git a/gui/qt/debugger/cdebughighlighter.cpp b/gui/qt/debugger/cdebughighlighter.cpp index c37eaf39d..154f30845 100644 --- a/gui/qt/debugger/cdebughighlighter.cpp +++ b/gui/qt/debugger/cdebughighlighter.cpp @@ -182,7 +182,7 @@ void CDebugHighlighter::highlightBlock(const QString &text) { case ParseState::MultilineComment: if (i == text.length()) { setFormat(start, i - start, m_sources->m_commentFormat); - } else if (i > start + 1 + (baseState != ParseState::MultilineComment) && + } else if (i > start + (baseState != ParseState::MultilineComment) && c == '/' && text[i - 1] == '*') { setFormat(start, i + 1 - start, m_sources->m_commentFormat); start = i + 1; diff --git a/gui/qt/debugger/debuginfo.cpp b/gui/qt/debugger/debuginfo.cpp new file mode 100644 index 000000000..dc122005b --- /dev/null +++ b/gui/qt/debugger/debuginfo.cpp @@ -0,0 +1,1904 @@ +#include "debuginfo.h" + +#define MY_DEBUG + +#ifdef MY_DEBUG +#include +#include +#endif + +namespace debuginfo { +namespace { +template constexpr Type consume(Data &data) noexcept { + auto res = data.complete(); + data = data.subview(sizeof(Type)); + return res; +} +_Str consume_strz(Data &data) noexcept { + _Str str = data.bitcast<_Str::element_type>(); + auto end = std::find(str.begin(), str.end(), '\0'); + if (end == str.end()) { + return ""; + } + return data.take_first(end - str.begin() + 1).bitcast<_Str::element_type>(); +} +} // end anonymous namespace + +namespace elf { +enum { + ELFCLASS32 = 1, + ELFDATA2LSB = 1, + ELFOSABI_STANDALONE = 255, + ET_EXEC = 2, + EM_Z80 = 220, + EF_Z80_EZ80 = 1, + EV_CURRENT = 1, + SHT_NULL = 0, + SHT_PROGBITS = 1, + SHT_SYMTAB = 2, + SHT_STRTAB = 3, + SHT_NOBITS = 8, + SHF_WRITE = 1 << 0, + SHF_ALLOC = 1 << 1, + SHF_EXECINSTR = 1 << 2, + SHF_MERGE = 1 << 4, + SHF_STRINGS = 1 << 5, +}; + +const File::Header File::Header::ez80 { + .ei_mag = { '\x7F', 'E','L','F' }, + .ei_class = ELFCLASS32, + .ei_data = ELFDATA2LSB, + .ei_version = EV_CURRENT, + .ei_osabi = ELFOSABI_STANDALONE, + .e_type = ET_EXEC, + .e_machine = EM_Z80, + .e_version = EV_CURRENT, + .e_flags = EF_Z80_EZ80, +}; + + +Data ProgramHeader::data(const File &file) const noexcept { + return file._M_data.subview(p_offset, p_filesz); +} + +_Str SectionHeader::name(const File &file) const noexcept { + auto name = file.section_headers()[file.header().e_shstrndx].data(file).subview(sh_name); + return consume_strz(name); +} +Data SectionHeader::data(const File &file) const noexcept { + if (sh_type == SHT_NOBITS) + return {}; + return file._M_data.subview(sh_offset, sh_size); +} +} // end namespace elf + +namespace dwarf { +void File::validate(const Header &expected) { + if (!elf::File::validate(expected)) + return error("Invalid ELF header"); + for (const auto &sh : section_headers()) { + auto name = sh.name(*this); + if (name == ".debug_abbrev") + _M_abbrev.data(sh.data(*this)); + else if (name == ".debug_addr") + _M_addr.data(sh.data(*this)); + else if (name == ".debug_frame") + _M_frame.data(sh.data(*this)); + else if (name == ".debug_info") + _M_info.data(sh.data(*this)); + else if (name == ".debug_line") + _M_line.data(sh.data(*this)); + else if (name == ".debug_line_str") + _M_line_str.data(sh.data(*this)); + else if (name == ".debug_line_str_offsets") + _M_line_str_offsets.data(sh.data(*this)); + else if (name == ".debug_loclists") + _M_loclists.data(sh.data(*this)); + else if (name == ".debug_rnglists") + _M_rnglists.data(sh.data(*this)); + else if (name == ".debug_str") + _M_str.data(sh.data(*this)); + else if (name == ".debug_str_offsets") + _M_str_offsets.data(sh.data(*this)); + } + _M_frame.__parse(); + _M_info.__parse(_M_line, _M_loclists, _M_rnglists); +} + +namespace section { +namespace { +template constexpr +std::enable_if_t::value, std::underlying_type_t> underlying(Enum e) noexcept { + return std::underlying_type_t(e); +} +constexpr _SizedIntegral consume_uleb128(Data &data) noexcept { + _Sbyte cur{}; + std::uintmax_t res{}; + std::size_t shift{}; + do { + cur = consume<_Sbyte>(data); + res |= (cur & 0x7F) << shift; + shift += std::numeric_limits<_Sbyte>::digits; + } while (cur < 0); + return { res, shift }; +} +constexpr _SizedIntegral consume_sleb128(Data &data) noexcept { + _Sbyte cur{}; + std::intmax_t res{}; + std::size_t shift{}; + do { + cur = consume<_Sbyte>(data); + res |= (cur & 0x7F) << shift; + shift += std::numeric_limits<_Sbyte>::digits; + } while (cur < 0); + if (shift <= std::numeric_limits::digits) { + res = res << (std::numeric_limits::digits - shift + 1) + >> (std::numeric_limits::digits - shift + 1); + } + return { res, shift }; +} +_Val consume_form(const Abbrev::_Attr &attr, Data &data, _Form &fixup) noexcept { + switch (attr._M_at) { + default: + break; + case _At::DW_AT_high_pc: + fixup = _Form::_HighPC; + break; + case _At::DW_AT_decl_file: + fixup = _Form::_DeclFile; + break; + } + + switch (auto cur = attr._M_form) { + case _Form::DW_FORM_strx3: + case _Form::DW_FORM_addrx3: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_addr: + return consume<_Addr>(data); + case _Form::DW_FORM_block2: + return data.take_first(consume<_Half>(data)); + case _Form::DW_FORM_block4: + return data.take_first(consume<_Word>(data)); + case _Form::DW_FORM_ref2: + case _Form::DW_FORM_strx2: + case _Form::DW_FORM_addrx2: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_data2: + return consume<_Half>(data); + case _Form::DW_FORM_ref4: + case _Form::DW_FORM_strx4: + case _Form::DW_FORM_addrx4: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_data4: + return consume<_Word>(data); + case _Form::DW_FORM_ref8: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_data8: + return consume<_Long>(data); + case _Form::DW_FORM_string: + return consume_strz(data); + case _Form::DW_FORM_block: + case _Form::DW_FORM_exprloc: + return data.take_first(consume_uleb128(data)); + case _Form::DW_FORM_block1: + return data.take_first(consume<_Byte>(data)); + case _Form::DW_FORM_ref1: + case _Form::DW_FORM_strx1: + case _Form::DW_FORM_addrx1: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_data1: + return consume<_Byte>(data); + case _Form::DW_FORM_flag: + return bool(consume<_Byte>(data)); + case _Form::DW_FORM_sdata: + return consume_sleb128(data); + case _Form::DW_FORM_strp: + case _Form::DW_FORM_ref_addr: + case _Form::DW_FORM_line_strp: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_sec_offset: + return consume<_Off>(data); + case _Form::DW_FORM_ref_udata: + case _Form::DW_FORM_strx: + case _Form::DW_FORM_addrx: + case _Form::DW_FORM_loclistx: + case _Form::DW_FORM_rnglistx: + fixup = cur; + [[gnu::fallthrough]]; + case _Form::DW_FORM_udata: + return consume_uleb128(data); + case _Form::DW_FORM_indirect: + if (fixup == _Form::_None) { + auto form = consume_uleb128(data); + if (form <= underlying(_Form::_None) || form >= underlying(_Form::_HighPC)) + return {}; + auto val = consume_form({ _At::_None, _Form(form.get()), {} }, data, cur); + if (cur != _Form::DW_FORM_indirect) + fixup = cur; + return val; + } + [[gnu::fallthrough]]; + default: + return {}; + case _Form::DW_FORM_flag_present: + return true; + case _Form::DW_FORM_data16: + return data.take_first(16); + case _Form::DW_FORM_implicit_const: + if (attr._M_at == _At::_None) + return {}; + return attr._M_implicit_const; + } +} +_Str strx(_Word index, const _Die &unit_entry, const File &file) noexcept { + auto str_offsets = file.str_offsets().get(unit_entry); + if (index >= str_offsets.size()) { + file.error("Out of range index into str offsets"); + return {}; + } + return file.str().get(str_offsets[index]); +} +_Addr addrx(_Word index, const _Die &unit_entry, const File &file) noexcept { + auto addr = file.addr().get(unit_entry); + if (index >= addr.size()) { + file.error("Out of range index into addr"); + return {}; + } + return addr[index]; +} + +#ifdef MY_DEBUG +template constexpr std::size_t size(const Type (&)[Extent]) noexcept { + return Extent; +} + +QDebug operator<<[[gnu::unused]](QDebug debug, _Tag tag) { + static const char *names[] = { + "_None", + "DW_TAG_array_type", + "DW_TAG_class_type", + "DW_TAG_entry_point", + "DW_TAG_enumeration_type", + "DW_TAG_formal_parameter", + "_Reserved", + "_Reserved", + "DW_TAG_imported_declaration", + "_Reserved", + "DW_TAG_label", + "DW_TAG_lexical_block", + "_Reserved", + "DW_TAG_member", + "_Reserved", + "DW_TAG_pointer_type", + "DW_TAG_reference_type", + "DW_TAG_compile_unit", + "DW_TAG_string_type", + "DW_TAG_structure_type", + "_Reserved", + "DW_TAG_subroutine_type", + "DW_TAG_typedef", + "DW_TAG_union_type", + "DW_TAG_unspecified_parameters", + "DW_TAG_variant", + "DW_TAG_common_block", + "DW_TAG_common_inclusion", + "DW_TAG_inheritance", + "DW_TAG_inlined_subroutine", + "DW_TAG_module", + "DW_TAG_ptr_to_member_type", + "DW_TAG_set_type", + "DW_TAG_subrange_type", + "DW_TAG_with_stmt", + "DW_TAG_access_declaration", + "DW_TAG_base_type", + "DW_TAG_catch_block", + "DW_TAG_const_type", + "DW_TAG_constant", + "DW_TAG_enumerator", + "DW_TAG_file_type", + "DW_TAG_friend", + "DW_TAG_namelist", + "DW_TAG_namelist_item", + "DW_TAG_packed_type", + "DW_TAG_subprogram", + "DW_TAG_template_type_parameter", + "DW_TAG_template_value_parameter", + "DW_TAG_thrown_type", + "DW_TAG_try_block", + "DW_TAG_variant_part", + "DW_TAG_variable", + "DW_TAG_volatile_type", + "DW_TAG_dwarf_procedure", + "DW_TAG_restrict_type", + "DW_TAG_interface_type", + "DW_TAG_namespace", + "DW_TAG_imported_module", + "DW_TAG_unspecified_type", + "DW_TAG_partial_unit", + "DW_TAG_imported_unit", + "_Reserved", + "DW_TAG_condition", + "DW_TAG_shared_type", + "DW_TAG_type_unit", + "DW_TAG_rvalue_reference_type", + "DW_TAG_template_alias", + "DW_TAG_coarray_type", + "DW_TAG_generic_subrange", + "DW_TAG_dynamic_type", + "DW_TAG_atomic_type", + "DW_TAG_call_site", + "DW_TAG_call_site_parameter", + "DW_TAG_skeleton_unit", + "DW_TAG_immutable_type", + "_Unknown", + }; + static_assert(size(names) == std::size_t(_Tag::_Unused), "Missing names"); + QDebugStateSaver saver(debug); + return debug.nospace().noquote() << Qt::left << qSetFieldWidth(31) + << (_Word(tag) < size(names) ? names[_Word(tag)] : "_Reserved"); +} +QDebug operator<<[[gnu::unused]](QDebug debug, _At at) { + const char *names[] = { + "_None", + "DW_AT_sibling", + "DW_AT_location", + "DW_AT_name", + "_Reserved", + "_Reserved", + "_Reserved", + "_Reserved", + "_Reserved", + "DW_AT_ordering", + "_Reserved", + "DW_AT_byte_size", + "_Reserved", + "DW_AT_bit_size", + "_Reserved", + "_Reserved", + "DW_AT_stmt_list", + "DW_AT_low_pc", + "DW_AT_high_pc", + "DW_AT_language", + "_Reserved", + "DW_AT_discr", + "DW_AT_discr_value", + "DW_AT_visibility", + "DW_AT_import", + "DW_AT_string_length", + "DW_AT_common_reference", + "DW_AT_comp_dir", + "DW_AT_const_value", + "DW_AT_containing_type", + "DW_AT_default_value", + "_Reserved", + "DW_AT_inline", + "DW_AT_is_optional", + "DW_AT_lower_bound", + "_Reserved", + "_Reserved", + "DW_AT_producer", + "_Reserved", + "DW_AT_prototyped", + "_Reserved", + "_Reserved", + "DW_AT_return_addr", + "_Reserved", + "DW_AT_start_scope", + "_Reserved", + "DW_AT_bit_stride", + "DW_AT_upper_bound", + "_Reserved", + "DW_AT_abstract_origin", + "DW_AT_accessibility", + "DW_AT_address_class", + "DW_AT_artificial", + "DW_AT_base_types", + "DW_AT_calling_convention", + "DW_AT_count", + "DW_AT_data_member_location", + "DW_AT_decl_column", + "DW_AT_decl_file", + "DW_AT_decl_line", + "DW_AT_declaration", + "DW_AT_discr_list", + "DW_AT_encoding", + "DW_AT_external", + "DW_AT_frame_base", + "DW_AT_friend", + "DW_AT_identifier_case", + "_Reserved", + "DW_AT_namelist_item", + "DW_AT_priority", + "DW_AT_segment", + "DW_AT_specification", + "DW_AT_static_link", + "DW_AT_type", + "DW_AT_use_location", + "DW_AT_variable_parameter", + "DW_AT_virtuality", + "DW_AT_vtable_elem_location", + "DW_AT_allocated", + "DW_AT_associated", + "DW_AT_data_location", + "DW_AT_byte_stride", + "DW_AT_entry_pc", + "DW_AT_use_UTF8", + "DW_AT_extension", + "DW_AT_ranges", + "DW_AT_trampoline", + "DW_AT_call_column", + "DW_AT_call_file", + "DW_AT_call_line", + "DW_AT_description", + "DW_AT_binary_scale", + "DW_AT_decimal_scale", + "DW_AT_small", + "DW_AT_decimal_sign", + "DW_AT_digit_count", + "DW_AT_picture_string", + "DW_AT_mutable", + "DW_AT_threads_scaled", + "DW_AT_explicit", + "DW_AT_object_pointer", + "DW_AT_endianity", + "DW_AT_elemental", + "DW_AT_pure", + "DW_AT_recursive", + "DW_AT_signature", + "DW_AT_main_subprogram", + "DW_AT_data_bit_offset", + "DW_AT_const_expr", + "DW_AT_enum_class", + "DW_AT_linkage_name", + "DW_AT_string_length_bit_size", + "DW_AT_string_length_byte_size", + "DW_AT_rank", + "DW_AT_str_offsets_base", + "DW_AT_addr_base", + "DW_AT_rnglists_base", + "_Reserved", + "DW_AT_dwo_name", + "DW_AT_reference", + "DW_AT_rvalue_reference", + "DW_AT_macros", + "DW_AT_call_all_calls", + "DW_AT_call_all_source_calls", + "DW_AT_call_all_tail_calls", + "DW_AT_call_return_pc", + "DW_AT_call_value", + "DW_AT_call_origin", + "DW_AT_call_parameter", + "DW_AT_call_pc", + "DW_AT_call_tail_call", + "DW_AT_call_target", + "DW_AT_call_target_clobbered", + "DW_AT_call_data_location", + "DW_AT_call_data_value", + "DW_AT_noreturn", + "DW_AT_alignment", + "DW_AT_export_symbols", + "DW_AT_deleted", + "DW_AT_defaulted", + "DW_AT_loclists_base", + "_Unknown", + }; + static_assert(size(names) == std::size_t(_At::_Unused), "Missing names"); + QDebugStateSaver saver(debug); + return debug.nospace().noquote() << Qt::left << qSetFieldWidth(30) + << (_Word(at) < size(names) ? names[_Word(at)] : "_Reserved"); +} +QDebug operator<<[[gnu::used]](QDebug debug, _Form form) { + const char *names[] = { + "_None", + "DW_FORM_addr", + "_Reserved", + "DW_FORM_block2", + "DW_FORM_block4", + "DW_FORM_data2", + "DW_FORM_data4", + "DW_FORM_data8", + "DW_FORM_string", + "DW_FORM_block", + "DW_FORM_block1", + "DW_FORM_data1", + "DW_FORM_flag", + "DW_FORM_sdata", + "DW_FORM_strp", + "DW_FORM_udata", + "DW_FORM_ref_addr", + "DW_FORM_ref1", + "DW_FORM_ref2", + "DW_FORM_ref4", + "DW_FORM_ref8", + "DW_FORM_ref_udata", + "DW_FORM_indirect", + "DW_FORM_sec_offset", + "DW_FORM_exprloc", + "DW_FORM_flag_present", + "DW_FORM_strx", + "DW_FORM_addrx", + "DW_FORM_ref_sup4", + "DW_FORM_strp_sup", + "DW_FORM_data16", + "DW_FORM_line_strp", + "DW_FORM_ref_sig8", + "DW_FORM_implicit_const", + "DW_FORM_loclistx", + "DW_FORM_rnglistx", + "DW_FORM_ref_sup8", + "DW_FORM_strx1", + "DW_FORM_strx2", + "DW_FORM_strx3", + "DW_FORM_strx4", + "DW_FORM_addrx1", + "DW_FORM_addrx2", + "DW_FORM_addrx3", + "DW_FORM_addrx4", + "_HighPC", + "_File", + }; + static_assert(size(names) == std::size_t(_Form::_Unused), "Missing names"); + QDebugStateSaver saver(debug); + return debug.nospace().noquote() << Qt::left << qSetFieldWidth(29) + << (_Word(form) < size(names) ? names[_Word(form)] : "_Reserved"); +} +QDebug operator<<[[gnu::unused]](QDebug debug, _Str str) { + return debug << QString::fromUtf8(str.data(), str.rtrim('\0').size()); +} +template QDebug operator<<[[gnu::unused]](QDebug debug, + const _Path &path) { + QString str; + for (auto c : path.canon()) + str += c; + return debug << str; +} +QDebug operator<<[[gnu::unused]](QDebug debug, _Addr addr) { + QDebugStateSaver saver(debug); + return debug << Qt::hex << Qt::showbase << _Word(addr); +} +QDebug operator<<[[gnu::unused]](QDebug debug, const _Die &entry); +QDebug operator<<[[gnu::unused]](QDebug debug, const _Val &val) { + QDebugStateSaver saver(debug); + if (val.is_none()) + return debug.noquote() << ""; + if (val.is_addr()) + return debug << val.addr(); + if (val.is_cst()) + return debug << Qt::hex << Qt::showbase << val.cst(); + if (val.is_data()) + return debug.noquote() << QByteArray::fromRawData( + val.data().bitcast<_Str::element_type>().data(), val.data().size()).toHex(' '); + if (val.is_file()) + return debug.quote() << val.file().path(); + if (val.is_flag()) + return debug << val.flag(); + if (val.is_ref()) + return debug.quote() << val.ref()._M_tag << val.ref(); + if (val.is_str()) + return debug.quote() << val.str(); + debug.nospace(); + if (val.is_loclist()) { + bool first = true; + for (auto loc : val.loclist()) { + if (first) + first = false; + else + debug << ", "; + debug << '[' << loc._M_range.first << ',' << loc._M_range.second << ") " << loc._M_expr; + } + return debug; + } + if (val.is_rnglist()) { + bool first = true; + for (auto rng : val.rnglist()) { + if (first) + first = false; + else + debug << ", "; + debug << '[' << rng.first << ',' << rng.second << ')'; + } + return debug; + } + return debug << ""; +} +QDebug operator<<[[gnu::unused]](QDebug debug, const _Die &entry) { + auto name = std::cref(entry.attr(_At::DW_AT_name)); + if (name.get().is_none()) + name = entry.attr(_At::DW_AT_abstract_origin).ref().attr(_At::DW_AT_name); + return debug << name; +} +#endif +} // end anonymous namespace + +const SrcFile SrcFile::_S_null; + +const _Val _Val::_S_none; + +const _Val &_Die::attr(_At at) const noexcept { + auto attr = _M_attrs.find(at); + if (attr != _M_attrs.end()) + return attr->second; + attr = _M_attrs.find(_At::DW_AT_abstract_origin); + if (attr != _M_attrs.end()) + return attr->second.ref().attr(at); + return _Val::_S_none; +} + +const _Die _Die::_S_null{}; + +auto Abbrev::get(_Off base) const noexcept -> std::unordered_map<_Word, _Entry> { + std::unordered_map<_Word, _Entry> abbrevs; + auto unit = _M_data.subview(base); + while (!error()) { + auto code = consume_uleb128(unit); + if (!code) { + return abbrevs; + } + if (_Slong(code) != _Sword(code)) { + error("Unsupported code value size"); + return {}; + } + _Entry entry; + auto tag = consume_uleb128(unit); + switch (tag) { + case underlying(_Tag::_None): + error("Invalid tag"); + return {}; + default: +#ifdef MY_DEBUG + if (tag >= underlying(_Tag::_Unknown)) { + qWarning() << "Unknown TAG" << Qt::hex << Qt::showbase << tag.get(); + } +#endif + entry._M_tag = _Tag(std::min(tag, decltype(tag)(_Tag::_Unknown)).get()); + break; + // Vendor Extensions + case DW_TAG_GNU_call_site: + entry._M_tag = _Tag::DW_TAG_call_site; + break; + case DW_TAG_GNU_call_site_parameter: + entry._M_tag = _Tag::DW_TAG_call_site_parameter; + break; + } + entry._M_children = consume<_Byte>(unit); +#ifdef MY_DEBUG_DISABLED + qDebug().nospace() << '[' << code.get() << "] " << entry._M_tag + << (entry._M_children ? " DW_CHILDREN_yes" : " DW_CHILDREN_no"); +#endif + while (!error()) { + auto type = consume_uleb128(unit), form = consume_uleb128(unit); + if (!type && !form) { + break; + } + auto at = _At(type.get()); + switch (type) { + case underlying(_At::_None): + error("Invalid at"); + return {}; + case underlying(_At::DW_AT_low_pc): + switch (tag) { + default: + at = _At::DW_AT_low_pc; + break; + case DW_TAG_GNU_call_site: + at = _At::DW_AT_call_return_pc; + break; + } + break; + case underlying(_At::DW_AT_abstract_origin): + switch (tag) { + default: + at = _At::DW_AT_abstract_origin; + break; + case DW_TAG_GNU_call_site: + at = _At::DW_AT_call_origin; + break; + } + break; + default: +#ifdef MY_DEBUG + if (type >= underlying(_At::_Unknown)) { + qWarning() << "Unknown AT" << Qt::hex << Qt::showbase << at; + } +#endif + at = _At(std::min(type, decltype(type)(_At::_Unknown)).get()); + break; + // Vendor Extensions + case DW_AT_GNU_call_site_value: + at = _At::DW_AT_call_value; + break; + case DW_AT_GNU_call_site_target: + at = _At::DW_AT_call_target; + break; + case DW_AT_GNU_tail_call: + at = _At::DW_AT_call_tail_call; + break; + case DW_AT_GNU_all_call_sites: + at = _At::DW_AT_call_all_calls; + break; + } + if (form <= underlying(_Form::_None) || form >= underlying(_Form::_HighPC)) { + error("Out of range abbrev attr form"); + return {}; + } + _Slong implicit_const{}; + if (_Form(form.get()) == _Form::DW_FORM_implicit_const) { + implicit_const = consume_sleb128(unit); + } + entry._M_attrs.push_back({ std::min(_At(at), _At::_Unknown), + _Form(form.get()), implicit_const }); +#ifdef MY_DEBUG_DISABLED + qDebug() << '\t' << entry._M_attrs.back()._M_at << entry._M_attrs.back()._M_form; +#endif + } +#ifdef MY_DEBUG_DISABLED + qDebug() << ""; +#endif + entry._M_attrs.shrink_to_fit(); + abbrevs.emplace(code, std::move(entry)); + } + return {}; +} + +_AddrView Addr::get(const _Die &unit_entry) const noexcept { + auto base = unit_entry.attr(_At::DW_AT_addr_base); + if (base.is_none()) { + return {}; + } + auto unit = _M_data.subview(base.cst() - sizeof(_Word) + - sizeof(_Half) - sizeof(_Byte) - sizeof(_Byte)); + auto unit_length = consume<_Word>(unit); + unit = unit.first(unit_length); + auto version = consume<_Half>(unit); + if (version < 5 || version > 5) { + error("Unsupported addr version"); + return {}; + } + auto address_size = consume<_Byte>(unit); + if (address_size != sizeof(_Addr)) { + error("Unsupported address size"); + return {}; + } + auto segment_selector_size = consume<_Byte>(unit); + if (segment_selector_size) { + error("Unsupported segment selector size"); + return {}; + } + return unit.bitcast(); +} + +const Frame::RuleSet Frame::RuleSet::_S_undef; + +void Frame::__parse() { + // Table 7.29: Call frame instruction encodings + enum { + DW_CFA_advance_loc = 0x1, + DW_CFA_offset = 0x2, + DW_CFA_restore = 0x3, + DW_CFA_nop = 0, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + // Vendor Extensions + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + }; + struct CIE { + _Word code_alignment_factor; + _Sword data_alignment_factor; + RuleSet initial_rules; + }; + std::unordered_map<_Off, CIE> cies; + + const auto evaluate_instructions = [&](const CIE &cie, RuleSet &rules, Data instructions, + _Word location = _InvalidWord) { + const auto set_loc = [&](_Addr new_location) { + if (location == _InvalidWord) + return error("Invalid location in initial instructions"); + _M_rules[std::exchange(location, new_location)] = rules; + }; + const auto advance_loc = [&](_Word advance) { + set_loc(location + advance * cie.code_alignment_factor); + }; + const auto parse_reg = [&](_Long reg) -> Reg { + if (reg >= decltype(reg)(Reg::None)) { + error("Unknown register"); + return Reg::None; + } + return Reg(reg); + }; + std::vector stack; + while (!instructions.empty() && !error()) { + Reg reg; + switch (auto opcode = consume<_Byte>(instructions)) { + case DW_CFA_nop: + break; + case DW_CFA_set_loc: + set_loc(consume<_Addr>(instructions)); + break; + case DW_CFA_advance_loc1: + advance_loc(consume<_Byte>(instructions)); + break; + case DW_CFA_advance_loc2: + advance_loc(consume<_Half>(instructions)); + break; + case DW_CFA_advance_loc4: + advance_loc(consume<_Word>(instructions)); + break; + case DW_CFA_offset_extended: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::off(consume_uleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_restore_extended: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = cie.initial_rules[reg]; + break; + case DW_CFA_undefined: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::undef(); + break; + case DW_CFA_same_value: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::same_val(); + break; + case DW_CFA_register: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::reg(parse_reg(consume_uleb128(instructions))); + break; + case DW_CFA_remember_state: + stack.push_back(rules); + break; + case DW_CFA_restore_state: + if (stack.empty()) + return error("Underflow state stack"); + rules = stack.back(); + stack.pop_back(); + break; + case DW_CFA_def_cfa: + reg = parse_reg(consume_uleb128(instructions)); + rules.cfa() = Rule::reg_off(reg, consume_uleb128(instructions)); + break; + case DW_CFA_def_cfa_register: + rules.cfa() = Rule::reg_off(parse_reg(consume_uleb128(instructions)), + rules.cfa().off()); + break; + case DW_CFA_def_cfa_offset: + rules.cfa() = Rule::reg_off(rules.cfa().reg(), consume_uleb128(instructions)); + break; + case DW_CFA_def_cfa_expression: + rules.cfa() = Rule::val_expr(*this, instructions.take_first( + consume_uleb128(instructions))); + break; + case DW_CFA_expression: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::expr(*this, instructions.take_first( + consume_uleb128(instructions))); + break; + case DW_CFA_offset_extended_sf: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::off(consume_sleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_def_cfa_sf: + reg = parse_reg(consume_uleb128(instructions)); + rules.cfa() = Rule::reg_off(reg, consume_sleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_def_cfa_offset_sf: + rules.cfa() = Rule::reg_off(rules.cfa().reg(), consume_sleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_val_offset: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::val_off(consume_uleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_val_offset_sf: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::val_off(consume_sleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_val_expression: + reg = parse_reg(consume_uleb128(instructions)); + rules[reg] = Rule::val_expr(*this, instructions.take_first( + consume_uleb128(instructions))); + break; + default: { + decltype(opcode) info = opcode & ((1 << 6) - 1); + switch (opcode >> 6) { + case DW_CFA_advance_loc: + advance_loc(info); + break; + case DW_CFA_offset: + reg = parse_reg(info); + rules[reg] = Rule::off(consume_uleb128(instructions) + * cie.data_alignment_factor); + break; + case DW_CFA_restore: + reg = parse_reg(info); + rules[reg] = cie.initial_rules[reg]; + break; + default: +#ifdef MY_DEBUG + qWarning() << "Unknown opcode" << Qt::hex << Qt::showbase << opcode; +#endif + return error("Unknown opcode"); + } + break; + } + } + } + if (location != _InvalidWord) { + _M_rules[location] = rules; + } + }; + + Data data = _M_data; + while (!data.empty() && !error()) { + _Off entry_offset = std::distance(_M_data.begin(), data.begin()); + auto length = consume<_Word>(data); + auto entry = data.take_first(length); + auto cie_offset = consume<_Off>(entry); + if (cie_offset == _InvalidOff) { + auto &cie = cies[entry_offset]; + auto version = consume<_Byte>(entry); + if (version < 1 || version == 2 || version > 4) + return error("Unsupported frame version"); + auto augmentation = consume_strz(entry); + if (augmentation != "") + return error("Unsupported augmentation"); + auto address_size = consume<_Byte>(entry); + if (address_size != sizeof(_Addr)) + return error("Unsupported address size"); + auto segment_selector_size = consume<_Byte>(entry); + if (segment_selector_size) + return error("Unsupported segment selector size"); + auto code_alignment_factor = consume_uleb128(entry); + cie.code_alignment_factor = code_alignment_factor; + if (cie.code_alignment_factor != code_alignment_factor) + return error("Unsupported code alignment factor"); + auto data_alignment_factor = consume_sleb128(entry); + cie.data_alignment_factor = data_alignment_factor; + if (cie.data_alignment_factor != data_alignment_factor) + return error("Unsupported data alignment factor"); + auto return_address_register = consume_uleb128(entry); + if (return_address_register >= decltype(return_address_register)(Reg::None)) + return error("Out of range register"); + auto initial_instructions = entry; + evaluate_instructions(cie, cie.initial_rules, initial_instructions); + } else { + auto it = cies.find(cie_offset); + if (it == cies.end()) + return error("Invalid CIE_pointer"); + const auto &cie = it->second; + auto initial_location = consume<_Addr>(entry); + auto address_range = consume<_Addr>(entry); + auto instructions = entry; + RuleSet rules = cie.initial_rules; + evaluate_instructions(cie, rules, instructions, initial_location); + _M_rules.emplace(initial_location + address_range, RuleSet::undef()); + } + } +} + +auto Frame::get(_Addr addr) const noexcept -> const RuleSet & { + auto it = _M_rules.upper_bound(addr); + if (it == _M_rules.begin()) + return RuleSet::undef(); + return (--it)->second; +} + +auto Info::_Scope::get(_Addr addr) const noexcept -> const _Scope & { + auto it = _M_children.upper_bound(addr); + if (it == _M_children.begin() || addr >= (--it)->second.first) + return _Scope::_S_null; + return it->second.second; +} + +void Info::_Scope::dump(int indent) const { +#ifdef MY_DEBUG + qDebug().noquote() << QString(indent, ' ') << _M_entry; + for (const auto &child : _M_children) { + qDebug().noquote().nospace() << QString(indent, ' ') + << '[' << child.first << ',' << child.second.first << ')'; + child.second.second.dump(indent + 1); + } +#endif +} + +const Info::_Scope Info::_Scope::_S_null; + +void Info::__parse(Line &line, LocLists &loclists, RngLists &rnglists) { + struct Fixup { + _Word entry; + _At at; + _Form form; + }; + struct Unit { + _Off offset; + std::vector fixups; + std::vector<_Word> file_map; + std::vector> loclists, rnglists; + }; + std::vector units; + std::unordered_map<_Off, _Word> entry_offsets; + + { + std::vector<_Word> stack{ _InvalidWord }; + Data data = _M_data; + while (!data.empty()) { + _Off unit_offset = std::distance(_M_data.begin(), data.begin()); + auto unit_length = consume<_Word>(data); + auto unit = data.take_first(unit_length); + auto version = consume<_Half>(unit); + if (version < 2 || version > 5) + return error("Unsupported info version"); + auto unit_type = version < 5 ? _Byte(DW_UT_compile) : consume<_Byte>(unit); + if (unit_type != DW_UT_compile) + return error("Unsupported unit type"); + _Byte address_size; + if (version >= 5) { + address_size = consume<_Byte>(unit); + } + auto abbrevs = _M_file.abbrev().get(consume<_Off>(unit)); + if (version < 5) { + address_size = consume<_Byte>(unit); + } + if (address_size != sizeof(_Addr)) + return error("Unsupported address size"); + + units.emplace_back(); + units.back().offset = unit_offset; + auto first = true; + Abbrev::_Attr early_fixups[] = { + { _At::DW_AT_name, _Form::_None, {} }, + { _At::DW_AT_comp_dir, _Form::_None, {} }, + { _At::DW_AT_low_pc, _Form::_None, {} }, + }; + while (!error() && !unit.empty()) { + _Off entry_offset = std::distance(_M_data.begin(), unit.begin()); + auto code = consume_uleb128(unit); + if (code) { + auto abbrev = abbrevs.find(code); + if (abbrev == abbrevs.end()) + return error("Missing abbrev code"); + if (first != (abbrev->second._M_tag == _Tag::DW_TAG_compile_unit)) + return error("Expected only the first entry to be a compile unit"); + + _Word entry_index = _M_entries.size(); + entry_offsets.emplace(entry_offset, entry_index); + if (stack.back() != _InvalidWord) { + _M_entries[stack.back()]._M_sibling = entry_index; + } + stack.back() = entry_index; + if (abbrev->second._M_children) { + stack.push_back(_InvalidWord); + } + + _M_entries.emplace_back(); + auto &entry = _M_entries.back(); + entry._M_tag = abbrev->second._M_tag; + entry._M_children = abbrev->second._M_children; + for (const auto &attr : abbrev->second._M_attrs) { + auto fixup = _Form::_None; + auto val = consume_form(attr, unit, fixup); + if (attr._M_at == _At::_Unknown) { + continue; + } + auto inserted = entry._M_attrs.emplace(attr._M_at, val); + if (!inserted.second) + return error("Duplicate attr"); + else if (val.is_none()) { +#ifdef MY_DEBUG + qWarning() << "Unsupported form" << attr._M_form; +#endif + return error("Unsupported form"); + } + if (fixup != _Form::_None) { + if (first) { + for (auto &early_fixup : early_fixups) { + if (attr._M_at == early_fixup._M_at) { + early_fixup._M_form = std::exchange(fixup, _Form::_None); + break; + } + } + } + if (fixup != _Form::_None) { + units.back().fixups.push_back({ entry_index, attr._M_at, fixup }); + } + } + } + + if (first) { + for (auto &early_fixup : early_fixups) { + if (early_fixup._M_form == _Form::_None) { + continue; + } + auto &val = entry._M_attrs[early_fixup._M_at]; + switch (early_fixup._M_form) { + case _Form::DW_FORM_strp: + val = _M_file.str().get(val.cst()); + break; + case _Form::DW_FORM_strx: + case _Form::DW_FORM_strx1: + case _Form::DW_FORM_strx2: + case _Form::DW_FORM_strx3: + case _Form::DW_FORM_strx4: + val = strx(val.cst(), entry, _M_file); + break; + case _Form::DW_FORM_addrx: + case _Form::DW_FORM_addrx1: + case _Form::DW_FORM_addrx2: + case _Form::DW_FORM_addrx3: + case _Form::DW_FORM_addrx4: + val = addrx(val.cst(), entry, _M_file); + break; + default: +#ifdef MY_DEBUG + qWarning() << "Unsupported early form" << early_fixup._M_form; +#endif + return error("Unsupported early form"); + } + } + units.back().file_map = line.__parse(address_size, entry); + units.back().loclists = loclists.__parse(entry); + units.back().rnglists = rnglists.__parse(entry); + } + first = false; + } else if (stack.size() > 1u) { + stack.pop_back(); + } + } + if (stack.size() != 1u) + return error("Unexpected end of info unit"); + + if (error()) return; + } + _M_entries.shrink_to_fit(); + line.shrink_to_fit(); + loclists.shrink_to_fit(); + rnglists.shrink_to_fit(); + } + + { + _Word unit_index{}; + for (const auto &unit : units) { + const auto &unit_entry = _M_entries[unit_index]; + auto addr = _M_file.addr().get(unit_entry); + auto str_offsets = _M_file.str_offsets().get(unit_entry); + + for (const auto &fixup : unit.fixups) { + if (error()) return; + + auto &val = _M_entries[fixup.entry]._M_attrs.at(fixup.at); + switch (fixup.form) { + case _Form::DW_FORM_strp: + val = _M_file.str().get(val.cst()); + break; + case _Form::DW_FORM_ref_addr: + case _Form::DW_FORM_ref1: + case _Form::DW_FORM_ref2: + case _Form::DW_FORM_ref4: + case _Form::DW_FORM_ref8: + case _Form::DW_FORM_ref_udata: { + auto base = fixup.form == _Form::DW_FORM_ref_addr ? 0u : unit.offset; + auto offset = entry_offsets.find(base + val.cst()); + if (offset == entry_offsets.end()) + return error("Unknown entry offset"); + val = _M_entries[offset->second]; + break; + } + case _Form::DW_FORM_strx: + case _Form::DW_FORM_strx1: + case _Form::DW_FORM_strx2: + case _Form::DW_FORM_strx3: + case _Form::DW_FORM_strx4: + if (val.cst() >= str_offsets.size()) + return error("Out of range index into str offsets"); + val = _M_file.str().get(str_offsets[val.cst()]); + break; + case _Form::DW_FORM_addrx: + case _Form::DW_FORM_addrx1: + case _Form::DW_FORM_addrx2: + case _Form::DW_FORM_addrx3: + case _Form::DW_FORM_addrx4: + if (val.cst() >= addr.size()) + return error("Out of range index into addr"); + val = addr[val.cst()]; + break; + case _Form::DW_FORM_line_strp: + val = _M_file.line_str().get(val.cst()); + break; + case _Form::DW_FORM_loclistx: + if (val.cst() >= unit.loclists.size()) + return error("Out of range index into location lists"); + val = _M_file.loclists().get(unit.loclists[val.cst()]); + break; + case _Form::DW_FORM_rnglistx: + if (val.cst() >= unit.rnglists.size()) + return error("Out of range index into range lists is"); + val = _M_file.rnglists().get(unit.rnglists[val.cst()]); + break; + case _Form::_HighPC: + if (val.is_cst()) { + val = _M_entries[fixup.entry].attr(_At::DW_AT_low_pc).addr() + _Addr(val.cst()); + } + break; + case _Form::_DeclFile: + if (val.cst() >= unit.file_map.size()) + return error("Out of range index into files"); + val = _M_file.line().get(unit.file_map[val.cst()]); + break; + default: + return error("Unknown fixup"); + } + } + unit_index = _M_entries[unit_index]._M_sibling; + } + } + + { + std::vector, + std::reference_wrapper<_Scope>>> stack{ + { _Die::_S_null, _M_scopes.front() } + }; + bool children = true; + for (const _Die &entry : _M_entries) { + if (!children) { + bool sibling = false; + while (!sibling) { + assert(stack.size() > 1 && "Invalid tree structure"); + sibling = stack.back().first.get()._M_sibling; + stack.pop_back(); + } + } + children = entry._M_children; + switch (entry._M_tag) { + case _Tag::DW_TAG_compile_unit: + break; + case _Tag::DW_TAG_subprogram: + if (!(entry.attr(_At::DW_AT_declaration).flag() || + entry.attr(_At::DW_AT_inline).cst() & 1)) + break; + stack.emplace_back(entry, _M_scopes.front()); + continue; + case _Tag::DW_TAG_lexical_block: + case _Tag::DW_TAG_inlined_subroutine: + if (stack.back().second.get().entry()._M_tag != _Tag::_None) + break; + [[gnu::fallthrough]]; + default: + switch (entry._M_tag) { + case _Tag::DW_TAG_label: + case _Tag::DW_TAG_variable: + stack.back().second.get().add(entry); + break; + default: + break; + } + stack.emplace_back(entry, stack.back().second); + continue; + } + _M_scopes.emplace_back(_M_scopes.back(), entry); + const auto &low_pc = entry.attr(_At::DW_AT_low_pc); + const auto &high_pc = entry.attr(_At::DW_AT_high_pc); + const auto &ranges = entry.attr(_At::DW_AT_ranges); + if (low_pc.is_addr() && high_pc.is_addr()) + stack.back().second.get().set(low_pc.addr(), high_pc.addr(), _M_scopes.back()); + else if (ranges.is_rnglist()) + for (const auto &rng : ranges.rnglist()) + stack.back().second.get().set(rng.first, rng.second, _M_scopes.back()); + stack.emplace_back(entry, _M_scopes.back()); + } + } + +#ifdef MY_DEBUG_DISABLED + root().dump(); +#endif + +#ifdef MY_DEBUG_DISABLED + if (!error()) { + for (std::size_t i{}; i != _M_file.line().size(); ++i) { + const auto &file = _M_file.line().get(i); + auto debug = qDebug() << file.path() << ':'; + for (auto part : file.path()) + debug << part; + } + qDebug() << ""; + } +#endif + +#ifdef MY_DEBUG_DISABLED + if (!error()) { + for (const auto &entry : _M_entries) { + switch (entry._M_tag) { + default: break; // whatever + case _Tag::DW_TAG_compile_unit: + qDebug() << entry._M_tag << ':' << entry._M_attrs.at(_At::DW_AT_low_pc) + << '-' << entry._M_attrs.at(_At::DW_AT_high_pc); + break; + } + } + } +#endif + +#ifdef MY_DEBUG_DISABLED + if (!error()) { + std::vector stack; + QString indent; + for (const auto &entry : _M_entries) { + qDebug().noquote().nospace() << indent << entry._M_tag; + for (const auto &attr : entry._M_attrs) + qDebug().noquote().nospace() << indent << " " << attr.first + << '(' << attr.second << ')'; + if (entry._M_children) { + indent += " "; + stack.push_back(entry._M_sibling); + } else if (!entry._M_sibling && !stack.empty()) { + bool cur; + do { + indent.chop(2); + cur = stack.back(); + stack.pop_back(); + } while (!cur && !stack.empty()); + } + } + } +#endif +} + +std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { + std::vector<_Word> file_map; + auto base = unit_entry.attr(_At::DW_AT_stmt_list); + if (base.is_none()) { + return file_map; + } + auto unit = _M_data.subview(base.cst()); + auto unit_length = consume<_Word>(unit); + unit = unit.first(unit_length); + auto version = consume<_Half>(unit); + if (version < 2 || version > 5) { + error("Unsupported line version"); + return {}; + } + _Byte segment_selector_size{}; + if (version >= 5) { + address_size = consume<_Byte>(unit); + segment_selector_size = consume<_Byte>(unit); + } + if (address_size != sizeof(_Addr)) { + error("Unsupported address size"); + return {}; + } + if (segment_selector_size) { + error("Unsupported segment selector size"); + return {}; + } + auto header_length = consume<_Word>(unit); + auto header = unit.take_first(header_length); + auto minimum_instruction_length = consume<_Byte>(header); + if (minimum_instruction_length != 1) { + error("Unsupported minimum instruction length"); + return {}; + } + auto maximum_operations_per_instruction = version < 4 ? 1u : consume<_Byte>(header); + if (maximum_operations_per_instruction != 1) { + error("Unsupported maximum operations per instruction"); + return {}; + } + auto default_is_stmt = consume<_Byte>(header); + auto line_base = consume<_Sbyte>(header); + auto line_range = consume<_Byte>(header); + auto opcode_base = consume<_Byte>(header); + if (!opcode_base) { + error("Invalid opcode base"); + return {}; + } + auto standard_opcode_lengths = header.take_first(opcode_base - 1).bitcast(); + auto parse_header_entries = [&](std::initializer_list<_EntryFormat> default_format, + _At default_path_attr) -> std::vector<_Entry> { + auto entry_format_count = version < 5 ? default_format.size() : consume<_Byte>(header); + std::array<_EntryFormat, 5> entry_format; + if (entry_format_count > entry_format.size()) { + error("Unsupported entry format count"); + return {}; + } + if (version < 5) { + std::copy_n(default_format.begin(), entry_format_count, entry_format.begin()); + } else { + for (decltype(entry_format_count) i{}; i != entry_format_count; i++) { + entry_format[i].first = _Lnct(consume_uleb128(header).get()); + entry_format[i].second = _Form(consume_uleb128(header).get()); + } + } + auto entries_count = version < 5 ? 0u : consume_uleb128(header).get(); + std::vector<_Entry> entries; + entries.reserve(entries_count); + if (version < 5) { + entries.emplace_back(); + entries.back()._M_path = unit_entry.attr(default_path_attr).str(); + entries.back()._M_directory_index = 0; + } + while (version < 5 || entries.size() < entries_count) { + if (header.empty()) { + error("Unexpected end of header"); + return {}; + } + _Entry entry; + for (decltype(entry_format_count) i{}; i != entry_format_count; i++) { + auto fixup = _Form::_Unused; + auto val = consume_form({ _At::_None, entry_format[i].second, 0 }, header, fixup); + if (fixup != _Form::_Unused) { + error("Unexpected fixup in line header"); + return {}; + } + switch (entry_format[i].first) { + case _Lnct::DW_LNCT_path: + if (!val.is_str()) { + error("Unexpected form for path"); + return {}; + } + if (version < 5 && val.str() == "") { + return entries; + } + entry._M_path = val.str(); + break; + case _Lnct::DW_LNCT_directory_index: + if (!val.is_cst()) { + error("Unexpected form for directory index"); + return {}; + } + entry._M_directory_index = val.cst(); + break; + case _Lnct::DW_LNCT_timestamp: + if (!val.is_cst() && !val.is_data()) { + error("Unexpected form for timestamp"); + return {}; + } + entry._M_timestamp = val.cst(); + break; + case _Lnct::DW_LNCT_size: + if (!val.is_cst()) { + error("Unexpected form for size"); + return {}; + } + entry._M_size = val.cst(); + break; + case _Lnct::DW_LNCT_MD5: + if (!val.is_data()) { + error("Unexpected form for MD5"); + return {}; + } + if (val.data().size_bytes() != 16) { + error("Unexpected size for MD5"); + return {}; + } + entry._M_MD5 = val.data(); + break; + default: + error("Unsupported line number content description"); + return {}; + } + } + entries.emplace_back(std::move(entry)); + } + return entries; + }; + auto directories = parse_header_entries({ + { _Lnct::DW_LNCT_path, _Form::DW_FORM_string }, + }, _At::DW_AT_comp_dir); + auto file_names = parse_header_entries({ + { _Lnct::DW_LNCT_path, _Form::DW_FORM_string }, + { _Lnct::DW_LNCT_directory_index, _Form::DW_FORM_udata }, + { _Lnct::DW_LNCT_timestamp, _Form::DW_FORM_udata }, + { _Lnct::DW_LNCT_size, _Form::DW_FORM_udata }, + }, _At::DW_AT_name); + file_map.reserve(file_names.size()); + for (const auto &file : file_names) { + _Str dir; + if (file._M_directory_index < directories.size()) { + dir = directories[file._M_directory_index]._M_path; + } + auto inserted = _M_paths.insert({ { unit_entry.attr(_At::DW_AT_comp_dir).str(), + dir, file._M_path }, size() }); + if (inserted.second) { + _M_files.emplace_back(inserted.first->first, file._M_MD5.data()); + } + file_map.push_back(inserted.first->second); + } + + _Loc state; + auto reset_state = [&]() { + state._M_file = 1; + state._M_line = 1; + state._M_column = 0; + state._M_address = 0; + state._M_is_stmt = default_is_stmt; + state._M_basic_block = false; + state._M_end_sequence = false; + state._M_prologue_end = false; + state._M_epilogue_begin = false; + state._M_isa = 0; + state._M_discriminator = 0; + }; + reset_state(); + auto emit_loc = [&]() { + if (state._M_file >= file_map.size()) + return error("Out of bounds file index"); + auto file = file_map[state._M_file]; +#ifdef MY_DEBUG_DISABLED + qDebug() << _M_files[file].path() << state._M_line << state._M_column + << Qt::showbase << Qt::hex << _Word(state._M_address); +#endif + auto inserted_loc = _M_locs.emplace(state._M_address, state); + if (!inserted_loc.second && + inserted_loc.first->second._M_end_sequence) { + inserted_loc.first->second = state; + inserted_loc.second = true; + } + if (inserted_loc.second) { + inserted_loc.first->second._M_file = file; + if (state._M_is_stmt) { + auto inserted_line = _M_files[file].lines().insert( + { { state._M_line, state._M_column }, state._M_address }); + if (!inserted_line.second && + inserted_line.first->second > state._M_address) { + inserted_line.first->second = state._M_address; + } + } + } + state._M_basic_block = false; + state._M_prologue_end = false; + state._M_epilogue_begin = false; + state._M_discriminator = 0; + }; + + while (!error() && !unit.empty()) { + auto opc = consume<_Byte>(unit); + switch (opc) { + default: + if (opc >= opcode_base) { + // 1. special opcodes + opc -= opcode_base; + state._M_address += opc / line_range + / maximum_operations_per_instruction + * minimum_instruction_length; + state._M_line += line_base + opc % line_range; + emit_loc(); + } else { + // 2. standard opcodes + for (auto i = standard_opcode_lengths[opc - 1]; i; --i) { + (void)consume_uleb128(unit); + } + } + break; + case DW_LNS_copy: + emit_loc(); + break; + case DW_LNS_advance_pc: + state._M_address += consume_uleb128(unit).get(); + break; + case DW_LNS_advance_line: + state._M_line += consume_sleb128(unit); + break; + case DW_LNS_set_file: + state._M_file = consume_uleb128(unit); + break; + case DW_LNS_set_column: + state._M_column = consume_uleb128(unit); + break; + case DW_LNS_negate_stmt: + state._M_is_stmt = !state._M_is_stmt; + break; + case DW_LNS_set_basic_block: + state._M_basic_block = true; + break; + case DW_LNS_const_add_pc: + state._M_address += (0xFFu - opcode_base) / line_range + / maximum_operations_per_instruction * minimum_instruction_length; + break; + case DW_LNS_fixed_advance_pc: + state._M_address += consume<_Half>(unit); + break; + case DW_LNS_set_prologue_end: + state._M_prologue_end = true; + break; + case DW_LNS_set_epilogue_begin: + state._M_epilogue_begin = true; + break; + case DW_LNS_set_isa: + state._M_isa = consume_uleb128(unit); + break; + case DW_LNS_extended: { + auto len = consume_uleb128(unit); + auto instruction = unit.take_first(len); + switch (opc = consume<_Byte>(instruction)) { + // 3. extended opcodes + case DW_LNE_end_sequence: + state._M_end_sequence = true; + emit_loc(); + reset_state(); + break; + case DW_LNE_set_address: { + state._M_address = consume<_Addr>(instruction); + break; + } + case DW_LNE_set_discriminator: + state._M_discriminator = consume_uleb128(instruction); + break; + } + break; + } + } + } + return file_map; +} +const SrcFile &Line::get(_Files::size_type pos) const noexcept { + if (pos >= size()) { + error("Index into files out of bounds"); + return SrcFile::_S_null; + } + return _M_files[pos]; +} + +std::vector> LocLists::__parse(const _Die &unit_entry) { + std::vector> indices; + auto base = unit_entry.attr(_At::DW_AT_loclists_base); + if (base.is_none()) { + return indices; + } + auto unit = _M_data.subview(base.cst() - sizeof(_Word) + - sizeof(_Half) - sizeof(_Byte) - sizeof(_Byte) - sizeof(_Word)); + auto unit_length = consume<_Word>(unit); + unit = unit.first(unit_length); + auto version = consume<_Half>(unit); + if (version < 5 || version > 5) { + error("Unsupported loclists version"); + return {}; + } + auto address_size = consume<_Byte>(unit); + if (address_size != sizeof(_Addr)) { + error("Unsupported address size"); + return {}; + } + auto segment_selector_size = consume<_Byte>(unit); + if (segment_selector_size) { + error("Unsupported segment selector size"); + return {}; + } + auto offset_entry_count = consume<_Word>(unit); + auto addr = _M_file.addr().get(unit_entry); + auto base_addr = unit_entry.attr(_At::DW_AT_low_pc).addr(); + for (auto offset : unit.bitcast().first(offset_entry_count)) { + if (error()) { + return {}; + } + + indices.emplace_back(_M_locs.size(), _InvalidWord); + auto loclist = unit.subview(offset); + while (!error() && indices.back().second == _InvalidWord) { + _Long base_index, start_index, end_index; + _Addr start_addr, end_addr; + switch (consume<_Byte>(loclist)) { + case DW_LLE_end_of_list: + indices.back().second = _M_locs.size(); + continue; + case DW_LLE_base_addressx: + base_index = consume_uleb128(loclist); + if (base_index >= addr.size()) { + error("Out of range index into addr"); + return {}; + } + base_addr = addr[base_index]; + continue; + case DW_LLE_startx_endx: + start_index = consume_uleb128(loclist); + end_index = consume_uleb128(loclist); + if (start_index >= addr.size() || + end_index >= addr.size()) { + error("Out of range index into addr"); + return {}; + } + start_addr = addr[start_index]; + end_addr = addr[end_index]; + break; + case DW_LLE_startx_length: + start_index = consume_uleb128(loclist); + if (start_index >= addr.size()) { + error("Out of range index into addr"); + return {}; + } + start_addr = addr[start_index]; + end_addr = start_addr + _Addr(consume_uleb128(loclist)); + break; + case DW_LLE_offset_pair: + start_addr = base_addr + _Addr(consume_uleb128(loclist)); + end_addr = base_addr + _Addr(consume_uleb128(loclist)); + break; + case DW_LLE_default_location: + _M_locs.push_back({ { std::numeric_limits<_Addr>::min(), + std::numeric_limits<_Addr>::max() }, {} }); + continue; + case DW_LLE_base_address: + base_addr = consume<_Addr>(loclist); + continue; + case DW_LLE_start_end: + start_addr = consume<_Addr>(loclist); + end_addr = consume<_Addr>(loclist); + break; + case DW_LLE_start_length: + start_addr = consume<_Addr>(loclist); + end_addr = start_addr + consume<_Addr>(loclist); + break; + default: + error("Unknown range list entry kind"); + return {}; + } + _M_locs.push_back({ { start_addr, end_addr }, + loclist.take_first(consume_uleb128(loclist)) }); + } + } + return indices; +} +_LocView LocLists::get(std::pair<_Word, _Word> indices) const noexcept { + if (indices.first > indices.second || indices.first > _M_locs.size() + || indices.second > _M_locs.size()) { + error("Out of range index into location offsets"); + return {}; + } + return { _M_locs.data() + indices.first, _M_locs.data() + indices.second }; +} + +std::vector> RngLists::__parse(const _Die &unit_entry) { + std::vector> indices; + auto base = unit_entry.attr(_At::DW_AT_rnglists_base); + if (base.is_none()) { + return indices; + } + auto unit = _M_data.subview(base.cst() - sizeof(_Word) + - sizeof(_Half) - sizeof(_Byte) - sizeof(_Byte) - sizeof(_Word)); + auto unit_length = consume<_Word>(unit); + unit = unit.first(unit_length); + auto version = consume<_Half>(unit); + if (version < 5 || version > 5) { + error("Unsupported rnglists version"); + return {}; + } + auto address_size = consume<_Byte>(unit); + if (address_size != sizeof(_Addr)) { + error("Unsupported address size"); + return {}; + } + auto segment_selector_size = consume<_Byte>(unit); + if (segment_selector_size) { + error("Unsupported segment selector size"); + return {}; + } + auto offset_entry_count = consume<_Word>(unit); + auto addr = _M_file.addr().get(unit_entry); + auto base_addr = unit_entry.attr(_At::DW_AT_low_pc).addr(); + for (auto offset : unit.bitcast().first(offset_entry_count)) { + if (error()) { + return {}; + } + + indices.emplace_back(_M_rngs.size(), _InvalidWord); + auto rnglist = unit.subview(offset); + while (!error() && indices.back().second == _InvalidWord) { + _Long base_index, start_index, end_index; + _Addr start_addr, end_addr; + switch (consume<_Byte>(rnglist)) { + case DW_RLE_end_of_list: + indices.back().second = _M_rngs.size(); + continue; + case DW_RLE_base_addressx: + base_index = consume_uleb128(rnglist); + if (base_index >= addr.size()) { + error("Out of range index into addr"); + return {}; + } + base_addr = addr[base_index]; + continue; + case DW_RLE_startx_endx: + start_index = consume_uleb128(rnglist); + end_index = consume_uleb128(rnglist); + if (start_index >= addr.size() || + end_index >= addr.size()) { + error("Out of range index into addr"); + return {}; + } + start_addr = addr[start_index]; + end_addr = addr[end_index]; + break; + case DW_RLE_startx_length: + start_index = consume_uleb128(rnglist); + if (start_index >= addr.size()) { + error("Out of range index into addr"); + return {}; + } + start_addr = addr[start_index]; + end_addr = start_addr + _Addr(consume_uleb128(rnglist)); + break; + case DW_RLE_offset_pair: + start_addr = base_addr + _Addr(consume_uleb128(rnglist)); + end_addr = base_addr + _Addr(consume_uleb128(rnglist)); + break; + case DW_RLE_base_address: + base_addr = consume<_Addr>(rnglist); + continue; + case DW_RLE_start_end: + start_addr = consume<_Addr>(rnglist); + end_addr = consume<_Addr>(rnglist); + break; + case DW_RLE_start_length: + start_addr = consume<_Addr>(rnglist); + end_addr = start_addr + consume<_Addr>(rnglist); + break; + default: + error("Unknown range list entry kind"); + return {}; + } + _M_rngs.emplace_back(start_addr, end_addr); + } + } + return indices; +} +_RngView RngLists::get(std::pair<_Word, _Word> indices) const noexcept { + if (indices.first > indices.second || indices.first > _M_rngs.size() + || indices.second > _M_rngs.size()) { + error("Out of range index into range offsets"); + return {}; + } + return { _M_rngs.data() + indices.first, _M_rngs.data() + indices.second }; +} + +_Str Str::get(_Word offset) const noexcept { + Data temp(_M_data.subview(offset)); + return consume_strz(temp); +} + +_OffView StrOffsets::get(const _Die &unit_entry) const noexcept { + auto base = unit_entry.attr(_At::DW_AT_str_offsets_base); + if (base.is_none()) { + return {}; + } + auto unit = _M_data.subview(base.cst() - sizeof(_Word) - sizeof(_Half) - sizeof(_Half)); + unit = unit.first(consume<_Word>(unit)); + auto version = consume<_Half>(unit); + if (version < 5 || version > 5) { + error("Unsupported str offsets version"); + return {}; + } + consume<_Half>(unit); + return unit.bitcast<_OffView::element_type>(); +} +} // end namespace section +} // end namespace dwarf +} // end namespace debuginfo diff --git a/gui/qt/debugger/debuginfo.h b/gui/qt/debugger/debuginfo.h new file mode 100644 index 000000000..e4fc90ce8 --- /dev/null +++ b/gui/qt/debugger/debuginfo.h @@ -0,0 +1,1687 @@ +#ifndef DEBUGINFO_H +#define DEBUGINFO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace debuginfo { +enum class _Void : unsigned char {}; +using _Sbyte = std::int8_t; +using _Byte = std::uint8_t; +using _Shalf = std::int16_t; +using _Half = std::uint16_t; +using _Off = std::uint32_t; +using _Sword = std::int32_t; +using _Word = std::uint32_t; +using _Slong = std::int64_t; +using _Long = std::uint64_t; + +class [[gnu::packed]] _Addr { + std::array<_Byte, 3> _M_addr; + +public: + constexpr _Addr() : _M_addr() {} + constexpr _Addr(_Word __value) noexcept + : _M_addr{ _Byte(__value >> 0), _Byte(__value >> 8), _Byte(__value >> 16) } {} + constexpr _Addr( _Long __value) noexcept : _Addr(_Word(__value)) {} + constexpr _Addr(_Sword __value) noexcept : _Addr(_Word(__value)) {} + constexpr _Addr(_Slong __value) noexcept : _Addr(_Word(__value)) {} + _Addr &operator=(_Word __value) { + _M_addr[0] = __value >> 0; + _M_addr[1] = __value >> 8; + _M_addr[2] = __value >> 16; + return *this; + } + _Addr &operator=( _Long __value) { return *this = _Word(__value); } + _Addr &operator=(_Sword __value) { return *this = _Word(__value); } + _Addr &operator=(_Slong __value) { return *this = _Word(__value); } + constexpr operator _Word() const noexcept { + return _M_addr[0] << 0 | _M_addr[1] << 8 | _M_addr[2] << 16; + } + constexpr operator _Long() const noexcept { return _Word(*this); } + constexpr operator _Sword() const noexcept { + return _M_addr[0] << 0 | _M_addr[1] << 8 | _Sword(_M_addr[2] << 24) >> 8; + } + constexpr operator _Slong() const noexcept { return _Sword(*this); } + + constexpr _Addr operator+() const noexcept { return +_Word(*this); } + constexpr _Addr operator+(_Addr __addr) const noexcept { return _Word(*this) + _Word(__addr); } + constexpr _Addr &operator+=(_Addr __addr) noexcept { return *this = *this + __addr; } + constexpr _Addr &operator++() noexcept { return *this += 1; } + constexpr _Addr operator++(int) noexcept { _Addr __res(*this); ++(*this); return __res; } + constexpr _Addr operator-() const noexcept { return -_Word(*this); } + constexpr _Addr operator-(_Addr __addr) const noexcept { return _Word(*this) - _Word(__addr); } + constexpr _Addr &operator-=(_Addr __addr) noexcept { return *this = *this - __addr; } + constexpr _Addr &operator--() noexcept { return *this -= 1; } + constexpr _Addr operator--(int) noexcept { _Addr __res(*this); --(*this); return __res; } + constexpr _Addr operator*(_Addr __addr) const noexcept { return _Word(*this) * _Word(__addr); } + constexpr _Addr &operator*=(_Addr __addr) noexcept { return *this = *this * __addr; } + constexpr _Addr operator/(_Addr __addr) const noexcept { return _Word(*this) / _Word(__addr); } + constexpr _Addr &operator/=(_Addr __addr) noexcept { return *this = *this / __addr; } + constexpr _Addr operator%(_Addr __addr) const noexcept { return _Word(*this) % _Word(__addr); } + constexpr _Addr &operator%=(_Addr __addr) noexcept { return *this = *this % __addr; } + + constexpr _Addr operator^(_Addr __addr) const noexcept { return _Word(*this) ^ _Word(__addr); } + constexpr _Addr &operator^=(_Addr __addr) noexcept { return *this = *this ^ __addr; } + constexpr _Addr operator&(_Addr __addr) const noexcept { return _Word(*this) & _Word(__addr); } + constexpr _Addr &operator&=(_Addr __addr) noexcept { return *this = *this & __addr; } + constexpr _Addr operator|(_Addr __addr) const noexcept { return _Word(*this) | _Word(__addr); } + constexpr _Addr &operator|=(_Addr __addr) noexcept { return *this = *this | __addr; } + constexpr _Addr operator~() const noexcept { return ~_Word(*this); } + + constexpr _Addr operator<<(_Addr __addr) const noexcept { return _Word(*this) << _Word(__addr); } + constexpr _Addr &operator<<=(_Addr __addr) noexcept { return *this = *this << __addr; } + constexpr _Addr operator>>(_Addr __addr) const noexcept { return _Word(*this) >> _Word(__addr); } + constexpr _Addr &operator>>=(_Addr __addr) noexcept { return *this = *this >> __addr; } + + constexpr bool operator!() const noexcept { return !_Word(*this); } + constexpr bool operator==(_Addr __addr) const noexcept { return _Word(*this) == _Word(__addr); } + constexpr bool operator!=(_Addr __addr) const noexcept { return _Word(*this) != _Word(__addr); } + constexpr bool operator< (_Addr __addr) const noexcept { return _Word(*this) < _Word(__addr); } + constexpr bool operator> (_Addr __addr) const noexcept { return _Word(*this) > _Word(__addr); } + constexpr bool operator<=(_Addr __addr) const noexcept { return _Word(*this) <= _Word(__addr); } + constexpr bool operator>=(_Addr __addr) const noexcept { return _Word(*this) >= _Word(__addr); } +}; +static_assert(sizeof(_Addr) == 3, "_Addr should be 3 bytes"); +constexpr _Off _InvalidOff = std::numeric_limits<_Off>::max(); +constexpr _Word _InvalidWord = std::numeric_limits<_Word>::max(); + +template class _View { +public: + using element_type = _Type; + using value_type = std::decay_t>; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = element_type *; + using const_pointer = const element_type *; + using reference = element_type &; + using const_reference = const element_type &; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator; + using const_iterator = const_pointer; + using const_reverse_iterator = std::reverse_iterator; + + static const _View _S_empty; + + constexpr _View() noexcept : _M_size(0), _M_ptr(nullptr) {} + constexpr _View(iterator __first, size_type __count) noexcept : _M_size(__count), _M_ptr(__first) {} + constexpr _View(iterator __first, iterator __last) noexcept : _M_size(std::distance(__first, __last)), _M_ptr(__first) {} + template _View(element_type (&__arr)[_Size]) noexcept : _M_size(_Size), _M_ptr(__arr) {} + ~_View() noexcept = default; + constexpr _View(const _View &) noexcept = default; + _View &operator=(const _View &) noexcept = default; + + constexpr iterator begin() const noexcept { return _M_ptr; } + constexpr iterator end() const noexcept { return _M_ptr + _M_size; } + constexpr const_iterator cbegin() const noexcept { return _M_ptr; } + constexpr const_iterator cend() const noexcept { return _M_ptr + _M_size; } + constexpr reverse_iterator rbegin() const noexcept { return end(); } + constexpr reverse_iterator rend() const noexcept { return begin(); } + constexpr const_reverse_iterator crbegin() const noexcept { return cend(); } + constexpr const_reverse_iterator crend() const noexcept { return cbegin(); } + + constexpr reference front() const noexcept { assert(!empty()); return _M_ptr[0]; } + constexpr value_type front_value() const noexcept { if (empty()) return {}; return front(); } + constexpr reference back() const noexcept { assert(!empty()); return _M_ptr[_M_size - 1]; } + constexpr value_type back_value() const noexcept { if (empty()) return {}; return back(); } + constexpr reference operator[](size_type __pos) const noexcept { assert(__pos < _M_size); return _M_ptr[__pos]; } + constexpr value_type value(size_type __pos) const noexcept { if (__pos < _M_size) return (*this)[__pos]; return {}; } + constexpr pointer data() const noexcept { return _M_ptr; } + + constexpr size_type size() const noexcept { return _M_size; } + constexpr size_type size_bytes() const noexcept { return _M_size * size_type(sizeof(element_type)); } + constexpr bool empty() const noexcept { return !_M_size; } + + constexpr _View first(size_type __count) const noexcept { return { _M_ptr, std::min(_M_size, __count) }; } + constexpr _View last(size_type __count) const noexcept { return { _M_ptr + (_M_size - std::min(_M_size, __count)), std::min(_M_size, __count) }; } + constexpr _View subview(size_type __offset, size_type __count = std::numeric_limits::max()) const noexcept { return { _M_ptr + __offset, std::min(_M_size - std::min(_M_size, __offset), __count) }; } + + template constexpr _View<_Tp> bitcast() const noexcept { return { reinterpret_cast::pointer>(_M_ptr), size_bytes() / size_type(sizeof(_Tp)) }; } + + constexpr bool operator==(const _View &__view) const noexcept { + return std::equal(begin(), end(), __view.begin(), __view.end()); + } + constexpr bool operator!=(const _View &__view) const noexcept { return !(*this == __view); } + constexpr bool operator< (const _View &__view) const noexcept { + return std::lexicographical_compare(begin(), end(), __view.begin(), __view.end()); + } + constexpr bool operator> (const _View &__view) const noexcept { return __view < *this; } + constexpr bool operator<=(const _View &__view) const noexcept { return !(*this < __view); } + constexpr bool operator>=(const _View &__view) const noexcept { return !(__view < *this); } + + constexpr void drop_first(size_type __count) noexcept { + __count = std::min(_M_size, __count); + _M_size -= __count; + _M_ptr += __count; + } + constexpr _View take_first(size_type __count) noexcept { + _View __res = first(__count); + drop_first(__count); + return __res; + } + constexpr void drop_last(size_type __count) noexcept { + _M_size -= std::min(_M_size, __count); + } + constexpr _View take_last(size_type __count) noexcept { + _View __res = last(__count); + drop_last(__count); + return __res; + } + + constexpr _View ltrim(value_type __trim) const noexcept { + _View __res(*this); + while (!__res.empty() && __res.front() == __trim) { + __res.drop_first(1); + } + return __res; + } + constexpr _View rtrim(value_type __trim) const noexcept { + _View __res(*this); + while (!__res.empty() && __res.back() == __trim) { + __res.drop_last(1); + } + return __res; + } + + template constexpr _Long partial() const noexcept { + _Tp __res{}; + std::memcpy(&__res, _M_ptr, std::min(_M_size, size_type(sizeof(_Tp)))); + return __res; + } + template constexpr _Long complete() const noexcept { + _Tp __res{}; + if (size_bytes() >= sizeof(_Tp)) { + std::memcpy(&__res, _M_ptr, std::min(_M_size, size_type(sizeof(_Tp)))); + } + return __res; + } + +private: + size_type _M_size; + pointer _M_ptr; +}; +template const _View<_Type> _View<_Type>::_S_empty; + +using Data = _View; +using _Str = _View; +using _OffView = _View; +using _AddrView = _View; + +template class _Path { + using _Parts = std::array; + +public: + static const _Path _S_null; + static constexpr _Str::value_type sep = +#ifdef _WIN32 + '\\'; +#else + '/'; +#endif + static constexpr bool is_sep(_Str::value_type __c) noexcept { return __c == sep || __c == '/'; } + static constexpr bool is_abs(_Str __part) noexcept { + return is_sep(__part.value(0)) +#ifdef _WIN32 + || (__part.value(1) == ':' && is_sep(__part.value(2))) +#endif + ; + } + + using value_type = typename _Parts::value_type; + using size_type = typename _Parts::size_type; + using difference_type = typename _Parts::difference_type; + using reference = typename _Parts::reference; + using const_reference = typename _Parts::const_reference; + using pointer = typename _Parts::pointer; + using const_pointer = typename _Parts::const_pointer; + using iterator = typename _Parts::iterator; + using const_iterator = typename _Parts::const_iterator; + using reverse_iterator = typename _Parts::reverse_iterator; + using const_reverse_iterator = typename _Parts::const_reverse_iterator; + + constexpr _Path() noexcept = default; + constexpr _Path(std::initializer_list<_Str> __parts) noexcept { + auto __it = _M_parts.begin(); + for (auto __part : __parts) { + if (__part.rtrim('\0').empty()) continue; + if (is_abs(__part)) { + __it = _M_parts.begin(); + } + if (__it == _M_parts.end()) { + __it = _M_parts.begin(); + break; + } + *__it++ = __part; + } + while (__it != _M_parts.end()) { + *__it++ = {}; + } + } + + constexpr const_reference at(size_type __pos) const { return _M_parts[__pos]; } + constexpr const_reference operator[](size_type __pos) const noexcept { + if (__pos < _M_parts.size()) + return _M_parts[__pos]; + return _Str::_S_empty; + } + constexpr const_reference front() const noexcept { return (*this)[0]; } + constexpr const_reference back() const noexcept { return (*this)[size()]; } + pointer data() const noexcept { return _M_parts.data(); } + + auto begin() const noexcept { return _M_parts.begin(); } + auto end() const noexcept { + return std::find_if(_M_parts.begin(), _M_parts.end(), + [](const_reference __part) { return __part.empty(); }); + } + auto cbegin() const noexcept { return const_iterator(begin()); } + auto cend() const noexcept { return const_iterator(end()); } + auto rbegin() const noexcept { return reverse_iterator(end()); } + auto rend() const noexcept { return reverse_iterator(begin()); } + auto crbegin() const noexcept { return const_reverse_iterator(rbegin()); } + auto crend() const noexcept { return const_reverse_iterator(rend()); } + + constexpr bool empty() const noexcept { return _M_parts.front().empty(); } + constexpr size_type size() const noexcept { return std::distance(begin(), end()); } + constexpr size_type max_size() const noexcept { return _MaxParts; } + bool is_abs() const noexcept { return is_abs(front()); } + + constexpr bool operator==(const _Path &__path) const noexcept { + return std::equal(canon().begin(), canon().end(), + __path.canon().begin(), __path.canon().end()); + } + constexpr bool operator!=(const _Path &__path) const noexcept { return !(*this == __path); } + constexpr bool operator< (const _Path &__path) const noexcept { + return std::lexicographical_compare(canon().begin(), canon().end(), + __path.canon().begin(), __path.canon().end()); + } + constexpr bool operator> (const _Path &__path) const noexcept { return __path < *this; } + constexpr bool operator<=(const _Path &__path) const noexcept { return !(*this < __path); } + constexpr bool operator>=(const _Path &__path) const noexcept { return !(__path < *this); } + +private: + class _Canon { + friend _Path; + + enum class _State : bool { _AtSep, _Default }; + + constexpr _Canon(const _Path &__path) noexcept : _M_path(__path) {} + + public: + using value_type = _Str::value_type; + using size_type = _Str::size_type; + using difference_type = _Str::difference_type; + using reference = _Str::reference; + using const_reference = _Str::const_reference; + using pointer = _Str::pointer; + using const_pointer = _Str::const_pointer; + + class iterator { + friend _Canon; + + constexpr auto tie() const noexcept { return std::tie(_M_pos, _M_it, _M_state); } + constexpr const _Str &part() const noexcept { return _M_path[_M_pos]; } + constexpr void next() noexcept { + while (_M_it != part().end() && is_sep(*_M_it)) { + ++_M_it; + _M_state = _State::_AtSep; + } + while (_M_it == part().end() || *_M_it == '\0') { + ++_M_pos; + _M_it = part().begin(); + if (part().empty()) { + _M_pos = _MaxParts; + return; + } + _M_state = _State::_AtSep; + } + } + + constexpr iterator(const _Path &__path, _Path::size_type __pos) noexcept + : _M_path(__path), _M_pos(__pos), _M_it(part().begin()) { next(); } + + public: + using value_type = std::iterator_traits<_Str::iterator>::value_type; + using difference_type = std::iterator_traits<_Str::iterator>::difference_type; + using pointer = std::iterator_traits<_Str::iterator>::pointer; + using reference = std::iterator_traits<_Str::iterator>::reference; + using iterator_category = std::forward_iterator_tag; + + constexpr iterator() : _M_path(_S_null), _M_pos(), _M_it() {} + + constexpr reference operator*() const noexcept { + switch (_M_state) { + case _State::_AtSep: + return sep; + case _State::_Default: + return *_M_it; + } + } + constexpr pointer operator->() const noexcept { return &*this; } + constexpr iterator &operator++() noexcept { + if (std::exchange(_M_state, _State::_Default) == _State::_Default) { + ++_M_it; + next(); + } + return *this; + } + constexpr iterator operator++(int) noexcept { auto __res(*this); ++*this; return __res; } + + constexpr bool operator==(const iterator &__it) const noexcept { return tie() == __it.tie(); } + constexpr bool operator!=(const iterator &__it) const noexcept { return tie() != __it.tie(); } + constexpr bool operator< (const iterator &__it) const noexcept { return tie() < __it.tie(); } + constexpr bool operator> (const iterator &__it) const noexcept { return tie() > __it.tie(); } + constexpr bool operator<=(const iterator &__it) const noexcept { return tie() <= __it.tie(); } + constexpr bool operator>=(const iterator &__it) const noexcept { return tie() >= __it.tie(); } + + private: + const _Path &_M_path; + _Path::size_type _M_pos; + _Str::iterator _M_it; + _State _M_state = _State::_Default; + }; + using const_iterator = iterator; + + constexpr iterator begin() const noexcept { return { _M_path, 0 }; } + constexpr iterator end() const noexcept { return { _M_path, _MaxParts }; } + constexpr const_iterator cbegin() const noexcept { return begin(); } + constexpr const_iterator cend() const noexcept { return end(); } + + private: + const _Path &_M_path; + }; + +public: + constexpr _Canon canon() const noexcept { return *this; } + +private: + std::array<_Str, _MaxParts> _M_parts; +}; +template const _Path<_MaxParts> _Path<_MaxParts>::_S_null; +template const _Str::value_type _Path<_MaxParts>::sep; +} // end namespace debuginfo + +namespace std { +template<> struct numeric_limits : numeric_limits { + static constexpr int digits = numeric_limits::digits * sizeof(debuginfo::_Addr); + static constexpr int digits10 = M_LN2 / M_LN10 * digits; + static_assert(digits == 24 && digits10 == 7, "Miscalculated _Addr digits"); + + static constexpr debuginfo::_Addr min() noexcept { + return numeric_limits::min(); + } + static constexpr debuginfo::_Addr lowest() noexcept { + return numeric_limits::lowest(); + } + static constexpr debuginfo::_Addr max() noexcept { + return numeric_limits::max(); + } + static constexpr debuginfo::_Addr epsilon() noexcept { + return numeric_limits::epsilon(); + } + static constexpr debuginfo::_Addr round_error() noexcept { + return numeric_limits::round_error(); + } + static constexpr debuginfo::_Addr infinity() noexcept { + return numeric_limits::infinity(); + } + static constexpr debuginfo::_Addr quiet_NaN() noexcept { + return numeric_limits::quiet_NaN(); + } + static constexpr debuginfo::_Addr signaling_NaN() noexcept { + return numeric_limits::signaling_NaN(); + } + static constexpr debuginfo::_Addr denorm_min() noexcept { + return numeric_limits::denorm_min(); + } +}; +template<> struct numeric_limits : numeric_limits {}; +template<> struct numeric_limits< volatile debuginfo::_Addr> : numeric_limits {}; +template<> struct numeric_limits : numeric_limits {}; + +template<> struct hash { + constexpr std::size_t operator()(debuginfo::_Str __str) const noexcept { + debuginfo::_Word __hash = 5381; + for (auto c : __str.rtrim('\0')) { + __hash = __hash * 33 + debuginfo::_Byte(c); + } + return __hash; + } +}; + +template struct hash> { + constexpr std::size_t operator()(const debuginfo::_Path<_MaxParts> &__path) const noexcept { + debuginfo::_Word __hash = 5381; + for (auto c : __path.canon()) { + __hash = __hash * 33 + debuginfo::_Byte(c); + } + return __hash; + } +}; +} // end namespace std + +namespace debuginfo { +namespace elf { +class File; + +struct [[gnu::packed]] ProgramHeader { + _Word p_type; + _Off p_offset; + _Word p_vaddr; + _Word p_paddr; + _Word p_filesz; + _Word p_memsz; + _Word p_flags; + _Word p_align; + + Data data(const File &___file) const noexcept; +}; + +struct [[gnu::packed]] SectionHeader { + _Word sh_name; + _Word sh_type; + _Word sh_flags; + _Word sh_addr; + _Off sh_offset; + _Word sh_size; + _Word sh_link; + _Word sh_info; + _Word sh_addralign; + _Word sh_entsize; + + _Str name(const File &__file) const noexcept; + Data data(const File &___file) const noexcept; +}; + +class File { + friend SectionHeader; + friend ProgramHeader; + + constexpr bool has_header() const noexcept { return _M_data.size_bytes() >= sizeof(Header); } + + template constexpr _View<_Header> get( + _Off __offset, _Half __size, _Half __count) const noexcept { + if (__size != sizeof(_Header)) { + return {}; + } + return _M_data.subview(__offset).bitcast<_Header>().first(__count); + } + +public: + struct Header { + _Byte ei_mag[4]; + _Byte ei_class; + _Byte ei_data; + _Byte ei_version; + _Byte ei_osabi; + _Byte ei_pad[8]; + _Half e_type; + _Half e_machine; + _Word e_version; + _Word e_entry; + _Off e_phoff; + _Off e_shoff; + _Word e_flags; + _Half e_ehsize; + _Half e_phentsize; + _Half e_phnum; + _Half e_shentsize; + _Half e_shnum; + _Half e_shstrndx; + + const static Header ez80; + }; + + explicit File(Data __data) noexcept : _M_data(__data) {} + + constexpr auto &header() const noexcept { + assert(has_header()); + return _M_data.bitcast().front(); + } + + constexpr bool validate(const Header &__expected) const noexcept { + return has_header() && !std::memcmp(&header(), &__expected, offsetof(Header, e_entry)) && + (header().e_flags & __expected.e_flags) == __expected.e_flags && + header().e_ehsize == sizeof(Header) && header().e_shstrndx < header().e_shnum; + } + + constexpr auto program_headers() const noexcept { + return get(header().e_phoff, header().e_phentsize, header().e_phnum); + } + + constexpr auto section_headers() const noexcept { + return get(header().e_shoff, header().e_shentsize, header().e_shnum); + } + +protected: + const Data _M_data; +}; +} // end namespace elf + +namespace dwarf { +enum class Reg : _Byte { BC, DE, HL, AF, IX, IY, SPS, SPL, PC, None }; + +// Table 7.3: Tag encodings +enum class _Tag : _Byte { + _None, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + // For Internal Use + _Unknown, + _Unused +}; +enum { + // Vendor Extensions + DW_TAG_lo_user = 0x4080, + DW_TAG_GNU_call_site = 0x4109, + DW_TAG_GNU_call_site_parameter = 0x410a, + DW_TAG_hi_user = 0xffff, +}; +// Table 7.5: Attribute encodings +enum class _At : _Byte { + _None, + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // exprloc, loclist + DW_AT_name = 0x03, // string + DW_AT_ordering = 0x09, // constant + DW_AT_byte_size = 0x0b, // constant, exprloc, reference + DW_AT_bit_size = 0x0d, // constant, exprloc, reference + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address, constant + DW_AT_language = 0x13, // constant + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // exprloc, loclist, reference + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // constant, reference, flag + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // constant, exprloc, reference + DW_AT_producer = 0x25, // string + DW_AT_prototyped = 0x27, // flag + DW_AT_return_addr = 0x2a, // exprloc, loclist + DW_AT_start_scope = 0x2c, // constant, rnglist + DW_AT_bit_stride = 0x2e, // constant, exprloc, reference + DW_AT_upper_bound = 0x2f, // constant, exprloc, reference + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // constant, exprloc, reference + DW_AT_data_member_location = 0x38, // constant, exprloc, loclist + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // exprloc, loclist + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constantmacptr + DW_AT_namelist_item = 0x44, // reference + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // exprloc, loclist + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // exprloc, loclist + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // exprloc, loclist + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // exprloc, loclist + DW_AT_allocated = 0x4e, // constant, exprloc, reference + DW_AT_associated = 0x4f, // constant, exprloc, reference + DW_AT_data_location = 0x50, // exprloc + DW_AT_byte_stride = 0x51, // constant, exprloc, reference + DW_AT_entry_pc = 0x52, // address, constant + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rnglist + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + DW_AT_signature = 0x69, // reference + DW_AT_main_subprogram = 0x6a, // flag + DW_AT_data_bit_offset = 0x6b, // constant + DW_AT_const_expr = 0x6c, // flag + DW_AT_enum_class = 0x6d, // flag + DW_AT_linkage_name = 0x6e, // string + DW_AT_string_length_bit_size = 0x6f, // constant + DW_AT_string_length_byte_size = 0x70, // constant + DW_AT_rank = 0x71, // constant, exprloc + DW_AT_str_offsets_base = 0x72, // stroffsetsptr + DW_AT_addr_base = 0x73, // addrptr + DW_AT_rnglists_base = 0x74, // rnglistsptr + DW_AT_dwo_name = 0x76, // string + DW_AT_reference = 0x77, // flag + DW_AT_rvalue_reference = 0x78, // flag + DW_AT_macros = 0x79, // macptr + DW_AT_call_all_calls = 0x7a, // flag + DW_AT_call_all_source_calls = 0x7b, // flag + DW_AT_call_all_tail_calls = 0x7c, // flag + DW_AT_call_return_pc = 0x7d, // address + DW_AT_call_value = 0x7e, // exprloc + DW_AT_call_origin = 0x7f, // exprloc + DW_AT_call_parameter = 0x80, // reference + DW_AT_call_pc = 0x81, // address + DW_AT_call_tail_call = 0x82, // flag + DW_AT_call_target = 0x83, // exprloc + DW_AT_call_target_clobbered = 0x84, // exprloc + DW_AT_call_data_location = 0x85, // exprloc + DW_AT_call_data_value = 0x86, // exprloc + DW_AT_noreturn = 0x87, // flag + DW_AT_alignment = 0x88, // constant + DW_AT_export_symbols = 0x89, // flag + DW_AT_deleted = 0x8a, // flag + DW_AT_defaulted = 0x8b, // constant + DW_AT_loclists_base = 0x8c, // loclistsptr + // For Internal Use + _Unknown, + _Unused +}; +enum { + // Vendor Extensions + DW_AT_lo_user = 0x2000, + DW_AT_GNU_call_site_value = 0x2111, + DW_AT_GNU_call_site_target = 0x2113, + DW_AT_GNU_tail_call = 0x2115, + DW_AT_GNU_all_call_sites = 0x2117, + DW_AT_hi_user = 0x3fff, +}; +// Table 7.6: Attribute form encodings +enum class _Form : _Byte { + _None, + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, + // For Internal Use + _HighPC, + _DeclFile, + _Unused +}; +// Table 7.9: DWARF operation encodings +enum { + _None, + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_fram_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + // Vendor Extensions + DW_OP_lo_user = 0xe0, + DW_OP_GNU_entry_value = 0xf3, + DW_OP_hi_user = 0xff, +}; + +class File; + +namespace section { +struct _Die; +class Line; +class LocLists; +class RngLists; + +using _Rng = std::pair<_Addr, _Addr>; +using _RngView = _View; + +struct _Loc { + _Rng _M_range; + Data _M_expr; +}; +using _LocView = _View; + +class SrcFile { + using _Lines = std::map, _Addr>; + +public: + static const SrcFile _S_null; + + SrcFile() noexcept : _M_md5(nullptr) {} + explicit SrcFile(const _Path<3> &__path, Data::element_type *__md5) noexcept + : _M_path(__path), _M_md5(__md5) {} + constexpr const _Path<3> &path() const noexcept { return _M_path; } + constexpr _Lines &lines() noexcept { return _M_lines; } + constexpr const _Lines &lines() const noexcept { return _M_lines; } + constexpr Data::element_type *md5() const noexcept { return _M_md5; } + +private: + _Path<3> _M_path; + Data::element_type *_M_md5; + _Lines _M_lines; +}; + +template class _SizedIntegral { +public: + template constexpr _SizedIntegral( + _Int __value, std::enable_if_t::value + && std::is_signed<_Int>::value == std::is_signed<_Type>::value, std::size_t> __width + = std::numeric_limits<_Int>::digits) noexcept : _M_value(__value), _M_width(__width) {} + template explicit constexpr _SizedIntegral( + _Enum __value, std::enable_if_t::value + && std::is_signed>::value == + std::is_signed<_Type>::value, std::size_t> __width + = std::numeric_limits>::digits) noexcept + : _M_value(_Type(__value)), _M_width(__width) {} + constexpr std::size_t width() const noexcept { return _M_width; } + constexpr _Type get() const noexcept { return _M_value; } + constexpr operator _Type() const noexcept { return _M_value; } + +private: + _Type _M_value; + std::size_t _M_width; +}; + +class _Val { + enum class _Kind : _Byte { + _None, + _Addr, + _Cst, + _Data, + _File, + _Flag, + _Ref, + _LocList, + _RngList, + _Str, + }; + + static constexpr bool is_large(std::size_t bits) noexcept { + return bits > std::numeric_limits::digits; + } + constexpr std::size_t size() const noexcept { return _M_addr; } + constexpr bool is_large() const noexcept { + return is_cst() && size() > std::numeric_limits::digits; + } + std::uintmax_t *take_large() const & { return new std::uintmax_t(*_M_large); } + std::uintmax_t *take_large() && { return std::exchange(_M_large, nullptr); } + void maybe_moved_from() const & {} + void maybe_moved_from() && { + _M_kind = _Kind::_None; + _U_addr = {}; + _U_ptr = {}; + } + template _Val ©(_Ty &&__val) { + if (this == &__val) return *this; + if (is_large() && __val.is_large()) { + _M_flag = __val._M_flag; + _M_addr = __val._M_addr; + *_M_large = *__val._M_large; + return *this; + } + this->~_Val(); + _M_kind = __val._M_kind; + _M_flag = __val._M_flag; + switch (_M_kind) { + case _Kind::_None: + case _Kind::_File: + case _Kind::_Flag: + case _Kind::_Ref: + _U_addr = __val._U_addr; + break; + case _Kind::_Addr: + case _Kind::_Cst: + case _Kind::_Data: + case _Kind::_LocList: + case _Kind::_RngList: + case _Kind::_Str: + _M_addr = __val._M_addr; + break; + } + switch (_M_kind) { + case _Kind::_None: + case _Kind::_Addr: + case _Kind::_Flag: + _U_ptr = __val._U_ptr; + break; + case _Kind::_Cst: + if (is_large()) + _M_large = std::forward<_Ty>(__val).take_large(); + else + _M_small = __val._M_small; + break; + case _Kind::_Data: + _M_data = __val._M_data; + break; + case _Kind::_File: + _M_file = __val._M_file; + break; + case _Kind::_Ref: + _M_ref = __val._M_ref; + break; + case _Kind::_LocList: + _M_loclist = __val._M_loclist; + break; + case _Kind::_RngList: + _M_rnglist = __val._M_rnglist; + break; + case _Kind::_Str: + _M_str = __val._M_str; + break; + } + std::forward<_Ty>(__val).maybe_moved_from(); + return *this; + } + +public: + static const _Val _S_none; + + _Val(const _Val &__val) : _Val() { copy(__val); } + _Val(_Val &&__val) : _Val() { copy(std::move(__val)); } + _Val &operator=(const _Val &__val) { return copy(__val); } + _Val &operator=(_Val &&__val) { return copy(std::move(__val)); } + ~_Val() noexcept { if (is_large()) delete _M_large; } + + constexpr _Val() noexcept : _M_kind(_Kind::_None), _M_flag(), _U_addr(), _U_ptr() {} + constexpr bool is_none() const noexcept { return _M_kind == _Kind::_None; } + + constexpr _Val(_Addr __addr) noexcept : _M_kind(_Kind::_Addr), _M_flag(), _M_addr(__addr), _U_ptr() {} + constexpr bool is_addr() const noexcept { return _M_kind == _Kind::_Addr; } + constexpr _Addr addr() const noexcept { if (is_addr()) return _M_addr; return {}; } + + template _Val(_SizedIntegral<_Int> __cst) + : _M_kind(_Kind::_Cst), _M_flag(std::is_signed<_Int>::value), _M_addr(__cst.width()), _U_ptr() { + if (is_large()) { + _M_large = new std::uintmax_t(__cst); + } else { + _M_small = __cst; + } + } + template::value, int> = 0> + _Val(_Int __cst) : _Val(_SizedIntegral<_Int>(__cst)) {} + constexpr bool is_cst() const noexcept { return _M_kind == _Kind::_Cst; } + constexpr std::uintmax_t cst() const noexcept { + if (is_large()) return *_M_large; + if (is_cst()) return _M_small; + if (is_data()) return data().partial(); + return {}; + } + + constexpr _Val(Data __data) noexcept + : _M_kind(_Kind::_Data), _M_flag(), _M_addr(__data.size()), + _M_data(__data.data()) { assert(size() == __data.size() && "Overflow"); } + constexpr bool is_data() const noexcept { return _M_kind == _Kind::_Data; } + constexpr Data data() const noexcept { if (is_data()) return { _M_data, size() }; return {}; } + + constexpr _Val(const _Die &__ref) noexcept + : _M_kind(_Kind::_Ref), _M_flag(), _U_addr(), _M_ref(&__ref) {} + constexpr bool is_ref() const noexcept { return _M_kind == _Kind::_Ref; } + constexpr const _Die &ref() const noexcept; + + constexpr _Val(const SrcFile &__file) + : _M_kind(_Kind::_File), _M_flag(), _U_addr(), _M_file(&__file) {} + constexpr bool is_file() const noexcept { return _M_kind == _Kind::_File; } + constexpr const SrcFile &file() const noexcept { return is_file() ? *_M_file : SrcFile::_S_null; } + + constexpr _Val(bool __flag) noexcept + : _M_kind(_Kind::_Flag), _M_flag(__flag), _U_addr(), _U_ptr() {} + constexpr bool is_flag() const noexcept { return _M_kind == _Kind::_Flag; } + constexpr bool flag() const noexcept { if (is_flag()) return _M_flag; return {}; } + + constexpr _Val(_LocView __loclist) noexcept + : _M_kind(_Kind::_LocList), _M_flag(), _M_addr(__loclist.size()), + _M_loclist(__loclist.data()) { assert(size() == __loclist.size() && "Overflow"); } + constexpr bool is_loclist() const noexcept { return _M_kind == _Kind::_LocList; } + constexpr _LocView loclist() const noexcept { + if (is_loclist()) return { _M_loclist, size() }; + return {}; + } + + constexpr _Val(_RngView __rnglist) noexcept + : _M_kind(_Kind::_RngList), _M_flag(), _M_addr(__rnglist.size()), + _M_rnglist(__rnglist.data()) { assert(size() == __rnglist.size() && "Overflow"); } + constexpr bool is_rnglist() const noexcept { return _M_kind == _Kind::_RngList; } + constexpr _RngView rnglist() const noexcept { + if (is_rnglist()) return { _M_rnglist, size() }; + return {}; + } + + constexpr _Val(_Str __str) noexcept + : _M_kind(_Kind::_Str), _M_flag(), _M_addr(__str.size()), + _M_str(__str.data()) { assert(size() == __str.size() && "Overflow"); } + constexpr bool is_str() const noexcept { return _M_kind == _Kind::_Str; } + constexpr _Str str() const noexcept { if (is_str()) return { _M_str, size() }; return {}; } + +private: + _Kind _M_kind : 4; + bool _M_flag : 1; + union { + struct {} _U_addr; + _Addr _M_addr; + }; + union { + struct {} _U_ptr; + std::uintptr_t _M_small; + std::uintmax_t *_M_large; + Data::pointer _M_data; + const SrcFile *_M_file; + const _Die *_M_ref; + _LocView::pointer _M_loclist; + _RngView::pointer _M_rnglist; + _Str::pointer _M_str; + }; +}; + +struct _Die { + _Tag _M_tag : std::numeric_limits<_Sbyte>::digits; + bool _M_children : 1; + _Word _M_sibling : std::numeric_limits<_Word>::digits - std::numeric_limits<_Byte>::digits; + std::unordered_map<_At, _Val> _M_attrs; + + const _Val &attr(_At __at) const noexcept; + + static const _Die _S_null; +}; + +constexpr const _Die &_Val::ref() const noexcept { return is_ref() ? *_M_ref : _Die::_S_null; } + +class Base { +public: + constexpr Base(File &__file) noexcept : _M_file(__file) {} + constexpr void error(const char *__error) const noexcept; + constexpr const char *error() const noexcept; + constexpr void data(Data __data) noexcept { _M_data = __data; } + + File &_M_file; + Data _M_data; +}; + +class Abbrev : public Base { +public: + struct _Attr { + _At _M_at; + _Form _M_form; + _Slong _M_implicit_const; + }; + struct _Entry { + _Tag _M_tag : std::numeric_limits<_Sbyte>::digits; + bool _M_children : 1; + std::vector<_Attr> _M_attrs; + }; + + Abbrev(File &__file) noexcept : Base(__file) {} + std::unordered_map<_Word, _Entry> get(_Off __base) const noexcept; +}; + +class Addr : public Base { +public: + Addr(File &__file) noexcept : Base(__file) {} + _AddrView get(const _Die &__unit_entry) const noexcept; +}; + +class Frame : public Base { +public: + class Rule { + enum class _Kind : _Byte { + _Undef, + _SameVal, + _Off, + _ValOff, + _Reg, + _RegOff, + _Expr, + _ValExpr, + _Arch, + }; + + constexpr Rule(_Kind __kind, Reg __reg = Reg::None) noexcept : Rule(__kind, __reg, {}) {} + constexpr Rule(_Kind __kind, _Addr __off) noexcept + : _M_kind(__kind), _M_reg(Reg::None), _M_off(__off) {} + constexpr Rule(_Kind __kind, Reg __reg, _Addr __off) noexcept + : _M_kind(__kind), _M_reg(__reg), _M_off(__off) {} + + public: + constexpr Rule() noexcept : Rule(_Kind::_Undef) {} + static constexpr Rule undef() noexcept { return {}; } + constexpr bool is_undef() const noexcept { return _M_kind == _Kind::_Undef; } + + static constexpr Rule same_val() noexcept { return { _Kind::_SameVal }; } + constexpr bool is_same_val() const noexcept { return _M_kind == _Kind::_SameVal; } + + static constexpr Rule off(_Sword __off) noexcept { return { _Kind::_Off, __off }; } + constexpr bool is_off() const noexcept { return _M_kind == _Kind::_Off; } + + static constexpr Rule val_off(_Sword __off) noexcept { return { _Kind::_ValOff, __off }; } + constexpr bool is_val_off() const noexcept { return _M_kind == _Kind::_ValOff; } + + static constexpr Rule reg(Reg __reg) noexcept { return { _Kind::_ValOff, __reg }; } + constexpr bool is_reg() const noexcept { return _M_kind == _Kind::_Reg; } + + static constexpr Rule reg_off(Reg __reg, _Sword __off) noexcept { + return { _Kind::_RegOff, __reg, __off }; + } + constexpr bool is_reg_off() const noexcept { return _M_kind == _Kind::_RegOff; } + + static Rule expr(Frame &__frame, Data __expr) noexcept { + return { _Kind::_Expr, __frame.expr(__expr) }; + } + constexpr bool is_expr() const noexcept { return _M_kind == _Kind::_Expr; } + + static Rule val_expr(Frame &__frame, Data __expr) noexcept { + return { _Kind::_ValExpr, __frame.expr(__expr) }; + } + constexpr bool is_val_expr() const noexcept { return _M_kind == _Kind::_ValExpr; } + + constexpr bool is_arch() const noexcept { return _M_kind == _Kind::_Arch; } + + constexpr bool is_val() const noexcept { + return is_same_val() || is_reg() || is_reg_off() || is_val_off() || is_val_expr(); + } + constexpr bool is_addr() const noexcept { return is_off() || is_expr(); } + + constexpr Reg reg() const noexcept { return _M_reg; } + constexpr _Sword off() const noexcept { return _M_off; } + constexpr Data expr(const Frame &__frame) const noexcept { + if (is_expr() || is_val_expr()) + return __frame.expr(_M_off); + return {}; + } + + private: + _Kind _M_kind : 4; + Reg _M_reg : 4; + _Addr _M_off; + }; + class RuleSet { + public: + constexpr Rule &cfa() noexcept { return _M_cfa; } + constexpr Rule cfa() const noexcept { return _M_cfa; } + constexpr Rule &operator[](Reg __reg) noexcept { + assert(__reg >= Reg() && __reg < Reg::None); + return _M_rules[std::size_t(__reg)]; + } + constexpr Rule operator[](Reg __reg) const noexcept { + if (__reg >= Reg() && __reg < Reg::None) + return _M_rules[std::size_t(__reg)]; + return {}; + } + + static constexpr const RuleSet &undef() noexcept { return _S_undef; } + + private: + Rule _M_cfa; + Rule _M_rules[std::size_t(Reg::None)]; + + static const RuleSet _S_undef; + }; + +private: + using _Exprs = std::vector; + using _Rules = std::map<_Addr, RuleSet>; + + friend Rule; + _Exprs::size_type expr(Data __expr) noexcept { + auto __pos = _M_exprs.size(); + _M_exprs.push_back(__expr); + return __pos; + } + Data expr(_Exprs::size_type __pos) const noexcept { + if (__pos < _M_exprs.size()) + return _M_exprs[__pos]; + return {}; + } + +public: + Frame(File &__file) noexcept : Base(__file) {} + void __parse(); + const RuleSet &get(_Addr __addr) const noexcept; + +private: + _Exprs _M_exprs; + _Rules _M_rules; +}; + +class Info : public Base { + // Table 7.2: Unit header unit type encodings + enum { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + }; + +public: + class _Scope { + public: + _Scope(const _Scope &__parent = _S_null, const _Die &__entry = _Die::_S_null) noexcept + : _M_parent(__parent), _M_entry(__entry) {} + void set(_Addr __low, _Addr __high, _Scope &__child) { + _M_children.insert({__low, { __high, __child } }); + } + void add(const _Die &__entry) { _M_entities.push_back(__entry); } + + explicit constexpr operator bool() const noexcept { return this != &_S_null; } + static constexpr const _Scope &null() noexcept { return _S_null; } + + constexpr const _Scope &parent() const noexcept { return _M_parent; } + constexpr bool is_unit() const noexcept { + return _M_entry._M_tag == _Tag::DW_TAG_compile_unit; + } + constexpr bool is_function() const noexcept { + return _M_entry._M_tag == _Tag::DW_TAG_inlined_subroutine || + _M_entry._M_tag == _Tag::DW_TAG_subprogram; + } + constexpr const _Scope &function() const noexcept { + return is_function() ? *this : _M_parent ? _M_parent.function() : null(); + } + constexpr const _Die &entry() const noexcept { return _M_entry; } + auto begin() const { return _M_entities.begin(); } + auto end() const { return _M_entities.end(); } + const _Scope &get(_Addr __addr) const noexcept; + void dump[[gnu::used]](int indent = 0) const; + + private: + const _Scope &_M_parent; + const _Die &_M_entry; + std::vector> _M_entities; + std::map<_Addr, std::pair<_Addr, const _Scope &>> _M_children; + + static const _Scope _S_null; + }; + + Info(File &__file) noexcept : Base(__file), _M_scopes{{}} {} + void __parse(Line &line, LocLists &loclists, RngLists &rnglists); + const _Scope &root() const noexcept { return _M_scopes.front(); } + +private: + std::vector<_Die> _M_entries; + std::deque<_Scope> _M_scopes; +}; + +class Line : public Base { + // Table 7.25: Line number standard opcode encodings + enum { + DW_LNS_extended = 0x00, + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, + }; + // Table 7.26: Line number extended opcode encodings + enum { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_set_discriminator = 0x04, + // Vendor Extensions + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, + }; + // Table 7.27: Line number header entry format encodings + enum class _Lnct : _Byte { + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + }; + enum { + // Vendor Extensions + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff, + }; + + using _EntryFormat = std::pair<_Lnct, _Form>; + struct _Entry { + _Str _M_path; + _Word _M_directory_index = _InvalidWord; + _Long _M_timestamp{}; + _Long _M_size{}; + Data _M_MD5; + }; + +public: + struct _Loc { + _Word _M_file; + _Word _M_line; + _Word _M_column; + _Addr _M_address; + bool _M_is_stmt : 1; + bool _M_basic_block : 1; + bool _M_end_sequence : 1; + bool _M_prologue_end : 1; + bool _M_epilogue_begin : 1; + _Byte _M_isa : 1; + _Byte _M_discriminator : 2; + }; + +private: + using _Files = std::vector; + using _Paths = std::unordered_map<_Path<3>, _Word>; + using _Locs = std::map<_Addr, _Loc>; + +public: + Line(File &__file) noexcept : Base(__file) {} + std::vector<_Word> __parse(_Byte __address_size, const _Die &__unit_entry); + void shrink_to_fit() noexcept { _M_files.shrink_to_fit(); } + const SrcFile &get(_Files::size_type __pos) const noexcept; + _Files::size_type size() const noexcept { return _M_files.size(); } + constexpr const _Locs &locs() const noexcept { return _M_locs; } + +private: + _Files _M_files; + _Paths _M_paths; + _Locs _M_locs; +}; + +class LocLists : public Base { + // Table 7.10: Location list entry encoding values + enum { + DW_LLE_end_of_list = 0x00, + DW_LLE_base_addressx = 0x01, + DW_LLE_startx_endx = 0x02, + DW_LLE_startx_length = 0x03, + DW_LLE_offset_pair = 0x04, + DW_LLE_default_location = 0x05, + DW_LLE_base_address = 0x06, + DW_LLE_start_end = 0x07, + DW_LLE_start_length = 0x08, + }; + +public: + LocLists(File &__file) noexcept : Base(__file) {} + std::vector> __parse(const _Die &__unit_entry); + void shrink_to_fit() noexcept { _M_locs.shrink_to_fit(); } + _LocView get(std::pair<_Word, _Word> __indices) const noexcept; + +private: + std::vector<_Loc> _M_locs; +}; + +class RngLists : public Base { + // Table 7.30: Range list entry encoding values + enum { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07, + }; + +public: + RngLists(File &__file) noexcept : Base(__file) {} + std::vector> __parse(const _Die &__unit_entry); + void shrink_to_fit() noexcept { _M_rngs.shrink_to_fit(); } + _RngView get(std::pair<_Word, _Word> __indices) const noexcept; + +private: + std::vector<_Rng> _M_rngs; +}; + +class Str : public Base { +public: + Str(File &__file) noexcept : Base(__file) {} + _Str get(_Off __offset) const noexcept; +}; + +class StrOffsets : public Base { +public: + StrOffsets(File &__file) noexcept : Base(__file) {} + _OffView get(const _Die &__unit_entry) const noexcept; +}; +} + +class File : public elf::File { +public: + explicit File(Data __data) + : elf::File(__data), _M_error(nullptr), _M_abbrev(*this), + _M_addr(*this), _M_frame(*this), _M_info(*this), _M_line(*this), + _M_line_str(*this), _M_line_str_offsets(*this), _M_loclists(*this), + _M_rnglists(*this), _M_str(*this), _M_str_offsets(*this) {} + + void validate(const Header &__expected); + + constexpr void error(const char *__error) const noexcept { + if (!_M_error) { + _M_error = __error; + } + } + constexpr const char *error() const noexcept { return _M_error; }; + + constexpr const section::Abbrev &abbrev() const noexcept { return _M_abbrev; } + constexpr const section::Addr &addr() const noexcept { return _M_addr; } + constexpr const section::Frame &frame() const noexcept { return _M_frame; } + constexpr const section::Info &info() const noexcept { return _M_info; } + constexpr const section::Line &line() const noexcept { return _M_line; } + constexpr const section::Str &line_str() const noexcept { return _M_line_str; } + constexpr const section::StrOffsets &line_str_offsets() const noexcept { return _M_line_str_offsets; } + constexpr const section::LocLists &loclists() const noexcept { return _M_loclists; } + constexpr const section::RngLists &rnglists() const noexcept { return _M_rnglists; } + constexpr const section::Str &str() const noexcept { return _M_str; } + constexpr const section::StrOffsets &str_offsets() const noexcept { return _M_str_offsets; } + +private: + mutable const char *_M_error; + section::Abbrev _M_abbrev; + section::Addr _M_addr; + section::Frame _M_frame; + section::Info _M_info; + section::Line _M_line; + section::Str _M_line_str; + section::StrOffsets _M_line_str_offsets; + section::LocLists _M_loclists; + section::RngLists _M_rnglists; + section::Str _M_str; + section::StrOffsets _M_str_offsets; +}; + +constexpr void section::Base::error(const char *__error) const noexcept { _M_file.error(__error); } +constexpr const char *section::Base::error() const noexcept { return _M_file.error(); } +} // end namespace elf +} // end namespace debuginfo + +#endif diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index 6c586239d..fd9441eb9 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1,9 +1,12 @@ #include "sourceswidget.h" + #include "cdebughighlighter.h" -#include "mainwindow.h" -#include "utils.h" +#include "debuginfo.h" +#include "../mainwindow.h" +#include "../utils.h" #include "../../core/mem.h" +#include #include #include #include @@ -153,7 +156,7 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { #define using(const, field) const decltype(m_##field) &field() const { \ return static_cast(QObject::parent())->m_##field; \ } - using(const, sources) + using(const, debugFile) using(const, stringList) using(const, stringMap) #undef using @@ -167,9 +170,13 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { int parent, parentIndex, childIndex; QList children; + Symbol symbol; qint32 base; Context context; + + std::string value; + QString data[2]; Flags flags; }; @@ -178,14 +185,10 @@ class SourcesWidget::VariableModel : public QAbstractItemModel { QVector m_variables; QStringList m_topLevelData[2]; QList> m_topLevelChildren; - bool lookupAddr(quint32 addr, int *sourceIndex = nullptr, - int *functionIndex = nullptr, int *lineIndex = nullptr) const { - return static_cast(QObject::parent())-> - lookupAddr(addr, sourceIndex, functionIndex, lineIndex); - } static quint32 sizeOfSymbol(const Symbol &symbol, Context context); QString variableTypeToString(const Variable &variable) const; QString variableValueToString(const Variable &variable) const; + int createVariable(int parent, int parentIndex, int childIndex, const debuginfo::dwarf::section::_Die &entry); int createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, qint32 base = 0, const QString &name = {}); void createVariable(const QModelIndex &parent, const Symbol &symbol, qint32 base = 0, const QString &name = {}); void removeTopLevels(int first, int last); @@ -214,18 +217,56 @@ class SourcesWidget::GlobalModel : public VariableModel { void init(const QStringList &paths); }; class SourcesWidget::StackModel : public VariableModel { + static constexpr quint32 undefValue = ~0u; + + using RegsBase = std::array; + struct Regs : RegsBase { + quint32 &operator[](debuginfo::dwarf::Reg reg) { + return RegsBase::operator[](std::size_t(reg)); + } + const quint32 &operator[](debuginfo::dwarf::Reg reg) const { + return RegsBase::operator[](std::size_t(reg)); + } + }; struct StackEntry { - quint32 ix, pc, cookie[2]; - const Source *source; - const Function *function; + quint32 cfa; + Regs regs; + const debuginfo::dwarf::section::Info::_Scope *scope; + + bool same(const StackEntry &other) const { + return cfa == other.cfa && + regs[debuginfo::dwarf::Reg::PC] == other.regs[debuginfo::dwarf::Reg::PC] && + scope == other.scope; + } }; QVector m_stack; + + static void computeRule(const StackEntry &state, quint32 &value, + const debuginfo::dwarf::section::Frame::Rule &rule); + public: explicit StackModel(SourcesWidget *parent) : VariableModel(parent) {} void update() override; void init(); }; +class SourcesWidget::DebugFile : protected QFile, public debuginfo::dwarf::File { +private: + debuginfo::Data mapData() { + if (open(ReadOnly)) { + if (auto ptr = reinterpret_cast(map(0, size()))) { + return { ptr, debuginfo::Data::size_type(size()) }; + } + } + return {}; + } + +public: + DebugFile(const QString &path) : QFile(path), debuginfo::dwarf::File(mapData()) {} + + using debuginfo::dwarf::File::error; +}; + namespace { enum class DebugTokenKind { Eof, @@ -253,10 +294,10 @@ enum class DebugTokenKind { Value, }; -class DebugFile : public QFile { +class DbgFile : public QFile { public: bool success; - DebugFile(QString name) : QFile(name), success(open(QIODevice::ReadOnly)) {} + DbgFile(QString name) : QFile(name), success(open(QIODevice::ReadOnly)) {} void setErrorString(const QString &errorString) { if (success) { success = false; @@ -323,7 +364,7 @@ class DebugFile : public QFile { return QString::fromLocal8Bit(result); } }; -} +} // end anonymous namespace SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { QPushButton *button = new QPushButton(tr("Select Debug File")); @@ -368,342 +409,88 @@ SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { m_errorFormat.setBackground(QColor::fromRgb(0xFF3F3F)); } +SourcesWidget::~SourcesWidget() = default; + void SourcesWidget::selectDebugFile() { - QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "/home/jacob/Programming/calc/ez80/c/debug/bin", tr("Debug File (*.dbg)")); + QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "/home/jacob/Programming/ez80/oiram/bin", tr("Debug File (*.debug)")); if (debugName.isNull()) { return; } - QStringList paths; - QVector sources; - QStringList stringList; - QHash stringMap; - DebugFile debugFile(debugName); - QDir dir = QFileInfo(debugFile).dir(); - dir.cdUp(); - Line prevLine{0, 0}; - bool eof = false; - quint32 lang = 0; - QStack scopes; - scopes.reserve(3); - bool inFunction = false; - Symbol *curSymbol = Q_NULLPTR; - while (debugFile.success && !debugFile.atEnd()) { - switch (DebugTokenKind(debugFile.readULEB128())) { - case DebugTokenKind::Eof: { - if (!debugFile.atEnd() || !scopes.isEmpty()) { - debugFile.setErrorString(tr("Unexpected EOF token")); - break; - } - eof = true; - break; - } - case DebugTokenKind::Alias: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected alias token")); - break; - } - Scope &scope = *scopes.top(); - curSymbol->alias = debugFile.readULEB128(); - scope.symbolMap.insert(curSymbol->alias, scope.symbolList.count() - 1); - break; - } - case DebugTokenKind::BegFunc: { - if (scopes.count() != 1 || curSymbol) { - debugFile.setErrorString(tr("Unexpected function token")); - break; - } - Source &source = sources.last(); - QVector &functionList = source.functionList; - QMultiHash &functionMap = source.functionMap; - int functionIndex = functionList.count(); - Function function; - function.name = debugFile.readULEB128(); - functionMap.insert(function.name, functionIndex); - functionMap.insert(debugFile.readULEB128(), functionIndex); - prevLine.num += debugFile.readULEB128(); - prevLine.addr += debugFile.readULEB128(); - function.lines.append(prevLine); - functionList.append(function); - scopes.push(&functionList.last()); - inFunction = true; - break; - } - case DebugTokenKind::BegRec: { - if (scopes.isEmpty() || curSymbol) { - debugFile.setErrorString(tr("Unexpected record begin token")); - break; - } - Scope &scope = *scopes.top(); - Record record; - record.name = debugFile.readULEB128(); - record.size = debugFile.readULEB128(); - scope.recordMap.insert(record.name, scope.recordList.count()); - scope.recordList.append(record); - scopes.push(&scope.recordList.last()); - break; - } - case DebugTokenKind::Class: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected class begin token")); - break; - } - curSymbol->kind = SymbolKind(debugFile.readULEB128()); - break; - } - case DebugTokenKind::Debug: { - if (lang ? lang != debugFile.readULEB128() - : !(lang = debugFile.readULEB128())) { - debugFile.setErrorString(tr("Mixed source languages")); - break; - } - break; - } - case DebugTokenKind::Define: { - if (scopes.isEmpty() || curSymbol) { - debugFile.setErrorString(tr("Unexpected define symbol begin token")); - break; - } - Scope &scope = *scopes.top(); - Symbol symbol; - symbol.name = debugFile.readULEB128(); - scope.symbolMap.insert(symbol.name, scope.symbolList.count()); - scope.symbolList.append(symbol); - curSymbol = &scope.symbolList.last(); - break; - } - case DebugTokenKind::Dim: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected dimension token")); - break; - } - curSymbol->dims.append(debugFile.readULEB128()); - break; - } - case DebugTokenKind::End: { - if (scopes.count() != 1 || curSymbol) { - debugFile.setErrorString(tr("Unexpected source file end token")); - break; - } - sources.last().endAddr = prevLine.addr += debugFile.readULEB128(); - scopes.pop(); - break; - } - case DebugTokenKind::Endef: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected define symbol end token")); - break; - } - curSymbol = Q_NULLPTR; - break; - } - case DebugTokenKind::EndFunc: { - if (scopes.count() != 2 || !inFunction || curSymbol) { - debugFile.setErrorString(tr("Unexpected function end token")); - break; - } - debugFile.readULEB128(); - debugFile.readULEB128(); - prevLine.num += debugFile.readULEB128(); - prevLine.addr += debugFile.readULEB128(); - sources.last().functionList.last().lines.append(prevLine); - scopes.pop(); - inFunction = false; - break; - } - case DebugTokenKind::EndRec: { - if (scopes.count() <= 1 + inFunction || curSymbol) { - debugFile.setErrorString(tr("Unexpected record end token")); - break; - } - debugFile.readULEB128(); - scopes.pop(); - break; - } - case DebugTokenKind::File: { - if (!scopes.isEmpty()) { - debugFile.setErrorString(tr("Unexpected source file begin token")); - break; - } - paths << dir.path(); - QStringList components = debugFile.readString().split('\\'); - for (QStringList::iterator i = components.begin(), e = components.end(); - debugFile.success && i != e; ) { - QStringList component(*i++); - QDirIterator matches(paths.last(), component, QDir::Readable | - (i != e ? QDir::Dirs : QDir::Files)); - if (matches.hasNext()) { - paths.last() = matches.next(); - } else { - debugFile.setErrorString(tr("Unable to find source file \"%0\""). - arg(components. - join(QDir::separator()))); - } - } - if (debugFile.success) { - sources.resize(sources.count() + 1); - sources.last().startAddr = prevLine.addr += debugFile.readULEB128(); - prevLine.num = 0; - scopes.push(&sources.last()); - } - break; - } - case DebugTokenKind::Length: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected length token")); - break; - } - curSymbol->length = debugFile.readULEB128(); - break; - } - case DebugTokenKind::Line: { - if (scopes.count() != 2 || !inFunction || curSymbol) { - debugFile.setErrorString(tr("Unexpected line token")); - break; - } - prevLine.num += debugFile.readULEB128(); - prevLine.addr += debugFile.readULEB128(); - sources.last().functionList.last().lines.append(prevLine); - break; - } - case DebugTokenKind::Strings: { - if (!scopes.isEmpty()) { - debugFile.setErrorString(tr("Unexpected strings token")); - break; - } - quint32 count = debugFile.readULEB128(); - stringList.reserve(count); - stringMap.reserve(count); - for (quint32 index = 0; debugFile.success && index != count; ++index) { - QString string = debugFile.readString(); - if (string.isEmpty()) { - debugFile.setErrorString(tr("Invalid string in string table")); - } else { - stringList << string; - stringMap[string] = index; - } - } - break; - } - case DebugTokenKind::Tag: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected tag token")); - break; - } - curSymbol->tag = debugFile.readULEB128(); - break; - } - case DebugTokenKind::Type: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected value token")); - break; - } - curSymbol->type = debugFile.readSLEB128(); - break; - } - case DebugTokenKind::Value: { - if (!curSymbol) { - debugFile.setErrorString(tr("Unexpected value token")); - break; - } - curSymbol->value = debugFile.readSLEB128(); - break; - } - default: debugFile.setErrorString(tr("Unknown debug token kind")); - } - } - if (sources.isEmpty()) { - debugFile.setErrorString(tr("No source files")); - } - if (!eof) { - debugFile.setErrorString(tr("Missing EOF token")); - } - if (stringList.value(lang - 1) != "C") { - debugFile.setErrorString(tr("Unknown source language \"%0\""). - arg(stringList.value(lang - 1))); - } - if (debugFile.success) { - for (int i = 0; i != m_tabs->count(); i++) { + auto debugFile = std::make_unique(debugName); + debugFile->validate(debuginfo::elf::File::Header::ez80); + + if (const char *error = debugFile->error()) { + QMessageBox dialog(QMessageBox::Critical, + findParent(this)->MSG_ERROR, + tr("Error loading debug file:"), QMessageBox::Ok, this); + dialog.setInformativeText(tr(error)); + dialog.exec(); + } else { + for (int i = 0; i != m_tabs->count(); ++i) { m_tabs->widget(i)->deleteLater(); } m_tabs->clear(); - for (int i = 0; i != sources.count(); i++) { - QFile sourceFile(paths.at(i)); + + QStringList changedSourcePaths; + for (std::size_t i = 0; i != debugFile->line().size(); ++i) { + auto source = debugFile->line().get(i); + QString sourcePath; + sourcePath.reserve(std::accumulate(source.path().begin(), source.path().end(), -1, + [](int length, const auto &str) { + return length + str.rtrim('\0').size() + 1; + })); + std::for_each(source.path().canon().begin(), source.path().canon().end(), + [&sourcePath](char c) { sourcePath += c; }); + QFile sourceFile(sourcePath); if (sourceFile.open(QIODevice::ReadOnly)) { - QPlainTextEdit *sourceView = new QPlainTextEdit(sourceFile.readAll()); + QByteArray sourceContents = sourceFile.readAll(); + if (source.md5() && QCryptographicHash::hash(sourceContents, QCryptographicHash::Md5) != + QByteArray::fromRawData(reinterpret_cast(source.md5()), + QCryptographicHash::hashLength(QCryptographicHash::Md5))) { + changedSourcePaths << sourcePath; + } + QPlainTextEdit *sourceView = new QPlainTextEdit(sourceContents); sourceView->setObjectName(QStringLiteral("sourceView%0").arg(i)); sourceView->setReadOnly(true); sourceView->setCenterOnScroll(true); sourceView->setContextMenuPolicy(Qt::CustomContextMenu); connect(sourceView, &QWidget::customContextMenuRequested, this, &SourcesWidget::sourceContextMenu); - m_tabs->addTab(sourceView, QFileInfo(sourceFile).baseName()); + m_tabs->addTab(sourceView, QFileInfo(sourceFile).fileName()); new CDebugHighlighter(this, sourceView); } } - m_sources = std::move(sources); - m_stringList = std::move(stringList); - m_stringMap = std::move(stringMap); - m_globalModel->init(paths); - m_stackModel->init(); - } else { - QMessageBox dialog(QMessageBox::Critical, - findParent(this)->MSG_ERROR, - tr("Error loading debug file:"), QMessageBox::Ok, this); - dialog.setInformativeText(debugFile.errorString()); - dialog.exec(); + if (!changedSourcePaths.empty()) { + QMessageBox dialog(QMessageBox::Warning, findParent(this)->MSG_WARNING, + tr("Some source files have changed since compilation:"), QMessageBox::Ok, + this); + dialog.setInformativeText(changedSourcePaths.join('\n')); + dialog.exec(); + } + //m_stringList = std::move(stringList); + //m_stringMap = std::move(stringMap); + //m_globalModel->init(paths); + //m_stackModel->init(); + + m_debugFile = std::move(debugFile); } } -bool SourcesWidget::lookupAddr(quint32 addr, int *sourceIndex, int *functionIndex, - int *lineIndex) const { - if (!sourceIndex && !functionIndex && !lineIndex) { - return true; - } - const SourceBase sourceKey{addr, addr}; - auto source = std::upper_bound(m_sources.begin(), m_sources.end(), sourceKey, - [](const SourceBase &first, - const SourceBase &second) { - return first.endAddr < second.startAddr; - }); - if (source == m_sources.begin() || - addr < (--source)->startAddr || addr >= source->endAddr) { - return false; - } - if (sourceIndex) { - *sourceIndex = std::distance(m_sources.begin(), source); - if (!functionIndex && !lineIndex) { - return true; - } +auto SourcesWidget::getLocInfo(quint32 addr) const -> LocInfo { + if (!m_debugFile) { + return {}; } - const Line lineKey{0, addr}; - const FunctionBase functionKey{{lineKey}}; - auto function = std::upper_bound(source->functionList.begin(), - source->functionList.end(), functionKey, - [](const FunctionBase &first, - const FunctionBase &second) { - return first.lines.last().addr < - second.lines.first().addr; - }); - if (function == source->functionList.begin() || - addr < (--function)->lines.first().addr || - addr >= function->lines.last().addr) { - return false; - } - if (functionIndex) { - *functionIndex = std::distance(source->functionList.begin(), function); - if (!lineIndex) { - return true; - } + const auto &locs = m_debugFile->line().locs(); + auto after = locs.upper_bound(addr); + if (after == locs.begin() || after == locs.end()) { + return {}; + } + auto cur = std::prev(after); + if (cur->second._M_end_sequence) { + return {}; } - auto line = - std::prev(std::upper_bound(std::next(function->lines.begin()), - std::prev(function->lines.end()), lineKey, - [](const Line &first, const Line &second) { - return first.addr < second.addr; - })); - *lineIndex = std::distance(function->lines.begin(), line); - return true; + return { after->second._M_address, cur->second._M_file, cur->second._M_line, cur->second._M_column }; } void SourcesWidget::updateFormats() { @@ -713,32 +500,47 @@ void SourcesWidget::updateFormats() { } } -void SourcesWidget::updatePC(quint32 pc) { +void SourcesWidget::updatePC() { m_globalModel->update(); m_stackModel->update(); - for (auto &sourceView : m_tabs->findChildren()) { + for (auto sourceView : m_tabs->findChildren()) { sourceView->setExtraSelections({}); } - int sourceIndex, functionIndex, lineIndex; - if (!lookupAddr(pc, &sourceIndex, &functionIndex, &lineIndex)) { + if (!m_debugFile) { + return; + } + const auto &locs = m_debugFile->line().locs(); + auto it = locs.upper_bound(cpu.registers.PC); + if (it == locs.begin()) { return; } - QPlainTextEdit *sourceView = m_tabs->findChild(QStringLiteral("sourceView%0").arg(sourceIndex)); - QTextBlock block = sourceView->document()->findBlockByNumber(m_sources.at(sourceIndex).functionList.at(functionIndex).lines.at(lineIndex).num - 1); + if (!(--it)->second._M_line) { + return; + } + QPlainTextEdit *sourceView = m_tabs->findChild( + QStringLiteral("sourceView%0").arg(it->second._M_file)); + QTextBlock block = sourceView->document()->findBlockByNumber(it->second._M_line - 1); if (!block.isValid()) { return; } QTextEdit::ExtraSelection pcSelection; pcSelection.cursor = QTextCursor(block); - if (pcSelection.cursor.movePosition(QTextCursor::EndOfWord)) { + if (it->second._M_column) { + pcSelection.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, + it->second._M_column - 1); + } else if (pcSelection.cursor.movePosition(QTextCursor::EndOfWord)) { pcSelection.cursor.movePosition(QTextCursor::StartOfBlock); } else { pcSelection.cursor.movePosition(QTextCursor::NextWord); } sourceView->setTextCursor(pcSelection.cursor); - pcSelection.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - pcSelection.cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); - pcSelection.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + if (it->second._M_column) { + pcSelection.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + } else { + pcSelection.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + pcSelection.cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor); + pcSelection.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + } pcSelection.format.setBackground(QColor::fromRgb(0xFFFF99)); sourceView->setExtraSelections({pcSelection}); sourceView->centerCursor(); @@ -747,12 +549,20 @@ void SourcesWidget::updatePC(quint32 pc) { void SourcesWidget::updateAddr(quint32 addr, unsigned type, bool state) { (void)type; // TODO: implement - int sourceIndex, functionIndex, lineIndex; - if (!lookupAddr(addr, &sourceIndex, &functionIndex, &lineIndex)) { + if (!m_debugFile) { + return; + } + const auto &locs = m_debugFile->line().locs(); + auto it = locs.upper_bound(addr); + if (it == locs.begin()) { + return; + } + if (!(--it)->second._M_line) { return; } - auto sourceView = m_tabs->findChild(QStringLiteral("sourceView%0").arg(sourceIndex)); - QTextBlock block = sourceView->document()->findBlockByNumber(m_sources.at(sourceIndex).functionList.at(functionIndex).lines.at(lineIndex).num - 1); + auto sourceView = m_tabs->findChild( + QStringLiteral("sourceView%0").arg(it->second._M_file)); + QTextBlock block = sourceView->document()->findBlockByNumber(it->second._M_line - 1); if (!block.isValid()) { return; } @@ -765,31 +575,10 @@ void SourcesWidget::updateAddr(quint32 addr, unsigned type, bool state) { } void SourcesWidget::sourceContextMenu(const QPoint &pos) { - QPlainTextEdit *sourceView = static_cast(m_tabs->currentWidget()); - const QVector &functions = m_sources.at(sourceView->objectName().midRef(QStringLiteral("sourceView").count()).toInt()).functionList; - quint32 num = sourceView->cursorForPosition(pos).block().blockNumber() + 1; - const Line lineKey{num, 0}; - const FunctionBase functionKey{{lineKey}}; - auto function = std::upper_bound(functions.begin(), functions.end(), - functionKey, - [](const FunctionBase &first, - const FunctionBase &second) { - return first.lines.last().num < - second.lines.first().num; - }); - if (function == functions.begin()) { - return; - } - const QVector &lines = (--function)->lines; - if (lineKey.num < lines.first().num || lineKey.num > lines.last().num) { + if (!m_debugFile) { return; } - auto line = - std::prev(std::upper_bound(std::next(lines.begin()), std::prev(lines.end()), - lineKey, - [](const Line &first, const Line &second) { - return first.num < second.num; - })); + QPlainTextEdit *sourceView = static_cast(m_tabs->currentWidget()); QMenu menu; QAction runUntilAction(tr("Run Until")); @@ -799,10 +588,20 @@ void SourcesWidget::sourceContextMenu(const QPoint &pos) { menu.addAction(&toggleBreakAction); QAction *action = menu.exec(sourceView->mapToGlobal(pos)); + + const auto &source = m_debugFile->line() + .get(sourceView->objectName().midRef(QStringLiteral("sourceView").count()).toInt()); + QTextCursor cursor = sourceView->cursorForPosition(pos); + auto it = source.lines().upper_bound({ cursor.blockNumber() + 1, cursor.columnNumber() }); + if (it == source.lines().begin()) { + return; + } + --it; + if (action == &runUntilAction) { - emit runUntilTriggered(line->addr); + emit runUntilTriggered(it->second); } else if (action == &toggleBreakAction) { - emit breakToggled(line->addr); + emit breakToggled(it->second); } } @@ -1034,17 +833,37 @@ QString SourcesWidget::VariableModel::variableValueToString(const Variable &vari case 1: case 6: if (quint32 value = mem_peek_long(addr)) { - int sourceIndex, functionIndex; - if (lookupAddr(value, &sourceIndex, &functionIndex)) { - return '&' + stringList().value(sources().at(sourceIndex).functionList.at(functionIndex).name - 1); - } else { + //int sourceIndex, functionIndex; + //if (lookupAddr(value, &sourceIndex, &functionIndex)) { + // return '&' + stringList().value(sources().at(sourceIndex).functionList.at(functionIndex).name - 1); + //} else { return "0x" + QString::number(value, 16).rightJustified(6, '0'); - } + //} } return "NULL"; } } +int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, int childIndex, const debuginfo::dwarf::section::_Die &entry) { + int id = m_freeVariable; + if (id == -1) { + id = m_variables.count(); + m_variables.resize(id + 1); + } else { + m_freeVariable = m_variables.at(id).parent; + } + auto &variable = m_variables[id]; + variable.parent = parent; + variable.parentIndex = parentIndex; + variable.childIndex = childIndex; + variable.flags = {}; + variable.flags.setFlag(Variable::Flag::Visited, m_visited); + const auto name = entry.attr(debuginfo::dwarf::_At::DW_AT_name); + variable.data[0] = QString::fromUtf8(name.str().data(), name.str().rtrim('\0').size()); + variable.data[1] = ""; + return id; +} + int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, int childIndex, const Symbol &symbol, Context context, qint32 base, const QString &name) { @@ -1062,7 +881,7 @@ int SourcesWidget::VariableModel::createVariable(int parent, int parentIndex, in variable.symbol = symbol; variable.base = base; variable.context = context; - variable.flags = 0; + variable.flags = {}; variable.flags.setFlag(Variable::Flag::Visited, m_visited); variable.data[0] = name.isNull() ? variableTypeToString(variable) : name; variable.data[1] = variableValueToString(variable); @@ -1097,10 +916,8 @@ void SourcesWidget::VariableModel::removeTopLevels(int first, int last) { deleteVariable(id); } } - m_topLevelData[0].erase(m_topLevelData[0].begin() + first, - m_topLevelData[0].begin() + last + 1); - m_topLevelData[1].erase(m_topLevelData[1].begin() + first, - m_topLevelData[1].begin() + last + 1); + for (auto &topLevelData : m_topLevelData) + topLevelData.erase(topLevelData.begin() + first, topLevelData.begin() + last + 1); m_topLevelChildren.erase(m_topLevelChildren.begin() + first, m_topLevelChildren.begin() + last + 1); endRemoveRows(); @@ -1258,9 +1075,8 @@ QVariant SourcesWidget::VariableModel::data(const QModelIndex &index, int role) } switch (role) { case Qt::DisplayRole: - if (index.internalId() == s_topLevelId) { + if (index.internalId() == s_topLevelId) return m_topLevelData[index.column()].at(index.row()); - } return m_variables.at(index.internalId()).data[index.column()]; case Qt::TextAlignmentRole: return int((index.column() ? Qt::AlignLeft : Qt::AlignRight) | @@ -1385,179 +1201,317 @@ void SourcesWidget::GlobalModel::init(const QStringList &paths) { beginResetModel(); m_freeVariable = -1; m_variables.clear(); - m_topLevelData[0].clear(); - m_topLevelData[1].clear(); + for (auto &topLevelData : m_topLevelData) + topLevelData.clear(); m_topLevelChildren.clear(); - int prefixLength = commonPrefixLength(paths); - for (int parent = 0; parent < sources().count(); ++parent) { - QStringRef path = paths.at(parent).midRef(prefixLength); - int lastSeparator = path.lastIndexOf('/') + 1; - m_topLevelData[0] << path.left(lastSeparator).toString(); - m_topLevelData[1] << path.mid(lastSeparator).toString(); - m_topLevelChildren.append(QList()); - - auto &source = sources().at(parent); - auto &symbols = source.symbolList; - Context context = { nullptr, &source }; - for (int child = 0; child < symbols.count(); ++child) { - auto &symbol = symbols.at(child); - QString name = stringList().value(symbol.name - 1); - m_topLevelChildren.last() << - createVariable(s_topLevelParent, parent, child, symbol, context); - } - } + //int prefixLength = commonPrefixLength(paths); + //for (int parent = 0; parent < sources().count(); ++parent) { + // QStringRef path = paths.at(parent).midRef(prefixLength); + // int lastSeparator = path.lastIndexOf('/') + 1; + // m_topLevelData[0] << path.left(lastSeparator).toString(); + // m_topLevelData[1] << path.mid(lastSeparator).toString(); + // m_topLevelChildren.append(QList()); + // + // auto &source = sources().at(parent); + // auto &symbols = source.symbolList; + // Context context = { nullptr, &source }; + // for (int child = 0; child < symbols.count(); ++child) { + // auto &symbol = symbols.at(child); + // QString name = stringList().value(symbol.name - 1); + // m_topLevelChildren.last() << + // createVariable(s_topLevelParent, parent, child, symbol, context); + // } + //} endResetModel(); } -void SourcesWidget::StackModel::update() { - QVector stack; - quint32 ix = cpu.registers.IX, pc = cpu.registers.PC; - bool top = true; - forever { - int sourceIndex, functionIndex; - if (!lookupAddr(pc, &sourceIndex, &functionIndex, nullptr)) { - break; +constexpr quint32 SourcesWidget::StackModel::undefValue; + +void SourcesWidget::StackModel::computeRule(const StackEntry &entry, quint32 &value, + const debuginfo::dwarf::section::Frame::Rule &rule) { + if (rule.is_same_val()) { + return; + } + value = undefValue; + if (rule.is_off()) { + if (entry.cfa != undefValue) { + value = mem_peek_long((entry.cfa + rule.off()) & 0xFFFFFFu); } - const Source &source = sources().at(sourceIndex); - const Function &function = source.functionList.at(functionIndex); - static const quint8 prolog[2 + 5 + 2] = {0xDD,0xE5, 0xDD,0x21,0,0,0, 0xDD,0x39}, - epilog[2 + 2 + 1] = {0xDD,0xF9, 0xDD,0xE1, 0xC9}; - static const quint32 stackTop = 0xD1A87E, stackBot = 0xD1987E; - quint32 prologStartAddr = function.lines.first().addr, - prologEndAddr = function.lines.at(1).addr, - epilogStartAddr = function.lines.last().addr - sizeof(epilog); - void *prologPtr = phys_mem_ptr(prologStartAddr, sizeof(prolog)), - *epilogPtr = phys_mem_ptr(epilogStartAddr, sizeof(epilog)); - if (!prologPtr || memcmp(prologPtr, prolog, sizeof(prolog)) || - !epilogPtr || memcmp(epilogPtr, epilog, sizeof(epilog))) { - break; + } else if (rule.is_val_off()) { + if (entry.cfa != undefValue) { + value = (entry.cfa + rule.off()) & 0xFFFFFFu; } - if (pc >= prologEndAddr && pc <= epilogStartAddr) { - stack << StackEntry{ ix, pc, { mem_peek_long(ix), mem_peek_long(ix + 3) }, - &source, &function }; + } else if (rule.is_reg()) { + if (rule.reg() != debuginfo::dwarf::Reg::None) { + value = entry.regs[rule.reg()]; } - quint32 ixAddr, pcAddr; - if (pc < prologStartAddr + sizeof(prolog)) { - if (!top) { - break; - } - if (pc < prologStartAddr + 2) { // PUSH IX - ixAddr = ~0u; - pcAddr = cpu.registers.SPL; - } else { // LD IX,0 \ ADD IX,SP - ixAddr = cpu.registers.SPL; - pcAddr = cpu.registers.SPL + 3; - } - } else if (pc >= epilogStartAddr + 2 + 2) { // LD SP,IX \ POP IX - if (!top) { - break; + } else if (rule.is_reg_off()) { + if (rule.reg() != debuginfo::dwarf::Reg::None) { + quint32 reg = entry.regs[rule.reg()]; + if (reg != undefValue) { + value = (reg + rule.off()) & 0xFFFFFFu; } - ixAddr = ~0u; - pcAddr = cpu.registers.SPL; - } else { - ixAddr = ix; - pcAddr = ix + 3; } - if (ixAddr > stackBot + 3 && ixAddr <= stackTop - 3) { - ix = mem_peek_long(ixAddr); - } else if (ixAddr != ~0u) { - break; - } - if (pcAddr > stackBot + 3 && pcAddr <= stackTop - 3) { - pc = mem_peek_long(pcAddr); - } else { - break; - } - top = false; } - if (stack.isEmpty()) { - ix = cpu.registers.SPL; - } else { - ix = stack.last().ix; - } - int aliveIndex = m_stack.count(); - while (aliveIndex--) { - auto &entry = m_stack.at(aliveIndex); - if (entry.ix <= ix || - mem_peek_long(entry.ix) != entry.cookie[0] || - mem_peek_long(entry.ix + 3) != entry.cookie[1]) { +} + +void SourcesWidget::StackModel::update() { + if (!debugFile()) { + return; + } + + QVector stack; + StackEntry entry; + entry.cfa = undefValue; + entry.regs[debuginfo::dwarf::Reg::BC] = cpu.registers.BC; + entry.regs[debuginfo::dwarf::Reg::DE] = cpu.registers.DE; + entry.regs[debuginfo::dwarf::Reg::HL] = cpu.registers.HL; + entry.regs[debuginfo::dwarf::Reg::AF] = cpu.registers.AF; + entry.regs[debuginfo::dwarf::Reg::IX] = cpu.registers.IX; + entry.regs[debuginfo::dwarf::Reg::IY] = cpu.registers.IY; + entry.regs[debuginfo::dwarf::Reg::SPS] = cpu.registers.SPS; + entry.regs[debuginfo::dwarf::Reg::SPL] = cpu.registers.SPL; + entry.regs[debuginfo::dwarf::Reg::PC] = cpu.registers.PC; + QSet seen; + seen << undefValue; + int fudgePC = 0; + forever { + quint32 pc = (entry.regs[debuginfo::dwarf::Reg::PC] + fudgePC) & 0xFFFFFFu; + const auto &rules = debugFile()->frame().get(pc); + computeRule(entry, entry.cfa, rules.cfa()); + if (seen.contains(entry.cfa) || seen.size() > 1500) break; + seen << entry.cfa; + { + int index = stack.count(); + const debuginfo::dwarf::section::Info::_Scope *prev = + &debuginfo::dwarf::section::Info::_Scope::null(); + const auto insert = [&]() { + if (*prev && !prev->is_unit()) { + entry.scope = prev; + stack.insert(index, entry); + } + }; + for (const auto *scope = &debugFile()->info().root(); + *scope; prev = scope, scope = &scope->get(pc)) + if (scope->is_function()) + insert(); + insert(); } - } - int commonSuffix = m_stack.count() - aliveIndex++; - stack << m_stack.mid(aliveIndex); - for (; commonSuffix <= stack.count() && commonSuffix <= m_stack.count() && - stack.at(stack.count() - commonSuffix).function == - m_stack.at(m_stack.count() - commonSuffix).function; ++commonSuffix); + for (debuginfo::dwarf::Reg reg{}; reg != debuginfo::dwarf::Reg::None; + reg = debuginfo::dwarf::Reg(std::size_t(reg) + 1)) + computeRule(entry, entry.regs[reg], rules[reg]); + fudgePC = -1; + } + int commonSuffix = 1; + while (commonSuffix <= stack.count() && commonSuffix <= m_stack.count() && + stack.at(stack.count() - commonSuffix).same(m_stack.at(m_stack.count() - commonSuffix))) + ++commonSuffix; + commonSuffix = 1; removeTopLevels(0, m_stack.count() - commonSuffix); VariableModel::update(); if (commonSuffix <= stack.count()) { beginInsertRows(QModelIndex(), 0, stack.count() - commonSuffix); for (int parent = stack.count() - commonSuffix; parent >= 0; --parent) { - auto &entry = stack.at(parent); - qDebug().nospace().noquote() << stringList().at(entry.function->name - 1) << ':'; - m_topLevelData[0].prepend(stringList().value(entry.function->name - 1)); - m_topLevelData[1].prepend("()"); + const auto &entry = stack.at(parent); + const auto pc = entry.regs[debuginfo::dwarf::Reg::PC]; + const auto &name = entry.scope->function().entry().attr(debuginfo::dwarf::_At::DW_AT_name); + m_topLevelData[0].prepend(QString::fromUtf8(name.str().data(), + name.str().rtrim('\0').size())); + m_topLevelData[1].prepend(QStringLiteral("()")); m_topLevelChildren.prepend({}); - Context context = { entry.function, entry.source }; - auto &symbols = entry.function->symbolList; - for (int child = 0; child < symbols.count(); ++child) { - auto &symbol = symbols.at(child); - QString name = stringList().value(symbol.name - 1); - m_topLevelChildren.first() << - createVariable(s_topLevelParent, parent - stack.count(), child, symbol, - context, symbol.kind == SymbolKind::StackSlot ? entry.ix : 0); + int child = 0; + for (const auto *scope = entry.scope, + *prev = &debuginfo::dwarf::section::Info::_Scope::null(); + !prev->is_function(); prev = scope, scope = &scope->parent()) { + for (const auto &entity : *scope) { + switch (entity.get()._M_tag) { + case debuginfo::dwarf::_Tag::DW_TAG_variable: { + const auto &location = + entity.get().attr(debuginfo::dwarf::_At::DW_AT_location); + debuginfo::Data expr; + if (location.is_data()) + expr = location.data(); + else if (location.is_loclist()) + for (auto loc : location.loclist()) { + if (pc < decltype(pc)(loc._M_range.first)) + break; + if (pc < decltype(pc)(loc._M_range.second)) { + expr = expr = loc._M_expr; + break; + } + } + if (!expr.empty()) + m_topLevelChildren.first() << createVariable(s_topLevelParent, + parent - stack.count(), + child++, entity); + break; + } + default: + break; + } + } } } - qDebug() << ""; endInsertRows(); } int firstParentChanged = -1; const QVector changedRoles{ Qt::DisplayRole }; for (int parent = 0; parent < stack.count(); ++parent) { - auto &symbols = stack.at(parent).function->symbolList; - QStringList argumentList; - for (int child = 0; child < symbols.count(); ++child) { - auto &symbol = symbols.at(child); - if (symbol.kind != SymbolKind::StackSlot) { - continue; - } - if (symbol.value < 0) { - break; - } - argumentList << - m_variables.at(m_topLevelChildren.at(parent).at(child)).data[1]; - if (argumentList.last().isEmpty()) { - argumentList.last() = '?'; - } - } - QString arguments = '(' + argumentList.join(", ") + ')'; - bool argumentsChanged = m_topLevelData[1].at(parent) != arguments; - m_topLevelData[1][parent] = arguments; - if (argumentsChanged ^ (firstParentChanged == -1)) { - if (argumentsChanged) { - emit dataChanged(createIndex(firstParentChanged, 1, s_topLevelId), - createIndex(parent - 1, 1, s_topLevelId), changedRoles); - firstParentChanged = -1; - } else { - firstParentChanged = parent; - } - } } - if (firstParentChanged != -1) { + if (firstParentChanged != -1) emit dataChanged(createIndex(firstParentChanged, 1, s_topLevelId), createIndex(stack.count() - 1, 1, s_topLevelId), changedRoles); - } m_stack = stack; + + //QVector stack; + //quint32 ix = cpu.registers.IX, pc = cpu.registers.PC; + //bool top = true; + //forever { + // int sourceIndex, functionIndex; + // if (!lookupAddr(pc, &sourceIndex, &functionIndex)) { + // break; + // } + // const Source &source = sources().at(sourceIndex); + // const Function &function = source.functionList.at(functionIndex); + // static const quint8 prolog[2 + 5 + 2] = {0xDD,0xE5, 0xDD,0x21,0,0,0, 0xDD,0x39}, + // epilog[2 + 2 + 1] = {0xDD,0xF9, 0xDD,0xE1, 0xC9}; + // static const quint32 stackTop = 0xD1A87E, stackBot = 0xD1987E; + // quint32 prologStartAddr = function.lines.first().addr, + // prologEndAddr = function.lines.at(1).addr, + // epilogStartAddr = function.lines.last().addr - sizeof(epilog); + // void *prologPtr = phys_mem_ptr(prologStartAddr, sizeof(prolog)), + // *epilogPtr = phys_mem_ptr(epilogStartAddr, sizeof(epilog)); + // if (!prologPtr || memcmp(prologPtr, prolog, sizeof(prolog)) || + // !epilogPtr || memcmp(epilogPtr, epilog, sizeof(epilog))) { + // break; + // } + // if (pc >= prologEndAddr && pc <= epilogStartAddr) { + // stack << StackEntry{ ix, pc, { mem_peek_long(ix), mem_peek_long(ix + 3) }, + // &source, &function }; + // } + // quint32 ixAddr, pcAddr; + // if (pc < prologStartAddr + sizeof(prolog)) { + // if (!top) { + // break; + // } + // if (pc < prologStartAddr + 2) { // PUSH IX + // ixAddr = ~0u; + // pcAddr = cpu.registers.SPL; + // } else { // LD IX,0 \ ADD IX,SP + // ixAddr = cpu.registers.SPL; + // pcAddr = cpu.registers.SPL + 3; + // } + // } else if (pc >= epilogStartAddr + 2 + 2) { // LD SP,IX \ POP IX + // if (!top) { + // break; + // } + // ixAddr = ~0u; + // pcAddr = cpu.registers.SPL; + // } else { + // ixAddr = ix; + // pcAddr = ix + 3; + // } + // if (ixAddr > stackBot + 3 && ixAddr <= stackTop - 3) { + // ix = mem_peek_long(ixAddr); + // } else if (ixAddr != ~0u) { + // break; + // } + // if (pcAddr > stackBot + 3 && pcAddr <= stackTop - 3) { + // pc = mem_peek_long(pcAddr); + // } else { + // break; + // } + // top = false; + //} + //if (stack.isEmpty()) { + // ix = cpu.registers.SPL; + //} else { + // ix = stack.last().ix; + //} + //int aliveIndex = m_stack.count(); + //while (aliveIndex--) { + // auto &entry = m_stack.at(aliveIndex); + // if (entry.ix <= ix || + // mem_peek_long(entry.ix) != entry.cookie[0] || + // mem_peek_long(entry.ix + 3) != entry.cookie[1]) { + // break; + // } + //} + //int commonSuffix = m_stack.count() - aliveIndex++; + //stack << m_stack.mid(aliveIndex); + //for (; commonSuffix <= stack.count() && commonSuffix <= m_stack.count() && + // stack.at(stack.count() - commonSuffix).function == + // m_stack.at(m_stack.count() - commonSuffix).function; ++commonSuffix); + //removeTopLevels(0, m_stack.count() - commonSuffix); + //VariableModel::update(); + //if (commonSuffix <= stack.count()) { + // beginInsertRows(QModelIndex(), 0, stack.count() - commonSuffix); + // for (int parent = stack.count() - commonSuffix; parent >= 0; --parent) { + // auto &entry = stack.at(parent); + // qDebug().nospace().noquote() << stringList().at(entry.function->name - 1) << ':'; + // m_topLevelData[0].prepend(stringList().value(entry.function->name - 1)); + // m_topLevelData[1].prepend("()"); + // m_topLevelChildren.prepend({}); + // Context context = { entry.function, entry.source }; + // auto &symbols = entry.function->symbolList; + // for (int child = 0; child < symbols.count(); ++child) { + // auto &symbol = symbols.at(child); + // QString name = stringList().value(symbol.name - 1); + // m_topLevelChildren.first() << + // createVariable(s_topLevelParent, parent - stack.count(), child, symbol, + // context, symbol.kind == SymbolKind::StackSlot ? entry.ix : 0); + // } + // } + // qDebug() << ""; + // endInsertRows(); + //} + //int firstParentChanged = -1; + //const QVector changedRoles{ Qt::DisplayRole }; + //for (int parent = 0; parent < stack.count(); ++parent) { + // auto &symbols = stack.at(parent).function->symbolList; + // QStringList argumentList; + // for (int child = 0; child < symbols.count(); ++child) { + // auto &symbol = symbols.at(child); + // if (symbol.kind != SymbolKind::StackSlot) { + // continue; + // } + // if (symbol.value < 0) { + // break; + // } + // argumentList << + // m_variables.at(m_topLevelChildren.at(parent).at(child)).data[1]; + // if (argumentList.last().isEmpty()) { + // argumentList.last() = '?'; + // } + // } + // QString arguments = '(' + argumentList.join(", ") + ')'; + // bool argumentsChanged = m_topLevelData[1].at(parent) != arguments; + // m_topLevelData[1][parent] = arguments; + // if (argumentsChanged ^ (firstParentChanged == -1)) { + // if (argumentsChanged) { + // emit dataChanged(createIndex(firstParentChanged, 1, s_topLevelId), + // createIndex(parent - 1, 1, s_topLevelId), changedRoles); + // firstParentChanged = -1; + // } else { + // firstParentChanged = parent; + // } + // } + //} + //if (firstParentChanged != -1) { + // emit dataChanged(createIndex(firstParentChanged, 1, s_topLevelId), + // createIndex(stack.count() - 1, 1, s_topLevelId), changedRoles); + //} + //m_stack = stack; } + void SourcesWidget::StackModel::init() { beginResetModel(); m_freeVariable = -1; m_variables.clear(); - m_topLevelData[0].clear(); - m_topLevelData[1].clear(); + for (auto &topLevelData : m_topLevelData) + topLevelData.clear(); m_topLevelChildren.clear(); m_stack.clear(); endResetModel(); - update(); } diff --git a/gui/qt/debugger/sourceswidget.h b/gui/qt/debugger/sourceswidget.h index cb99f1ca5..c05c3b84d 100644 --- a/gui/qt/debugger/sourceswidget.h +++ b/gui/qt/debugger/sourceswidget.h @@ -5,6 +5,8 @@ class CDebugHighlighter; +#include + #include #include #include @@ -25,16 +27,35 @@ class SourcesWidget : public QWidget { public: SourcesWidget(QWidget * = Q_NULLPTR); + ~SourcesWidget() override; void setSourceFont(const QFont &font); + class LocInfo { + quint32 m_end, m_file, m_line, m_col; + + public: + constexpr LocInfo() noexcept : m_end(~0u), m_file(), m_line(), m_col() {} + constexpr LocInfo(quint32 end, quint32 file, quint32 line, quint32 col) noexcept + : m_end(end), m_file(file), m_line(line), m_col(col) {} + + constexpr bool invalid() const noexcept { return m_end == ~0u; } + constexpr bool unknown() const noexcept { return !m_line; } + constexpr quint32 end() const noexcept { return m_end; } + constexpr void end(quint32 end) noexcept { m_end = end; } + constexpr bool sameSourceLoc(const LocInfo &other) const noexcept { + return m_file == other.m_file && m_line == other.m_line && m_col == other.m_col; + } + }; + LocInfo getLocInfo(quint32 addr) const; + signals: void runUntilTriggered(quint32 addr); void breakToggled(quint32 addr, bool gui = true); public slots: void selectDebugFile(); - void updatePC(quint32 pc); + void updatePC(); void updateAddr(quint32 addr, unsigned type, bool state); private slots: @@ -47,6 +68,7 @@ private slots: class VariableModel; class GlobalModel; class StackModel; + class DebugFile; QTabWidget *m_tabs; //QTextCursor m_pcCursor; @@ -56,6 +78,7 @@ private slots: StackModel *m_stackModel; QTextCharFormat m_defaultFormat, m_operatorFormat, m_literalFormat, m_escapeFormat, m_preprocessorFormat, m_commentFormat, m_keywordFormat, m_identifierFormat, m_errorFormat; + std::unique_ptr m_debugFile; constexpr static quint32 s_unnamed = 0; struct Line { @@ -105,7 +128,7 @@ private slots: QVector functionList; QMultiHash functionMap; }; - QVector m_sources; + //QVector m_sources; QStringList m_stringList; QHash m_stringMap; #ifdef QT_DEBUG @@ -117,9 +140,6 @@ private slots: friend QDebug operator<<(QDebug debug, const Function &function); friend QDebug operator<<(QDebug debug, const Source &sourc); #endif - - bool lookupAddr(quint32 addr, int *sourceIndex = nullptr, - int *functionIndex = nullptr, int *lineIndex = nullptr) const; }; #endif diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index ae1d78c4c..5f214dd50 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -11,8 +11,9 @@ #include "dockwidget.h" #include "datawidget.h" #include "vartablemodel.h" -#include "debugger/hexwidget.h" #include "debugger/disasm.h" +#include "debugger/hexwidget.h" +#include "debugger/sourceswidget.h" #include "capture/animated-png.h" #include "../../core/vat.h" #include "../../core/debug/debug.h" @@ -767,6 +768,7 @@ private slots: bool m_setup = false; int m_fullscreen = FULLSCREEN_NONE; uint32_t m_runUntilAddr; + SourcesWidget::LocInfo m_runLoc; QPushButton *m_btnCancelTranser; QProgressBar *m_progressBar; diff --git a/gui/qt/mainwindow.ui b/gui/qt/mainwindow.ui index c627ad59b..1e3f32afe 100644 --- a/gui/qt/mainwindow.ui +++ b/gui/qt/mainwindow.ui @@ -583,6 +583,35 @@ + + + + true + + + + 0 + 0 + + + + + + + + :/icons/resources/icons/C.png:/icons/resources/icons/C.png + + + true + + + true + + + false + + + diff --git a/gui/qt/resources.qrc b/gui/qt/resources.qrc index b3b8a4391..a907730da 100644 --- a/gui/qt/resources.qrc +++ b/gui/qt/resources.qrc @@ -15,6 +15,7 @@ resources/icons/break_watch_port.png resources/icons/breakpoints.png resources/icons/bug.png + resources/icons/C.png resources/icons/capture.png resources/icons/change_cert.png resources/icons/change_language.png diff --git a/gui/qt/resources/icons/C.png b/gui/qt/resources/icons/C.png new file mode 100644 index 0000000000000000000000000000000000000000..8f2167ba39d498fa6a25824d3401d044ad937ed5 GIT binary patch literal 1762 zcmV<81|9i{P)EX>4Tx04R}tkv&MmP!xqvQ>9WW4t5Z6$WV2$AS&XhRVYG*P%E_RVDi#GXws0R zxHt-~1qXi?s}3&Cx;nTDg5VE`lcSTOiznMuzRM~TH^2P+-SN~T6UMI2K#o$`gO z$13M7&RV6$TKD8HjO6u|Wvu?YAB-u8*$ooQY@tDJmKRXbo~;!6mpfp z$gzMbG{~+W{11N5)+$U+cuA22(EZ{#AHzUs7iiWU=lj@knkPW;8MxBh{%Ql5`6RvG z)?!CM-!^b@-PV*n;Bp5Tcrs*DcBLRKp-=$c&*+sZeUi0SGI>+e)kfB+nZh(VB zV5CUd>pt)9>73iYJ+1lu05qy{j+W0^lmGw#24YJ`L;xxPDgY{+Q-(SK000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2jmJE2m&3!Kg$LH000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}000EJNklz}Q+_vgd9+4Z*FhV>-(<(KpOeZS}XJLlZ<6U@^#9F$Aj#nmU&`1OEl##@zK z(kmz8QYP-)7q|O{&kdx8fMMVkaKSKiv(kBDE}%`H%jS+;?2UTs#_K*cwn9ag@sy~U z+higk(!n#F4UQ*)9^h-B(=hb({0JP9&Zh-?*IH-y`9!*Qep-#w5dMPBcS)5~r7Ij}|*9GQ`E} zCA0Yi@QPvRGumRl^>zc@nig;utY<1#fhR+wS4_BVnV*d^G$!n|2=5ky7h5^dl1XSu z7C1|kp2N&Ak+m(KGYtJQRj_FSQbmZl|F3?fN;XYEQAuY@fSFDYE~icD!KxF;j1$dB zVM8(#9%SJzT&_!wWk3dhVo)KPr` zKy)PhhiS_L!AR3iUTw=3E^g7Z!0M_KP_CTnzMZqTTuWI=;+dD(u`E|u8Xr_VSx%O; zrUjI#Z`K;qw7_Go-5>XFTRvK%9wd(J4RS^|1HEBxrNmS>+;15A#mQ@w%GM=pYRL`1 zxhnV3_MXE%iI_Q^*!zJWi`#KA0mW+B7+tsPwfdE=blJgE$&E4*kC9C0$XY6@%QvRf zF3P&xE}us=9h3i%A?msg@@>d9wedop?8%vfTY*I)VcFzy`6FU^N7J%J)QHleNyUxg zsmJYWz*|?3r`GFoyM)K76#Pkrj|Op`i=pXjYXPiCBz$bPq4X2w{Fi2L-(BLC@*mPm zCDyVY-;5C1rD*|ASq1VcjQahpUU{=OaC6Luv$>6hE|d8WrxGDYsS5|WnVb%!2Iz}K z!dJ=?0Py=;PXjwds)X@Lgd2Vm>;@xEq!%QsNp;Kl1vai|JHP9P?dWK&VbM}~+Fj*!SzlxDAC=pWAM zWt1n;2|QP&#%BTV6cSJ?c}F7QwBO%)1gHfzm3_!OAo!dgr+GD&0dE5@0T0hP6QLhC z27F@}dgy^($0f)t%`4KF{|c2E0}}ZuxZg1JM5VL-0q5D-8Z4g|MF0Q*07*qoM6N<$ Ef-#^ykN^Mx literal 0 HcmV?d00001 From 28442f97bf481037339a3365aa2170292e534e33 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 6 Mar 2021 21:54:18 -0500 Subject: [PATCH 19/26] Fix unaligned pointer sanitizer issue. --- gui/qt/debugger/debuginfo.cpp | 103 +++++----- gui/qt/debugger/debuginfo.h | 357 ++++++++++++++++++---------------- 2 files changed, 244 insertions(+), 216 deletions(-) diff --git a/gui/qt/debugger/debuginfo.cpp b/gui/qt/debugger/debuginfo.cpp index dc122005b..7c5efdbe9 100644 --- a/gui/qt/debugger/debuginfo.cpp +++ b/gui/qt/debugger/debuginfo.cpp @@ -112,25 +112,25 @@ template constexpr std::enable_if_t::value, std::underlying_type_t> underlying(Enum e) noexcept { return std::underlying_type_t(e); } -constexpr _SizedIntegral consume_uleb128(Data &data) noexcept { - _Sbyte cur{}; +constexpr detail::SizedInteger consume_uleb128(Data &data) noexcept { + std::int8_t cur{}; std::uintmax_t res{}; std::size_t shift{}; do { cur = consume<_Sbyte>(data); res |= (cur & 0x7F) << shift; - shift += std::numeric_limits<_Sbyte>::digits; + shift += std::numeric_limits::digits; } while (cur < 0); return { res, shift }; } -constexpr _SizedIntegral consume_sleb128(Data &data) noexcept { - _Sbyte cur{}; +constexpr detail::SizedInteger consume_sleb128(Data &data) noexcept { + std::int8_t cur{}; std::intmax_t res{}; std::size_t shift{}; do { cur = consume<_Sbyte>(data); res |= (cur & 0x7F) << shift; - shift += std::numeric_limits<_Sbyte>::digits; + shift += std::numeric_limits::digits; } while (cur < 0); if (shift <= std::numeric_limits::digits) { res = res << (std::numeric_limits::digits - shift + 1) @@ -259,6 +259,12 @@ template constexpr std::size_t size(const Typ return Extent; } +template +QDebug operator<<[[gnu::unused]](QDebug debug, detail::UnalignedLittleEndianIntegerImpl< + Signed, Bits, Indices...> i) { + return debug << Qt::hex << Qt::showbase << i; +} + QDebug operator<<[[gnu::unused]](QDebug debug, _Tag tag) { static const char *names[] = { "_None", @@ -342,7 +348,8 @@ QDebug operator<<[[gnu::unused]](QDebug debug, _Tag tag) { static_assert(size(names) == std::size_t(_Tag::_Unused), "Missing names"); QDebugStateSaver saver(debug); return debug.nospace().noquote() << Qt::left << qSetFieldWidth(31) - << (_Word(tag) < size(names) ? names[_Word(tag)] : "_Reserved"); + << (std::size_t(tag) < size(names) + ? names[std::size_t(tag)] : "_Reserved"); } QDebug operator<<[[gnu::unused]](QDebug debug, _At at) { const char *names[] = { @@ -492,7 +499,8 @@ QDebug operator<<[[gnu::unused]](QDebug debug, _At at) { static_assert(size(names) == std::size_t(_At::_Unused), "Missing names"); QDebugStateSaver saver(debug); return debug.nospace().noquote() << Qt::left << qSetFieldWidth(30) - << (_Word(at) < size(names) ? names[_Word(at)] : "_Reserved"); + << (std::size_t(at) < size(names) + ? names[std::size_t(at)] : "_Reserved"); } QDebug operator<<[[gnu::used]](QDebug debug, _Form form) { const char *names[] = { @@ -547,7 +555,8 @@ QDebug operator<<[[gnu::used]](QDebug debug, _Form form) { static_assert(size(names) == std::size_t(_Form::_Unused), "Missing names"); QDebugStateSaver saver(debug); return debug.nospace().noquote() << Qt::left << qSetFieldWidth(29) - << (_Word(form) < size(names) ? names[_Word(form)] : "_Reserved"); + << (std::size_t(form) < size(names) + ? names[std::size_t(form)] : "_Reserved"); } QDebug operator<<[[gnu::unused]](QDebug debug, _Str str) { return debug << QString::fromUtf8(str.data(), str.rtrim('\0').size()); @@ -559,10 +568,6 @@ template QDebug operator<<[[gnu::unused]](QDebug debug, str += c; return debug << str; } -QDebug operator<<[[gnu::unused]](QDebug debug, _Addr addr) { - QDebugStateSaver saver(debug); - return debug << Qt::hex << Qt::showbase << _Word(addr); -} QDebug operator<<[[gnu::unused]](QDebug debug, const _Die &entry); QDebug operator<<[[gnu::unused]](QDebug debug, const _Val &val) { QDebugStateSaver saver(debug); @@ -633,15 +638,15 @@ const _Val &_Die::attr(_At at) const noexcept { const _Die _Die::_S_null{}; -auto Abbrev::get(_Off base) const noexcept -> std::unordered_map<_Word, _Entry> { - std::unordered_map<_Word, _Entry> abbrevs; +auto Abbrev::get(std::uint32_t base) const noexcept -> std::unordered_map { + std::unordered_map abbrevs; auto unit = _M_data.subview(base); while (!error()) { auto code = consume_uleb128(unit); if (!code) { return abbrevs; } - if (_Slong(code) != _Sword(code)) { + if (std::int64_t(code) != std::int32_t(code)) { error("Unsupported code value size"); return {}; } @@ -728,7 +733,7 @@ auto Abbrev::get(_Off base) const noexcept -> std::unordered_map<_Word, _Entry> error("Out of range abbrev attr form"); return {}; } - _Slong implicit_const{}; + std::int64_t implicit_const{}; if (_Form(form.get()) == _Form::DW_FORM_implicit_const) { implicit_const = consume_sleb128(unit); } @@ -810,11 +815,11 @@ void Frame::__parse() { DW_CFA_hi_user = 0x3f, }; struct CIE { - _Word code_alignment_factor; - _Sword data_alignment_factor; - RuleSet initial_rules; + std::uint32_t code_alignment_factor; + std::int32_t data_alignment_factor; + RuleSet initial_rules; }; - std::unordered_map<_Off, CIE> cies; + std::unordered_map cies; const auto evaluate_instructions = [&](const CIE &cie, RuleSet &rules, Data instructions, _Word location = _InvalidWord) { @@ -823,10 +828,10 @@ void Frame::__parse() { return error("Invalid location in initial instructions"); _M_rules[std::exchange(location, new_location)] = rules; }; - const auto advance_loc = [&](_Word advance) { + const auto advance_loc = [&](std::uint32_t advance) { set_loc(location + advance * cie.code_alignment_factor); }; - const auto parse_reg = [&](_Long reg) -> Reg { + const auto parse_reg = [&](std::uint64_t reg) -> Reg { if (reg >= decltype(reg)(Reg::None)) { error("Unknown register"); return Reg::None; @@ -1037,18 +1042,18 @@ const Info::_Scope Info::_Scope::_S_null; void Info::__parse(Line &line, LocLists &loclists, RngLists &rnglists) { struct Fixup { - _Word entry; + std::uint32_t entry; _At at; _Form form; }; struct Unit { - _Off offset; + std::uint32_t offset; std::vector fixups; - std::vector<_Word> file_map; - std::vector> loclists, rnglists; + std::vector file_map; + std::vector> loclists, rnglists; }; std::vector units; - std::unordered_map<_Off, _Word> entry_offsets; + std::unordered_map entry_offsets; { std::vector<_Word> stack{ _InvalidWord }; @@ -1092,7 +1097,7 @@ void Info::__parse(Line &line, LocLists &loclists, RngLists &rnglists) { if (first != (abbrev->second._M_tag == _Tag::DW_TAG_compile_unit)) return error("Expected only the first entry to be a compile unit"); - _Word entry_index = _M_entries.size(); + std::uint32_t entry_index = _M_entries.size(); entry_offsets.emplace(entry_offset, entry_index); if (stack.back() != _InvalidWord) { _M_entries[stack.back()]._M_sibling = entry_index; @@ -1374,8 +1379,8 @@ void Info::__parse(Line &line, LocLists &loclists, RngLists &rnglists) { #endif } -std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { - std::vector<_Word> file_map; +std::vector Line::__parse(std::uint8_t address_size, const _Die &unit_entry) { + std::vector file_map; auto base = unit_entry.attr(_At::DW_AT_stmt_list); if (base.is_none()) { return file_map; @@ -1388,7 +1393,7 @@ std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { error("Unsupported line version"); return {}; } - _Byte segment_selector_size{}; + std::uint8_t segment_selector_size{}; if (version >= 5) { address_size = consume<_Byte>(unit); segment_selector_size = consume<_Byte>(unit); @@ -1408,7 +1413,7 @@ std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { error("Unsupported minimum instruction length"); return {}; } - auto maximum_operations_per_instruction = version < 4 ? 1u : consume<_Byte>(header); + auto maximum_operations_per_instruction = version < 4 ? _Byte(1) : consume<_Byte>(header); if (maximum_operations_per_instruction != 1) { error("Unsupported maximum operations per instruction"); return {}; @@ -1424,7 +1429,7 @@ std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { auto standard_opcode_lengths = header.take_first(opcode_base - 1).bitcast(); auto parse_header_entries = [&](std::initializer_list<_EntryFormat> default_format, _At default_path_attr) -> std::vector<_Entry> { - auto entry_format_count = version < 5 ? default_format.size() : consume<_Byte>(header); + auto entry_format_count = version < 5 ? _Byte(default_format.size()) : consume<_Byte>(header); std::array<_EntryFormat, 5> entry_format; if (entry_format_count > entry_format.size()) { error("Unsupported entry format count"); @@ -1433,7 +1438,7 @@ std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { if (version < 5) { std::copy_n(default_format.begin(), entry_format_count, entry_format.begin()); } else { - for (decltype(entry_format_count) i{}; i != entry_format_count; i++) { + for (decltype(entry_format_count) i{}; i != entry_format_count; ++i) { entry_format[i].first = _Lnct(consume_uleb128(header).get()); entry_format[i].second = _Form(consume_uleb128(header).get()); } @@ -1452,7 +1457,7 @@ std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { return {}; } _Entry entry; - for (decltype(entry_format_count) i{}; i != entry_format_count; i++) { + for (decltype(entry_format_count) i{}; i != entry_format_count; ++i) { auto fixup = _Form::_Unused; auto val = consume_form({ _At::_None, entry_format[i].second, 0 }, header, fixup); if (fixup != _Form::_Unused) { @@ -1555,9 +1560,9 @@ std::vector<_Word> Line::__parse(_Byte address_size, const _Die &unit_entry) { auto file = file_map[state._M_file]; #ifdef MY_DEBUG_DISABLED qDebug() << _M_files[file].path() << state._M_line << state._M_column - << Qt::showbase << Qt::hex << _Word(state._M_address); + << Qt::showbase << Qt::hex << state._M_address; #endif - auto inserted_loc = _M_locs.emplace(state._M_address, state); + auto inserted_loc = _M_locs.emplace(std::uint32_t(state._M_address), state); if (!inserted_loc.second && inserted_loc.first->second._M_end_sequence) { inserted_loc.first->second = state; @@ -1668,8 +1673,8 @@ const SrcFile &Line::get(_Files::size_type pos) const noexcept { return _M_files[pos]; } -std::vector> LocLists::__parse(const _Die &unit_entry) { - std::vector> indices; +std::vector> LocLists::__parse(const _Die &unit_entry) { + std::vector> indices; auto base = unit_entry.attr(_At::DW_AT_loclists_base); if (base.is_none()) { return indices; @@ -1704,8 +1709,8 @@ std::vector> LocLists::__parse(const _Die &unit_entry) { indices.emplace_back(_M_locs.size(), _InvalidWord); auto loclist = unit.subview(offset); while (!error() && indices.back().second == _InvalidWord) { - _Long base_index, start_index, end_index; - _Addr start_addr, end_addr; + std::uint64_t base_index, start_index, end_index; + std::uint32_t start_addr, end_addr; switch (consume<_Byte>(loclist)) { case DW_LLE_end_of_list: indices.back().second = _M_locs.size(); @@ -1767,7 +1772,7 @@ std::vector> LocLists::__parse(const _Die &unit_entry) { } return indices; } -_LocView LocLists::get(std::pair<_Word, _Word> indices) const noexcept { +_LocView LocLists::get(std::pair indices) const noexcept { if (indices.first > indices.second || indices.first > _M_locs.size() || indices.second > _M_locs.size()) { error("Out of range index into location offsets"); @@ -1776,8 +1781,8 @@ _LocView LocLists::get(std::pair<_Word, _Word> indices) const noexcept { return { _M_locs.data() + indices.first, _M_locs.data() + indices.second }; } -std::vector> RngLists::__parse(const _Die &unit_entry) { - std::vector> indices; +std::vector> RngLists::__parse(const _Die &unit_entry) { + std::vector> indices; auto base = unit_entry.attr(_At::DW_AT_rnglists_base); if (base.is_none()) { return indices; @@ -1812,8 +1817,8 @@ std::vector> RngLists::__parse(const _Die &unit_entry) { indices.emplace_back(_M_rngs.size(), _InvalidWord); auto rnglist = unit.subview(offset); while (!error() && indices.back().second == _InvalidWord) { - _Long base_index, start_index, end_index; - _Addr start_addr, end_addr; + std::uint64_t base_index, start_index, end_index; + std::uint32_t start_addr, end_addr; switch (consume<_Byte>(rnglist)) { case DW_RLE_end_of_list: indices.back().second = _M_rngs.size(); @@ -1870,7 +1875,7 @@ std::vector> RngLists::__parse(const _Die &unit_entry) { } return indices; } -_RngView RngLists::get(std::pair<_Word, _Word> indices) const noexcept { +_RngView RngLists::get(std::pair indices) const noexcept { if (indices.first > indices.second || indices.first > _M_rngs.size() || indices.second > _M_rngs.size()) { error("Out of range index into range offsets"); @@ -1879,7 +1884,7 @@ _RngView RngLists::get(std::pair<_Word, _Word> indices) const noexcept { return { _M_rngs.data() + indices.first, _M_rngs.data() + indices.second }; } -_Str Str::get(_Word offset) const noexcept { +_Str Str::get(std::uint32_t offset) const noexcept { Data temp(_M_data.subview(offset)); return consume_strz(temp); } diff --git a/gui/qt/debugger/debuginfo.h b/gui/qt/debugger/debuginfo.h index e4fc90ce8..75e1330cb 100644 --- a/gui/qt/debugger/debuginfo.h +++ b/gui/qt/debugger/debuginfo.h @@ -21,86 +21,103 @@ #include namespace debuginfo { -enum class _Void : unsigned char {}; -using _Sbyte = std::int8_t; -using _Byte = std::uint8_t; -using _Shalf = std::int16_t; -using _Half = std::uint16_t; -using _Off = std::uint32_t; -using _Sword = std::int32_t; -using _Word = std::uint32_t; -using _Slong = std::int64_t; -using _Long = std::uint64_t; - -class [[gnu::packed]] _Addr { - std::array<_Byte, 3> _M_addr; +namespace detail { + +constexpr auto BitsPerByte = std::numeric_limits::digits; + +template +class UnalignedLittleEndianIntegerImpl { + using Byte = unsigned char; + using IntMax = std::conditional_t; + std::array m_value; public: - constexpr _Addr() : _M_addr() {} - constexpr _Addr(_Word __value) noexcept - : _M_addr{ _Byte(__value >> 0), _Byte(__value >> 8), _Byte(__value >> 16) } {} - constexpr _Addr( _Long __value) noexcept : _Addr(_Word(__value)) {} - constexpr _Addr(_Sword __value) noexcept : _Addr(_Word(__value)) {} - constexpr _Addr(_Slong __value) noexcept : _Addr(_Word(__value)) {} - _Addr &operator=(_Word __value) { - _M_addr[0] = __value >> 0; - _M_addr[1] = __value >> 8; - _M_addr[2] = __value >> 16; - return *this; + constexpr UnalignedLittleEndianIntegerImpl() : m_value{} {} + constexpr UnalignedLittleEndianIntegerImpl(IntMax value) + : m_value{ Byte(value >> Indices * BitsPerByte)... } {} + template + constexpr UnalignedLittleEndianIntegerImpl( + UnalignedLittleEndianIntegerImpl value) + : UnalignedLittleEndianIntegerImpl{IntMax(value)} {} + + constexpr UnalignedLittleEndianIntegerImpl &operator++() noexcept { + return *this = *this + 1; + } + constexpr UnalignedLittleEndianIntegerImpl &operator++(int) noexcept { + auto result = *this; + ++*this; + return result; } - _Addr &operator=( _Long __value) { return *this = _Word(__value); } - _Addr &operator=(_Sword __value) { return *this = _Word(__value); } - _Addr &operator=(_Slong __value) { return *this = _Word(__value); } - constexpr operator _Word() const noexcept { - return _M_addr[0] << 0 | _M_addr[1] << 8 | _M_addr[2] << 16; + constexpr UnalignedLittleEndianIntegerImpl &operator+=(IntMax value) noexcept { + return *this = *this + value; } - constexpr operator _Long() const noexcept { return _Word(*this); } - constexpr operator _Sword() const noexcept { - return _M_addr[0] << 0 | _M_addr[1] << 8 | _Sword(_M_addr[2] << 24) >> 8; + constexpr UnalignedLittleEndianIntegerImpl &operator--() noexcept { + return *this = *this - 1; + } + constexpr UnalignedLittleEndianIntegerImpl &operator--(int) noexcept { + auto result = *this; + --*this; + return result; + } + constexpr UnalignedLittleEndianIntegerImpl &operator-=(IntMax value) noexcept { + return *this = *this - value; + } + + constexpr operator IntMax() const noexcept { + constexpr IntMax extend = std::numeric_limits::digits + + std::numeric_limits::is_signed - Bits; + return ((IntMax(m_value[Indices]) << Indices * BitsPerByte) | ...) << extend >> extend; } - constexpr operator _Slong() const noexcept { return _Sword(*this); } - - constexpr _Addr operator+() const noexcept { return +_Word(*this); } - constexpr _Addr operator+(_Addr __addr) const noexcept { return _Word(*this) + _Word(__addr); } - constexpr _Addr &operator+=(_Addr __addr) noexcept { return *this = *this + __addr; } - constexpr _Addr &operator++() noexcept { return *this += 1; } - constexpr _Addr operator++(int) noexcept { _Addr __res(*this); ++(*this); return __res; } - constexpr _Addr operator-() const noexcept { return -_Word(*this); } - constexpr _Addr operator-(_Addr __addr) const noexcept { return _Word(*this) - _Word(__addr); } - constexpr _Addr &operator-=(_Addr __addr) noexcept { return *this = *this - __addr; } - constexpr _Addr &operator--() noexcept { return *this -= 1; } - constexpr _Addr operator--(int) noexcept { _Addr __res(*this); --(*this); return __res; } - constexpr _Addr operator*(_Addr __addr) const noexcept { return _Word(*this) * _Word(__addr); } - constexpr _Addr &operator*=(_Addr __addr) noexcept { return *this = *this * __addr; } - constexpr _Addr operator/(_Addr __addr) const noexcept { return _Word(*this) / _Word(__addr); } - constexpr _Addr &operator/=(_Addr __addr) noexcept { return *this = *this / __addr; } - constexpr _Addr operator%(_Addr __addr) const noexcept { return _Word(*this) % _Word(__addr); } - constexpr _Addr &operator%=(_Addr __addr) noexcept { return *this = *this % __addr; } - - constexpr _Addr operator^(_Addr __addr) const noexcept { return _Word(*this) ^ _Word(__addr); } - constexpr _Addr &operator^=(_Addr __addr) noexcept { return *this = *this ^ __addr; } - constexpr _Addr operator&(_Addr __addr) const noexcept { return _Word(*this) & _Word(__addr); } - constexpr _Addr &operator&=(_Addr __addr) noexcept { return *this = *this & __addr; } - constexpr _Addr operator|(_Addr __addr) const noexcept { return _Word(*this) | _Word(__addr); } - constexpr _Addr &operator|=(_Addr __addr) noexcept { return *this = *this | __addr; } - constexpr _Addr operator~() const noexcept { return ~_Word(*this); } - - constexpr _Addr operator<<(_Addr __addr) const noexcept { return _Word(*this) << _Word(__addr); } - constexpr _Addr &operator<<=(_Addr __addr) noexcept { return *this = *this << __addr; } - constexpr _Addr operator>>(_Addr __addr) const noexcept { return _Word(*this) >> _Word(__addr); } - constexpr _Addr &operator>>=(_Addr __addr) noexcept { return *this = *this >> __addr; } - - constexpr bool operator!() const noexcept { return !_Word(*this); } - constexpr bool operator==(_Addr __addr) const noexcept { return _Word(*this) == _Word(__addr); } - constexpr bool operator!=(_Addr __addr) const noexcept { return _Word(*this) != _Word(__addr); } - constexpr bool operator< (_Addr __addr) const noexcept { return _Word(*this) < _Word(__addr); } - constexpr bool operator> (_Addr __addr) const noexcept { return _Word(*this) > _Word(__addr); } - constexpr bool operator<=(_Addr __addr) const noexcept { return _Word(*this) <= _Word(__addr); } - constexpr bool operator>=(_Addr __addr) const noexcept { return _Word(*this) >= _Word(__addr); } }; -static_assert(sizeof(_Addr) == 3, "_Addr should be 3 bytes"); -constexpr _Off _InvalidOff = std::numeric_limits<_Off>::max(); -constexpr _Word _InvalidWord = std::numeric_limits<_Word>::max(); + +template +UnalignedLittleEndianIntegerImpl +UnalignedLittleEndianIntegerHelper(std::index_sequence); + +template +using UnalignedLittleEndianInteger = + decltype(UnalignedLittleEndianIntegerHelper( + std::make_index_sequence<(Bits + CHAR_BIT - 1) / CHAR_BIT>{})); + +template class SizedInteger { +public: + template constexpr SizedInteger( + Int value, std::enable_if_t::is_integer + && std::is_signed::value == std::is_signed::value, std::size_t> width + = std::numeric_limits::digits + std::numeric_limits::is_signed) noexcept + : m_value(value), m_width(width) {} + template explicit constexpr SizedInteger( + Enum value, std::enable_if_t::value + && std::is_signed>::value == + std::is_signed::value, std::size_t> width + = std::numeric_limits>::digits + + std::numeric_limits>::is_signed) noexcept + : m_value(Integer(value)), m_width(width) {} + constexpr std::size_t width() const noexcept { return m_width; } + constexpr Integer get() const noexcept { return m_value; } + constexpr operator Integer() const noexcept { return m_value; } + +private: + Integer m_value; + std::size_t m_width; +}; + +} // end namespace detail + +enum class _Void : unsigned char {}; +using _Sbyte = detail::UnalignedLittleEndianInteger; +using _Byte = detail::UnalignedLittleEndianInteger; +using _Shalf = detail::UnalignedLittleEndianInteger; +using _Half = detail::UnalignedLittleEndianInteger; +using _Addr = detail::UnalignedLittleEndianInteger; +using _Off = detail::UnalignedLittleEndianInteger; +using _Sword = detail::UnalignedLittleEndianInteger; +using _Word = detail::UnalignedLittleEndianInteger; +using _Slong = detail::UnalignedLittleEndianInteger; +using _Long = detail::UnalignedLittleEndianInteger; + +static_assert(alignof(_Addr) == 1 && sizeof(_Addr) == 3, + "_Addr should be unaligned and 3 bytes"); template class _View { public: @@ -414,45 +431,68 @@ template class _Path { }; template const _Path<_MaxParts> _Path<_MaxParts>::_S_null; template const _Str::value_type _Path<_MaxParts>::sep; + } // end namespace debuginfo namespace std { -template<> struct numeric_limits : numeric_limits { - static constexpr int digits = numeric_limits::digits * sizeof(debuginfo::_Addr); - static constexpr int digits10 = M_LN2 / M_LN10 * digits; - static_assert(digits == 24 && digits10 == 7, "Miscalculated _Addr digits"); +template +class numeric_limits< + debuginfo::detail::UnalignedLittleEndianIntegerImpl> { + using Integer = debuginfo::detail::UnalignedLittleEndianIntegerImpl; - static constexpr debuginfo::_Addr min() noexcept { - return numeric_limits::min(); - } - static constexpr debuginfo::_Addr lowest() noexcept { - return numeric_limits::lowest(); - } - static constexpr debuginfo::_Addr max() noexcept { - return numeric_limits::max(); - } - static constexpr debuginfo::_Addr epsilon() noexcept { - return numeric_limits::epsilon(); - } - static constexpr debuginfo::_Addr round_error() noexcept { - return numeric_limits::round_error(); - } - static constexpr debuginfo::_Addr infinity() noexcept { - return numeric_limits::infinity(); - } - static constexpr debuginfo::_Addr quiet_NaN() noexcept { - return numeric_limits::quiet_NaN(); - } - static constexpr debuginfo::_Addr signaling_NaN() noexcept { - return numeric_limits::signaling_NaN(); +public: + static constexpr bool is_specialized = true; + static constexpr bool is_signed = Signed; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_absent; + static constexpr bool has_denorm_loss = false; + static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_modulo = !Signed; + static constexpr int digits = Bits - Signed; + static constexpr int digits10 = M_LN2 / M_LN10 * digits; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool traps = true; + static constexpr bool tinyness_before = false; + + static constexpr Integer min() noexcept { + return Signed ? ~INTMAX_C(0) << (Bits - 1) : UINTMAX_C(0); } - static constexpr debuginfo::_Addr denorm_min() noexcept { - return numeric_limits::denorm_min(); + static constexpr Integer lowest() noexcept { + return Signed ? ~INTMAX_C(0) << (Bits - 1) : UINTMAX_C(0); } + static constexpr Integer max() noexcept { return ~min(); } + static constexpr Integer epsilon() noexcept { return 0; } + static constexpr Integer round_error() noexcept { return 0; } + static constexpr Integer infinity() noexcept { return 0; } + static constexpr Integer quiet_NaN() noexcept { return 0; } + static constexpr Integer signaling_NaN() noexcept { return 0; } + static constexpr Integer denorm_min() noexcept { return 0; } }; -template<> struct numeric_limits : numeric_limits {}; -template<> struct numeric_limits< volatile debuginfo::_Addr> : numeric_limits {}; -template<> struct numeric_limits : numeric_limits {}; +template +struct numeric_limits> + : numeric_limits> {}; +template +struct numeric_limits< volatile debuginfo::detail::UnalignedLittleEndianIntegerImpl< + Signed, Bits, Indices...>> + : numeric_limits> {}; +template +struct numeric_limits> + : numeric_limits> {}; template<> struct hash { constexpr std::size_t operator()(debuginfo::_Str __str) const noexcept { @@ -476,6 +516,9 @@ template struct hash> { } // end namespace std namespace debuginfo { +constexpr _Off _InvalidOff = std::numeric_limits<_Off>::max(); +constexpr _Word _InvalidWord = std::numeric_limits<_Word>::max(); + namespace elf { class File; @@ -515,7 +558,7 @@ class File { constexpr bool has_header() const noexcept { return _M_data.size_bytes() >= sizeof(Header); } template constexpr _View<_Header> get( - _Off __offset, _Half __size, _Half __count) const noexcept { + std::uint32_t __offset, std::uint16_t __size, std::uint16_t __count) const noexcept { if (__size != sizeof(_Header)) { return {}; } @@ -574,10 +617,10 @@ class File { } // end namespace elf namespace dwarf { -enum class Reg : _Byte { BC, DE, HL, AF, IX, IY, SPS, SPL, PC, None }; +enum class Reg : std::uint8_t { BC, DE, HL, AF, IX, IY, SPS, SPL, PC, None }; // Table 7.3: Tag encodings -enum class _Tag : _Byte { +enum class _Tag : std::uint8_t { _None, DW_TAG_array_type = 0x01, DW_TAG_class_type = 0x02, @@ -659,7 +702,7 @@ enum { DW_TAG_hi_user = 0xffff, }; // Table 7.5: Attribute encodings -enum class _At : _Byte { +enum class _At : std::uint8_t { _None, DW_AT_sibling = 0x01, // reference DW_AT_location = 0x02, // exprloc, loclist @@ -794,7 +837,7 @@ enum { DW_AT_hi_user = 0x3fff, }; // Table 7.6: Attribute form encodings -enum class _Form : _Byte { +enum class _Form : std::uint8_t { _None, DW_FORM_addr = 0x01, DW_FORM_block2 = 0x03, @@ -1053,29 +1096,8 @@ class SrcFile { _Lines _M_lines; }; -template class _SizedIntegral { -public: - template constexpr _SizedIntegral( - _Int __value, std::enable_if_t::value - && std::is_signed<_Int>::value == std::is_signed<_Type>::value, std::size_t> __width - = std::numeric_limits<_Int>::digits) noexcept : _M_value(__value), _M_width(__width) {} - template explicit constexpr _SizedIntegral( - _Enum __value, std::enable_if_t::value - && std::is_signed>::value == - std::is_signed<_Type>::value, std::size_t> __width - = std::numeric_limits>::digits) noexcept - : _M_value(_Type(__value)), _M_width(__width) {} - constexpr std::size_t width() const noexcept { return _M_width; } - constexpr _Type get() const noexcept { return _M_value; } - constexpr operator _Type() const noexcept { return _M_value; } - -private: - _Type _M_value; - std::size_t _M_width; -}; - class _Val { - enum class _Kind : _Byte { + enum class _Kind : std::uint8_t { _None, _Addr, _Cst, @@ -1181,16 +1203,16 @@ class _Val { constexpr bool is_addr() const noexcept { return _M_kind == _Kind::_Addr; } constexpr _Addr addr() const noexcept { if (is_addr()) return _M_addr; return {}; } - template _Val(_SizedIntegral<_Int> __cst) + template _Val(detail::SizedInteger<_Int> __cst) : _M_kind(_Kind::_Cst), _M_flag(std::is_signed<_Int>::value), _M_addr(__cst.width()), _U_ptr() { if (is_large()) { - _M_large = new std::uintmax_t(__cst); + _M_large = new std::uintmax_t(__cst.get()); } else { - _M_small = __cst; + _M_small = __cst.get(); } } - template::value, int> = 0> - _Val(_Int __cst) : _Val(_SizedIntegral<_Int>(__cst)) {} + template::is_integer, int> = 0> + _Val(_Int __cst) : _Val(detail::SizedInteger<_Int>(__cst)) {} constexpr bool is_cst() const noexcept { return _M_kind == _Kind::_Cst; } constexpr std::uintmax_t cst() const noexcept { if (is_large()) return *_M_large; @@ -1267,7 +1289,8 @@ class _Val { struct _Die { _Tag _M_tag : std::numeric_limits<_Sbyte>::digits; bool _M_children : 1; - _Word _M_sibling : std::numeric_limits<_Word>::digits - std::numeric_limits<_Byte>::digits; + std::uint32_t _M_sibling : std::numeric_limits<_Word>::digits - + std::numeric_limits<_Byte>::digits; std::unordered_map<_At, _Val> _M_attrs; const _Val &attr(_At __at) const noexcept; @@ -1293,16 +1316,16 @@ class Abbrev : public Base { struct _Attr { _At _M_at; _Form _M_form; - _Slong _M_implicit_const; + std::int64_t _M_implicit_const; }; struct _Entry { - _Tag _M_tag : std::numeric_limits<_Sbyte>::digits; + _Tag _M_tag : std::numeric_limits::digits; bool _M_children : 1; std::vector<_Attr> _M_attrs; }; Abbrev(File &__file) noexcept : Base(__file) {} - std::unordered_map<_Word, _Entry> get(_Off __base) const noexcept; + std::unordered_map get(std::uint32_t __base) const noexcept; }; class Addr : public Base { @@ -1314,7 +1337,7 @@ class Addr : public Base { class Frame : public Base { public: class Rule { - enum class _Kind : _Byte { + enum class _Kind : std::uint8_t { _Undef, _SameVal, _Off, @@ -1340,16 +1363,16 @@ class Frame : public Base { static constexpr Rule same_val() noexcept { return { _Kind::_SameVal }; } constexpr bool is_same_val() const noexcept { return _M_kind == _Kind::_SameVal; } - static constexpr Rule off(_Sword __off) noexcept { return { _Kind::_Off, __off }; } + static constexpr Rule off(std::int32_t __off) noexcept { return { _Kind::_Off, __off }; } constexpr bool is_off() const noexcept { return _M_kind == _Kind::_Off; } - static constexpr Rule val_off(_Sword __off) noexcept { return { _Kind::_ValOff, __off }; } + static constexpr Rule val_off(std::int32_t __off) noexcept { return { _Kind::_ValOff, __off }; } constexpr bool is_val_off() const noexcept { return _M_kind == _Kind::_ValOff; } static constexpr Rule reg(Reg __reg) noexcept { return { _Kind::_ValOff, __reg }; } constexpr bool is_reg() const noexcept { return _M_kind == _Kind::_Reg; } - static constexpr Rule reg_off(Reg __reg, _Sword __off) noexcept { + static constexpr Rule reg_off(Reg __reg, std::int32_t __off) noexcept { return { _Kind::_RegOff, __reg, __off }; } constexpr bool is_reg_off() const noexcept { return _M_kind == _Kind::_RegOff; } @@ -1372,7 +1395,7 @@ class Frame : public Base { constexpr bool is_addr() const noexcept { return is_off() || is_expr(); } constexpr Reg reg() const noexcept { return _M_reg; } - constexpr _Sword off() const noexcept { return _M_off; } + constexpr std::int32_t off() const noexcept { return _M_off; } constexpr Data expr(const Frame &__frame) const noexcept { if (is_expr() || is_val_expr()) return __frame.expr(_M_off); @@ -1519,7 +1542,7 @@ class Line : public Base { DW_LNE_hi_user = 0xff, }; // Table 7.27: Line number header entry format encodings - enum class _Lnct : _Byte { + enum class _Lnct : std::uint8_t { DW_LNCT_path = 0x1, DW_LNCT_directory_index = 0x2, DW_LNCT_timestamp = 0x3, @@ -1543,27 +1566,27 @@ class Line : public Base { public: struct _Loc { - _Word _M_file; - _Word _M_line; - _Word _M_column; - _Addr _M_address; - bool _M_is_stmt : 1; - bool _M_basic_block : 1; - bool _M_end_sequence : 1; - bool _M_prologue_end : 1; - bool _M_epilogue_begin : 1; - _Byte _M_isa : 1; - _Byte _M_discriminator : 2; + std::uint32_t _M_file; + std::uint32_t _M_line; + std::uint32_t _M_column; + std::uint32_t _M_address : 24; + bool _M_is_stmt : 1; + bool _M_basic_block : 1; + bool _M_end_sequence : 1; + bool _M_prologue_end : 1; + bool _M_epilogue_begin : 1; + std::uint8_t _M_isa : 1; + std::uint8_t _M_discriminator : 2; }; private: using _Files = std::vector; - using _Paths = std::unordered_map<_Path<3>, _Word>; - using _Locs = std::map<_Addr, _Loc>; + using _Paths = std::unordered_map<_Path<3>, std::uint32_t>; + using _Locs = std::map; public: Line(File &__file) noexcept : Base(__file) {} - std::vector<_Word> __parse(_Byte __address_size, const _Die &__unit_entry); + std::vector __parse(std::uint8_t __address_size, const _Die &__unit_entry); void shrink_to_fit() noexcept { _M_files.shrink_to_fit(); } const SrcFile &get(_Files::size_type __pos) const noexcept; _Files::size_type size() const noexcept { return _M_files.size(); } @@ -1591,9 +1614,9 @@ class LocLists : public Base { public: LocLists(File &__file) noexcept : Base(__file) {} - std::vector> __parse(const _Die &__unit_entry); + std::vector> __parse(const _Die &__unit_entry); void shrink_to_fit() noexcept { _M_locs.shrink_to_fit(); } - _LocView get(std::pair<_Word, _Word> __indices) const noexcept; + _LocView get(std::pair __indices) const noexcept; private: std::vector<_Loc> _M_locs; @@ -1614,9 +1637,9 @@ class RngLists : public Base { public: RngLists(File &__file) noexcept : Base(__file) {} - std::vector> __parse(const _Die &__unit_entry); + std::vector> __parse(const _Die &__unit_entry); void shrink_to_fit() noexcept { _M_rngs.shrink_to_fit(); } - _RngView get(std::pair<_Word, _Word> __indices) const noexcept; + _RngView get(std::pair __indices) const noexcept; private: std::vector<_Rng> _M_rngs; @@ -1625,7 +1648,7 @@ class RngLists : public Base { class Str : public Base { public: Str(File &__file) noexcept : Base(__file) {} - _Str get(_Off __offset) const noexcept; + _Str get(std::uint32_t __offset) const noexcept; }; class StrOffsets : public Base { From a931f45b848473d4a946814ca67eea4d1416c4f6 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 22 Aug 2021 21:49:24 -0400 Subject: [PATCH 20/26] Fix gcc errors and warnings. --- gui/qt/debugger/debuginfo.cpp | 13 ++++++++++++- gui/qt/debugger/debuginfo.h | 17 +++++++++-------- gui/qt/debugger/sourceswidget.cpp | 3 ++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/gui/qt/debugger/debuginfo.cpp b/gui/qt/debugger/debuginfo.cpp index 7c5efdbe9..7cc33fee8 100644 --- a/gui/qt/debugger/debuginfo.cpp +++ b/gui/qt/debugger/debuginfo.cpp @@ -51,10 +51,20 @@ const File::Header File::Header::ez80 { .ei_data = ELFDATA2LSB, .ei_version = EV_CURRENT, .ei_osabi = ELFOSABI_STANDALONE, + .ei_pad = {}, .e_type = ET_EXEC, .e_machine = EM_Z80, .e_version = EV_CURRENT, + .e_entry = {}, + .e_phoff = {}, + .e_shoff = {}, .e_flags = EF_Z80_EZ80, + .e_ehsize = {}, + .e_phentsize = {}, + .e_phnum = {}, + .e_shentsize = {}, + .e_shnum = {}, + .e_shstrndx = {}, }; @@ -1572,7 +1582,8 @@ std::vector Line::__parse(std::uint8_t address_size, const _Die & inserted_loc.first->second._M_file = file; if (state._M_is_stmt) { auto inserted_line = _M_files[file].lines().insert( - { { state._M_line, state._M_column }, state._M_address }); + { { state._M_line, state._M_column }, + std::uint32_t(state._M_address) }); if (!inserted_line.second && inserted_line.first->second > state._M_address) { inserted_line.first->second = state._M_address; diff --git a/gui/qt/debugger/debuginfo.h b/gui/qt/debugger/debuginfo.h index 75e1330cb..a0658427d 100644 --- a/gui/qt/debugger/debuginfo.h +++ b/gui/qt/debugger/debuginfo.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -26,13 +27,13 @@ namespace detail { constexpr auto BitsPerByte = std::numeric_limits::digits; template -class UnalignedLittleEndianIntegerImpl { +class [[gnu::packed]] UnalignedLittleEndianIntegerImpl { using Byte = unsigned char; using IntMax = std::conditional_t; - std::array m_value; public: - constexpr UnalignedLittleEndianIntegerImpl() : m_value{} {} + std::array m_value; + constexpr UnalignedLittleEndianIntegerImpl() = default; constexpr UnalignedLittleEndianIntegerImpl(IntMax value) : m_value{ Byte(value >> Indices * BitsPerByte)... } {} template @@ -116,8 +117,8 @@ using _Word = detail::UnalignedLittleEndianInteger; using _Slong = detail::UnalignedLittleEndianInteger; using _Long = detail::UnalignedLittleEndianInteger; -static_assert(alignof(_Addr) == 1 && sizeof(_Addr) == 3, - "_Addr should be unaligned and 3 bytes"); +static_assert(std::is_trivial_v<_Addr> && alignof(_Addr) == 1 && sizeof(_Addr) == 3, + "_Addr should be trivial, unaligned, and 3 bytes"); template class _View { public: @@ -1363,17 +1364,17 @@ class Frame : public Base { static constexpr Rule same_val() noexcept { return { _Kind::_SameVal }; } constexpr bool is_same_val() const noexcept { return _M_kind == _Kind::_SameVal; } - static constexpr Rule off(std::int32_t __off) noexcept { return { _Kind::_Off, __off }; } + static constexpr Rule off(std::int32_t __off) noexcept { return { _Kind::_Off, std::uint32_t(__off) }; } constexpr bool is_off() const noexcept { return _M_kind == _Kind::_Off; } - static constexpr Rule val_off(std::int32_t __off) noexcept { return { _Kind::_ValOff, __off }; } + static constexpr Rule val_off(std::int32_t __off) noexcept { return { _Kind::_ValOff, std::uint32_t(__off) }; } constexpr bool is_val_off() const noexcept { return _M_kind == _Kind::_ValOff; } static constexpr Rule reg(Reg __reg) noexcept { return { _Kind::_ValOff, __reg }; } constexpr bool is_reg() const noexcept { return _M_kind == _Kind::_Reg; } static constexpr Rule reg_off(Reg __reg, std::int32_t __off) noexcept { - return { _Kind::_RegOff, __reg, __off }; + return { _Kind::_RegOff, __reg, std::uint32_t(__off) }; } constexpr bool is_reg_off() const noexcept { return _M_kind == _Kind::_RegOff; } diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index fd9441eb9..cbc1cad3e 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -1198,6 +1198,7 @@ int SourcesWidget::GlobalModel::commonPrefixLength(const QStringList &paths) { } } void SourcesWidget::GlobalModel::init(const QStringList &paths) { + static_cast(paths); beginResetModel(); m_freeVariable = -1; m_variables.clear(); @@ -1314,7 +1315,7 @@ void SourcesWidget::StackModel::update() { beginInsertRows(QModelIndex(), 0, stack.count() - commonSuffix); for (int parent = stack.count() - commonSuffix; parent >= 0; --parent) { const auto &entry = stack.at(parent); - const auto pc = entry.regs[debuginfo::dwarf::Reg::PC]; + auto pc = entry.regs[debuginfo::dwarf::Reg::PC]; const auto &name = entry.scope->function().entry().attr(debuginfo::dwarf::_At::DW_AT_name); m_topLevelData[0].prepend(QString::fromUtf8(name.str().data(), name.str().rtrim('\0').size())); From 9eff395d91e9bb1d0daccb677cf7126c7dcbffce Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 22 Aug 2021 21:56:07 -0400 Subject: [PATCH 21/26] Fix unused variable warning. --- core/debug/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core/debug/debug.c b/core/debug/debug.c index 1713a4312..585ed8eeb 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -264,6 +264,7 @@ void debug_inst_repeat(bool first) { } void debug_record_jump(uint32_t addr) { + (void)addr; if (debug.step) { debug.tempExec = ~0u; } From 05ada31dd9a3a4f780c41649eaa64964142f4954 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 22 Aug 2021 22:08:22 -0400 Subject: [PATCH 22/26] Fix fallthrough warnings. --- core/debug/debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/debug/debug.c b/core/debug/debug.c index 585ed8eeb..0d8f5a9cd 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -4,8 +4,12 @@ #include "../atomics.h" #include "../mem.h" #include "../emu.h" + #include "../cpu.h" +#include "../defines.h" +#include "../emu.h" #include "../flash.h" +#include "../mem.h" #include "../vat.h" #include @@ -189,7 +193,7 @@ void debug_step(int mode, uint32_t addr) { break; case DBG_STEP_OUT: debug.step = debug.stepOver = false; - /* fallthrough */ + fallthrough; case DBG_RUN_OUT: gui_debug_close(); debug.stepOut = debug.stackIndex; @@ -199,7 +203,7 @@ void debug_step(int mode, uint32_t addr) { case DBG_STEP_NEXT: case DBG_RUN_UNTIL: gui_debug_close(); - /* fallthrough */ + fallthrough; case DBG_RUN_IN: case DBG_RUN_OVER: case DBG_RUN_OUT: From 386232377adb2000244a6bb2daf68864e420ea8d Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 4 Jun 2022 06:03:30 -0400 Subject: [PATCH 23/26] Fix ELF defines. --- gui/qt/debugger/debuginfo.cpp | 43 ++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/gui/qt/debugger/debuginfo.cpp b/gui/qt/debugger/debuginfo.cpp index 7cc33fee8..61736d5f6 100644 --- a/gui/qt/debugger/debuginfo.cpp +++ b/gui/qt/debugger/debuginfo.cpp @@ -26,23 +26,30 @@ _Str consume_strz(Data &data) noexcept { namespace elf { enum { - ELFCLASS32 = 1, - ELFDATA2LSB = 1, - ELFOSABI_STANDALONE = 255, - ET_EXEC = 2, - EM_Z80 = 220, - EF_Z80_EZ80 = 1, - EV_CURRENT = 1, - SHT_NULL = 0, - SHT_PROGBITS = 1, - SHT_SYMTAB = 2, - SHT_STRTAB = 3, - SHT_NOBITS = 8, - SHF_WRITE = 1 << 0, - SHF_ALLOC = 1 << 1, - SHF_EXECINSTR = 1 << 2, - SHF_MERGE = 1 << 4, - SHF_STRINGS = 1 << 5, + ELFCLASS32 = 1, + ELFDATA2LSB = 1, + ELFOSABI_STANDALONE = 255, + ET_EXEC = 2, + EM_Z80 = 220, + EF_Z80_MACH_Z80 = 1, + EF_Z80_MACH_Z180 = 2, + EF_Z80_MACH_R800 = 3, + EF_Z80_MACH_EZ80_Z80 = 4, + EF_Z80_MACH_EZ80_ADL = 4 | 1 << 7, + EF_Z80_MACH_GBZ80 = 5, + EF_Z80_MACH_Z80N = 6, + EF_Z80_MACH_MSK = (1 << 8) - 1, + EV_CURRENT = 1, + SHT_NULL = 0, + SHT_PROGBITS = 1, + SHT_SYMTAB = 2, + SHT_STRTAB = 3, + SHT_NOBITS = 8, + SHF_WRITE = 1 << 0, + SHF_ALLOC = 1 << 1, + SHF_EXECINSTR = 1 << 2, + SHF_MERGE = 1 << 4, + SHF_STRINGS = 1 << 5, }; const File::Header File::Header::ez80 { @@ -58,7 +65,7 @@ const File::Header File::Header::ez80 { .e_entry = {}, .e_phoff = {}, .e_shoff = {}, - .e_flags = EF_Z80_EZ80, + .e_flags = EF_Z80_MACH_EZ80_ADL, .e_ehsize = {}, .e_phentsize = {}, .e_phnum = {}, From 6bda5eac0581dfe416965c2493122660b4318880 Mon Sep 17 00:00:00 2001 From: Adrien Bertrand Date: Mon, 2 Jun 2025 08:06:51 +0200 Subject: [PATCH 24/26] src-level-debugging: misc. fixes. - debuginfo: fix size() usage type in paths insert - debuginfo: use special MSVC defines to get math constants - Use QStringView instead of QStringRef, for compat reasons. - sourceswidget: don't use a hardcoded path --- gui/qt/debugger/cdebughighlighter.cpp | 8 ++++---- gui/qt/debugger/cdebughighlighter.h | 2 +- gui/qt/debugger/debuginfo.cpp | 2 +- gui/qt/debugger/debuginfo.h | 5 +++++ gui/qt/debugger/sourceswidget.cpp | 8 ++++---- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/gui/qt/debugger/cdebughighlighter.cpp b/gui/qt/debugger/cdebughighlighter.cpp index 154f30845..39346cf6c 100644 --- a/gui/qt/debugger/cdebughighlighter.cpp +++ b/gui/qt/debugger/cdebughighlighter.cpp @@ -5,7 +5,7 @@ #include -const QSet CDebugHighlighter::s_keywords{ +const QSet CDebugHighlighter::s_keywords{ QStringLiteral("_Align"), QStringLiteral("_At"), QStringLiteral("auto"), @@ -151,7 +151,7 @@ void CDebugHighlighter::highlightBlock(const QString &text) { [[gnu::fallthrough]]; case ParseState::PreprocessorInstruction: if (c == ' ' || c == '\t') { - QStringRef instruction = text.midRef(start, i - start); + QStringView instruction = text.mid(start, i - start); if (instruction == QStringLiteral("include")) { state = ParseState::PreprocessorInclude; } else if (instruction == QStringLiteral("if")) { @@ -198,7 +198,7 @@ void CDebugHighlighter::highlightBlock(const QString &text) { if (i == text.length() || ((c < '0' || c > '9') && (c < 'A' || c > 'Z') && c != '_' && (c < 'a' || c > 'z'))) { - QStringRef token = text.midRef(start, i - start); + QStringView token = text.mid(start, i - start); if (state == ParseState::NumberLiteral) { if (c == '.' || ((c == '+' || c == '-') && i > start && (text[i - 1] == 'E' || text[i - 1] == 'e'))) { @@ -240,7 +240,7 @@ void CDebugHighlighter::highlightBlock(const QString &text) { : m_sources->m_errorFormat); } else { setFormat(start, i - start, - s_keywords.contains(token.toString()) ? + s_keywords.contains(token) ? m_sources->m_keywordFormat : m_sources->m_identifierFormat); } diff --git a/gui/qt/debugger/cdebughighlighter.h b/gui/qt/debugger/cdebughighlighter.h index 1553913f5..cc449e8e9 100644 --- a/gui/qt/debugger/cdebughighlighter.h +++ b/gui/qt/debugger/cdebughighlighter.h @@ -20,7 +20,7 @@ class CDebugHighlighter : public QSyntaxHighlighter { private: SourcesWidget *m_sources; - const static QSet s_keywords; + const static QSet s_keywords; }; #endif diff --git a/gui/qt/debugger/debuginfo.cpp b/gui/qt/debugger/debuginfo.cpp index 61736d5f6..8efb16c01 100644 --- a/gui/qt/debugger/debuginfo.cpp +++ b/gui/qt/debugger/debuginfo.cpp @@ -1549,7 +1549,7 @@ std::vector Line::__parse(std::uint8_t address_size, const _Die & dir = directories[file._M_directory_index]._M_path; } auto inserted = _M_paths.insert({ { unit_entry.attr(_At::DW_AT_comp_dir).str(), - dir, file._M_path }, size() }); + dir, file._M_path }, static_cast(size()) }); if (inserted.second) { _M_files.emplace_back(inserted.first->first, file._M_MD5.data()); } diff --git a/gui/qt/debugger/debuginfo.h b/gui/qt/debugger/debuginfo.h index a0658427d..ebc6c5beb 100644 --- a/gui/qt/debugger/debuginfo.h +++ b/gui/qt/debugger/debuginfo.h @@ -1,6 +1,11 @@ #ifndef DEBUGINFO_H #define DEBUGINFO_H +/* Enable math constants on MSVC */ +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif + #include #include #include diff --git a/gui/qt/debugger/sourceswidget.cpp b/gui/qt/debugger/sourceswidget.cpp index cbc1cad3e..c7975eaf2 100644 --- a/gui/qt/debugger/sourceswidget.cpp +++ b/gui/qt/debugger/sourceswidget.cpp @@ -412,7 +412,7 @@ SourcesWidget::SourcesWidget(QWidget *parent) : QWidget(parent) { SourcesWidget::~SourcesWidget() = default; void SourcesWidget::selectDebugFile() { - QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), "/home/jacob/Programming/ez80/oiram/bin", tr("Debug File (*.debug)")); + QString debugName = QFileDialog::getOpenFileName(this, tr("Open Debug File"), QString(), tr("Debug File (*.debug)")); if (debugName.isNull()) { return; } @@ -590,7 +590,7 @@ void SourcesWidget::sourceContextMenu(const QPoint &pos) { QAction *action = menu.exec(sourceView->mapToGlobal(pos)); const auto &source = m_debugFile->line() - .get(sourceView->objectName().midRef(QStringLiteral("sourceView").count()).toInt()); + .get(sourceView->objectName().mid(QStringLiteral("sourceView").count()).toInt()); QTextCursor cursor = sourceView->cursorForPosition(pos); auto it = source.lines().upper_bound({ cursor.blockNumber() + 1, cursor.columnNumber() }); if (it == source.lines().begin()) { @@ -1166,7 +1166,7 @@ void SourcesWidget::VariableModel::fetchMore(const QModelIndex &parent) { continue; } if (name.startsWith('*')) { - name = name.midRef(1) + "->"; + name = name.mid(1) + "->"; } else { name = name + '.'; } @@ -1188,7 +1188,7 @@ int SourcesWidget::GlobalModel::commonPrefixLength(const QStringList &paths) { if (!prefixLength) { return maxPrefixLength; } - QStringRef prefix = paths.first().leftRef(prefixLength); + QStringView prefix = paths.first().left(prefixLength); for (int i = 1; i < paths.count(); i++) { if (!paths.at(i).startsWith(prefix)) { return maxPrefixLength; From cbdfce9285d3d6877880ecb7287cc4c208b73400 Mon Sep 17 00:00:00 2001 From: Adrien Bertrand Date: Mon, 2 Jun 2025 08:07:07 +0200 Subject: [PATCH 25/26] cmake: add source-level debugging things --- gui/qt/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui/qt/CMakeLists.txt b/gui/qt/CMakeLists.txt index 27079500c..72c88080f 100644 --- a/gui/qt/CMakeLists.txt +++ b/gui/qt/CMakeLists.txt @@ -131,6 +131,9 @@ set(CEmu_Sources cemuopts.h datawidget.cpp datawidget.h debugger.cpp + debugger/cdebughighlighter.cpp debugger/cdebughighlighter.h + debugger/debuginfo.cpp debugger/debuginfo.h + debugger/sourceswidget.cpp debugger/sourceswidget.h debugger/disasm.cpp debugger/disasm.h debugger/hexwidget.cpp debugger/hexwidget.h debugger/visualizerdisplaywidget.cpp debugger/visualizerdisplaywidget.h From fe7294dfe85f22464a282ccdc19eae0f110cfff1 Mon Sep 17 00:00:00 2001 From: Adrien Bertrand Date: Tue, 14 Oct 2025 09:08:03 +0200 Subject: [PATCH 26/26] ci: add feature/source-level-debugging to the ci --- .github/workflows/build.linux.workflow.yml | 2 +- .github/workflows/build.mac.workflow.yml | 2 +- .github/workflows/build.windows.workflow.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.linux.workflow.yml b/.github/workflows/build.linux.workflow.yml index 4dd2f01d4..24e7fb680 100644 --- a/.github/workflows/build.linux.workflow.yml +++ b/.github/workflows/build.linux.workflow.yml @@ -6,7 +6,7 @@ permissions: on: push: - branches: [ master, test-ci ] + branches: [ master, test-ci, feature/source-level-debugging ] pull_request: branches: [ master ] diff --git a/.github/workflows/build.mac.workflow.yml b/.github/workflows/build.mac.workflow.yml index 48ff0a3e7..d5987ed3f 100644 --- a/.github/workflows/build.mac.workflow.yml +++ b/.github/workflows/build.mac.workflow.yml @@ -6,7 +6,7 @@ permissions: on: push: - branches: [ master, test-ci ] + branches: [ master, test-ci, feature/source-level-debugging ] pull_request: branches: [ master ] diff --git a/.github/workflows/build.windows.workflow.yml b/.github/workflows/build.windows.workflow.yml index b01ab09e6..15493e50c 100644 --- a/.github/workflows/build.windows.workflow.yml +++ b/.github/workflows/build.windows.workflow.yml @@ -6,7 +6,7 @@ permissions: on: push: - branches: [ master, test-ci ] + branches: [ master, test-ci, feature/source-level-debugging ] pull_request: branches: [ master ]