1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "eventfilterdialog.h"
6
7#include <QLineEdit>
8#include <QCheckBox>
9#include <QTreeWidget>
10#include <QDialogButtonBox>
11#include <QPushButton>
12#include <QVBoxLayout>
13#include <QFormLayout>
14#include <QTreeWidget>
15#include <QDebug>
16
17namespace ReverseDebugger {
18namespace Internal {
19
20#define ARRAYSIZE(a) static_cast<int>(sizeof(a) / sizeof(a[0]))
21
22// see `kill -l`
23static const char *signalNames[] = {
24 nullptr, //0
25 nullptr, //1
26 "SIGINT", //2
27 "SIGQUIT", //3
28 "SIGILL", //4
29 "SIGTRAP", //5
30 "SIGABRT", //6
31 "SIGBUS", //7
32 "SIGFPE", //8
33 nullptr, //9
34 nullptr, //10
35 "SIGSEGV", //11
36 nullptr, //12
37 "SIGPIPE", //13
38 nullptr, //14
39 "SIGTERM", //15
40 nullptr, //16
41 "SIGCHLD", //17
42 "SIGCONT", //18
43};
44
45static const char *dbusNames[] = {
46 nullptr, //0
47 "method call", //1
48 "method return", //2
49 "method error", //3
50 "send signal", //4
51};
52
53static const char *x11Names[] = {
54 nullptr, //0
55 nullptr, //1
56 "KeyPress", //2
57 "KeyRelease", //3
58 "ButtonPress", //4
59 "ButtonRelease", //5
60 nullptr, //6
61 nullptr, //7
62 nullptr, //8
63 "FocusIn", //9
64 "FocusOut", //10
65 nullptr, //11
66 nullptr, //12
67 nullptr, //13
68 nullptr, //14
69 nullptr, //15
70 "CreateNotify", //16
71 "DestroyNotify", //17
72 "UnmapNotify", //18
73 "MapNotify", //19
74};
75
76const char *syscallNames[] = {
77 "desc", //0
78 "file", //1
79 "memory", //2
80 "process", //3
81 "signal", //4
82 "ipc", //5
83 "network", //6
84 "vdso", //7
85};
86
87static const char *syscallKindTips[] = {
88 "Trace all file descriptor related system calls.",
89 "Trace all system calls which take a file name as an argument.",
90 "Trace all memory mapping related system calls.",
91 "Trace all system calls which involve process management.",
92 "Trace all signal related system calls.",
93 "Trace all IPC related system calls.",
94 "Trace all the network related system calls.",
95 "Trace some time function in vdso:clock_gettime, gettimeofday, time.",
96};
97
98#define SIGNAL_BASE 100
99#define X11_BASE 200
100#define DBUS_BASE 300
101
102class EventFilterDialogPrivate
103{
104public:
105 QLineEdit *breakFunc = nullptr;
106 QLineEdit *globalVar = nullptr;
107 QLineEdit *maxHeapSize = nullptr;
108 QLineEdit *maxStackSize = nullptr;
109 QLineEdit *maxParamSize = nullptr;
110 QCheckBox *onlyCurrentThread = nullptr;
111 QTreeWidget *treeWidget = nullptr;
112 QDialogButtonBox *buttonBox = nullptr;
113
114 uchar *syscallFlags = nullptr;
115 uchar *dbusFlags = nullptr;
116 uchar *x11Flags = nullptr;
117 uchar *signalFlags = nullptr;
118};
119
120EventFilterDialog::EventFilterDialog(
121 QWidget *parent,
122 uchar *syscallFlags,
123 uchar *dbusFlags,
124 uchar *x11Flags,
125 uchar *signalFlags)
126 : QDialog(parent),
127 d(new EventFilterDialogPrivate)
128{
129 d->syscallFlags = syscallFlags;
130 d->dbusFlags = dbusFlags;
131 d->x11Flags = x11Flags;
132 d->signalFlags = signalFlags;
133
134 setupUi();
135}
136
137ReverseDebugger::Internal::EventFilterDialog::~EventFilterDialog()
138{
139 delete d;
140}
141
142int ReverseDebugger::Internal::EventFilterDialog::exec()
143{
144 connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
145 connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
146 connect(d->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
147 this, SLOT(itemClicked(QTreeWidgetItem *, int)));
148
149 d->treeWidget->expandAll();
150
151 return QDialog::exec();
152}
153
154QString EventFilterDialog::syscallKindNames() const
155{
156 QString ret;
157 // the last is vdso
158 for (int i = 0; i < ARRAYSIZE(syscallNames) - 1; ++i) {
159 if (d->syscallFlags[i] != 0) {
160 ret += QLatin1String(syscallNames[i]);
161 ret += QLatin1Char(',');
162 }
163 }
164
165 return ret;
166}
167
168QString EventFilterDialog::breakFunc() const
169{
170 return d->breakFunc->text();
171}
172
173QString EventFilterDialog::globalVar() const
174{
175 return d->globalVar->text();
176}
177
178QString EventFilterDialog::maxStackSize() const
179{
180 return d->maxStackSize->text().trimmed();
181}
182
183QString EventFilterDialog::maxHeapSize() const
184{
185 return d->maxHeapSize->text().trimmed();
186}
187
188QString EventFilterDialog::maxParamSize() const
189{
190 return d->maxParamSize->text().trimmed();
191}
192
193bool EventFilterDialog::onlyCurrentThread() const
194{
195 return d->onlyCurrentThread->isChecked();
196}
197
198void EventFilterDialog::setMaxStackSize(const QString &size)
199{
200 d->maxStackSize->setText(size);
201}
202
203void EventFilterDialog::setMaxHeapSize(const QString &size)
204{
205 d->maxHeapSize->setText(size);
206}
207
208void EventFilterDialog::setMaxParamSize(const QString &size)
209{
210 d->maxParamSize->setText(size);
211}
212
213void EventFilterDialog::setOnlyCurrentThread(bool b)
214{
215 d->onlyCurrentThread->setChecked(b);
216}
217
218void EventFilterDialog::itemClicked(QTreeWidgetItem *item, int column)
219{
220 Q_UNUSED(column)
221
222 int index = item->data(0, Qt::UserRole).toInt();
223 int state = item->checkState(0);
224 // Unchecked, PartiallyChecked, Checked
225
226 if (index < SIGNAL_BASE) {
227 d->syscallFlags[index] = (state == Qt::Checked);
228 qDebug() << "click tree item" << index
229 << ":" << syscallNames[index] << ", state=" << state;
230 } else if (index < X11_BASE) {
231 index -= SIGNAL_BASE;
232 d->signalFlags[index] = (state == Qt::Checked);
233 qDebug() << "click tree item" << index
234 << ":" << signalNames[index] << ", state=" << state;
235 } else if (index < DBUS_BASE) {
236 index -= X11_BASE;
237 d->x11Flags[index] = (state == Qt::Checked);
238 qDebug() << "click tree item" << index
239 << ":" << x11Names[index] << ", state=" << state;
240 } else {
241 index -= DBUS_BASE;
242 d->dbusFlags[index] = (state == Qt::Checked);
243 qDebug() << "click tree item" << index
244 << ":" << dbusNames[index] << ", state=" << state;
245 }
246}
247
248void EventFilterDialog::setupUi()
249{
250 setWindowTitle(tr("event filter."));
251 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
252
253 d->buttonBox = new QDialogButtonBox(this);
254 d->buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
255 d->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
256 d->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
257 d->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
258 d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
259
260 d->treeWidget = new QTreeWidget(this);
261 d->treeWidget->setColumnCount(1);
262
263 if (QTreeWidgetItem *header = d->treeWidget->headerItem()) {
264 header->setText(0, tr("event list"));
265 } else {
266 d->treeWidget->setHeaderLabel(tr("event list"));
267 }
268
269 // system call check list.
270 QTreeWidgetItem *syscall = new QTreeWidgetItem(
271 (QTreeWidget *)0, QStringList(tr("syscall filter")));
272 for (int i = 0; i < ARRAYSIZE(syscallNames); ++i) {
273 QTreeWidgetItem *item = new QTreeWidgetItem(syscall,
274 QStringList(tr(syscallNames[i])));
275 item->setData(0, Qt::UserRole, i);
276 item->setCheckState(0,
277 (0 == d->syscallFlags[i]) ? Qt::Unchecked : Qt::Checked);
278 item->setToolTip(0, tr(syscallKindTips[i]));
279
280 syscall->addChild(item);
281 }
282
283 // x11 event check list.
284 QTreeWidgetItem *x11events = new QTreeWidgetItem(
285 (QTreeWidget *)0, QStringList(tr("x11 events filter")));
286 for (int i = 0; i < ARRAYSIZE(x11Names); ++i) {
287 if (nullptr == x11Names[i]) {
288 d->x11Flags[i] = 0;
289 continue;
290 }
291
292 QTreeWidgetItem *item = new QTreeWidgetItem(x11events,
293 QStringList(tr(x11Names[i])));
294 item->setData(0, Qt::UserRole, i + X11_BASE);
295 item->setCheckState(0,
296 (0 == d->x11Flags[i]) ? Qt::Unchecked : Qt::Checked);
297
298 x11events->addChild(item);
299 }
300
301 // dbus message event list.
302 QTreeWidgetItem *dbusmsg = new QTreeWidgetItem(
303 (QTreeWidget *)0, QStringList(tr("dbus filter")));
304 for (int i = 0; i < ARRAYSIZE(dbusNames); ++i) {
305 if (nullptr == dbusNames[i]) {
306 d->dbusFlags[i] = 0;
307 continue;
308 }
309
310 QTreeWidgetItem *item = new QTreeWidgetItem(dbusmsg,
311 QStringList(tr(dbusNames[i])));
312 item->setData(0, Qt::UserRole, i + DBUS_BASE);
313 item->setCheckState(0,
314 (0 == d->dbusFlags[i]) ? Qt::Unchecked : Qt::Checked);
315
316 dbusmsg->addChild(item);
317 }
318
319 // insert three type of item to top level.
320 d->treeWidget->insertTopLevelItems(0, { syscall, x11events, dbusmsg });
321
322 d->maxHeapSize = new QLineEdit(this);
323 d->maxStackSize = new QLineEdit(this);
324 d->maxParamSize = new QLineEdit(this);
325 d->onlyCurrentThread = new QCheckBox(this);
326 d->globalVar = new QLineEdit(this);
327 d->breakFunc = new QLineEdit(this);
328
329 auto verticalLayout = new QVBoxLayout(this);
330 verticalLayout->addWidget(d->treeWidget);
331 verticalLayout->addStretch();
332
333 auto formLayout = new QFormLayout();
334 formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
335 formLayout->addRow(tr("Only record the thread where event occurred:"), d->onlyCurrentThread);
336 formLayout->addRow(tr("Record the size of heap memory (default is 0, in KB):"), d->maxHeapSize);
337 formLayout->addRow(tr("Record the size of stack memory (the default is 32, in KB):"), d->maxStackSize);
338 formLayout->addRow(tr("Record system call parameter size (default is 256, unit Byte):"), d->maxParamSize);
339 formLayout->addRow(tr("Record the specified global variable (format: [*]var1+size1[,[*]var2+size2,...]):"), d->globalVar);
340 formLayout->addRow(tr("Start record after the specified function is executed (c++ mangle name):"), d->breakFunc);
341
342 verticalLayout->addLayout(formLayout);
343 verticalLayout->addStretch();
344 verticalLayout->addWidget(d->buttonBox);
345}
346
347} // namespace Internal
348} // namespace Debugger
349