// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "modeltest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Valgrind::Callgrind; using namespace Callgrind::Internal; QTextStream qerr(stderr); void usage() { qerr << "modeltest CALLGRINDFILE ..." << endl; } ModelTestWidget::ModelTestWidget(CallgrindWidgetHandler *handler) : m_format(0) , m_event(0) , m_handler(handler) { QVBoxLayout *l = new QVBoxLayout; setLayout(l); QHBoxLayout *h = new QHBoxLayout; l->addLayout(h); l->addWidget(handler->flatView()); m_handler->populateActions(h); m_format = new QComboBox; m_format->addItem("absolute", CostDelegate::FormatAbsolute); m_format->addItem("relative", CostDelegate::FormatRelative); m_format->addItem("rel. to parent", CostDelegate::FormatRelativeToParent); connect(m_format, QOverload::of(&QComboBox::activated), this, &ModelTestWidget::formatChanged); h->addWidget(m_format); QDoubleSpinBox *minimumCost = new QDoubleSpinBox; minimumCost->setToolTip("Minimum cost"); minimumCost->setRange(0, 1); minimumCost->setDecimals(4); minimumCost->setSingleStep(0.01); connect(minimumCost, &QDoubleSpinBox::valueChanged, m_handler->proxyModel(), &DataProxyModel::setMinimumInclusiveCostRatio); minimumCost->setValue(0.0001); h->addWidget(minimumCost); m_handler->flatView()->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_handler->flatView(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showViewContextMenu(QPoint))); resize(qMin(QApplication::desktop()->width(), 1024), 600); } ModelTestWidget::~ModelTestWidget() { } void ModelTestWidget::formatChanged(int index) { CostDelegate::CostFormat format = static_cast(m_format->itemData(index).toInt()); m_handler->setCostFormat(format); } void ModelTestWidget::showViewContextMenu(const QPoint &pos) { const QModelIndex idx = m_handler->flatView()->indexAt(pos); if (!idx.isValid()) return; QMenu menu; QAction *showCostItems = menu.addAction("show cost items"); const ParseData* data = m_handler->dataModel()->parseData(); QAction *ret = menu.exec(m_handler->flatView()->mapToGlobal(pos)); if (ret == showCostItems) { ///TODO: put this into a reusable class? const Function *func = idx.data(DataModel::FunctionRole).value(); Q_ASSERT(func); QTableView *view = new QTableView(); view->setAttribute(Qt::WA_DeleteOnClose, true); const int rows = func->costItems().size(); const int columns = data->events().size() + data->positions().size() + 2; QStandardItemModel *model = new QStandardItemModel(rows, columns, view); int headerColumn = 0; const QStringList events = data->events(); for (const QString &event : events) { model->setHeaderData(headerColumn++, Qt::Horizontal, event); } const int lastEventColumn = headerColumn; const QStringList positions = data->positions(); for (const QString &pos : positions) { model->setHeaderData(headerColumn++, Qt::Horizontal, pos); } const int lastPosColumn = headerColumn; model->setHeaderData(headerColumn++, Qt::Horizontal, "Call"); model->setHeaderData(headerColumn++, Qt::Horizontal, "Differring File"); Q_ASSERT(headerColumn == columns); QVector totalCosts; totalCosts.fill(0, data->events().size()); for (int row = 0; row < rows; ++row) { const CostItem *item = func->costItems().at(row); for (int column = 0; column < columns; ++column) { QVariant value; if (column < lastEventColumn) { value = item->cost(column); totalCosts[column] += item->cost(column); } else if (column < lastPosColumn) { value = item->position(column - lastEventColumn); } else if (column == lastPosColumn) { if (item->call()) value = item->call()->callee()->name(); } else { value = item->differingFile(); } model->setData(model->index(row, column), value); } } QStringList totalCostsStrings; for (int i = 0; i < totalCosts.size(); ++i) { totalCostsStrings << QString("%1: %2").arg(totalCosts.at(i)).arg(data->events().at(i)); } view->setWindowTitle(totalCostsStrings.join(QLatin1String(", "))); view->setModel(model); view->show(); } } int main(int argc, char *argv[]) { QApplication app(argc, argv); if (app.arguments().count() < 2) { usage(); return 1; } ///TODO: multi-part callgrind files QFile file(app.arguments().at(1)); if (!file.open(QIODevice::ReadOnly)) { qerr << "could not open callgrind file for reading:" << file.fileName() << file.errorString(); return 3; } Parser p; p.parse(&file); ParseData *data = p.takeData(); if (!data) { qerr << "invalid callgrind file:" << file.fileName() << endl; return 2; } QMainWindow window; CallgrindWidgetHandler *handler = new CallgrindWidgetHandler(&window); ModelTestWidget *widget = new ModelTestWidget(handler); widget->setWindowTitle(file.fileName()); window.setCentralWidget(widget); QDockWidget *callerDock = new QDockWidget("callers", &window); callerDock->setWidget(handler->callersView()); window.addDockWidget(Qt::RightDockWidgetArea, callerDock); QDockWidget *calleeDock = new QDockWidget("callees", &window); calleeDock->setWidget(handler->calleesView()); window.addDockWidget(Qt::RightDockWidgetArea, calleeDock); QDockWidget *widgetDock = new QDockWidget("vizualisation", &window); widgetDock->setWidget(handler->visualisation()); window.addDockWidget(Qt::BottomDockWidgetArea, widgetDock); handler->setParseData(data); window.show(); return app.exec(); }