// SPDX-FileCopyrightText: 2021 Ernie Pasveer // // SPDX-License-Identifier: GPL-3.0-or-later #include "SeerEditorWidgetAssembly.h" #include "SeerAssemblyPreferenceDialog.h" #include "SeerUtl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SeerEditorWidgetAssembly::SeerEditorWidgetAssembly(QWidget* parent) : QWidget(parent) { // Construct the UI. setupUi(this); // Set the widgets. assemblyArea()->show(); searchTextLineEdit->enableReturnPressedOnClear(); showSearchBar(false); // Hide the search bar. ctrl+F to show it again. setSearchMatchCase(true); // Search with case sensitivity. _textSearchShortcut = new QShortcut(QKeySequence(tr("Ctrl+F")), this); _textSearchNextShortcut = new QShortcut(QKeySequence(tr("Ctrl+G")), this); _textSearchPrevShortcut = new QShortcut(QKeySequence(tr("Ctrl+Shift+G")), this); _lineSearchShortcut = new QShortcut(QKeySequence(tr("Ctrl+L")), this); _toggleBreakpointShortcut = new QShortcut(QKeySequence(tr("Ctrl+B")), this); setKeySettings(SeerKeySettings::populate()); _pcId = Seer::createID(); _spId = Seer::createID(); _flagsId = Seer::createID(); // Clear PC, SP, and FLAGS. pcLineEdit->clear(); spLineEdit->clear(); flagsLineEdit->clear(); // Preference menu. QMenu* menu = new QMenu(); _editPreferencesAction = menu->addAction("Assembly preferences"); preferencesToolButton->setMenu(menu); preferencesToolButton->setPopupMode(QToolButton::InstantPopup); // Connect things. QObject::connect(searchTextLineEdit, &QHistoryLineEdit::returnPressed, this, &SeerEditorWidgetAssembly::handleSearchTextLineEdit); QObject::connect(searchTextLineEdit, &QHistoryLineEdit::escapePressed, this, &SeerEditorWidgetAssembly::handleEscapePressed); #if QT_VERSION >= 0x060900 QObject::connect(matchCaseCheckBox, &QCheckBox::checkStateChanged, this, &SeerEditorWidgetAssembly::handleSearchTextLineEdit); #else QObject::connect(matchCaseCheckBox, &QCheckBox::stateChanged, this, &SeerEditorWidgetAssembly::handleSearchTextLineEdit); #endif QObject::connect(searchDownToolButton, &QToolButton::clicked, this, &SeerEditorWidgetAssembly::handleSearchDownToolButton); QObject::connect(searchUpToolButton, &QToolButton::clicked, this, &SeerEditorWidgetAssembly::handleSearchUpToolButton); QObject::connect(searchLineNumberLineEdit, &QHistoryLineEdit::returnPressed, this, &SeerEditorWidgetAssembly::handleSearchLineNumberLineEdit); QObject::connect(searchLineNumberLineEdit, &QHistoryLineEdit::escapePressed, this, &SeerEditorWidgetAssembly::handleEscapePressed); QObject::connect(searchCloseToolButton, &QToolButton::clicked, this, &SeerEditorWidgetAssembly::handleSearchCloseToolButton); QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerEditorWidgetAssembly::reloadAssembly); QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerEditorWidgetAssembly::reloadRegisters); #if QT_VERSION >= 0x060900 QObject::connect(showAddressCheckBox, &QCheckBox::checkStateChanged, this, &SeerEditorWidgetAssembly::handleShowAddressColumn); QObject::connect(showOffsetCheckBox, &QCheckBox::checkStateChanged, this, &SeerEditorWidgetAssembly::handleShowOffsetColumn); QObject::connect(showOpcodeCheckBox, &QCheckBox::checkStateChanged, this, &SeerEditorWidgetAssembly::handleShowOpcodeColumn); QObject::connect(showSourceCheckBox, &QCheckBox::checkStateChanged, this, &SeerEditorWidgetAssembly::handleShowSourceLines); #else QObject::connect(showAddressCheckBox, &QCheckBox::stateChanged, this, &SeerEditorWidgetAssembly::handleShowAddressColumn); QObject::connect(showOffsetCheckBox, &QCheckBox::stateChanged, this, &SeerEditorWidgetAssembly::handleShowOffsetColumn); QObject::connect(showOpcodeCheckBox, &QCheckBox::stateChanged, this, &SeerEditorWidgetAssembly::handleShowOpcodeColumn); QObject::connect(showSourceCheckBox, &QCheckBox::stateChanged, this, &SeerEditorWidgetAssembly::handleShowSourceLines); #endif QObject::connect(assemblyWidget, &SeerEditorWidgetAssemblyArea::showSearchBar, this, qOverload(&SeerEditorWidgetAssembly::showSearchBar)); QObject::connect(_textSearchShortcut, &QShortcut::activated, this, &SeerEditorWidgetAssembly::handleTextSearchShortcut); QObject::connect(_textSearchNextShortcut, &QShortcut::activated, this, &SeerEditorWidgetAssembly::handleSearchDownToolButton); QObject::connect(_textSearchPrevShortcut, &QShortcut::activated, this, &SeerEditorWidgetAssembly::handleSearchUpToolButton); QObject::connect(_lineSearchShortcut, &QShortcut::activated, this, &SeerEditorWidgetAssembly::handleLineSearchShortcut); QObject::connect(_toggleBreakpointShortcut, &QShortcut::activated, this, &SeerEditorWidgetAssembly::handleToggleBreakpointShortcut); QObject::connect(_editPreferencesAction, &QAction::triggered, this, &SeerEditorWidgetAssembly::handleEditPreferences); // Set defaults. setShowAddressColumn(true); setShowOffsetColumn(false); setShowOpcodeColumn(false); setShowSourceLines(false); setRegiserNamePC("$pc"); setRegiserNameSP("$sp"); setRegiserNameFLAGS("$ps"); readSettings(); } SeerEditorWidgetAssembly::~SeerEditorWidgetAssembly () { } SeerEditorWidgetAssemblyArea* SeerEditorWidgetAssembly::assemblyArea () { return assemblyWidget; } bool SeerEditorWidgetAssembly::isSearchBarShown () const { return searchBarWidget->isVisible(); } bool SeerEditorWidgetAssembly::searchMatchCase () const { return matchCaseCheckBox->isChecked(); } bool SeerEditorWidgetAssembly::showAddressColumn () const { return showAddressCheckBox->isChecked(); } bool SeerEditorWidgetAssembly::showOffsetColumn () const { return showOffsetCheckBox->isChecked(); } bool SeerEditorWidgetAssembly::showOpcodeColumn () const { return showOpcodeCheckBox->isChecked(); } bool SeerEditorWidgetAssembly::showSourceLines () const { return showSourceCheckBox->isChecked(); } QString SeerEditorWidgetAssembly::regiserNamePC () const { return _pcName; } QString SeerEditorWidgetAssembly::regiserNameFLAGS () const { return _flagsName; } QString SeerEditorWidgetAssembly::regiserNameSP () const { return _spName; } void SeerEditorWidgetAssembly::setKeySettings (const SeerKeySettings& settings) { _keySettings = settings; if (_keySettings.has("SearchText") == true) { _textSearchShortcut->setKey(_keySettings.get("SearchText")._sequence); } if (_keySettings.has("SearchTextNext") == true) { _textSearchNextShortcut->setKey(_keySettings.get("SearchTextNext")._sequence); } if (_keySettings.has("SearchTextPrev") == true) { _textSearchPrevShortcut->setKey(_keySettings.get("SearchTextPrev")._sequence); } if (_keySettings.has("ToggleBreakpoint") == true) { _toggleBreakpointShortcut->setKey(_keySettings.get("ToggleBreakpoint")._sequence); } } const SeerKeySettings& SeerEditorWidgetAssembly::keySettings () const { return _keySettings; } void SeerEditorWidgetAssembly::reloadAssembly () { QString addr = assemblyArea()->address(); assemblyArea()->setAddress(addr, true); pcLineEdit->clear(); spLineEdit->clear(); flagsLineEdit->clear(); // Get the PC, SP, and FLAGS if (regiserNamePC() != "") { emit evaluateVariableExpression(_pcId, regiserNamePC()); } if (regiserNameSP() != "") { emit evaluateVariableExpression(_spId, regiserNameSP()); } if (regiserNameFLAGS() != "") { emit evaluateVariableExpression(_flagsId, regiserNameFLAGS()); } } void SeerEditorWidgetAssembly::reloadRegisters () { pcLineEdit->clear(); spLineEdit->clear(); flagsLineEdit->clear(); // Get the PC, SP, and FLAGS if (regiserNamePC() != "") { emit evaluateVariableExpression(_pcId, regiserNamePC()); } if (regiserNameSP() != "") { emit evaluateVariableExpression(_spId, regiserNameSP()); } if (regiserNameFLAGS() != "") { emit evaluateVariableExpression(_flagsId, regiserNameFLAGS()); } } void SeerEditorWidgetAssembly::showSearchBar (bool flag) { showSearchBar(flag, ""); } void SeerEditorWidgetAssembly::showSearchBar (bool flag, QString field) { searchBarWidget->setVisible(flag); // If 'show', give the searchTextLineEdit or the searchTextLineEdit the focus. if (flag) { if (field == "text") { searchTextLineEdit->setFocus(Qt::MouseFocusReason); }else if (field == "line") { searchLineNumberLineEdit->setFocus(Qt::MouseFocusReason); }else{ searchTextLineEdit->setFocus(Qt::MouseFocusReason); } } } void SeerEditorWidgetAssembly::setSearchMatchCase (bool flag) { matchCaseCheckBox->setChecked(flag); } void SeerEditorWidgetAssembly::setShowAddressColumn (bool flag) { showAddressCheckBox->setChecked(flag); handleShowAddressColumn(); } void SeerEditorWidgetAssembly::setShowOffsetColumn (bool flag) { showOffsetCheckBox->setChecked(flag); handleShowOffsetColumn(); } void SeerEditorWidgetAssembly::setShowOpcodeColumn (bool flag) { showOpcodeCheckBox->setChecked(flag); handleShowOpcodeColumn(); } void SeerEditorWidgetAssembly::setShowSourceLines (bool flag) { showSourceCheckBox->setChecked(flag); handleShowSourceLines(); } void SeerEditorWidgetAssembly::setRegiserNamePC (const QString& name) { _pcName = name; } void SeerEditorWidgetAssembly::setRegiserNameFLAGS (const QString& name) { _flagsName = name; } void SeerEditorWidgetAssembly::setRegiserNameSP (const QString& name) { _spName = name; } void SeerEditorWidgetAssembly::handleText (const QString& text) { if (text.startsWith("*stopped")) { //qDebug() << text; // *stopped, // // reason="end-stepping-range", // // frame={addr="0x0000000000400b45", // func="main", // args=[{name="argc",value="1"},{name="argv",value="0x7fffffffd5b8"}], // file="helloworld.cpp", // fullname="/home/erniep/Development/Peak/src/Seer/helloworld/helloworld.cpp", // line="7", // arch="i386:x86-64"}, // // thread-id="1", // stopped-threads="all", // core="6" QString newtext = Seer::filterEscapes(text); // Filter escaped characters. QString frame_text = Seer::parseFirst(newtext, "frame=", '{', '}', false); if (frame_text == "") { return; } // Get the PC, SP, and FLAGS if (regiserNamePC() != "") { emit evaluateVariableExpression(_pcId, regiserNamePC()); } if (regiserNameSP() != "") { emit evaluateVariableExpression(_spId, regiserNameSP()); } if (regiserNameFLAGS() != "") { emit evaluateVariableExpression(_flagsId, regiserNameFLAGS()); } return; }else if (text.contains(QRegularExpression("^([0-9]+)\\^done,value="))) { QString id_text = text.section('^', 0,0); QString value_text = Seer::parseFirst(text, "value=", '"', '"', false); if (id_text == QString::number(_pcId)) { pcLineEdit->setText(Seer::filterEscapes(value_text)); return; } if (id_text == QString::number(_spId)) { spLineEdit->setText(Seer::filterEscapes(value_text)); return; } if (id_text == QString::number(_flagsId)) { flagsLineEdit->setText(Seer::filterEscapes(value_text)); return; } }else if (text.contains(QRegularExpression("^([0-9]+)\\^error,msg="))) { QString id_text = text.section('^', 0,0); QString msg_text = Seer::parseFirst(text, "value=", '"', '"', false); if (id_text == QString::number(_pcId)) { pcLineEdit->setText(Seer::filterEscapes(msg_text)); return; } if (id_text == QString::number(_spId)) { spLineEdit->setText(Seer::filterEscapes(msg_text)); return; } if (id_text == QString::number(_flagsId)) { flagsLineEdit->setText(Seer::filterEscapes(msg_text)); return; } }else if (text.startsWith("^error,msg=\"No registers.\"")) { // Clear PC, SP, and FLAGS. pcLineEdit->clear(); spLineEdit->clear(); flagsLineEdit->clear(); }else{ // Ignore others. } } void SeerEditorWidgetAssembly::handleSearchLineNumberLineEdit () { QString address = searchLineNumberLineEdit->text(); if (address == "") { showSearchBar(false); return; } searchLineNumberLineEdit->clear(); // Will emit the load signal, if 'address' is not already loaded. assemblyArea()->setAddress(address); assemblyArea()->setCurrentLine(address); } void SeerEditorWidgetAssembly::handleSearchTextLineEdit () { QString str = searchTextLineEdit->text(); matchesLabel->setText(""); if (str == "") { assemblyArea()->clearFindText(); showSearchBar(false); return; } int nMatches = assemblyArea()->findText(str, (searchMatchCase() ? QTextDocument::FindCaseSensitively : QTextDocument::FindFlags())); matchesLabel->setText(QString("(%1)").arg(nMatches)); } void SeerEditorWidgetAssembly::handleSearchDownToolButton () { QString str = searchTextLineEdit->text(); if (str == "") { return; } assemblyArea()->find(str, (searchMatchCase() ? QTextDocument::FindCaseSensitively : QTextDocument::FindFlags())); } void SeerEditorWidgetAssembly::handleSearchUpToolButton () { QString str = searchTextLineEdit->text(); if (str == "") { return; } assemblyArea()->find(str, (searchMatchCase() ? QTextDocument::FindCaseSensitively : QTextDocument::FindFlags()) | QTextDocument::FindBackward); } void SeerEditorWidgetAssembly::handleSearchCloseToolButton () { showSearchBar(false); } void SeerEditorWidgetAssembly::handleTextSearchShortcut () { if (isSearchBarShown() == true) { showSearchBar(false, ""); }else{ showSearchBar(true, "text"); } } void SeerEditorWidgetAssembly::handleLineSearchShortcut () { if (isSearchBarShown() == true) { showSearchBar(false, ""); }else{ showSearchBar(true, "line"); } } void SeerEditorWidgetAssembly::handleToggleBreakpointShortcut () { assemblyArea()->breakpointToggle(); } void SeerEditorWidgetAssembly::handleShowAddressColumn () { assemblyArea()->enableLineNumberArea(showAddressColumn()); } void SeerEditorWidgetAssembly::handleShowOffsetColumn () { assemblyArea()->enableOffsetArea(showOffsetColumn()); } void SeerEditorWidgetAssembly::handleShowOpcodeColumn () { assemblyArea()->enableOpcodeArea(showOpcodeColumn()); } void SeerEditorWidgetAssembly::handleShowSourceLines () { assemblyArea()->enableSourceLines(showSourceLines()); } void SeerEditorWidgetAssembly::handleEditPreferences () { // Bring up the register profile dialog. SeerAssemblyPreferenceDialog dlg(this); dlg.setRegiserNamePC(regiserNamePC()); dlg.setRegiserNameFLAGS(regiserNameFLAGS()); dlg.setRegiserNameSP(regiserNameSP()); dlg.setShowAssemblyAddress(showAddressColumn()); dlg.setShowAssemblyOffset(showOffsetColumn()); dlg.setShowAssemblyOpcode(showOpcodeColumn()); dlg.setShowAssemblySource(showSourceLines()); if (dlg.exec()) { setRegiserNamePC(dlg.regiserNamePC()); setRegiserNameFLAGS(dlg.regiserNameFLAGS()); setRegiserNameSP(dlg.regiserNameSP()); setShowAddressColumn(dlg.showAssemblyAddress()); setShowOffsetColumn(dlg.showAssemblyOffset()); setShowOpcodeColumn(dlg.showAssemblyOpcode()); setShowSourceLines(dlg.showAssemblySource()); reloadRegisters(); writeSettings(); } } void SeerEditorWidgetAssembly::handleEscapePressed () { showSearchBar(false, ""); } void SeerEditorWidgetAssembly::writeSettings () { QSettings settings; settings.beginGroup("assembly"); { settings.setValue("assemblyregisternamepc", regiserNamePC()); settings.setValue("assemblyregisternameflags", regiserNameFLAGS()); settings.setValue("assemblyregisternamesp", regiserNameSP()); settings.setValue("assemblyshowaddresscolumn", showAddressColumn()); settings.setValue("assemblyshowoffsetcolumn", showOffsetColumn()); settings.setValue("assemblyshowopcodecolumn", showOpcodeColumn()); settings.setValue("assemblyshowsourcelines", showSourceLines()); } settings.endGroup(); } void SeerEditorWidgetAssembly::readSettings () { QSettings settings; settings.beginGroup("assembly"); { setRegiserNamePC (settings.value("assemblyregisternamepc", "$pc").toString()); setRegiserNameFLAGS (settings.value("assemblyregisternameflags", "$ps").toString()); setRegiserNameSP (settings.value("assemblyregisternamesp", "$sp").toString()); setShowAddressColumn (settings.value("assemblyshowaddresscolumn", true).toBool()); setShowOffsetColumn (settings.value("assemblyshowoffsetcolumn", false).toBool()); setShowOpcodeColumn (settings.value("assemblyshowopcodecolumn", false).toBool()); setShowSourceLines (settings.value("assemblyshowsourcelines", false).toBool()); } settings.endGroup(); }