// Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "perfconfigeventsmodel.h" #include "perfconfigwidget.h" #include "perfprofilertr.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Utils; namespace PerfProfiler { namespace Internal { class SettingsDelegate : public QStyledItemDelegate { Q_OBJECT public: SettingsDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; PerfConfigWidget::PerfConfigWidget(PerfSettings *settings, QWidget *parent) : m_settings(settings) { setParent(parent); eventsView = new QTableView(this); eventsView->setMinimumSize(QSize(0, 300)); eventsView->setEditTriggers(QAbstractItemView::AllEditTriggers); eventsView->setSelectionMode(QAbstractItemView::SingleSelection); eventsView->setSelectionBehavior(QAbstractItemView::SelectRows); eventsView->setModel(new PerfConfigEventsModel(m_settings, this)); eventsView->setItemDelegate(new SettingsDelegate(this)); eventsView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); useTracePointsButton = new QPushButton(this); useTracePointsButton->setText(Tr::tr("Use Trace Points")); useTracePointsButton->setVisible(false); connect(useTracePointsButton, &QPushButton::pressed, this, &PerfConfigWidget::readTracePoints); addEventButton = new QPushButton(this); addEventButton->setText(Tr::tr("Add Event")); connect(addEventButton, &QPushButton::pressed, this, [this]() { auto model = eventsView->model(); model->insertRow(model->rowCount()); }); removeEventButton = new QPushButton(this); removeEventButton->setText(Tr::tr("Remove Event")); connect(removeEventButton, &QPushButton::pressed, this, [this]() { QModelIndex index = eventsView->currentIndex(); if (index.isValid()) eventsView->model()->removeRow(index.row()); }); resetButton = new QPushButton(this); resetButton->setText(Tr::tr("Reset")); connect(resetButton, &QPushButton::pressed, m_settings, &PerfSettings::resetToDefault); using namespace Layouting; Column { Row { st, useTracePointsButton, addEventButton, removeEventButton, resetButton }, eventsView, Grid { m_settings->callgraphMode, m_settings->stackSize, br, m_settings->sampleMode, m_settings->period, br, m_settings->extraArguments, }, st }.attachTo(this); } PerfConfigWidget::~PerfConfigWidget() = default; void PerfConfigWidget::setTarget(ProjectExplorer::Target *target) { ProjectExplorer::IDevice::ConstPtr device; if (target) { if (ProjectExplorer::Kit *kit = target->kit()) device = ProjectExplorer::DeviceKitAspect::device(kit); } if (device.isNull()) { useTracePointsButton->setEnabled(false); return; } QTC_ASSERT(device, return); QTC_CHECK(!m_process || m_process->state() == QProcess::NotRunning); m_process.reset(new QtcProcess); m_process->setCommand({device->filePath("perf"), {"probe", "-l"}}); connect(m_process.get(), &QtcProcess::done, this, &PerfConfigWidget::handleProcessDone); useTracePointsButton->setEnabled(true); } void PerfConfigWidget::setTracePointsButtonVisible(bool visible) { useTracePointsButton->setVisible(visible); } void PerfConfigWidget::apply() { m_settings->writeGlobalSettings(); } void PerfConfigWidget::readTracePoints() { QMessageBox messageBox; messageBox.setWindowTitle(Tr::tr("Use Trace Points")); messageBox.setIcon(QMessageBox::Question); messageBox.setText(Tr::tr("Replace events with trace points read from the device?")); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); if (messageBox.exec() == QMessageBox::Yes) { m_process->start(); useTracePointsButton->setEnabled(false); } } void PerfConfigWidget::handleProcessDone() { if (m_process->error() == QProcess::FailedToStart) { Core::AsynchronousMessageBox::warning( Tr::tr("Cannot List Trace Points"), Tr::tr("\"perf probe -l\" failed to start. Is perf installed?")); useTracePointsButton->setEnabled(true); return; } const QList lines = m_process->readAllStandardOutput().append(m_process->readAllStandardError()) .split('\n'); auto model = eventsView->model(); const int previousRows = model->rowCount(); QHash tracePoints; for (const QByteArray &line : lines) { const QByteArray trimmed = line.trimmed(); const int space = trimmed.indexOf(' '); if (space < 0) continue; // If the whole "on ..." string is the same, the trace points are redundant tracePoints[trimmed.mid(space + 1)] = trimmed.left(space); } if (tracePoints.isEmpty()) { Core::AsynchronousMessageBox::warning( Tr::tr("No Trace Points Found"), Tr::tr("Trace points can be defined with \"perf probe -a\".")); } else { for (const QByteArray &event : std::as_const(tracePoints)) { int row = model->rowCount(); model->insertRow(row); model->setData(model->index(row, PerfConfigEventsModel::ColumnEventType), PerfConfigEventsModel::EventTypeCustom); model->setData(model->index(row, PerfConfigEventsModel::ColumnSubType), QString::fromUtf8(event)); } model->removeRows(0, previousRows); m_settings->sampleMode.setVolatileValue(1); m_settings->period.setVolatileValue(1); } useTracePointsButton->setEnabled(true); } QWidget *SettingsDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) const int row = index.row(); const int column = index.column(); const PerfConfigEventsModel *model = qobject_cast(index.model()); auto getRowEventType = [&]() { return qvariant_cast( model->data(model->index(row, PerfConfigEventsModel::ColumnEventType), Qt::EditRole)); }; switch (column) { case PerfConfigEventsModel::ColumnEventType: { QComboBox *editor = new QComboBox(parent); QMetaEnum meta = QMetaEnum::fromType(); for (int i = 0; i < PerfConfigEventsModel::EventTypeInvalid; ++i) { editor->addItem(QString::fromLatin1(meta.valueToKey(i)).mid( static_cast(strlen("EventType"))).toLower(), i); } return editor; } case PerfConfigEventsModel::ColumnSubType: { PerfConfigEventsModel::EventType eventType = getRowEventType(); switch (eventType) { case PerfConfigEventsModel::EventTypeHardware: { QComboBox *editor = new QComboBox(parent); for (int i = PerfConfigEventsModel::SubTypeEventTypeHardware; i < PerfConfigEventsModel::SubTypeEventTypeSoftware; ++i) { editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeHardware, PerfConfigEventsModel::SubType(i)), i); } return editor; } case PerfConfigEventsModel::EventTypeSoftware: { QComboBox *editor = new QComboBox(parent); for (int i = PerfConfigEventsModel::SubTypeEventTypeSoftware; i < PerfConfigEventsModel::SubTypeEventTypeCache; ++i) { editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeSoftware, PerfConfigEventsModel::SubType(i)), i); } return editor; } case PerfConfigEventsModel::EventTypeCache: { QComboBox *editor = new QComboBox(parent); for (int i = PerfConfigEventsModel::SubTypeEventTypeCache; i < PerfConfigEventsModel::SubTypeInvalid; ++i) { editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeCache, PerfConfigEventsModel::SubType(i)), i); } return editor; } case PerfConfigEventsModel::EventTypeBreakpoint: { QLineEdit *editor = new QLineEdit(parent); editor->setText("0x0000000000000000"); editor->setValidator(new QRegularExpressionValidator( QRegularExpression("0x[0-9a-f]{16}"), parent)); return editor; } case PerfConfigEventsModel::EventTypeCustom: { QLineEdit *editor = new QLineEdit(parent); return editor; } case PerfConfigEventsModel::EventTypeRaw: { QLineEdit *editor = new QLineEdit(parent); editor->setText("r000"); editor->setValidator(new QRegularExpressionValidator( QRegularExpression("r[0-9a-f]{3}"), parent)); return editor; } case PerfConfigEventsModel::EventTypeInvalid: return nullptr; } return nullptr; // Will never be reached, but GCC cannot figure this out. } case PerfConfigEventsModel::ColumnOperation: { QComboBox *editor = new QComboBox(parent); PerfConfigEventsModel::EventType eventType = getRowEventType(); if (eventType == PerfConfigEventsModel::EventTypeCache) { editor->addItem("load", PerfConfigEventsModel::OperationLoad); editor->addItem("store", PerfConfigEventsModel::OperationStore); editor->addItem("prefetch", PerfConfigEventsModel::OperationPrefetch); } else if (eventType == PerfConfigEventsModel::EventTypeBreakpoint) { editor->addItem("r", PerfConfigEventsModel::OperationLoad); editor->addItem("rw", PerfConfigEventsModel::OperationLoad | PerfConfigEventsModel::OperationStore); editor->addItem("rwx", PerfConfigEventsModel::OperationLoad | PerfConfigEventsModel::OperationStore | PerfConfigEventsModel::OperationExecute); editor->addItem("rx", PerfConfigEventsModel::OperationLoad | PerfConfigEventsModel::OperationExecute); editor->addItem("w", PerfConfigEventsModel::OperationStore); editor->addItem("wx", PerfConfigEventsModel::OperationStore | PerfConfigEventsModel::OperationExecute); editor->addItem("x", PerfConfigEventsModel::OperationExecute); } else { editor->setEnabled(false); } return editor; } case PerfConfigEventsModel::ColumnResult: { QComboBox *editor = new QComboBox(parent); PerfConfigEventsModel::EventType eventType = getRowEventType(); if (eventType != PerfConfigEventsModel::EventTypeCache) { editor->setEnabled(false); } else { editor->addItem("refs", PerfConfigEventsModel::ResultRefs); editor->addItem("misses", PerfConfigEventsModel::ResultMisses); } return editor; } default: return nullptr; } } void SettingsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (QComboBox *combo = qobject_cast(editor)) { QVariant data = index.model()->data(index, Qt::EditRole); for (int i = 0, end = combo->count(); i != end; ++i) { if (combo->itemData(i) == data) { combo->setCurrentIndex(i); return; } } } else if (QLineEdit *lineedit = qobject_cast(editor)) { lineedit->setText(index.model()->data(index, Qt::DisplayRole).toString()); } } void SettingsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (QComboBox *combo = qobject_cast(editor)) { model->setData(index, combo->currentData()); } else if (QLineEdit *lineedit = qobject_cast(editor)) { QString text = lineedit->text(); QVariant type = model->data(model->index(index.row(), PerfConfigEventsModel::ColumnEventType), Qt::EditRole); switch (qvariant_cast(type)) { case PerfConfigEventsModel::EventTypeRaw: model->setData(index, text.mid(static_cast(strlen("r"))).toULongLong(nullptr, 16)); break; case PerfConfigEventsModel::EventTypeBreakpoint: model->setData(index, text.mid(static_cast(strlen("0x"))).toULongLong(nullptr, 16)); break; case PerfConfigEventsModel::EventTypeCustom: model->setData(index, text); break; default: break; } } } void SettingsDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index) editor->setGeometry(option.rect); } } // namespace Internal } // namespace PerfProfiler #include "perfconfigwidget.moc"