blob: 1edf68bd356b3216c071fbf8db5b6869414bff72 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
*/
/**
* @file KsAdvFilteringDialog.cpp
* @brief GUI Dialog for Advanced filtering settings.
*/
// KernelShark
#include "KsAdvFilteringDialog.hpp"
#include "libkshark.h"
#include "KsUtils.hpp"
/** Create dialog for Advanced Filtering. */
KsAdvFilteringDialog::KsAdvFilteringDialog(QWidget *parent)
: QDialog(parent),
_condToolBar1(this),
_condToolBar2(this),
_condToolBar3(this),
_descrLabel(this),
_sysEvLabel("System/Event: ", &_condToolBar1),
_opsLabel("Operator: ", this),
_fieldLabel("Field: ", this),
_systemComboBox(&_condToolBar1),
_eventComboBox(&_condToolBar1),
_opsComboBox(&_condToolBar2),
_fieldComboBox(&_condToolBar3),
_filterEdit(this),
_helpButton("Show Help", this),
_insertEvtButton("Insert", this),
_insertOpButton("Insert", this),
_insertFieldButton("Insert", this),
_applyButton("Apply", this),
_cancelButton("Cancel", this)
{
struct kshark_context *kshark_ctx(NULL);
int buttonWidth;
if (!kshark_instance(&kshark_ctx))
return;
auto lamAddLine = [&] {
QFrame* line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
_topLayout.addWidget(line);
};
setMinimumWidth(FONT_WIDTH * 80);
buttonWidth = STRING_WIDTH("--Show Help--");
_helpButton.setFixedWidth(buttonWidth);
_helpButton.setDefault(false);
_topLayout.addWidget(&_helpButton);
connect(&_helpButton, &QPushButton::pressed,
this, &KsAdvFilteringDialog::_help);
_descrLabel.setText(_description());
_topLayout.addWidget(&_descrLabel);
/*
* For the moment do not show the syntax description. It will be shown
* only if the "Show Help" button is clicked.
*/
_descrLabel.hide();
lamAddLine();
_getFilters(kshark_ctx);
if (_filters.count()) {
_makeFilterTable(kshark_ctx);
lamAddLine();
}
_condToolBar1.addWidget(&_sysEvLabel);
_condToolBar1.addWidget(&_systemComboBox);
_condToolBar1.addWidget(&_eventComboBox);
/*
* Using the old Signal-Slot syntax because QComboBox::currentIndexChanged
* has overloads.
*/
connect(&_systemComboBox, SIGNAL(currentIndexChanged(const QString&)),
this, SLOT(_systemChanged(const QString&)));
connect(&_eventComboBox, SIGNAL(currentIndexChanged(const QString&)),
this, SLOT(_eventChanged(const QString&)));
_setSystemCombo(kshark_ctx);
_condToolBar1.addSeparator();
_condToolBar1.addWidget(&_insertEvtButton);
_topLayout.addWidget(&_condToolBar1);
_opsComboBox.addItems(_operators());
_condToolBar2.addWidget(&_opsLabel);
_condToolBar2.addWidget(&_opsComboBox);
_condToolBar2.addSeparator();
_condToolBar2.addWidget(&_insertOpButton);
_topLayout.addWidget(&_condToolBar2);
_condToolBar3.addWidget(&_fieldLabel);
_condToolBar3.addWidget(&_fieldComboBox);
_condToolBar3.addSeparator();
_condToolBar3.addWidget(&_insertFieldButton);
_topLayout.addWidget(&_condToolBar3);
lamAddLine();
_filterEdit.setMinimumWidth(50 * FONT_WIDTH);
_topLayout.addWidget(&_filterEdit);
this->setLayout(&_topLayout);
buttonWidth = STRING_WIDTH("--Cancel--");
_applyButton.setFixedWidth(buttonWidth);
_applyButton.setDefault(true);
_cancelButton.setFixedWidth(buttonWidth);
_buttonLayout.addWidget(&_applyButton);
_buttonLayout.addWidget(&_cancelButton);
_buttonLayout.setAlignment(Qt::AlignLeft);
_topLayout.addLayout(&_buttonLayout);
connect(&_insertEvtButton, &QPushButton::pressed,
this, &KsAdvFilteringDialog::_insertEvt);
connect(&_insertOpButton, &QPushButton::pressed,
this, &KsAdvFilteringDialog::_insertOperator);
connect(&_insertFieldButton, &QPushButton::pressed,
this, &KsAdvFilteringDialog::_insertField);
_applyButtonConnection =
connect(&_applyButton, &QPushButton::pressed,
this, &KsAdvFilteringDialog::_applyPress);
connect(&_applyButton, &QPushButton::pressed,
this, &QWidget::close);
connect(&_cancelButton, &QPushButton::pressed,
this, &QWidget::close);
}
void KsAdvFilteringDialog::_setSystemCombo(struct kshark_context *kshark_ctx)
{
QStringList sysList;
tep_event **events;
int i(0), nEvts(0);
if (kshark_ctx->pevent) {
nEvts = tep_get_events_count(kshark_ctx->pevent);
events = tep_list_events(kshark_ctx->pevent,
TEP_EVENT_SORT_SYSTEM);
}
while (i < nEvts) {
QString sysName(events[i]->system);
sysList << sysName;
while (sysName == events[i]->system) {
if (++i == nEvts)
break;
}
}
qSort(sysList);
_systemComboBox.addItems(sysList);
i = _systemComboBox.findText("ftrace");
if (i >= 0)
_systemComboBox.setCurrentIndex(i);
}
QString KsAdvFilteringDialog::_description()
{
QString descrText = "Usage:\n";
descrText += " <sys/event>[,<sys/event>] : [!][(]<field><op><val>[)]";
descrText += "[&&/|| [(]<field><op><val>[)]]\n\n";
descrText += "Examples:\n\n";
descrText += " sched/sched_switch : next_prio < 100 && (prev_prio > 100";
descrText += "&& prev_pid != 0)\n\n";
descrText += " irq.* : irq != 38\n\n";
descrText += " .* : common_pid == 1234\n";
return descrText;
}
QStringList KsAdvFilteringDialog::_operators()
{
QStringList OpsList;
OpsList << ":" << "," << "==" << "!=" << ">" << "<" << ">=" << "<=";
OpsList << "=~" << "!~" << "!" << "(" << ")" << "+" << "-";
OpsList << "*" << "/" << "<<" << ">>" << "&&" << "||" << "&";
return OpsList;
}
void KsAdvFilteringDialog::_getFilters(struct kshark_context *kshark_ctx)
{
tep_event **events;
char *str;
events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
for (int i = 0; events[i]; i++) {
str = tep_filter_make_string(kshark_ctx->advanced_event_filter,
events[i]->id);
if (!str)
continue;
_filters.insert(events[i]->id,
QString("%1/%2:%3\n").arg(events[i]->system,
events[i]->name, str));
free(str);
}
}
void KsAdvFilteringDialog::_makeFilterTable(struct kshark_context *kshark_ctx)
{
QMapIterator<int, QString> f(_filters);
QTableWidgetItem *i1, *i2, *i3;
QStringList headers;
int count(0);
_table = new KsCheckBoxTable(this);
_table->setSelectionMode(QAbstractItemView::SingleSelection);
headers << "Delete" << "Event" << " Id" << "Filter";
_table->init(headers, _filters.count());
for(auto f : _filters.keys()) {
QStringList thisFilter = _filters.value(f).split(":");
i1 = new QTableWidgetItem(thisFilter[0]);
_table->setItem(count, 1, i1);
i2 = new QTableWidgetItem(tr("%1").arg(f));
_table->setItem(count, 2, i2);
i3 = new QTableWidgetItem(thisFilter[1]);
_table->setItem(count, 3, i3);
++count;
}
_table->setVisible(false);
_table->resizeColumnsToContents();
_table->setVisible(true);
_topLayout.addWidget(_table);
}
void KsAdvFilteringDialog::_help()
{
if (_descrLabel.isVisible()) {
_descrLabel.hide();
QApplication::processEvents();
_helpButton.setText("Show Help");
resize(width(), _noHelpHeight);
} else {
_helpButton.setText("Hide Help");
_noHelpHeight = height();
_descrLabel.show();
}
}
void KsAdvFilteringDialog::_systemChanged(const QString &sysName)
{
kshark_context *kshark_ctx(NULL);
QStringList evtsList;
tep_event **events;
int i, nEvts;
_eventComboBox.clear();
if (!kshark_instance(&kshark_ctx) || !kshark_ctx->pevent)
return;
nEvts = tep_get_events_count(kshark_ctx->pevent);
events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
for (i = 0; i < nEvts; ++i) {
if (sysName == events[i]->system)
evtsList << events[i]->name;
}
qSort(evtsList);
_eventComboBox.addItems(evtsList);
i = _eventComboBox.findText("function");
if (i >= 0)
_eventComboBox.setCurrentIndex(i);
}
QStringList
KsAdvFilteringDialog::_getEventFormatFields(struct tep_event *event)
{
tep_format_field *field, **fields = tep_event_fields(event);
QStringList fieldList;
for (field = *fields; field; field = field->next)
fieldList << field->name;
free(fields);
qSort(fieldList);
return fieldList;
}
void KsAdvFilteringDialog::_eventChanged(const QString &evtName)
{
QString sysName = _systemComboBox.currentText();
kshark_context *kshark_ctx(NULL);
QStringList fieldList;
tep_event **events;
int nEvts;
_fieldComboBox.clear();
if (!kshark_instance(&kshark_ctx) || !kshark_ctx->pevent)
return;
nEvts = tep_get_events_count(kshark_ctx->pevent);
events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
for (int i = 0; i < nEvts; ++i) {
if (evtName == events[i]->name &&
sysName == events[i]->system) {
fieldList = _getEventFormatFields(events[i]);
_fieldComboBox.addItems(fieldList);
return;
}
}
}
void KsAdvFilteringDialog::_insertEvt()
{
QString text = _filterEdit.text();
auto set_evt = [&] ()
{
text += _systemComboBox.currentText();
text += "/";
text += _eventComboBox.currentText();
};
if (text == "") {
set_evt();
text += ":";
} else {
QString evt = text;
text = "";
set_evt();
text += ",";
text += evt;
}
_filterEdit.setText(text);
}
void KsAdvFilteringDialog::_insertOperator()
{
QString text = _filterEdit.text();
text += _opsComboBox.currentText();
_filterEdit.setText(text);
}
void KsAdvFilteringDialog::_insertField()
{
QString text = _filterEdit.text();
text += _fieldComboBox.currentText();
_filterEdit.setText(text);
}
void KsAdvFilteringDialog::_applyPress()
{
QMapIterator<int, QString> f(_filters);
kshark_context *kshark_ctx(NULL);
const char *text;
tep_errno ret;
char *filter;
int i(0);
if (!kshark_instance(&kshark_ctx))
return;
while (f.hasNext()) {
f.next();
if (_table->_cb[i]->checkState() == Qt::Checked) {
tep_filter_remove_event(kshark_ctx->advanced_event_filter,
f.key());
}
++i;
}
auto job_done = [&]() {
/*
* Disconnect Apply button. This is done in order to protect
* against multiple clicks.
*/
disconnect(_applyButtonConnection);
emit dataReload();
};
text = _filterEdit.text().toLocal8Bit().data();
if (strlen(text) == 0) {
job_done();
return;
}
filter = (char*) malloc(strlen(text) + 1);
strcpy(filter, text);
ret = tep_filter_add_filter_str(kshark_ctx->advanced_event_filter,
filter);
if (ret < 0) {
char error_str[200];
tep_strerror(kshark_ctx->pevent, ret, error_str,
sizeof(error_str));
fprintf(stderr, "filter failed due to: %s\n", error_str);
free(filter);
return;
}
free(filter);
job_done();
}