Skip to content

Commit f5db174

Browse files
committed
feat: graph debugging dialog for unit-testing, code cleanup
1 parent f1a214f commit f5db174

15 files changed

Lines changed: 323 additions & 37 deletions

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ target_sources(${PROJECT_NAME}
3838
src/dialogs/analyzer.cpp
3939
src/dialogs/decoder.cpp
4040
src/dialogs/detail.cpp
41+
src/dialogs/devgraphs.cpp
4142
src/dialogs/flc.cpp
4243
src/dialogs/goto.cpp
4344
src/dialogs/loader.cpp
@@ -80,6 +81,8 @@ target_sources(${PROJECT_NAME}
8081
src/views/dashboard.cpp
8182
src/views/log.cpp
8283
src/views/welcome.cpp
84+
src/widgets/feedbackpushbutton.cpp
85+
src/widgets/feedbacktoolbutton.cpp
8386
src/main.cpp
8487
src/mainwindow.cpp
8588
src/statusbar.cpp

src/dialogs/devgraphs.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "devgraphs.h"
2+
#include "support/surfacerenderer.h"
3+
#include <QApplication>
4+
#include <QClipboard>
5+
#include <QHeaderView>
6+
7+
DevGraphsDialog::DevGraphsDialog(RDContext* ctx, QWidget* parent)
8+
: QDialog{parent}, m_ui{this}, m_context{ctx} {
9+
10+
m_ui.ftbcopyhash->setEnabled(false);
11+
m_ui.ftbcopygraph->setEnabled(false);
12+
13+
m_functionsmodel = new FunctionsModel(ctx, m_ui.tvfunctions);
14+
m_ui.tvfunctions->setModel(m_functionsmodel);
15+
16+
m_ui.ptedot->setFont(surface_renderer::get_font());
17+
m_ui.ptedot->setTabStopDistance(4 * surface_renderer::cell_width());
18+
19+
QHeaderView* hdrview = m_ui.tvfunctions->header();
20+
hdrview->setSectionResizeMode(0, QHeaderView::ResizeToContents);
21+
hdrview->setSectionResizeMode(1, QHeaderView::Stretch);
22+
23+
this->setFocus(); // don't focus function list
24+
25+
connect(m_ui.tvfunctions, &QTreeView::clicked, this,
26+
&DevGraphsDialog::show_dot);
27+
28+
connect(m_ui.ftbcopytests, &FeedbackPushButton::feedback, this,
29+
&DevGraphsDialog::copy_tests);
30+
31+
connect(m_ui.ftbcopygraph, &FeedbackPushButton::feedback, this,
32+
[&]() { qApp->clipboard()->setText(m_currentgraph); });
33+
34+
connect(m_ui.ftbcopyhash, &FeedbackPushButton::feedback, this, [&]() {
35+
qApp->clipboard()->setText(QString{"0x%1"}.arg(
36+
static_cast<qulonglong>(m_currenthash), 8, 16, '0'));
37+
});
38+
}
39+
40+
void DevGraphsDialog::copy_tests() {
41+
QString s = "{\n";
42+
43+
for(int i = 0; i < m_functionsmodel->rowCount({}); i++) {
44+
QModelIndex index = m_functionsmodel->index(i);
45+
RDAddress address = m_functionsmodel->address(index);
46+
const RDFunction* f = rd_find_function(m_context, address);
47+
Q_ASSERT(f);
48+
u32 hash = rd_function_get_hash(f);
49+
50+
s.append(QString{" {0x%1, 0x%2},\n"}
51+
.arg(static_cast<qulonglong>(address), 8, 16, '0')
52+
.arg(static_cast<qulonglong>(hash), 8, 16, '0'));
53+
}
54+
55+
s.append("};");
56+
qApp->clipboard()->setText(s);
57+
}
58+
59+
void DevGraphsDialog::show_dot(const QModelIndex& index) {
60+
m_currentgraph = {};
61+
m_currenthash = {};
62+
m_ui.ptedot->clear();
63+
64+
RDAddress address = m_functionsmodel->address(index);
65+
const char* dot = nullptr;
66+
67+
const RDFunction* f = rd_find_function(m_context, address);
68+
if(!f) goto fail;
69+
70+
dot = rd_function_generate_dot(f);
71+
if(!dot) goto fail;
72+
73+
m_currenthash = rd_function_get_hash(f);
74+
m_currentgraph = QString::fromUtf8(dot);
75+
76+
m_ui.ptedot->setPlainText(m_currentgraph);
77+
m_ui.ftbcopyhash->setEnabled(true);
78+
m_ui.ftbcopygraph->setEnabled(true);
79+
return;
80+
81+
fail:
82+
m_ui.ftbcopyhash->setEnabled(false);
83+
m_ui.ftbcopygraph->setEnabled(false);
84+
}

src/dialogs/devgraphs.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include "models/functions.h"
4+
#include "ui/devgraphsdialog.h"
5+
#include <redasm/redasm.h>
6+
7+
class DevGraphsDialog: public QDialog {
8+
Q_OBJECT
9+
10+
public:
11+
explicit DevGraphsDialog(RDContext* ctx, QWidget* parent = nullptr);
12+
13+
private Q_SLOTS:
14+
void show_dot(const QModelIndex& index);
15+
void copy_tests();
16+
17+
private:
18+
ui::DevGraphsDialog m_ui;
19+
RDContext* m_context;
20+
FunctionsModel* m_functionsmodel;
21+
QString m_currentgraph{};
22+
u32 m_currenthash{};
23+
};

src/mainwindow.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "mainwindow.h"
22
#include "dialogs/analyzer.h"
33
#include "dialogs/decoder.h"
4+
#include "dialogs/devgraphs.h"
45
#include "dialogs/flc.h"
56
#include "dialogs/loader.h"
67
#include "dialogs/memorymap.h"
@@ -75,11 +76,18 @@ MainWindow::MainWindow(QWidget* parent): QMainWindow{parent}, m_ui{this} {
7576
dlgflc->show();
7677
});
7778

78-
connect(m_ui.acttoolsdecoder, &QAction::triggered, this, [&]() {
79+
connect(m_ui.actdevdecoder, &QAction::triggered, this, [&]() {
7980
auto* dlgdecoder = new DecoderDialog(this);
8081
dlgdecoder->show();
8182
});
8283

84+
connect(m_ui.actdevgraphs, &QAction::triggered, this, [&]() {
85+
ContextView* ctxview = this->context_view();
86+
if(!ctxview) return;
87+
auto* dlggraphdots = new DevGraphsDialog(ctxview->context(), this);
88+
dlggraphdots->show();
89+
});
90+
8391
connect(m_ui.acttoolsproblems, &QAction::triggered, this,
8492
&MainWindow::show_problems);
8593

@@ -261,9 +269,11 @@ void MainWindow::enable_context_actions(bool e) { // NOLINT
261269

262270
m_ui.acttbseparator1->setVisible(e);
263271
m_ui.acttbseparator2->setVisible(e);
272+
m_ui.acttbseparator3->setVisible(e);
264273

265274
m_ui.acttoolsflc->setVisible(e);
266275
m_ui.acttoolsproblems->setVisible(e);
276+
m_ui.actdevgraphs->setVisible(e);
267277
m_ui.actviewexported->setVisible(e);
268278
m_ui.actviewsegmentregs->setVisible(e);
269279
m_ui.actviewsegments->setVisible(e);

src/models/functions.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ QVariant FunctionsModel::data(const QModelIndex& index, int role) const {
2222
RDAddress addr = rd_function_get_address(f);
2323

2424
switch(index.column()) {
25-
case 0: return rd_get_name(m_context, addr);
25+
case 0: return QString::fromUtf8(rd_to_hexaddr(m_context, addr));
26+
case 1: return QString::fromUtf8(rd_get_name(m_context, addr));
2627
default: break;
2728
}
2829
}
@@ -37,14 +38,15 @@ QVariant FunctionsModel::headerData(int section, Qt::Orientation orientation,
3738
if(orientation == Qt::Vertical || role != Qt::DisplayRole) return {};
3839

3940
switch(section) {
40-
case 0: return "Functions";
41+
case 0: return "Address";
42+
case 1: return "Functions";
4143
default: break;
4244
}
4345

4446
return {};
4547
}
4648

47-
int FunctionsModel::columnCount(const QModelIndex&) const { return 1; }
49+
int FunctionsModel::columnCount(const QModelIndex&) const { return 2; }
4850

4951
int FunctionsModel::rowCount(const QModelIndex&) const {
5052
return static_cast<int>(rd_slice_length(m_functions));

src/support/utils.cpp

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ namespace {
2121

2222
constexpr int LOGO_SIZE = 64;
2323
constexpr int LOGO_MARGIN = 5;
24-
constexpr int FEEDBACK_INTERVAL = 1000;
25-
const char* const FEEDBACK_IN_PROGRESS = "__redasm_feedback__";
2624

2725
QPixmap copy_screenshot(QWidget* w) {
2826
auto* stackw = qobject_cast<QStackedWidget*>(w);
@@ -52,17 +50,6 @@ QPixmap copy_screenshot(QWidget* w) {
5250
return scrshot;
5351
}
5452

55-
void confirm_feedback(QToolButton* tb) {
56-
QIcon icon = tb->icon();
57-
tb->setProperty(utils::FEEDBACK_IN_PROGRESS, true);
58-
tb->setIcon(FA_ICON_COLOR(0xf00c, theme_provider::color(RD_THEME_SUCCESS)));
59-
60-
QTimer::singleShot(utils::FEEDBACK_INTERVAL, [tb, icon]() {
61-
tb->setIcon(icon);
62-
tb->setProperty(utils::FEEDBACK_IN_PROGRESS, QVariant{});
63-
});
64-
}
65-
6653
} // namespace
6754

6855
QString to_hex_addr(RDAddress address, const RDSegment* seg) {
@@ -127,21 +114,17 @@ QMenu* create_surface_menu(ISurface* surface) {
127114
return menu;
128115
}
129116

130-
QToolButton* create_screenshot_button(QWidget* w) {
131-
auto* pb = new QToolButton(w);
132-
pb->setIcon(FA_ICON(0xf030));
133-
134-
QObject::connect(pb, &QToolButton::clicked, w, [w, pb]() {
135-
if(!pb->property(utils::FEEDBACK_IN_PROGRESS).isNull()) return;
117+
FeedbackToolButton* create_screenshot_button(QWidget* w) {
118+
auto* tbfeedback = new FeedbackToolButton(w);
119+
tbfeedback->setIcon(FA_ICON(0xf030));
136120

121+
QObject::connect(tbfeedback, &FeedbackToolButton::feedback, w, [w]() {
137122
QPixmap s = utils::copy_screenshot(w);
138123
if(s.isNull()) return;
139-
140124
qApp->clipboard()->setPixmap(s);
141-
utils::confirm_feedback(pb);
142125
});
143126

144-
return pb;
127+
return tbfeedback;
145128
}
146129

147130
QPixmap get_logo() {

src/support/utils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "views/surface/isurface.h"
4+
#include "widgets/feedbacktoolbutton.h"
45
#include <QString>
56
#include <QStringList>
67
#include <redasm/redasm.h>
@@ -21,7 +22,7 @@ inline QList<QByteArray> kb_search_paths; // C compatibility
2122
QString to_hex_addr(RDAddress address, const RDSegment* seg = nullptr);
2223
QString confidence_text(RDConfidence c);
2324
QMenu* create_surface_menu(ISurface* surface);
24-
QToolButton* create_screenshot_button(QWidget* w);
25+
FeedbackToolButton* create_screenshot_button(QWidget* w);
2526
QPixmap get_logo();
2627
bool handle_key_press(ISurface* surface, QKeyEvent* e);
2728
void configure_hex_input(QLineEdit* le);

src/ui/devgraphsdialog.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#pragma once
2+
3+
#include "widgets/feedbackpushbutton.h"
4+
#include <QDialog>
5+
#include <QHBoxLayout>
6+
#include <QPlainTextEdit>
7+
#include <QSplitter>
8+
#include <QTreeView>
9+
#include <QVBoxLayout>
10+
11+
namespace ui {
12+
13+
struct DevGraphsDialog {
14+
FeedbackPushButton *ftbcopytests, *ftbcopyhash, *ftbcopygraph;
15+
QTreeView* tvfunctions;
16+
QPlainTextEdit* ptedot;
17+
18+
explicit DevGraphsDialog(QDialog* self) {
19+
self->setAttribute(Qt::WA_DeleteOnClose);
20+
self->setWindowTitle("Graph DOTs");
21+
self->setFixedSize(1000, 600);
22+
23+
this->tvfunctions = new QTreeView();
24+
this->tvfunctions->setUniformRowHeights(true);
25+
this->tvfunctions->setRootIsDecorated(false);
26+
27+
this->ftbcopytests = new FeedbackPushButton();
28+
this->ftbcopytests->setText("Copy Tests");
29+
this->ftbcopytests->set_feedback_text("Copied");
30+
31+
auto* vbox_left = new QVBoxLayout(new QWidget());
32+
vbox_left->addWidget(this->tvfunctions, 1);
33+
vbox_left->addWidget(this->ftbcopytests);
34+
35+
this->ptedot = new QPlainTextEdit();
36+
this->ptedot->setUndoRedoEnabled(false);
37+
this->ptedot->setReadOnly(true);
38+
39+
this->ftbcopyhash = new FeedbackPushButton();
40+
this->ftbcopyhash->setText("Copy Hash");
41+
this->ftbcopyhash->set_feedback_text("Copied");
42+
this->ftbcopygraph = new FeedbackPushButton();
43+
this->ftbcopygraph->setText("Copy Graph");
44+
this->ftbcopygraph->set_feedback_text("Copied");
45+
46+
auto* hbox = new QHBoxLayout();
47+
hbox->addWidget(this->ftbcopyhash);
48+
hbox->addWidget(this->ftbcopygraph);
49+
50+
auto* vbox_right = new QVBoxLayout(new QWidget());
51+
vbox_right->addWidget(this->ptedot, 1);
52+
vbox_right->addLayout(hbox);
53+
54+
auto* splitter = new QSplitter(Qt::Horizontal);
55+
splitter->addWidget(vbox_left->parentWidget());
56+
splitter->addWidget(vbox_right->parentWidget());
57+
splitter->setStretchFactor(1, 1);
58+
59+
auto* vbox = new QVBoxLayout(self);
60+
vbox->setContentsMargins(0, 0, 0, 0);
61+
vbox->addWidget(splitter);
62+
}
63+
};
64+
65+
} // namespace ui

src/ui/mainwindow.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ struct MainWindow {
1717
QStatusBar* statusbar;
1818
QStackedWidget* stackwidget;
1919
QMenu *mnufile, *mnuedit, *mnuview, *mnutools, *mnuwindow, *mnuhelp;
20-
QMenu* mnurecents;
20+
QMenu *mnurecents, *mnudev;
2121
QAction *actfileopen, *actfileclose, *actfileexit;
2222
QAction* actwinrestoredefault;
23-
QAction *acttoolsflc, *acttoolsdecoder, *acttoolsproblems;
2423
QAction *actedit, *actview, *acttools;
24+
QAction *acttoolsflc, *acttoolsproblems;
25+
QAction *actdevdecoder, *actdevgraphs;
2526
QAction *actviewmemorymap, *actviewsegments, *actviewmappings,
2627
*actviewsegmentregs, *actviewstrings, *actviewimported,
2728
*actviewexported;
28-
QAction *acttbseparator1, *acttbseparator2;
29+
QAction *acttbseparator1, *acttbseparator2, *acttbseparator3;
2930
::LogView* logview;
3031

3132
explicit MainWindow(QMainWindow* self) {
@@ -68,20 +69,24 @@ struct MainWindow {
6869
this->actwinrestoredefault =
6970
this->mnuwindow->addAction("Restore Default");
7071

72+
this->acttoolsproblems = this->mnutools->addAction("&Problems");
73+
this->acttoolsproblems->setVisible(false);
74+
7175
this->acttoolsflc = this->mnutools->addAction(
7276
"&FLC", QKeySequence{Qt::CTRL | Qt::Key_L});
7377
this->acttoolsflc->setVisible(false);
7478

79+
this->acttbseparator3 = this->mnutools->addSeparator();
80+
81+
this->mnudev = this->mnutools->addMenu("Dev");
82+
this->actdevdecoder = this->mnudev->addAction("&Decoder");
83+
this->actdevgraphs = this->mnudev->addAction("&Graphs");
84+
7585
this->mnuhelp->addAction(actions::get(actions::OPEN_ABOUT));
7686
this->mnuhelp->addSeparator();
7787
this->mnuhelp->addAction(actions::get(actions::OPEN_HOME));
7888
this->mnuhelp->addAction(actions::get(actions::OPEN_GITHUB));
7989

80-
this->acttoolsproblems = this->mnutools->addAction("&Problems");
81-
this->acttoolsproblems->setVisible(false);
82-
83-
this->acttoolsdecoder = this->mnutools->addAction("&Decoder");
84-
8590
this->actviewmemorymap = this->mnuview->addAction(
8691
"Memory Map", QKeySequence{Qt::SHIFT | Qt::Key_F1});
8792

src/views/context.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ ContextView::ContextView(RDContext* ctx, QWidget* parent)
1313

1414
m_functionsmodel = new FunctionsModel(ctx, this);
1515
m_ui.tvfunctions->setModel(m_functionsmodel);
16-
m_ui.tvfunctions->header()->setSectionResizeMode(0, QHeaderView::Stretch);
16+
m_ui.tvfunctions->header()->hideSection(0);
17+
m_ui.tvfunctions->header()->setSectionResizeMode(1, QHeaderView::Stretch);
1718

1819
m_notify_timer.start();
1920
statusbar::set_busy_status();

0 commit comments

Comments
 (0)