mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-24 05:45:27 +00:00
Attach update (#603)
* Handle process detaching * List procs wth current filename in separate table; * Refresh proc list every second * Fixed restarting debug executable check * Update r2 * Added option to break esil execution on invalid instructions (#597) * Added continue until call in esil * Remove set registers button * Reordered backtrace widget columns
This commit is contained in:
parent
08245a8694
commit
cb173aa616
@ -884,9 +884,8 @@ void CutterCore::startDebug()
|
|||||||
if (!currentlyDebugging) {
|
if (!currentlyDebugging) {
|
||||||
offsetPriorDebugging = getOffset();
|
offsetPriorDebugging = getOffset();
|
||||||
}
|
}
|
||||||
// FIXME: we do a 'ds' here since otherwise the continue until commands
|
// FIXME: we do a 'dr' here since otherwise the process continues
|
||||||
// sometimes do not work in r2.
|
cmd("ood; dr");
|
||||||
cmd("ood; ds");
|
|
||||||
emit registersChanged();
|
emit registersChanged();
|
||||||
if (!currentlyDebugging) {
|
if (!currentlyDebugging) {
|
||||||
setConfig("asm.flags", false);
|
setConfig("asm.flags", false);
|
||||||
@ -934,6 +933,8 @@ void CutterCore::attachDebug(int pid)
|
|||||||
// prevent register flags from appearing during debug/emul
|
// prevent register flags from appearing during debug/emul
|
||||||
setConfig("asm.flags", false);
|
setConfig("asm.flags", false);
|
||||||
currentlyDebugging = true;
|
currentlyDebugging = true;
|
||||||
|
currentlyOpenFile = getConfig("file.path");
|
||||||
|
currentlyAttachedToPID = pid;
|
||||||
emit flagsChanged();
|
emit flagsChanged();
|
||||||
emit changeDebugView();
|
emit changeDebugView();
|
||||||
}
|
}
|
||||||
@ -945,9 +946,11 @@ void CutterCore::stopDebug()
|
|||||||
if (currentlyEmulating) {
|
if (currentlyEmulating) {
|
||||||
cmd("aeim-; aei-; wcr; .ar-");
|
cmd("aeim-; aei-; wcr; .ar-");
|
||||||
currentlyEmulating = false;
|
currentlyEmulating = false;
|
||||||
|
} else if (currentlyAttachedToPID != -1) {
|
||||||
|
cmd(QString("dp- %1; o %2; .ar-").arg(QString::number(currentlyAttachedToPID), currentlyOpenFile));
|
||||||
|
currentlyAttachedToPID = -1;
|
||||||
} else {
|
} else {
|
||||||
// we do a ds since otherwise the process does not die.
|
cmd("dk 9; oo; .ar-");
|
||||||
cmd("dk 9; ds; oo; .ar-");
|
|
||||||
}
|
}
|
||||||
seek(offsetPriorDebugging);
|
seek(offsetPriorDebugging);
|
||||||
setConfig("asm.flags", true);
|
setConfig("asm.flags", true);
|
||||||
|
@ -528,6 +528,8 @@ public:
|
|||||||
void setDebugPlugin(QString plugin);
|
void setDebugPlugin(QString plugin);
|
||||||
bool currentlyDebugging = false;
|
bool currentlyDebugging = false;
|
||||||
bool currentlyEmulating = false;
|
bool currentlyEmulating = false;
|
||||||
|
int currentlyAttachedToPID = -1;
|
||||||
|
QString currentlyOpenFile;
|
||||||
|
|
||||||
QString getDecompiledCodePDC(RVA addr);
|
QString getDecompiledCodePDC(RVA addr);
|
||||||
bool getR2DecAvailable();
|
bool getR2DecAvailable();
|
||||||
|
@ -5,15 +5,29 @@
|
|||||||
|
|
||||||
#include "utils/Helpers.h"
|
#include "utils/Helpers.h"
|
||||||
|
|
||||||
ProcessModel::ProcessModel(QList<ProcessDescription> *processes, QObject *parent)
|
#include <QScrollBar>
|
||||||
: QAbstractListModel(parent),
|
|
||||||
processes(processes)
|
// ------------
|
||||||
|
// ProcessModel
|
||||||
|
// ------------
|
||||||
|
ProcessModel::ProcessModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
|
updateData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessModel::updateData()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
processes = Core()->getAllProcesses();
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ProcessModel::rowCount(const QModelIndex &) const
|
int ProcessModel::rowCount(const QModelIndex &) const
|
||||||
{
|
{
|
||||||
return processes->count();
|
return processes.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ProcessModel::columnCount(const QModelIndex &) const
|
int ProcessModel::columnCount(const QModelIndex &) const
|
||||||
@ -23,10 +37,10 @@ int ProcessModel::columnCount(const QModelIndex &) const
|
|||||||
|
|
||||||
QVariant ProcessModel::data(const QModelIndex &index, int role) const
|
QVariant ProcessModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (index.row() >= processes->count())
|
if (index.row() >= processes.count())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
const ProcessDescription &proc = processes->at(index.row());
|
const ProcessDescription &proc = processes.at(index.row());
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
@ -70,16 +84,67 @@ QVariant ProcessModel::headerData(int section, Qt::Orientation, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessModel::beginReloadProcess()
|
bool ProcessModel::lessThan(const ProcessDescription &leftProc, const ProcessDescription &rightProc,
|
||||||
|
int column)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
switch (column) {
|
||||||
|
case ProcessModel::PidColumn:
|
||||||
|
return leftProc.pid < rightProc.pid;
|
||||||
|
case ProcessModel::UidColumn:
|
||||||
|
return leftProc.uid < rightProc.uid;
|
||||||
|
case ProcessModel::StatusColumn:
|
||||||
|
return leftProc.status < rightProc.status;
|
||||||
|
case ProcessModel::PathColumn:
|
||||||
|
return leftProc.path < rightProc.path;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftProc.pid < rightProc.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessModel::endReloadProcess()
|
// ------------------------------
|
||||||
|
// ProcessBeingAnalysedProxyModel
|
||||||
|
// ------------------------------
|
||||||
|
ProcessBeingAnalysedProxyModel::ProcessBeingAnalysedProxyModel(ProcessModel *sourceModel,
|
||||||
|
QObject *parent)
|
||||||
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
endResetModel();
|
setSourceModel(sourceModel);
|
||||||
|
|
||||||
|
// @SEE: Should there be a getFilename() in Core()? Not the first time I use this
|
||||||
|
processBeingAnalysedFilename = processPathToFilename(Core()->getConfig("file.path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ProcessBeingAnalysedProxyModel::processPathToFilename(const QString &path) const
|
||||||
|
{
|
||||||
|
// removes the arguments and gets filename from the process path
|
||||||
|
return path.split(" ").first().split("/").last();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessBeingAnalysedProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
QModelIndex index = sourceModel()->index(row, 0, parent);
|
||||||
|
ProcessDescription item = index.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
||||||
|
|
||||||
|
QString procFilename = processPathToFilename(item.path);
|
||||||
|
return procFilename == processBeingAnalysedFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProcessBeingAnalysedProxyModel::lessThan(const QModelIndex &left,
|
||||||
|
const QModelIndex &right) const
|
||||||
|
{
|
||||||
|
ProcessDescription leftProc = left.data(
|
||||||
|
ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
||||||
|
ProcessDescription rightProc = right.data(
|
||||||
|
ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
||||||
|
|
||||||
|
return ProcessModel::lessThan(leftProc, rightProc, left.column());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
// ProcessProxyModel
|
||||||
|
// -----------------
|
||||||
ProcessProxyModel::ProcessProxyModel(ProcessModel *sourceModel, QObject *parent)
|
ProcessProxyModel::ProcessProxyModel(ProcessModel *sourceModel, QObject *parent)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
@ -100,22 +165,12 @@ bool ProcessProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
|
|||||||
ProcessDescription rightProc = right.data(
|
ProcessDescription rightProc = right.data(
|
||||||
ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
||||||
|
|
||||||
switch (left.column()) {
|
return ProcessModel::lessThan(leftProc, rightProc, left.column());
|
||||||
case ProcessModel::PidColumn:
|
|
||||||
return leftProc.pid < rightProc.pid;
|
|
||||||
case ProcessModel::UidColumn:
|
|
||||||
return leftProc.uid < rightProc.uid;
|
|
||||||
case ProcessModel::StatusColumn:
|
|
||||||
return leftProc.status < rightProc.status;
|
|
||||||
case ProcessModel::PathColumn:
|
|
||||||
return leftProc.path < rightProc.path;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return leftProc.pid < rightProc.pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// AttachProcDialog
|
||||||
|
// ----------------
|
||||||
AttachProcDialog::AttachProcDialog(QWidget *parent) :
|
AttachProcDialog::AttachProcDialog(QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::AttachProcDialog)
|
ui(new Ui::AttachProcDialog)
|
||||||
@ -123,22 +178,97 @@ AttachProcDialog::AttachProcDialog(QWidget *parent) :
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
|
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
|
||||||
|
|
||||||
processes = Core()->getAllProcesses();
|
processModel = new ProcessModel(this);
|
||||||
processModel = new ProcessModel(&processes, this);
|
|
||||||
processProxyModel = new ProcessProxyModel(processModel, this);
|
processProxyModel = new ProcessProxyModel(processModel, this);
|
||||||
ui->procTreeView->setModel(processProxyModel);
|
processBeingAnalyzedProxyModel = new ProcessBeingAnalysedProxyModel(processModel, this);
|
||||||
ui->procTreeView->sortByColumn(ProcessModel::PidColumn, Qt::AscendingOrder);
|
|
||||||
connect(ui->filterLineEdit, SIGNAL(textChanged(const QString &)), processProxyModel,
|
// View of all processes
|
||||||
SLOT(setFilterWildcard(const QString &)));
|
auto allView = ui->allProcView;
|
||||||
qhelpers::setVerticalScrollMode(ui->procTreeView);
|
allView->setModel(processProxyModel);
|
||||||
|
allView->sortByColumn(ProcessModel::PidColumn, Qt::DescendingOrder);
|
||||||
|
|
||||||
|
// View of the processes with the same name as the one being analyzed
|
||||||
|
auto smallView = ui->procBeingAnalyzedView;
|
||||||
|
smallView->setModel(processBeingAnalyzedProxyModel);
|
||||||
|
smallView->setCurrentIndex(smallView->model()->index(0, 0));
|
||||||
|
|
||||||
|
// To get the 'FocusIn' events
|
||||||
|
allView->installEventFilter(this);
|
||||||
|
smallView->installEventFilter(this);
|
||||||
|
|
||||||
// focus on filter line
|
// focus on filter line
|
||||||
ui->filterLineEdit->setFocus();
|
ui->filterLineEdit->setFocus();
|
||||||
// Event filter for capturing Ctrl/Cmd+Return
|
connect(ui->filterLineEdit, SIGNAL(textChanged(const QString &)), processProxyModel,
|
||||||
ui->filterLineEdit->installEventFilter(this);
|
SLOT(setFilterWildcard(const QString &)));
|
||||||
|
|
||||||
|
// Update the processes every 'updateIntervalMs' seconds
|
||||||
|
timer = new QTimer(this);
|
||||||
|
connect(timer, SIGNAL(timeout()), this, SLOT(updateModelData()));
|
||||||
|
timer->start(updateIntervalMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachProcDialog::~AttachProcDialog() {}
|
AttachProcDialog::~AttachProcDialog()
|
||||||
|
{
|
||||||
|
timer->stop();
|
||||||
|
delete timer;
|
||||||
|
delete processBeingAnalyzedProxyModel;
|
||||||
|
delete processProxyModel;
|
||||||
|
delete processModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachProcDialog::updateModelData()
|
||||||
|
{
|
||||||
|
auto allView = ui->allProcView;
|
||||||
|
auto smallView = ui->procBeingAnalyzedView;
|
||||||
|
|
||||||
|
// Save the old selection and scroll position so that we can update and
|
||||||
|
// model and then restore it.
|
||||||
|
bool allViewHadSelection = allView->selectionModel()->hasSelection();
|
||||||
|
bool smallViewHadSelection = smallView->selectionModel()->hasSelection();
|
||||||
|
int allViewPrevScrollPos = 0;
|
||||||
|
int smallViewPrevScrollPos = 0;
|
||||||
|
int allViewPrevPID = 0;
|
||||||
|
int smallViewPrevPID = 0;
|
||||||
|
|
||||||
|
if (allViewHadSelection) {
|
||||||
|
allViewPrevScrollPos = allView->verticalScrollBar()->value();
|
||||||
|
allViewPrevPID = allView->selectionModel()->currentIndex().data(
|
||||||
|
ProcessModel::ProcDescriptionRole).value<ProcessDescription>().pid;
|
||||||
|
}
|
||||||
|
if (smallViewHadSelection) {
|
||||||
|
smallViewPrevScrollPos = smallView->verticalScrollBar()->value();
|
||||||
|
smallViewPrevPID = smallView->selectionModel()->currentIndex().data(
|
||||||
|
ProcessModel::ProcDescriptionRole).value<ProcessDescription>().pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the model update
|
||||||
|
processModel->updateData();
|
||||||
|
|
||||||
|
// Restore the selection and scroll position
|
||||||
|
if (allViewHadSelection) {
|
||||||
|
QModelIndexList idx = allView->model()->match(
|
||||||
|
allView->model()->index(0, 0), Qt::DisplayRole, QVariant::fromValue(allViewPrevPID));
|
||||||
|
if (!idx.isEmpty()) {
|
||||||
|
allView->setCurrentIndex(idx.first());
|
||||||
|
allView->verticalScrollBar()->setValue(allViewPrevScrollPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (smallViewHadSelection) {
|
||||||
|
QModelIndexList idx = smallView->model()->match(
|
||||||
|
smallView->model()->index(0, 0), Qt::DisplayRole, QVariant::fromValue(smallViewPrevPID));
|
||||||
|
|
||||||
|
if (!idx.isEmpty()) {
|
||||||
|
smallView->setCurrentIndex(idx.first());
|
||||||
|
smallView->verticalScrollBar()->setValue(smallViewPrevScrollPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init selection if nothing was ever selected yet, and a new process with the same name
|
||||||
|
// as the one being analysed was launched.
|
||||||
|
if (!allView->selectionModel()->hasSelection() && !smallView->selectionModel()->hasSelection()) {
|
||||||
|
smallView->setCurrentIndex(smallView->model()->index(0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AttachProcDialog::on_buttonBox_accepted()
|
void AttachProcDialog::on_buttonBox_accepted()
|
||||||
{
|
{
|
||||||
@ -149,23 +279,16 @@ void AttachProcDialog::on_buttonBox_rejected()
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AttachProcDialog::getPID()
|
|
||||||
{
|
|
||||||
ProcessDescription proc = ui->procTreeView->selectionModel()->currentIndex().data(
|
|
||||||
ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
|
||||||
return proc.pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AttachProcDialog::eventFilter(QObject *obj, QEvent *event)
|
bool AttachProcDialog::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
Q_UNUSED(obj);
|
if (event->type() == QEvent::FocusIn) {
|
||||||
if (event -> type() == QEvent::KeyPress) {
|
if (obj == ui->allProcView) {
|
||||||
QKeyEvent *keyEvent = static_cast <QKeyEvent *> (event);
|
ui->procBeingAnalyzedView->selectionModel()->clearSelection();
|
||||||
|
wasAllProcViewLastPressed = true;
|
||||||
// Confirm comment by pressing Ctrl/Cmd+Return
|
return true;
|
||||||
if ((keyEvent -> modifiers() & Qt::ControlModifier) &&
|
} else if (obj == ui->procBeingAnalyzedView) {
|
||||||
((keyEvent -> key() == Qt::Key_Enter) || (keyEvent -> key() == Qt::Key_Return))) {
|
ui->allProcView->selectionModel()->clearSelection();
|
||||||
this->accept();
|
wasAllProcViewLastPressed = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,8 +296,37 @@ bool AttachProcDialog::eventFilter(QObject *obj, QEvent *event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachProcDialog::on_procTreeView_doubleClicked(const QModelIndex &index)
|
int AttachProcDialog::getPID()
|
||||||
{
|
{
|
||||||
ProcessDescription proc = index.data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>();
|
int pid;
|
||||||
|
|
||||||
|
// Here we need to know which table was selected last to get the proper PID
|
||||||
|
if (wasAllProcViewLastPressed && ui->allProcView->selectionModel()->hasSelection()) {
|
||||||
|
pid = ui->allProcView->selectionModel()->currentIndex().
|
||||||
|
data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>().pid;
|
||||||
|
} else if (!wasAllProcViewLastPressed
|
||||||
|
&& ui->procBeingAnalyzedView->selectionModel()->hasSelection()) {
|
||||||
|
pid = ui->procBeingAnalyzedView->selectionModel()->currentIndex().
|
||||||
|
data(ProcessModel::ProcDescriptionRole).value<ProcessDescription>().pid;
|
||||||
|
} else {
|
||||||
|
// Error attaching. No process selected! Happens when you press ENTER but
|
||||||
|
// there was no process with the same name as the one being analyzed.
|
||||||
|
pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachProcDialog::on_allProcView_doubleClicked(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
Q_UNUSED(index);
|
||||||
|
|
||||||
|
accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachProcDialog::on_procBeingAnalyzedView_doubleClicked(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
Q_UNUSED(index);
|
||||||
|
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class AttachProcDialog;
|
class AttachProcDialog;
|
||||||
@ -20,22 +21,23 @@ class ProcessModel: public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<ProcessDescription> *processes;
|
QList<ProcessDescription> processes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Column { PidColumn = 0, UidColumn, StatusColumn, PathColumn, ColumnCount };
|
enum Column { PidColumn = 0, UidColumn, StatusColumn, PathColumn, ColumnCount };
|
||||||
enum Role { ProcDescriptionRole = Qt::UserRole };
|
enum Role { ProcDescriptionRole = Qt::UserRole };
|
||||||
|
|
||||||
ProcessModel(QList<ProcessDescription> *processes, QObject *parent = 0);
|
ProcessModel(QObject *parent = 0);
|
||||||
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
QVariant data(const QModelIndex &index, int role) const;
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
static bool lessThan(const ProcessDescription &left, const ProcessDescription &right, int column);
|
||||||
|
|
||||||
void beginReloadProcess();
|
public slots:
|
||||||
void endReloadProcess();
|
void updateData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -53,6 +55,23 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessBeingAnalysedProxyModel : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProcessBeingAnalysedProxyModel(ProcessModel *sourceModel, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow(int row, const QModelIndex &parent) const override;
|
||||||
|
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString processBeingAnalysedFilename;
|
||||||
|
QString processPathToFilename(const QString &path) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AttachProcDialog : public QDialog
|
class AttachProcDialog : public QDialog
|
||||||
{
|
{
|
||||||
@ -67,16 +86,21 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void on_buttonBox_accepted();
|
void on_buttonBox_accepted();
|
||||||
void on_buttonBox_rejected();
|
void on_buttonBox_rejected();
|
||||||
void on_procTreeView_doubleClicked(const QModelIndex &index);
|
void on_allProcView_doubleClicked(const QModelIndex &index);
|
||||||
|
void on_procBeingAnalyzedView_doubleClicked(const QModelIndex &index);
|
||||||
|
void updateModelData();
|
||||||
|
|
||||||
signals:
|
|
||||||
void attachProcess(int pid);
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::AttachProcDialog> ui;
|
std::unique_ptr<Ui::AttachProcDialog> ui;
|
||||||
bool eventFilter(QObject *obj, QEvent *event);
|
bool eventFilter(QObject *obj, QEvent *event);
|
||||||
|
|
||||||
ProcessModel *processModel;
|
ProcessModel *processModel;
|
||||||
ProcessProxyModel *processProxyModel;
|
ProcessProxyModel *processProxyModel;
|
||||||
QList<ProcessDescription> processes;
|
ProcessBeingAnalysedProxyModel *processBeingAnalyzedProxyModel;
|
||||||
|
|
||||||
|
// whether the 'small table' or 'table with all procs' was last focused
|
||||||
|
bool wasAllProcViewLastPressed = false;
|
||||||
|
|
||||||
|
QTimer *timer;
|
||||||
|
const int updateIntervalMs = 1000;
|
||||||
};
|
};
|
||||||
|
@ -30,42 +30,115 @@
|
|||||||
<number>2</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="procTreeView">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="styleSheet">
|
<property name="maximumSize">
|
||||||
<string notr="true">QTreeView::item
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>150</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Processes with same name as currently open file:</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QTreeView" name="procBeingAnalyzedView">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>30</y>
|
||||||
|
<width>794</width>
|
||||||
|
<height>111</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QTreeView::item
|
||||||
{
|
{
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
padding-bottom: 1px;
|
padding-bottom: 1px;
|
||||||
}</string>
|
}</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShape">
|
<property name="frameShape">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<enum>QFrame::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="lineWidth">
|
<property name="lineWidth">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="indentation">
|
<property name="indentation">
|
||||||
<number>8</number>
|
<number>8</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="sortingEnabled">
|
<property name="sortingEnabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>All processes:</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QTreeView" name="allProcView">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>50</y>
|
||||||
|
<width>794</width>
|
||||||
|
<height>331</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QTreeView::item
|
||||||
|
{
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="indentation">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="filterLineEdit">
|
<widget class="QLineEdit" name="filterLineEdit">
|
||||||
<property name="placeholderText">
|
<property name="placeholderText">
|
||||||
<string>Quick Filter</string>
|
<string>Quick Filter</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -13,11 +13,11 @@ BacktraceWidget::BacktraceWidget(MainWindow *main, QAction *action) :
|
|||||||
// setup backtrace model
|
// setup backtrace model
|
||||||
QString PC = Core()->getRegisterName("PC");
|
QString PC = Core()->getRegisterName("PC");
|
||||||
QString SP = Core()->getRegisterName("SP");
|
QString SP = Core()->getRegisterName("SP");
|
||||||
modelBacktrace->setHorizontalHeaderItem(0, new QStandardItem(PC));
|
modelBacktrace->setHorizontalHeaderItem(0, new QStandardItem(tr("Func Name")));
|
||||||
modelBacktrace->setHorizontalHeaderItem(1, new QStandardItem(SP));
|
modelBacktrace->setHorizontalHeaderItem(1, new QStandardItem(SP));
|
||||||
modelBacktrace->setHorizontalHeaderItem(2, new QStandardItem(tr("Frame Size")));
|
modelBacktrace->setHorizontalHeaderItem(2, new QStandardItem(PC));
|
||||||
modelBacktrace->setHorizontalHeaderItem(3, new QStandardItem(tr("Func Name")));
|
modelBacktrace->setHorizontalHeaderItem(3, new QStandardItem(tr("Description")));
|
||||||
modelBacktrace->setHorizontalHeaderItem(4, new QStandardItem(tr("Description")));
|
modelBacktrace->setHorizontalHeaderItem(4, new QStandardItem(tr("Frame Size")));
|
||||||
viewBacktrace->setFont(Config()->getFont());
|
viewBacktrace->setFont(Config()->getFont());
|
||||||
viewBacktrace->setModel(modelBacktrace);
|
viewBacktrace->setModel(modelBacktrace);
|
||||||
ui->verticalLayout->addWidget(viewBacktrace);
|
ui->verticalLayout->addWidget(viewBacktrace);
|
||||||
@ -42,21 +42,21 @@ void BacktraceWidget::setBacktraceGrid()
|
|||||||
QJsonObject backtraceItem = value.toObject();
|
QJsonObject backtraceItem = value.toObject();
|
||||||
QString progCounter = RAddressString(backtraceItem["pc"].toVariant().toULongLong());
|
QString progCounter = RAddressString(backtraceItem["pc"].toVariant().toULongLong());
|
||||||
QString stackPointer = RAddressString(backtraceItem["sp"].toVariant().toULongLong());
|
QString stackPointer = RAddressString(backtraceItem["sp"].toVariant().toULongLong());
|
||||||
int frameSize = backtraceItem["frame_size"].toInt();
|
int frameSize = backtraceItem["frame_size"].toVariant().toInt();
|
||||||
QString funcName = backtraceItem["fname"].toString();
|
QString funcName = backtraceItem["fname"].toString();
|
||||||
QString desc = backtraceItem["desc"].toString();
|
QString desc = backtraceItem["desc"].toString();
|
||||||
|
|
||||||
QStandardItem *rowPC = new QStandardItem(progCounter);
|
QStandardItem *rowPC = new QStandardItem(progCounter);
|
||||||
QStandardItem *rowSP = new QStandardItem(stackPointer);
|
QStandardItem *rowSP = new QStandardItem(stackPointer);
|
||||||
QStandardItem *rowFrameSize = new QStandardItem(frameSize);
|
QStandardItem *rowFrameSize = new QStandardItem(QString::number(frameSize));
|
||||||
QStandardItem *rowFuncName = new QStandardItem(funcName);
|
QStandardItem *rowFuncName = new QStandardItem(funcName);
|
||||||
QStandardItem *rowDesc = new QStandardItem(desc);
|
QStandardItem *rowDesc = new QStandardItem(desc);
|
||||||
|
|
||||||
modelBacktrace->setItem(i, 0, rowPC);
|
modelBacktrace->setItem(i, 0, rowFuncName);
|
||||||
modelBacktrace->setItem(i, 1, rowSP);
|
modelBacktrace->setItem(i, 1, rowSP);
|
||||||
modelBacktrace->setItem(i, 2, rowFrameSize);
|
modelBacktrace->setItem(i, 2, rowPC);
|
||||||
modelBacktrace->setItem(i, 3, rowFuncName);
|
modelBacktrace->setItem(i, 3, rowDesc);
|
||||||
modelBacktrace->setItem(i, 4, rowDesc);
|
modelBacktrace->setItem(i, 4, rowFrameSize);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
viewBacktrace->setModel(modelBacktrace);
|
viewBacktrace->setModel(modelBacktrace);
|
||||||
|
@ -29,7 +29,7 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
|
|||||||
actionStart->setShortcut(QKeySequence(Qt::Key_F9));
|
actionStart->setShortcut(QKeySequence(Qt::Key_F9));
|
||||||
actionStartEmul = new QAction(startEmulIcon, tr("Start emulation"), parent);
|
actionStartEmul = new QAction(startEmulIcon, tr("Start emulation"), parent);
|
||||||
actionAttach = new QAction(startAttachIcon, tr("Attach to process"), parent);
|
actionAttach = new QAction(startAttachIcon, tr("Attach to process"), parent);
|
||||||
QAction *actionStop = new QAction(stopIcon, tr("Stop debug"), parent);
|
actionStop = new QAction(stopIcon, tr("Stop debug"), parent);
|
||||||
actionContinue = new QAction(continueIcon, tr("Continue"), parent);
|
actionContinue = new QAction(continueIcon, tr("Continue"), parent);
|
||||||
actionContinue->setShortcut(QKeySequence(Qt::Key_F5));
|
actionContinue->setShortcut(QKeySequence(Qt::Key_F5));
|
||||||
actionContinueUntilMain = new QAction(continueUntilMainIcon, tr("Continue until main"), parent);
|
actionContinueUntilMain = new QAction(continueUntilMainIcon, tr("Continue until main"), parent);
|
||||||
@ -82,18 +82,20 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
|
|||||||
actionContinueUntilMain->setVisible(true);
|
actionContinueUntilMain->setVisible(true);
|
||||||
actionStepOut->setVisible(true);
|
actionStepOut->setVisible(true);
|
||||||
this->colorToolbar(false);
|
this->colorToolbar(false);
|
||||||
|
actionStop->setText("Stop debug");
|
||||||
|
colorToolbar(false);
|
||||||
});
|
});
|
||||||
connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
|
connect(actionStep, &QAction::triggered, Core(), &CutterCore::stepDebug);
|
||||||
connect(actionStart, &QAction::triggered, [ = ]() {
|
connect(actionStart, &QAction::triggered, [=]() {
|
||||||
QString filename = Core()->getConfig("file.lastpath");
|
QString filename = Core()->getConfig("file.path").split(" ").first();
|
||||||
QFileInfo info(filename);
|
QFileInfo info(filename);
|
||||||
if (!info.isExecutable()) {
|
if (!Core()->currentlyDebugging && !info.isExecutable()) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setText(QString("File '%1' does not have executable permissions.").arg(filename));
|
msgBox.setText(QString("File '%1' does not have executable permissions.").arg(filename));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->colorToolbar(true);
|
colorToolbar(true);
|
||||||
actionAttach->setVisible(false);
|
actionAttach->setVisible(false);
|
||||||
actionStartEmul->setVisible(false);
|
actionStartEmul->setVisible(false);
|
||||||
Core()->startDebug();
|
Core()->startDebug();
|
||||||
@ -107,7 +109,8 @@ DebugToolbar::DebugToolbar(MainWindow *main, QWidget *parent) :
|
|||||||
actionContinueUntilMain->setVisible(false);
|
actionContinueUntilMain->setVisible(false);
|
||||||
actionStepOut->setVisible(false);
|
actionStepOut->setVisible(false);
|
||||||
continueUntilButton->setDefaultAction(actionContinueUntilSyscall);
|
continueUntilButton->setDefaultAction(actionContinueUntilSyscall);
|
||||||
this->colorToolbar(true);
|
actionStop->setText("Stop emulation");
|
||||||
|
colorToolbar(true);
|
||||||
});
|
});
|
||||||
connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
|
connect(actionStepOver, &QAction::triggered, Core(), &CutterCore::stepOverDebug);
|
||||||
connect(actionStepOut, &QAction::triggered, Core(), &CutterCore::stepOutDebug);
|
connect(actionStepOut, &QAction::triggered, Core(), &CutterCore::stepOutDebug);
|
||||||
@ -125,28 +128,40 @@ void DebugToolbar::continueUntilMain()
|
|||||||
void DebugToolbar::colorToolbar(bool p)
|
void DebugToolbar::colorToolbar(bool p)
|
||||||
{
|
{
|
||||||
if (p) {
|
if (p) {
|
||||||
this->setStyleSheet("QToolBar {background: green;}");
|
setStyleSheet("QToolBar {background: green;}");
|
||||||
} else {
|
} else {
|
||||||
this->setStyleSheet("");
|
setStyleSheet("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugToolbar::attachProcessDialog()
|
void DebugToolbar::attachProcessDialog()
|
||||||
{
|
{
|
||||||
AttachProcDialog *dialog = new AttachProcDialog(this);
|
AttachProcDialog *dialog = new AttachProcDialog(this);
|
||||||
|
bool success = false;
|
||||||
if (dialog->exec()) {
|
while (!success) {
|
||||||
int pid = dialog->getPID();
|
success = true;
|
||||||
attachProcess(pid);
|
if (dialog->exec()) {
|
||||||
|
int pid = dialog->getPID();
|
||||||
|
if (pid >= 0) {
|
||||||
|
attachProcess(pid);
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setText("Error attaching. No process selected!");
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
delete dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugToolbar::attachProcess(int pid)
|
void DebugToolbar::attachProcess(int pid)
|
||||||
{
|
{
|
||||||
// hide unwanted buttons
|
// hide unwanted buttons
|
||||||
this->colorToolbar(true);
|
colorToolbar(true);
|
||||||
this->actionStart->setVisible(false);
|
actionStart->setVisible(false);
|
||||||
this->actionStartEmul->setVisible(false);
|
actionStartEmul->setVisible(false);
|
||||||
|
actionStop->setText("Detach from process");
|
||||||
// attach
|
// attach
|
||||||
Core()->attachDebug(pid);
|
Core()->attachDebug(pid);
|
||||||
}
|
}
|
@ -21,6 +21,7 @@ public:
|
|||||||
QAction *actionStep;
|
QAction *actionStep;
|
||||||
QAction *actionStepOver;
|
QAction *actionStepOver;
|
||||||
QAction *actionStepOut;
|
QAction *actionStepOut;
|
||||||
|
QAction *actionStop;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MainWindow *main;
|
MainWindow *main;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "utils/JsonModel.h"
|
#include "utils/JsonModel.h"
|
||||||
|
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "QPushButton"
|
|
||||||
|
|
||||||
RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) :
|
RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) :
|
||||||
CutterDockWidget(main, action),
|
CutterDockWidget(main, action),
|
||||||
@ -12,12 +11,9 @@ RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) :
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// setup register layout
|
// setup register layout
|
||||||
|
registerLayout->setVerticalSpacing(0);
|
||||||
ui->verticalLayout->addLayout(registerLayout);
|
ui->verticalLayout->addLayout(registerLayout);
|
||||||
|
|
||||||
buttonSetRegisters = new QPushButton("Set registers", this);
|
|
||||||
connect(buttonSetRegisters, &QPushButton::clicked, this, &RegistersWidget::handleButton);
|
|
||||||
|
|
||||||
ui->verticalLayout->addWidget(buttonSetRegisters);
|
|
||||||
connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents);
|
connect(Core(), &CutterCore::refreshAll, this, &RegistersWidget::updateContents);
|
||||||
connect(Core(), &CutterCore::registersChanged, this, &RegistersWidget::updateContents);
|
connect(Core(), &CutterCore::registersChanged, this, &RegistersWidget::updateContents);
|
||||||
}
|
}
|
||||||
@ -29,28 +25,6 @@ void RegistersWidget::updateContents()
|
|||||||
setRegisterGrid();
|
setRegisterGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegistersWidget::handleButton()
|
|
||||||
{
|
|
||||||
int j = 0;
|
|
||||||
int i = 0;
|
|
||||||
int col = 0;
|
|
||||||
for (j = 0; j < registerLen; j++) {
|
|
||||||
QWidget *regName = registerLayout->itemAtPosition(i, col)->widget();
|
|
||||||
QWidget *regValue = registerLayout->itemAtPosition(i, col + 1)->widget();
|
|
||||||
QLabel *regLabel = qobject_cast<QLabel *>(regName);
|
|
||||||
QLineEdit *regLine = qobject_cast<QLineEdit *>(regValue);
|
|
||||||
QString regNameString = regLabel->text();
|
|
||||||
QString regValueString = regLine->text();
|
|
||||||
Core()->setRegister(regNameString, regValueString);
|
|
||||||
i++;
|
|
||||||
if (i >= registerLen / numCols + 1) {
|
|
||||||
i = 0;
|
|
||||||
col += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setRegisterGrid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RegistersWidget::setRegisterGrid()
|
void RegistersWidget::setRegisterGrid()
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -26,12 +26,10 @@ public:
|
|||||||
private slots:
|
private slots:
|
||||||
void updateContents();
|
void updateContents();
|
||||||
void setRegisterGrid();
|
void setRegisterGrid();
|
||||||
void handleButton();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::RegistersWidget> ui;
|
std::unique_ptr<Ui::RegistersWidget> ui;
|
||||||
QGridLayout *registerLayout = new QGridLayout;
|
QGridLayout *registerLayout = new QGridLayout;
|
||||||
int numCols = 2;
|
int numCols = 2;
|
||||||
int registerLen = 0;
|
int registerLen = 0;
|
||||||
QPushButton *buttonSetRegisters;
|
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user