2024-09-27 16:16:02 -05:00
|
|
|
#include "SeerStackDumpBrowserWidget.h"
|
2024-10-02 18:04:46 -05:00
|
|
|
#include "SeerStackDumpSettingsDialog.h"
|
2024-09-27 16:16:02 -05:00
|
|
|
#include "SeerUtl.h"
|
|
|
|
|
#include <QtWidgets/QTableWidget>
|
|
|
|
|
#include <QtWidgets/QApplication>
|
2024-10-02 18:04:46 -05:00
|
|
|
#include <QtGui/QFontDatabase>
|
|
|
|
|
#include <QtCore/QSettings>
|
2024-09-29 10:42:07 -05:00
|
|
|
#include <QtCore/QVector>
|
2024-09-27 16:16:02 -05:00
|
|
|
#include <QtCore/QDebug>
|
2024-10-05 10:37:29 -05:00
|
|
|
#include <cmath>
|
2024-09-27 16:16:02 -05:00
|
|
|
|
|
|
|
|
SeerStackDumpBrowserWidget::SeerStackDumpBrowserWidget (QWidget* parent) : QWidget(parent) {
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
_spExpressionId = Seer::createID();
|
|
|
|
|
_dumpExpressionId = Seer::createID();
|
2024-09-27 16:16:02 -05:00
|
|
|
|
|
|
|
|
// Construct the UI.
|
|
|
|
|
setupUi(this);
|
|
|
|
|
|
|
|
|
|
// Setup the widgets
|
|
|
|
|
stackTableWidget->setMouseTracking(true);
|
|
|
|
|
stackTableWidget->setSortingEnabled(false);
|
|
|
|
|
stackTableWidget->resizeColumnToContents(0); // 2 byte address
|
|
|
|
|
stackTableWidget->resizeColumnToContents(1); // 2 byte value
|
|
|
|
|
stackTableWidget->resizeColumnToContents(2); // 4 byte value
|
|
|
|
|
stackTableWidget->resizeColumnToContents(3); // 8 byte value
|
2024-10-02 18:04:46 -05:00
|
|
|
stackTableWidget->resizeColumnToContents(4); // N byte ascii
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->resizeRowsToContents();
|
2024-09-27 16:16:02 -05:00
|
|
|
|
|
|
|
|
stackTableWidget->clearContents();
|
|
|
|
|
|
|
|
|
|
// Connect things.
|
2024-10-02 18:04:46 -05:00
|
|
|
QObject::connect(formatComboBox, &QComboBox::currentTextChanged, this, &SeerStackDumpBrowserWidget::handleFormatComboBox);
|
|
|
|
|
QObject::connect(visualizerToolButton, &QToolButton::clicked, this, &SeerStackDumpBrowserWidget::handleVisualizerToolButton);
|
|
|
|
|
QObject::connect(preferencesToolButton, &QToolButton::clicked, this, &SeerStackDumpBrowserWidget::handlePreferencesToolButton);
|
2024-10-05 10:37:29 -05:00
|
|
|
QObject::connect(stackTableWidget, &QTableWidget::cellDoubleClicked, this, &SeerStackDumpBrowserWidget::handleCellDoubleClicked);
|
2024-10-02 18:04:46 -05:00
|
|
|
|
|
|
|
|
setStackPointerExpression("$sp");
|
2024-10-05 10:37:29 -05:00
|
|
|
setBytesBeforeSP(16);
|
|
|
|
|
setBytesAfterSP(16);
|
2024-10-02 18:04:46 -05:00
|
|
|
setAsciiBytes(8);
|
|
|
|
|
|
|
|
|
|
// Restore settings.
|
|
|
|
|
readSettings();
|
2024-09-27 16:16:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SeerStackDumpBrowserWidget::~SeerStackDumpBrowserWidget () {
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 18:04:46 -05:00
|
|
|
void SeerStackDumpBrowserWidget::setStackPointerExpression (const QString& expression) {
|
|
|
|
|
_stackPointerExpression = expression;
|
|
|
|
|
|
|
|
|
|
spExpressionLabel->setText(_stackPointerExpression);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString SeerStackDumpBrowserWidget::stackPointerExpression () const {
|
|
|
|
|
return _stackPointerExpression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::setBytesBeforeSP (int nbytes) {
|
2024-10-05 10:37:29 -05:00
|
|
|
_bytesBeforeSP = ceil(nbytes / 8.0) * 8; // Round to 8.
|
2024-10-02 18:04:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SeerStackDumpBrowserWidget::bytesBeforeSP () const {
|
|
|
|
|
return _bytesBeforeSP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::setBytesAfterSP (int nbytes) {
|
2024-10-05 10:37:29 -05:00
|
|
|
_bytesAfterSP = ceil(nbytes / 8.0) * 8; // Round to 8.
|
2024-10-02 18:04:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SeerStackDumpBrowserWidget::bytesAfterSP () const {
|
|
|
|
|
return _bytesAfterSP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::setAsciiBytes (int nbytes) {
|
|
|
|
|
_asciiBytes = nbytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int SeerStackDumpBrowserWidget::asciiBytes () const {
|
|
|
|
|
return _asciiBytes;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 16:16:02 -05:00
|
|
|
void SeerStackDumpBrowserWidget::handleText (const QString& text) {
|
|
|
|
|
|
|
|
|
|
// -data-read-memory-bytes -o 16 $sp 32
|
|
|
|
|
// ^done,memory=[
|
|
|
|
|
// {
|
|
|
|
|
// begin="0x00007fffffffda80",
|
|
|
|
|
// offset="0x0000000000000000",
|
|
|
|
|
// end="0x00007fffffffdaa0",
|
|
|
|
|
// contents="0000000000000000e80300000000000098dcffffff7f0000e803000001000000"
|
|
|
|
|
// }
|
|
|
|
|
// ]
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// -data-evaluate-expression $sp
|
|
|
|
|
// ^done,value="0x7fffffffda90"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QApplication::setOverrideCursor(Qt::BusyCursor);
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
if (text.contains(QRegularExpression("^([0-9]+)\\^done,value="))) {
|
|
|
|
|
|
|
|
|
|
// ^done,value="0x7fffffffda90"
|
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
|
|
|
QString value_text = Seer::parseFirst(text, "value=", '"', '"', false);
|
|
|
|
|
|
|
|
|
|
if (id_text.toInt() != _spExpressionId) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addressLineEdit->setText(value_text);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
emit refreshStackDump(_dumpExpressionId, value_text, -bytesBeforeSP(), bytesBeforeSP()+bytesAfterSP()+2);
|
2024-09-29 10:42:07 -05:00
|
|
|
|
|
|
|
|
}else if (text.contains(QRegularExpression("^([0-9]+)\\^done,memory="))) {
|
|
|
|
|
|
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
|
|
|
|
|
|
|
|
if (id_text.toInt() != _dumpExpressionId) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString begin_text = Seer::parseFirst(text, "begin=", '"', '"', false);
|
|
|
|
|
QString contents_text = Seer::parseFirst(text, "contents=", '"', '"', false);
|
|
|
|
|
|
|
|
|
|
_populateTable(begin_text, contents_text);
|
|
|
|
|
|
2024-09-27 16:16:02 -05:00
|
|
|
}else if (text.contains(QRegularExpression("^([0-9]+)\\^error,msg=\"No registers.\""))) {
|
|
|
|
|
|
|
|
|
|
addressLineEdit->setText("");
|
|
|
|
|
|
|
|
|
|
}else if (text.startsWith("^error,msg=\"No registers.\"")) {
|
|
|
|
|
stackTableWidget->clearContents();
|
|
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
// Ignore others.
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stackTableWidget->resizeColumnToContents(0);
|
|
|
|
|
stackTableWidget->resizeColumnToContents(1);
|
|
|
|
|
stackTableWidget->resizeColumnToContents(2);
|
|
|
|
|
stackTableWidget->resizeColumnToContents(3);
|
|
|
|
|
stackTableWidget->resizeColumnToContents(4);
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->resizeRowsToContents();
|
2024-09-27 16:16:02 -05:00
|
|
|
|
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::handleStoppingPointReached () {
|
|
|
|
|
|
|
|
|
|
// Don't do any work if the widget is hidden.
|
|
|
|
|
if (isHidden()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
void SeerStackDumpBrowserWidget::handleFormatComboBox (const QString& text) {
|
|
|
|
|
|
|
|
|
|
Q_UNUSED(text);
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::handleVisualizerToolButton () {
|
|
|
|
|
|
|
|
|
|
emit addMemoryVisualize(addressLineEdit->text());
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 18:04:46 -05:00
|
|
|
void SeerStackDumpBrowserWidget::handlePreferencesToolButton () {
|
|
|
|
|
|
|
|
|
|
// Bring up the register edit dialog.
|
|
|
|
|
SeerStackDumpSettingsDialog dlg(this);
|
|
|
|
|
dlg.setStackPointerExpression(stackPointerExpression());
|
|
|
|
|
dlg.setBytesBeforeSP(bytesBeforeSP());
|
|
|
|
|
dlg.setBytesAfterSP(bytesAfterSP());
|
|
|
|
|
dlg.setAsciiBytes(asciiBytes());
|
|
|
|
|
|
|
|
|
|
//dlg.set(item->text(1), item->text(2));
|
|
|
|
|
|
|
|
|
|
int ret = dlg.exec();
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setStackPointerExpression(dlg.stackPointerExpression());
|
|
|
|
|
setBytesBeforeSP(dlg.bytesBeforeSP());
|
|
|
|
|
setBytesAfterSP(dlg.bytesAfterSP());
|
|
|
|
|
setAsciiBytes(dlg.asciiBytes());
|
|
|
|
|
|
|
|
|
|
writeSettings();
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
void SeerStackDumpBrowserWidget::handleCellDoubleClicked (int row, int col) {
|
|
|
|
|
|
|
|
|
|
Q_UNUSED(col);
|
|
|
|
|
|
|
|
|
|
QTableWidgetItem* item = stackTableWidget->item(row, 0);
|
|
|
|
|
|
|
|
|
|
if (item == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString address = item->text();
|
|
|
|
|
|
|
|
|
|
if (address.mid(0,2) != "0x") {
|
|
|
|
|
address.insert(0,"0x");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit addMemoryVisualize(address);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 16:16:02 -05:00
|
|
|
void SeerStackDumpBrowserWidget::refresh () {
|
|
|
|
|
|
|
|
|
|
// Don't do any work if the widget is hidden.
|
|
|
|
|
if (isHidden()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 18:04:46 -05:00
|
|
|
emit refreshStackPointer(_spExpressionId, stackPointerExpression());
|
2024-09-27 16:16:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::showEvent (QShowEvent* event) {
|
|
|
|
|
|
|
|
|
|
QWidget::showEvent(event);
|
|
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
void SeerStackDumpBrowserWidget::_populateTable (QString address, QString contents) {
|
|
|
|
|
|
|
|
|
|
QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
|
|
|
|
|
|
|
|
|
// Loop off '0x'.
|
|
|
|
|
if (address.mid(0,2) == "0x") {
|
|
|
|
|
address = address.mid(2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
quint64 address64 = address.toULongLong(&ok, 16);
|
|
|
|
|
quint64 pos64 = address64;
|
|
|
|
|
int nrows = contents.length()/2/2; // Divide by 2 for a "FF" character, then again for 2 bytes per row.
|
|
|
|
|
|
|
|
|
|
// Convert the contents to raw bytes.
|
|
|
|
|
QVector<quint8> bytes;
|
|
|
|
|
for (int pos=0; pos<contents.length(); pos+=2) {
|
|
|
|
|
|
|
|
|
|
// Get a hex value "FF".
|
|
|
|
|
QString str = contents.mid(pos, 2);
|
|
|
|
|
|
|
|
|
|
// Ignore "0x";
|
|
|
|
|
if (str == "0x" || str == "0X") {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert string hex to quint8.
|
|
|
|
|
quint8 hex = str.toUInt(&ok, 16);
|
|
|
|
|
if (ok) {
|
|
|
|
|
bytes.push_back(hex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Resize table.
|
|
|
|
|
stackTableWidget->clearContents();
|
|
|
|
|
stackTableWidget->setRowCount(nrows);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
int spRow = -1;
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
// Fill in the address column.
|
|
|
|
|
for (int i=0,r=0; i < contents.length()/2; i+=2,pos64+=2,r++) {
|
|
|
|
|
QString str = QString::number(pos64, 16);
|
|
|
|
|
|
|
|
|
|
QTableWidgetItem* item = new QTableWidgetItem;
|
|
|
|
|
item->setText(str);
|
|
|
|
|
item->setFont(fixedFont);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
if (addressLineEdit->text().contains(str)) {
|
|
|
|
|
item->setBackground(QBrush(QColor(Qt::lightGray)));
|
|
|
|
|
spRow = r;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->setItem(r,0,item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill in the 2byte column.
|
|
|
|
|
for (int i=0,r=0; i<bytes.size(); i+=2,r++) {
|
|
|
|
|
|
|
|
|
|
QString str;
|
|
|
|
|
|
|
|
|
|
if (formatComboBox->currentText() == "hex") {
|
|
|
|
|
str = Seer::ucharToHex(bytes, i, 2);
|
|
|
|
|
}else if (formatComboBox->currentText() == "octal") {
|
|
|
|
|
str = Seer::ucharToOctal(bytes, i, 2);
|
|
|
|
|
}else if (formatComboBox->currentText() == "uint") {
|
|
|
|
|
str = Seer::ucharToUShort(bytes, i, 1);
|
|
|
|
|
}else if (formatComboBox->currentText() == "int") {
|
|
|
|
|
str = Seer::ucharToShort(bytes, i, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTableWidgetItem* item = new QTableWidgetItem;
|
|
|
|
|
item->setText(str);
|
|
|
|
|
item->setFont(fixedFont);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
if (r == spRow) {
|
|
|
|
|
item->setBackground(QBrush(QColor(Qt::lightGray)));
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->setItem(r,1,item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill in the 4byte column.
|
|
|
|
|
for (int i=0,r=0; i < bytes.size(); i+=2,r++) {
|
|
|
|
|
|
|
|
|
|
QString str;
|
|
|
|
|
|
|
|
|
|
if (formatComboBox->currentText() == "hex") {
|
|
|
|
|
str = Seer::ucharToHex(bytes, i, 4);
|
|
|
|
|
}else if (formatComboBox->currentText() == "octal") {
|
|
|
|
|
str = Seer::ucharToOctal(bytes, i, 4);
|
|
|
|
|
}else if (formatComboBox->currentText() == "uint") {
|
|
|
|
|
str = Seer::ucharToUInt(bytes, i, 1);
|
|
|
|
|
}else if (formatComboBox->currentText() == "int") {
|
|
|
|
|
str = Seer::ucharToInt(bytes, i, 1);
|
|
|
|
|
}else if (formatComboBox->currentText() == "float") {
|
|
|
|
|
str = Seer::ucharToFloat(bytes, i, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTableWidgetItem* item = new QTableWidgetItem;
|
|
|
|
|
item->setText(str);
|
|
|
|
|
item->setFont(fixedFont);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
if (r == spRow) {
|
|
|
|
|
item->setBackground(QBrush(QColor(Qt::lightGray)));
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->setItem(r,2,item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill in the 8byte column.
|
|
|
|
|
for (int i=0,r=0; i < bytes.size(); i+=2,r++) {
|
|
|
|
|
|
|
|
|
|
QString str;
|
|
|
|
|
|
|
|
|
|
if (formatComboBox->currentText() == "hex") {
|
|
|
|
|
str = Seer::ucharToHex(bytes, i, 8);
|
|
|
|
|
}else if (formatComboBox->currentText() == "octal") {
|
|
|
|
|
str = Seer::ucharToOctal(bytes, i, 8);
|
|
|
|
|
}else if (formatComboBox->currentText() == "uint") {
|
|
|
|
|
str = Seer::ucharToULong(bytes, i, 1);
|
|
|
|
|
}else if (formatComboBox->currentText() == "int") {
|
|
|
|
|
str = Seer::ucharToLong(bytes, i, 1);
|
|
|
|
|
}else if (formatComboBox->currentText() == "float") {
|
|
|
|
|
str = Seer::ucharToDouble(bytes, i, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTableWidgetItem* item = new QTableWidgetItem;
|
|
|
|
|
item->setText(str);
|
|
|
|
|
item->setFont(fixedFont);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
if (r == spRow) {
|
|
|
|
|
item->setBackground(QBrush(QColor(Qt::lightGray)));
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->setItem(r,3,item);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 18:04:46 -05:00
|
|
|
// Fill in the Nbyte ascii column.
|
2024-09-29 10:42:07 -05:00
|
|
|
for (int i=0,r=0; i < bytes.size(); i+=2,r++) {
|
|
|
|
|
|
2024-10-02 18:04:46 -05:00
|
|
|
QString str = Seer::ucharToAscii(bytes, i, asciiBytes());
|
2024-09-29 10:42:07 -05:00
|
|
|
|
|
|
|
|
QTableWidgetItem* item = new QTableWidgetItem;
|
|
|
|
|
item->setText(str);
|
|
|
|
|
item->setFont(fixedFont);
|
|
|
|
|
|
2024-10-05 10:37:29 -05:00
|
|
|
if (r == spRow) {
|
|
|
|
|
item->setBackground(QBrush(QColor(Qt::lightGray)));
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 10:42:07 -05:00
|
|
|
stackTableWidget->setItem(r,4,item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 18:04:46 -05:00
|
|
|
void SeerStackDumpBrowserWidget::writeSettings () {
|
|
|
|
|
|
|
|
|
|
QSettings settings;
|
|
|
|
|
|
|
|
|
|
settings.beginGroup("stackdumpwindow"); {
|
|
|
|
|
settings.setValue("stackpointerexpression", stackPointerExpression());
|
|
|
|
|
settings.setValue("bytesbeforesp", bytesBeforeSP());
|
|
|
|
|
settings.setValue("bytesaftersp", bytesAfterSP());
|
|
|
|
|
settings.setValue("asciibytes", asciiBytes());
|
|
|
|
|
} settings.endGroup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SeerStackDumpBrowserWidget::readSettings () {
|
|
|
|
|
|
|
|
|
|
QSettings settings;
|
|
|
|
|
|
|
|
|
|
settings.beginGroup("stackdumpwindow"); {
|
|
|
|
|
|
|
|
|
|
setStackPointerExpression(settings.value("stackpointerexpression", "$sp").toString());
|
|
|
|
|
setBytesBeforeSP(settings.value("bytesbeforesp", 32).toInt());
|
|
|
|
|
setBytesAfterSP(settings.value("bytesaftersp", 32).toInt());
|
|
|
|
|
setAsciiBytes(settings.value("asciibytes", 8).toInt());
|
|
|
|
|
|
|
|
|
|
} settings.endGroup();
|
|
|
|
|
}
|
|
|
|
|
|