forked from qt-creator/qt-creator
Tracing: Add Chrome Trace Format Visualizer plugin
This new plugin adds a viewer for Chrome Trace Format (CTF) files (aka Trace Event Format). It uses the same UI components as the QML Profiler timeline and the Perf Profiler. The Trace Event Format is generated by different kinds of tracing tools. Usually the files are display with the trace-viewer, built into Chrome (chrome://tracing). This plugin was developed because of the high memory usage of trace-viewer, which makes it difficult to use with trace files bigger than 100 MB. The plugin fully supports all event types used in data generated by LTTng, converted to CTF by https://github.com/KDAB/ctf2ctf. Some of the more advanced event types used for example in Android system traces, though, are not supported. The viewer will silently ignore unsupported event types. Supported Event Types: - Begin, End, Duration and Instant events - Counter events (graphs) - Metadata events (process and thread name) The plugin uses nlohmann/json instead of QJson because of the ~128 MB object size limit by QJson. [ChangeLog][Tracing][CtfVisualizer] Added Chrome Trace Format Visualizer plugin Change-Id: I5969f7f83f3305712d4aec04487e2403510af64b Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
39
README.md
39
README.md
@@ -465,3 +465,42 @@ SQLite (https://www.sqlite.org) is in the Public Domain.
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
|
||||
The font and license files can be found in QtCreator/src/libs/3rdparty/fonts.
|
||||
|
||||
### JSON Library by Niels Lohmann
|
||||
|
||||
Used by the Chrome Trace Format Visualizer plugin instead of QJson
|
||||
because of QJson's current hard limit of 128 Mb object size and
|
||||
trace files often being much larger.
|
||||
|
||||
The sources can be found in `QtCreator/src/libs/3rdparty/json`.
|
||||
|
||||
The class is licensed under the MIT License:
|
||||
|
||||
Copyright © 2013-2019 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the “Software”), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
\endcode
|
||||
|
||||
The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is
|
||||
licensed under the MIT License (see above). Copyright © 2008-2009 Björn
|
||||
Hoehrmann bjoern@hoehrmann.de
|
||||
|
||||
The class contains a slightly modified version of the Grisu2 algorithm
|
||||
from Florian Loitsch which is licensed under the MIT License (see above).
|
||||
Copyright © 2009 Florian Loitsch
|
||||
|
@@ -508,5 +508,46 @@
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
\endcode
|
||||
|
||||
\li \b{JSON Library by Niels Lohmann}
|
||||
|
||||
Used by the Chrome Trace Format Visualizer plugin instead of QJson
|
||||
because of QJson's current hard limit of 128 Mb object size and
|
||||
trace files often being much larger.
|
||||
|
||||
The sources can be found in
|
||||
\c QtCreator/src/libs/3rdparty/json.
|
||||
|
||||
The class is licensed under the MIT License:
|
||||
|
||||
Copyright (C) 2013-2019 Niels Lohmann
|
||||
|
||||
\code
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
\endcode
|
||||
|
||||
The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is
|
||||
licensed under the MIT License (see above). Copyright (C) 2008-2009 Bjoern
|
||||
Hoehrmann <bjoern@hoehrmann.de>
|
||||
|
||||
The class contains a slightly modified version of the Grisu2 algorithm
|
||||
from Florian Loitsch which is licensed under the MIT License (see above).
|
||||
Copyright (C) 2009 Florian Loitsch
|
||||
|
||||
\endlist
|
||||
*/
|
||||
|
20842
src/libs/3rdparty/json/json.hpp
vendored
Normal file
20842
src/libs/3rdparty/json/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
20
src/plugins/ctfvisualizer/CtfVisualizer.json.in
Normal file
20
src/plugins/ctfvisualizer/CtfVisualizer.json.in
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
\"Name\" : \"CtfVisualizer\",
|
||||
\"Version\" : \"$$QTCREATOR_VERSION\",
|
||||
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
|
||||
\"Revision\" : \"$$QTC_PLUGIN_REVISION\",
|
||||
\"Vendor\" : \"KDAB Group, www.kdab.com\",
|
||||
\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com\",
|
||||
\"License\" : [ \"Commercial Usage\",
|
||||
\"\",
|
||||
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
|
||||
\"\",
|
||||
\"GNU General Public License Usage\",
|
||||
\"\",
|
||||
\"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
|
||||
],
|
||||
\"Category\" : \"Code Analyzer\",
|
||||
\"Description\" : \"Chrome Trace Format Visualizer Plugin.\",
|
||||
\"Url\" : \"https://www.kdab.com\",
|
||||
$$dependencyList
|
||||
}
|
189
src/plugins/ctfvisualizer/ctfstatisticsmodel.cpp
Normal file
189
src/plugins/ctfvisualizer/ctfstatisticsmodel.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "ctfstatisticsmodel.h"
|
||||
|
||||
#include "ctfvisualizerconstants.h"
|
||||
|
||||
#include <tracing/timelineformattime.h>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace Constants;
|
||||
|
||||
CtfStatisticsModel::CtfStatisticsModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CtfStatisticsModel::beginLoading()
|
||||
{
|
||||
beginResetModel();
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
void CtfStatisticsModel::addEvent(const json &event, qint64 durationInNs)
|
||||
{
|
||||
const std::string name = event.value(CtfEventNameKey, "");
|
||||
|
||||
EventData &data = m_data[QString::fromStdString(name)];
|
||||
++data.count;
|
||||
if (durationInNs >= 0) {
|
||||
data.totalDuration += durationInNs;
|
||||
data.minDuration = std::min(data.minDuration, durationInNs);
|
||||
data.maxDuration = std::max(data.maxDuration, durationInNs);
|
||||
}
|
||||
}
|
||||
|
||||
void CtfStatisticsModel::endLoading()
|
||||
{
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int CtfStatisticsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : m_data.size();
|
||||
}
|
||||
|
||||
int CtfStatisticsModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : Column::COUNT;
|
||||
}
|
||||
|
||||
QVariant CtfStatisticsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
QString title = m_data.keys().at(index.row());
|
||||
|
||||
switch (role) {
|
||||
case Qt::TextAlignmentRole:
|
||||
switch (index.column()) {
|
||||
case Column::Title:
|
||||
return Qt::AlignLeft;
|
||||
case Column::Count:
|
||||
case Column::TotalDuration:
|
||||
case Column::MinDuration:
|
||||
case Column::AvgDuration:
|
||||
case Column::MaxDuration:
|
||||
return Qt::AlignRight;
|
||||
}
|
||||
case SortRole:
|
||||
switch (index.column()) {
|
||||
case Column::Title:
|
||||
return title;
|
||||
case Column::Count:
|
||||
return m_data.value(title).count;
|
||||
case Column::TotalDuration:
|
||||
return m_data.value(title).totalDuration;
|
||||
case Column::MinDuration:
|
||||
{
|
||||
auto minDuration = m_data.value(title).minDuration;
|
||||
return minDuration != std::numeric_limits<qint64>::max() ? minDuration : 0;
|
||||
}
|
||||
case Column::AvgDuration:
|
||||
{
|
||||
auto data = m_data.value(title);
|
||||
if (data.totalDuration > 0 && data.count > 0)
|
||||
return double(data.totalDuration) / data.count;
|
||||
return 0;
|
||||
}
|
||||
case Column::MaxDuration:
|
||||
return m_data.value(title).maxDuration;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
case Qt::DisplayRole:
|
||||
switch (index.column()) {
|
||||
case Column::Title:
|
||||
return title;
|
||||
case Column::Count:
|
||||
return m_data.value(title).count;
|
||||
case Column::TotalDuration:
|
||||
{
|
||||
auto totalDuration = m_data.value(title).totalDuration;
|
||||
if (totalDuration > 0)
|
||||
return Timeline::formatTime(totalDuration);
|
||||
else
|
||||
return "-";
|
||||
}
|
||||
case Column::MinDuration:
|
||||
{
|
||||
auto minDuration = m_data.value(title).minDuration;
|
||||
if (minDuration != std::numeric_limits<qint64>::max())
|
||||
return Timeline::formatTime(minDuration);
|
||||
else
|
||||
return "-";
|
||||
}
|
||||
case Column::AvgDuration:
|
||||
{
|
||||
auto data = m_data.value(title);
|
||||
if (data.totalDuration > 0 && data.count > 0)
|
||||
return Timeline::formatTime(data.totalDuration / data.count);
|
||||
return "-";
|
||||
}
|
||||
case Column::MaxDuration:
|
||||
{
|
||||
auto maxDuration = m_data.value(title).maxDuration;
|
||||
if (maxDuration > 0)
|
||||
return Timeline::formatTime(maxDuration);
|
||||
else
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant CtfStatisticsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
|
||||
return QAbstractTableModel::headerData(section, orientation, role);
|
||||
|
||||
switch (section) {
|
||||
case Column::Title:
|
||||
return tr("Title");
|
||||
case Column::Count:
|
||||
return tr("Count");
|
||||
case Column::TotalDuration:
|
||||
return tr("Total Time");
|
||||
case Column::MinDuration:
|
||||
return tr("Minimum Time");
|
||||
case Column::AvgDuration:
|
||||
return tr("Average Time");
|
||||
case Column::MaxDuration:
|
||||
return tr("Maximum Time");
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // CtfVisualizer
|
84
src/plugins/ctfvisualizer/ctfstatisticsmodel.h
Normal file
84
src/plugins/ctfvisualizer/ctfstatisticsmodel.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "libs/3rdparty/json/json.hpp"
|
||||
|
||||
#include <QHash>
|
||||
#include <QStack>
|
||||
#include <QVector>
|
||||
#include <QPointer>
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfStatisticsModel : public QAbstractTableModel
|
||||
{
|
||||
|
||||
public:
|
||||
enum Role {
|
||||
SortRole = Qt::UserRole + 1,
|
||||
};
|
||||
|
||||
enum Column {
|
||||
Title = 0,
|
||||
Count,
|
||||
TotalDuration,
|
||||
MinDuration,
|
||||
AvgDuration,
|
||||
MaxDuration,
|
||||
COUNT
|
||||
};
|
||||
|
||||
struct EventData {
|
||||
int count = 0;
|
||||
qint64 totalDuration = 0.0;
|
||||
qint64 minDuration = std::numeric_limits<qint64>::max();
|
||||
qint64 maxDuration = 0.0;
|
||||
};
|
||||
|
||||
explicit CtfStatisticsModel(QObject *parent);
|
||||
~CtfStatisticsModel() override = default;
|
||||
|
||||
void beginLoading();
|
||||
void addEvent(const nlohmann::json &event, qint64 duration);
|
||||
void endLoading();
|
||||
|
||||
private:
|
||||
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
|
||||
QHash<QString, EventData> m_data;
|
||||
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // CtfVisualizer
|
84
src/plugins/ctfvisualizer/ctfstatisticsview.cpp
Normal file
84
src/plugins/ctfvisualizer/ctfstatisticsview.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "ctfstatisticsview.h"
|
||||
|
||||
#include "ctfstatisticsmodel.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
CtfStatisticsView::CtfStatisticsView(CtfStatisticsModel *model, QWidget *parent)
|
||||
: Utils::TreeView(parent)
|
||||
{
|
||||
setObjectName(QLatin1String("CtfVisualizerStatisticsView"));
|
||||
|
||||
auto sortModel = new QSortFilterProxyModel(this);
|
||||
sortModel->setSourceModel(model);
|
||||
sortModel->setSortRole(CtfStatisticsModel::SortRole);
|
||||
sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
setModel(sortModel);
|
||||
|
||||
header()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
header()->setDefaultSectionSize(100);
|
||||
header()->setMinimumSectionSize(50);
|
||||
header()->setStretchLastSection(false);
|
||||
header()->setSectionResizeMode(CtfStatisticsModel::Column::Title, QHeaderView::Stretch);
|
||||
setRootIsDecorated(false);
|
||||
setUniformRowHeights(true);
|
||||
setSortingEnabled(true);
|
||||
|
||||
connect(selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
[this] (const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
Q_UNUSED(previous);
|
||||
QModelIndex index = this->model()->index(current.row(), CtfStatisticsModel::Title);
|
||||
QString title = this->model()->data(index).toString();
|
||||
emit this->eventTypeSelected(title);
|
||||
});
|
||||
}
|
||||
|
||||
void CtfStatisticsView::selectByTitle(const QString &title)
|
||||
{
|
||||
auto model = this->model();
|
||||
for (int row = 0; row < model->rowCount(); ++row)
|
||||
{
|
||||
auto index = model->index(row, CtfStatisticsModel::Column::Title);
|
||||
if (model->data(index).toString() == title)
|
||||
{
|
||||
QItemSelection selection(index, model->index(row, CtfStatisticsModel::Column::COUNT - 1));
|
||||
selectionModel()->select(selection, QItemSelectionModel::SelectCurrent);
|
||||
scrollTo(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // CtfVisualizer
|
52
src/plugins/ctfvisualizer/ctfstatisticsview.h
Normal file
52
src/plugins/ctfvisualizer/ctfstatisticsview.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ctfvisualizerconstants.h"
|
||||
|
||||
#include <utils/itemviews.h>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfStatisticsModel;
|
||||
|
||||
class CtfStatisticsView : public Utils::TreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CtfStatisticsView(CtfStatisticsModel *model, QWidget *parent = nullptr);
|
||||
~CtfStatisticsView() override = default;
|
||||
|
||||
void selectByTitle(const QString &title);
|
||||
|
||||
signals:
|
||||
void eventTypeSelected(const QString &title);
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // CtfVisualizer
|
383
src/plugins/ctfvisualizer/ctftimelinemodel.cpp
Normal file
383
src/plugins/ctfvisualizer/ctftimelinemodel.cpp
Normal file
@@ -0,0 +1,383 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "ctftimelinemodel.h"
|
||||
|
||||
#include "ctftracemanager.h"
|
||||
#include "ctfvisualizerconstants.h"
|
||||
|
||||
#include <tracing/timelineformattime.h>
|
||||
#include <tracing/timelinemodelaggregator.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace Constants;
|
||||
|
||||
CtfTimelineModel::CtfTimelineModel(Timeline::TimelineModelAggregator *parent,
|
||||
CtfTraceManager *traceManager, int tid, int pid)
|
||||
: Timeline::TimelineModel (parent)
|
||||
, m_traceManager(traceManager)
|
||||
, m_threadId(tid)
|
||||
, m_processId(pid)
|
||||
{
|
||||
updateName();
|
||||
setCollapsedRowCount(1);
|
||||
setCategoryColor(colorByHue(pid * 25));
|
||||
setHasMixedTypesInExpandedState(true);
|
||||
}
|
||||
|
||||
QRgb CtfTimelineModel::color(int index) const
|
||||
{
|
||||
return colorBySelectionId(index);
|
||||
}
|
||||
|
||||
QVariantList CtfTimelineModel::labels() const
|
||||
{
|
||||
QVariantList result;
|
||||
|
||||
QVector<std::string> sortedCounterNames = m_counterNames;
|
||||
std::sort(sortedCounterNames.begin(), sortedCounterNames.end());
|
||||
for (int i = 0; i < sortedCounterNames.size(); ++i) {
|
||||
QVariantMap element;
|
||||
element.insert(QLatin1String("description"), QString("~ %1").arg(QString::fromStdString(sortedCounterNames[i])));
|
||||
element.insert(QLatin1String("id"), i);
|
||||
result << element;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_maxStackSize; ++i) {
|
||||
QVariantMap element;
|
||||
element.insert(QLatin1String("description"), QStringLiteral("- ").append(tr("Stack Level %1").arg(i)));
|
||||
element.insert(QLatin1String("id"), m_counterNames.size() + i);
|
||||
result << element;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap CtfTimelineModel::orderedDetails(int index) const
|
||||
{
|
||||
QMap<int, QPair<QString, QString>> info = m_details.value(index);
|
||||
info.insert(2, {tr("Start"), Timeline::formatTime(startTime(index))});
|
||||
info.insert(3, {tr("Wall Duration"), Timeline::formatTime(duration(index))});
|
||||
QVariantMap data;
|
||||
QString title = info.value(0).second;
|
||||
data.insert("title", title);
|
||||
QVariantList content;
|
||||
auto it = info.constBegin();
|
||||
auto end = info.constEnd();
|
||||
++it; // skip title
|
||||
while (it != end) {
|
||||
content.append(it.value().first);
|
||||
content.append(it.value().second);
|
||||
++it;
|
||||
}
|
||||
data.insert("content", content);
|
||||
emit detailsRequested(title);
|
||||
return data;
|
||||
}
|
||||
|
||||
int CtfTimelineModel::expandedRow(int index) const
|
||||
{
|
||||
// m_itemToCounterIdx[index] is 0 for non-counter indexes
|
||||
const int counterIdx = m_itemToCounterIdx.value(index, 0);
|
||||
if (counterIdx > 0) {
|
||||
return m_counterIndexToRow[counterIdx - 1] + 1;
|
||||
}
|
||||
return m_nestingLevels.value(index) + m_counterData.size() + 1;
|
||||
}
|
||||
|
||||
int CtfTimelineModel::collapsedRow(int index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CtfTimelineModel::typeId(int index) const
|
||||
{
|
||||
QTC_ASSERT(index >= 0 && index < count(), return -1);
|
||||
return selectionId(index);
|
||||
}
|
||||
|
||||
bool CtfTimelineModel::handlesTypeId(int typeId) const
|
||||
{
|
||||
return m_handledTypeIds.contains(typeId);
|
||||
}
|
||||
|
||||
float CtfTimelineModel::relativeHeight(int index) const
|
||||
{
|
||||
// m_itemToCounterIdx[index] is 0 for non-counter indexes
|
||||
const int counterIdx = m_itemToCounterIdx.value(index, 0);
|
||||
if (counterIdx > 0) {
|
||||
const CounterData &data = m_counterData[counterIdx - 1];
|
||||
const float value = m_counterValues[index] / std::max(data.max, 1.0f);
|
||||
return value;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
QPair<bool, qint64> CtfTimelineModel::addEvent(const json &event, double timeOffset)
|
||||
{
|
||||
const double timestamp = event.value(CtfTracingClockTimestampKey, 0.0);
|
||||
const qint64 normalizedTime = qint64((timestamp - timeOffset) * 1000);
|
||||
const std::string eventPhase = event.value(CtfEventPhaseKey, "");
|
||||
const std::string name = event.value(CtfEventNameKey, "");
|
||||
qint64 duration = -1;
|
||||
|
||||
const int selectionId = m_traceManager->getSelectionId(name);
|
||||
m_handledTypeIds.insert(selectionId);
|
||||
|
||||
bool visibleOnTimeline = false;
|
||||
if (eventPhase == CtfEventTypeBegin || eventPhase == CtfEventTypeComplete
|
||||
|| eventPhase == CtfEventTypeInstant || eventPhase == CtfEventTypeInstantDeprecated) {
|
||||
duration = newStackEvent(event, normalizedTime, eventPhase, name, selectionId);
|
||||
visibleOnTimeline = true;
|
||||
} else if (eventPhase == CtfEventTypeEnd) {
|
||||
duration = closeStackEvent(event, timestamp, normalizedTime);
|
||||
visibleOnTimeline = true;
|
||||
} else if (eventPhase == CtfEventTypeCounter) {
|
||||
addCounterValue(event, normalizedTime, name, selectionId);
|
||||
visibleOnTimeline = true;
|
||||
} else if (eventPhase == CtfEventTypeMetadata) {
|
||||
const std::string name = event[CtfEventNameKey];
|
||||
if (name == "thread_name") {
|
||||
m_threadName = QString::fromStdString(event["args"]["name"]);
|
||||
updateName();
|
||||
} else if (name == "process_name") {
|
||||
m_processName = QString::fromStdString(event["args"]["name"]);
|
||||
updateName();
|
||||
}
|
||||
}
|
||||
return {visibleOnTimeline, duration};
|
||||
}
|
||||
|
||||
void CtfTimelineModel::finalize(double traceBegin, double traceEnd, const QString &processName, const QString &threadName)
|
||||
{
|
||||
m_processName = processName;
|
||||
m_threadName = threadName;
|
||||
updateName();
|
||||
|
||||
qint64 normalizedEnd = qint64((traceEnd - traceBegin) * 1000);
|
||||
while (!m_openEventIds.isEmpty()) {
|
||||
int index = m_openEventIds.pop();
|
||||
qint64 duration = normalizedEnd - startTime(index);
|
||||
insertEnd(index, duration);
|
||||
m_details[index].insert(3, {reuse(tr("Wall Duration")), Timeline::formatTime(duration)});
|
||||
m_details[index].insert(6, {reuse(tr("Unfinished")), reuse(tr("true"))});
|
||||
}
|
||||
computeNesting();
|
||||
|
||||
QVector<std::string> sortedCounterNames = m_counterNames;
|
||||
std::sort(sortedCounterNames.begin(), sortedCounterNames.end());
|
||||
m_counterIndexToRow.resize(m_counterNames.size());
|
||||
for (int i = 0; i < m_counterIndexToRow.size(); ++i) {
|
||||
m_counterIndexToRow[i] = sortedCounterNames.indexOf(m_counterNames[i]);
|
||||
}
|
||||
|
||||
// we insert an empty row in expanded state because otherwise
|
||||
// the row label would be where the thread label is
|
||||
setExpandedRowCount(1 + m_counterData.size() + m_maxStackSize);
|
||||
emit contentChanged();
|
||||
}
|
||||
|
||||
void CtfTimelineModel::updateName()
|
||||
{
|
||||
if (m_threadName.isEmpty()) {
|
||||
setDisplayName(tr("> Thread %1").arg(m_threadId));
|
||||
} else {
|
||||
setDisplayName(QString("> %1 (%2)").arg(m_threadName).arg(m_threadId));
|
||||
}
|
||||
QString process = m_processName.isEmpty() ? QString::number(m_processId) :
|
||||
QString("%1 (%2)").arg(m_processName).arg(m_processId);
|
||||
QString thread = m_threadName.isEmpty() ? QString::number(m_threadId) :
|
||||
QString("%1 (%2)").arg(m_threadName).arg(m_threadId);
|
||||
setTooltip(QString("Process: %1\nThread: %2").arg(process).arg(thread));
|
||||
}
|
||||
|
||||
qint64 CtfTimelineModel::newStackEvent(const json &event, qint64 normalizedTime,
|
||||
const std::string &eventPhase, const std::string &name,
|
||||
int selectionId)
|
||||
{
|
||||
int nestingLevel = m_openEventIds.size();
|
||||
m_maxStackSize = std::max(m_maxStackSize, m_openEventIds.size() + 1);
|
||||
int index = 0;
|
||||
qint64 duration = -1;
|
||||
if (eventPhase == CtfEventTypeBegin) {
|
||||
index = insertStart(normalizedTime, selectionId);
|
||||
m_openEventIds.push(index);
|
||||
// if this event was inserted before existing events, we need to
|
||||
// check whether indexes stored in m_openEventIds need to be updated (increased):
|
||||
// (the top element is the current event and is skipped)
|
||||
for (int i = m_openEventIds.size() - 2; i >= 0; --i) {
|
||||
if (m_openEventIds[i] >= index) ++m_openEventIds[i];
|
||||
}
|
||||
} else if (eventPhase == CtfEventTypeComplete) {
|
||||
duration = qint64(event[CtfDurationKey]) * 1000;
|
||||
index = insert(normalizedTime, duration, selectionId);
|
||||
for (int i = m_openEventIds.size() - 1; i >= 0; --i) {
|
||||
if (m_openEventIds[i] >= index) {
|
||||
++m_openEventIds[i];
|
||||
// if the event is before an open event, the nesting level decreases:
|
||||
--nestingLevel;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index = insert(normalizedTime, 0, selectionId);
|
||||
for (int i = m_openEventIds.size() - 1; i >= 0; --i) {
|
||||
if (m_openEventIds[i] >= index) {
|
||||
++m_openEventIds[i];
|
||||
--nestingLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index >= m_details.size()) {
|
||||
m_details.resize(index + 1);
|
||||
m_details[index] = QMap<int, QPair<QString, QString>>();
|
||||
m_nestingLevels.resize(index + 1);
|
||||
m_nestingLevels[index] = nestingLevel;
|
||||
} else {
|
||||
m_details.insert(index, QMap<int, QPair<QString, QString>>());
|
||||
m_nestingLevels.insert(index, nestingLevel);
|
||||
}
|
||||
if (m_counterValues.size() > index) {
|
||||
// if the event was inserted before any counter, we need
|
||||
// to shift the values after it and update the last index:
|
||||
m_counterValues.insert(index, 0);
|
||||
m_itemToCounterIdx.insert(index, 0);
|
||||
for (CounterData &counterData: m_counterData) {
|
||||
if (counterData.startEventIndex >= index) ++counterData.startEventIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.empty()) {
|
||||
m_details[index].insert(0, {{}, reuse(QString::fromStdString(name))});
|
||||
}
|
||||
if (event.contains(CtfEventCategoryKey)) {
|
||||
m_details[index].insert(1, {reuse(tr("Categories")), reuse(QString::fromStdString(event[CtfEventCategoryKey]))});
|
||||
}
|
||||
if (event.contains("args") && !event["args"].empty()) {
|
||||
QString argsJson = QString::fromStdString(event["args"].dump(1));
|
||||
// strip leading and trailing curled brackets:
|
||||
argsJson = argsJson.size() > 4 ? argsJson.mid(2, argsJson.size() - 4) : argsJson;
|
||||
m_details[index].insert(4, {reuse(tr("Arguments")), reuse(argsJson)});
|
||||
}
|
||||
if (eventPhase == CtfEventTypeInstant) {
|
||||
m_details[index].insert(6, {reuse(tr("Instant")), reuse(tr("true"))});
|
||||
if (event.contains("s")) {
|
||||
std::string scope = event["s"];
|
||||
if (scope == "g") {
|
||||
m_details[index].insert(7, {reuse(tr("Scope")), reuse(tr("global"))});
|
||||
} else if (scope == "p") {
|
||||
m_details[index].insert(7, {reuse(tr("Scope")), reuse(tr("process"))});
|
||||
} else {
|
||||
m_details[index].insert(7, {reuse(tr("Scope")), reuse(tr("thread"))});
|
||||
}
|
||||
}
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
qint64 CtfTimelineModel::closeStackEvent(const json &event, double timestamp, qint64 normalizedTime)
|
||||
{
|
||||
if (m_openEventIds.isEmpty()) {
|
||||
qWarning() << QString("End event without open 'begin' event at timestamp %1").arg(timestamp, 0, 'f');
|
||||
return -1;
|
||||
} else {
|
||||
const int index = m_openEventIds.pop();
|
||||
const qint64 duration = normalizedTime - startTime(index);
|
||||
insertEnd(index, duration);
|
||||
if (event.contains("args") && !event["args"].empty()) {
|
||||
QString argsJson = QString::fromStdString(event["args"].dump(1));
|
||||
// strip leading and trailing curled brackets:
|
||||
argsJson = argsJson.size() > 4 ? argsJson.mid(2, argsJson.size() - 4) : argsJson;
|
||||
m_details[index].insert(5, {reuse(tr("Return Arguments")), reuse(argsJson)});
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
void CtfTimelineModel::addCounterValue(const json &event, qint64 normalizedTime,
|
||||
const std::string &name, int selectionId)
|
||||
{
|
||||
if (!event.contains("args")) return;
|
||||
// CTF documentation says all keys of 'args' should be displayed in
|
||||
// one stacked graph, but we will display them separately:
|
||||
for (auto it: event["args"].items()) {
|
||||
std::string counterName = event.contains("id") ?
|
||||
name + event.value("id", "") : name;
|
||||
const std::string &key = it.key();
|
||||
if (key != "value") {
|
||||
counterName += " " + key;
|
||||
}
|
||||
const float value = it.value();
|
||||
int counterIndex = m_counterNames.indexOf(counterName);
|
||||
if (counterIndex < 0) {
|
||||
counterIndex = m_counterData.size();
|
||||
m_counterNames.append(counterName);
|
||||
m_counterData.append(CounterData());
|
||||
}
|
||||
CounterData &data = m_counterData[counterIndex];
|
||||
if (data.startEventIndex >= 0) {
|
||||
insertEnd(data.startEventIndex, normalizedTime - data.end);
|
||||
}
|
||||
const int index = insertStart(normalizedTime, selectionId);
|
||||
data.startEventIndex = index;
|
||||
data.end = normalizedTime;
|
||||
data.min = std::min(data.min, value);
|
||||
data.max = std::max(data.max, value);
|
||||
if (index >= m_counterValues.size()) {
|
||||
m_counterValues.resize(index + 1);
|
||||
m_counterValues[index] = value;
|
||||
m_itemToCounterIdx.resize(index + 1);
|
||||
// m_itemToCounterIdx[index] == 0 is used to indicate a non-counter value
|
||||
m_itemToCounterIdx[index] = counterIndex + 1;
|
||||
} else {
|
||||
m_counterValues.insert(index, value);
|
||||
m_itemToCounterIdx.insert(index, counterIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QString &CtfTimelineModel::reuse(const QString &value)
|
||||
{
|
||||
auto it = m_reusableStrings.find(value);
|
||||
if (it == m_reusableStrings.end()) {
|
||||
m_reusableStrings.insert(value);
|
||||
return value;
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
||||
|
115
src/plugins/ctfvisualizer/ctftimelinemodel.h
Normal file
115
src/plugins/ctfvisualizer/ctftimelinemodel.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "libs/3rdparty/json/json.hpp"
|
||||
|
||||
#include <tracing/timelinemodel.h>
|
||||
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QStack>
|
||||
#include <QVector>
|
||||
|
||||
namespace Timeline {
|
||||
class TimelineModelAggregator;
|
||||
}
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfTraceManager;
|
||||
|
||||
class CtfTimelineModel : public Timeline::TimelineModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class CtfTraceManager;
|
||||
|
||||
public:
|
||||
explicit CtfTimelineModel(Timeline::TimelineModelAggregator *parent,
|
||||
CtfTraceManager *traceManager, int tid, int pid);
|
||||
|
||||
QRgb color(int index) const override;
|
||||
QVariantList labels() const override;
|
||||
QVariantMap orderedDetails(int index) const override;
|
||||
int expandedRow(int index) const override;
|
||||
int collapsedRow(int index) const override;
|
||||
|
||||
int typeId(int index) const override;
|
||||
bool handlesTypeId(int typeId) const override;
|
||||
float relativeHeight(int index) const override;
|
||||
|
||||
QPair<bool, qint64> addEvent(const nlohmann::json &event, double traceBegin);
|
||||
|
||||
void finalize(double traceBegin, double traceEnd, const QString &processName, const QString &threadName);
|
||||
|
||||
signals:
|
||||
void detailsRequested(const QString &eventName) const;
|
||||
|
||||
private:
|
||||
void updateName();
|
||||
|
||||
qint64 newStackEvent(const nlohmann::json &event, qint64 normalizedTime,
|
||||
const std::string &eventPhase, const std::string &name, int selectionId);
|
||||
|
||||
qint64 closeStackEvent(const nlohmann::json &event, double timestamp, qint64 normalizedTime);
|
||||
|
||||
void addCounterValue(const nlohmann::json &event, qint64 normalizedTime, const std::string &name, int selectionId);
|
||||
|
||||
const QString &reuse(const QString &value);
|
||||
|
||||
protected:
|
||||
CtfTraceManager *const m_traceManager;
|
||||
|
||||
int m_threadId;
|
||||
QString m_threadName;
|
||||
int m_processId;
|
||||
QString m_processName;
|
||||
|
||||
int m_maxStackSize = 0;
|
||||
QVector<int> m_nestingLevels;
|
||||
QVector<QMap<int, QPair<QString, QString>>> m_details;
|
||||
QSet<int> m_handledTypeIds;
|
||||
QStack<int> m_openEventIds;
|
||||
QSet<QString> m_reusableStrings;
|
||||
|
||||
struct CounterData {
|
||||
qint64 end = 0;
|
||||
int startEventIndex = -1;
|
||||
float min = std::numeric_limits<float>::max();
|
||||
float max = std::numeric_limits<float>::min();
|
||||
};
|
||||
|
||||
QVector<std::string> m_counterNames;
|
||||
QVector<CounterData> m_counterData;
|
||||
QVector<float> m_counterValues;
|
||||
QVector<int> m_itemToCounterIdx;
|
||||
QVector<int> m_counterIndexToRow;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
256
src/plugins/ctfvisualizer/ctftracemanager.cpp
Normal file
256
src/plugins/ctfvisualizer/ctftracemanager.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "ctftracemanager.h"
|
||||
|
||||
#include "ctftimelinemodel.h"
|
||||
#include "ctfstatisticsmodel.h"
|
||||
#include "ctfvisualizerconstants.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <tracing/timelinemodelaggregator.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
using namespace Constants;
|
||||
|
||||
|
||||
class CtfJsonParserCallback
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
explicit CtfJsonParserCallback(CtfTraceManager *traceManager)
|
||||
: m_traceManager(traceManager)
|
||||
{}
|
||||
|
||||
bool callback(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed)
|
||||
{
|
||||
if ((event == json::parse_event_t::array_start && depth == 0)
|
||||
|| (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) {
|
||||
m_isInTraceArray = true;
|
||||
m_traceArrayDepth = depth;
|
||||
return true;
|
||||
}
|
||||
if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) {
|
||||
m_isInTraceArray = false;
|
||||
return false;
|
||||
}
|
||||
if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) {
|
||||
m_traceManager->addEvent(parsed);
|
||||
return false;
|
||||
}
|
||||
if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) {
|
||||
// keep outer object and values in trace objects:
|
||||
return true;
|
||||
}
|
||||
// discard any objects outside of trace array:
|
||||
// TODO: parse other data, e.g. stack frames
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
CtfTraceManager *m_traceManager;
|
||||
|
||||
bool m_isInTraceArray = false;
|
||||
int m_traceArrayDepth = 0;
|
||||
};
|
||||
|
||||
CtfTraceManager::CtfTraceManager(QObject *parent,
|
||||
Timeline::TimelineModelAggregator *modelAggregator,
|
||||
CtfStatisticsModel *statisticsModel)
|
||||
: QObject(parent)
|
||||
, m_modelAggregator(modelAggregator)
|
||||
, m_statisticsModel(statisticsModel)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
qint64 CtfTraceManager::traceDuration() const
|
||||
{
|
||||
return qint64((m_traceEnd - m_traceBegin) * 1000);
|
||||
}
|
||||
|
||||
qint64 CtfTraceManager::traceBegin() const
|
||||
{
|
||||
return qint64((m_traceBegin - m_timeOffset) * 1000);
|
||||
}
|
||||
|
||||
qint64 CtfTraceManager::traceEnd() const
|
||||
{
|
||||
return qint64((m_traceEnd - m_timeOffset) * 1000);
|
||||
}
|
||||
|
||||
void CtfTraceManager::addEvent(const json &event)
|
||||
{
|
||||
const double timestamp = event.value(CtfTracingClockTimestampKey, -1.0);
|
||||
if (timestamp < 0) {
|
||||
// events without or with negative timestamp will be ignored
|
||||
return;
|
||||
}
|
||||
if (m_timeOffset < 0) {
|
||||
// the timestamp of the first event is used as the global offset
|
||||
m_timeOffset = timestamp;
|
||||
}
|
||||
|
||||
const int processId = event.value(CtfProcessIdKey, 0);
|
||||
const int threadId = event.contains(CtfThreadIdKey) ? int(event[CtfThreadIdKey]) : processId;
|
||||
if (!m_threadModels.contains(threadId)) {
|
||||
addModelForThread(threadId, processId);
|
||||
}
|
||||
if (event.value(CtfEventPhaseKey, "") == CtfEventTypeMetadata) {
|
||||
const std::string name = event[CtfEventNameKey];
|
||||
if (name == "thread_name") {
|
||||
m_threadNames[threadId] = QString::fromStdString(event["args"]["name"]);
|
||||
} else if (name == "process_name") {
|
||||
m_processNames[processId] = QString::fromStdString(event["args"]["name"]);
|
||||
}
|
||||
}
|
||||
|
||||
const QPair<bool, qint64> result = m_threadModels[threadId]->addEvent(event, m_timeOffset);
|
||||
const bool visibleOnTimeline = result.first;
|
||||
if (visibleOnTimeline) {
|
||||
m_traceBegin = std::min(m_traceBegin, timestamp);
|
||||
m_traceEnd = std::max(m_traceEnd, timestamp);
|
||||
m_statisticsModel->addEvent(event, result.second);
|
||||
} else if (m_timeOffset == timestamp) {
|
||||
// this timestamp was used as the time offset but it is not a visible element
|
||||
// -> reset the time offset again:
|
||||
m_timeOffset = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void CtfVisualizer::Internal::CtfTraceManager::load(const QString &filename)
|
||||
{
|
||||
clearAll();
|
||||
|
||||
std::ifstream file(filename.toStdString());
|
||||
if (!file.is_open()) {
|
||||
QMessageBox::warning(Core::ICore::mainWindow(),
|
||||
tr("CTF Visualizer"),
|
||||
tr("Cannot read the CTF file."));
|
||||
return;
|
||||
}
|
||||
CtfJsonParserCallback ctfParser(this);
|
||||
json::parser_callback_t callback = [&ctfParser](int depth, json::parse_event_t event, json &parsed) {
|
||||
return ctfParser.callback(depth, event, parsed);
|
||||
};
|
||||
m_statisticsModel->beginLoading();
|
||||
json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false);
|
||||
m_statisticsModel->endLoading();
|
||||
file.close();
|
||||
}
|
||||
|
||||
void CtfTraceManager::finalize()
|
||||
{
|
||||
bool userConsentToIgnoreDeepTraces = false;
|
||||
for (qint64 tid: m_threadModels.keys()) {
|
||||
if (m_threadModels[tid]->m_maxStackSize > 512) {
|
||||
if (!userConsentToIgnoreDeepTraces) {
|
||||
QMessageBox::StandardButton answer = QMessageBox::question(Core::ICore::mainWindow(),
|
||||
tr("CTF Visualizer"),
|
||||
tr("The trace contains threads with stack depth > 512.\nDo you want to display them anyway?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
if (answer == QMessageBox::No) {
|
||||
userConsentToIgnoreDeepTraces = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_threadModels.remove(tid);
|
||||
}
|
||||
}
|
||||
for (CtfTimelineModel *model: m_threadModels) {
|
||||
model->finalize(m_traceBegin, m_traceEnd,
|
||||
m_processNames[model->m_processId], m_threadNames[model->m_threadId]);
|
||||
}
|
||||
// TimelineModelAggregator::addModel() is called here because it
|
||||
// needs to be run in the main thread
|
||||
addModelsToAggregator();
|
||||
}
|
||||
|
||||
bool CtfTraceManager::isEmpty() const
|
||||
{
|
||||
return m_threadModels.isEmpty();
|
||||
}
|
||||
|
||||
int CtfTraceManager::getSelectionId(const std::string &name)
|
||||
{
|
||||
auto it = m_name2selectionId.find(name);
|
||||
if (it == m_name2selectionId.end())
|
||||
it = m_name2selectionId.insert(name, m_name2selectionId.size());
|
||||
return *it;
|
||||
}
|
||||
|
||||
void CtfTraceManager::addModelForThread(int threadId, int processId)
|
||||
{
|
||||
CtfTimelineModel *model = new CtfTimelineModel(m_modelAggregator, this, threadId, processId);
|
||||
m_threadModels.insert(threadId, model);
|
||||
connect(model, &CtfTimelineModel::detailsRequested, this,
|
||||
&CtfTraceManager::detailsRequested);
|
||||
}
|
||||
|
||||
void CtfTraceManager::addModelsToAggregator()
|
||||
{
|
||||
QList<CtfTimelineModel *> models = m_threadModels.values();
|
||||
std::sort(models.begin(), models.end(), [](const CtfTimelineModel *a, const CtfTimelineModel *b) -> bool {
|
||||
return (a->m_processId != b->m_processId) ? (a->m_processId < b->m_processId)
|
||||
: (std::abs(a->m_threadId) < std::abs(b->m_threadId));
|
||||
});
|
||||
|
||||
QVariantList modelsToAdd;
|
||||
for (CtfTimelineModel *model: models) {
|
||||
modelsToAdd.append(QVariant::fromValue(model));
|
||||
}
|
||||
m_modelAggregator->setModels(modelsToAdd);
|
||||
}
|
||||
|
||||
void CtfTraceManager::clearAll()
|
||||
{
|
||||
m_modelAggregator->clear();
|
||||
for (CtfTimelineModel *model: m_threadModels) {
|
||||
model->deleteLater();
|
||||
}
|
||||
m_threadModels.clear();
|
||||
m_traceBegin = std::numeric_limits<double>::max();
|
||||
m_traceEnd = std::numeric_limits<double>::min();
|
||||
m_timeOffset = -1;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
92
src/plugins/ctfvisualizer/ctftracemanager.h
Normal file
92
src/plugins/ctfvisualizer/ctftracemanager.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "libs/3rdparty/json/json.hpp"
|
||||
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
namespace Timeline {
|
||||
class TimelineModelAggregator;
|
||||
}
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfStatisticsModel;
|
||||
class CtfTimelineModel;
|
||||
|
||||
class CtfTraceManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CtfTraceManager(QObject *parent,
|
||||
Timeline::TimelineModelAggregator *modelAggregator,
|
||||
CtfStatisticsModel *statisticsModel);
|
||||
|
||||
qint64 traceDuration() const;
|
||||
qint64 traceBegin() const;
|
||||
qint64 traceEnd() const;
|
||||
|
||||
void addEvent(const nlohmann::json &event);
|
||||
|
||||
void load(const QString &filename);
|
||||
void finalize();
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
int getSelectionId(const std::string &name);
|
||||
|
||||
signals:
|
||||
void detailsRequested(const QString &title);
|
||||
|
||||
protected:
|
||||
|
||||
void addModelForThread(int threadId, int processId);
|
||||
void addModelsToAggregator();
|
||||
|
||||
void clearAll();
|
||||
|
||||
Timeline::TimelineModelAggregator *const m_modelAggregator;
|
||||
CtfStatisticsModel *const m_statisticsModel;
|
||||
|
||||
QHash<qint64, CtfTimelineModel *> m_threadModels;
|
||||
QHash<qint64, QString> m_processNames;
|
||||
QHash<qint64, QString> m_threadNames;
|
||||
QMap<std::string, int> m_name2selectionId;
|
||||
|
||||
double m_traceBegin = std::numeric_limits<double>::max();
|
||||
double m_traceEnd = std::numeric_limits<double>::min();
|
||||
double m_timeOffset = -1.0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
34
src/plugins/ctfvisualizer/ctfvisualizer.pro
Normal file
34
src/plugins/ctfvisualizer/ctfvisualizer.pro
Normal file
@@ -0,0 +1,34 @@
|
||||
TARGET = CtfVisualizer
|
||||
TEMPLATE = lib
|
||||
|
||||
QT += quick quickwidgets
|
||||
|
||||
include(../../qtcreatorplugin.pri)
|
||||
include(ctfvisualizer_dependencies.pri)
|
||||
|
||||
DEFINES += CTFVISUALIZER_LIBRARY
|
||||
|
||||
# CtfVisualizer files
|
||||
|
||||
SOURCES += \
|
||||
ctfstatisticsmodel.cpp \
|
||||
ctfstatisticsview.cpp \
|
||||
ctfvisualizerplugin.cpp \
|
||||
ctfvisualizertool.cpp \
|
||||
ctftimelinemodel.cpp \
|
||||
ctftracemanager.cpp \
|
||||
ctfvisualizertraceview.cpp
|
||||
|
||||
HEADERS += \
|
||||
ctfstatisticsmodel.h \
|
||||
ctfstatisticsview.h \
|
||||
ctfvisualizerplugin.h \
|
||||
ctfvisualizertool.h\
|
||||
ctftimelinemodel.h \
|
||||
ctftracemanager.h \
|
||||
ctfvisualizerconstants.h \
|
||||
ctfvisualizertraceview.h \
|
||||
../../libs/3rdparty/json/json.hpp
|
||||
|
||||
OTHER_FILES += \
|
||||
CtfVisualizer.json.in
|
9
src/plugins/ctfvisualizer/ctfvisualizer_dependencies.pri
Normal file
9
src/plugins/ctfvisualizer/ctfvisualizer_dependencies.pri
Normal file
@@ -0,0 +1,9 @@
|
||||
QTC_PLUGIN_NAME = CtfVisualizer
|
||||
|
||||
QTC_LIB_DEPENDS += \
|
||||
utils \
|
||||
tracing
|
||||
|
||||
QTC_PLUGIN_DEPENDS += \
|
||||
debugger \
|
||||
qtsupport
|
58
src/plugins/ctfvisualizer/ctfvisualizerconstants.h
Normal file
58
src/plugins/ctfvisualizer/ctfvisualizerconstants.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Constants {
|
||||
|
||||
const char CtfVisualizerMenuId[] = "Analyzer.Menu.CtfVisualizer";
|
||||
const char CtfVisualizerTaskLoadJson[] =
|
||||
"Analyzer.Menu.StartAnalyzer.CtfVisualizer.LoadTrace";
|
||||
|
||||
const char CtfVisualizerPerspectiveId[] = "CtfVisualizer.Perspective";
|
||||
|
||||
const char CtfTraceEventsKey[] = "traceEvents";
|
||||
|
||||
const char CtfEventNameKey[] = "name";
|
||||
const char CtfEventCategoryKey[] = "cat";
|
||||
const char CtfEventPhaseKey[] = "ph";
|
||||
const char CtfTracingClockTimestampKey[] = "ts";
|
||||
const char CtfProcessIdKey[] = "pid";
|
||||
const char CtfThreadIdKey[] = "tid";
|
||||
const char CtfDurationKey[] = "dur";
|
||||
|
||||
const char CtfEventTypeBegin[] = "B";
|
||||
const char CtfEventTypeEnd[] = "E";
|
||||
const char CtfEventTypeComplete[] = "X";
|
||||
const char CtfEventTypeMetadata[] = "M";
|
||||
const char CtfEventTypeInstant[] = "i";
|
||||
const char CtfEventTypeInstantDeprecated[] = "I";
|
||||
const char CtfEventTypeCounter[] = "C";
|
||||
|
||||
} // namespace Constants
|
||||
} // namespace CtfVisualizer
|
58
src/plugins/ctfvisualizer/ctfvisualizerplugin.cpp
Normal file
58
src/plugins/ctfvisualizer/ctfvisualizerplugin.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "ctfvisualizerplugin.h"
|
||||
|
||||
#include "ctfvisualizertool.h"
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfVisualizerPluginPrivate
|
||||
{
|
||||
public:
|
||||
CtfVisualizerTool profilerTool;
|
||||
};
|
||||
|
||||
CtfVisualizerPlugin::~CtfVisualizerPlugin()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool CtfVisualizerPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
Q_UNUSED(errorString)
|
||||
|
||||
d = new CtfVisualizerPluginPrivate;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CtfVisualizerPlugin::extensionsInitialized()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
49
src/plugins/ctfvisualizer/ctfvisualizerplugin.h
Normal file
49
src/plugins/ctfvisualizer/ctfvisualizerplugin.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfVisualizerPlugin : public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CtfVisualizer.json")
|
||||
|
||||
public:
|
||||
~CtfVisualizerPlugin();
|
||||
|
||||
bool initialize(const QStringList &arguments, QString *errorString) final;
|
||||
void extensionsInitialized() final;
|
||||
|
||||
class CtfVisualizerPluginPrivate *d = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
187
src/plugins/ctfvisualizer/ctfvisualizertool.cpp
Normal file
187
src/plugins/ctfvisualizer/ctfvisualizertool.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "ctfvisualizertool.h"
|
||||
|
||||
#include "ctftracemanager.h"
|
||||
|
||||
#include "ctfstatisticsmodel.h"
|
||||
#include "ctfstatisticsview.h"
|
||||
#include "ctfvisualizertraceview.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <debugger/analyzer/analyzerconstants.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QFutureInterface>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
|
||||
using namespace Core;
|
||||
using namespace CtfVisualizer::Constants;
|
||||
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
CtfVisualizerTool::CtfVisualizerTool()
|
||||
: QObject (nullptr)
|
||||
, m_isLoading(false)
|
||||
, m_loadJson(nullptr)
|
||||
, m_traceView(nullptr)
|
||||
, m_modelAggregator(new Timeline::TimelineModelAggregator(this))
|
||||
, m_zoomControl(new Timeline::TimelineZoomControl(this))
|
||||
, m_statisticsModel(new CtfStatisticsModel(this))
|
||||
, m_statisticsView(nullptr)
|
||||
, m_traceManager(new CtfTraceManager(this, m_modelAggregator.get(), m_statisticsModel.get()))
|
||||
{
|
||||
ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER);
|
||||
ActionContainer *options = ActionManager::createMenu(Constants::CtfVisualizerMenuId);
|
||||
options->menu()->setTitle(tr("Chrome Trace Format Viewer"));
|
||||
menu->addMenu(options, Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
|
||||
options->menu()->setEnabled(true);
|
||||
|
||||
const Core::Context globalContext(Core::Constants::C_GLOBAL);
|
||||
|
||||
m_loadJson.reset(new QAction(tr("Load JSON File"), options));
|
||||
Core::Command *command = Core::ActionManager::registerAction(m_loadJson.get(), Constants::CtfVisualizerTaskLoadJson,
|
||||
globalContext);
|
||||
connect(m_loadJson.get(), &QAction::triggered, this, &CtfVisualizerTool::loadJson);
|
||||
options->addAction(command);
|
||||
|
||||
m_perspective.setAboutToActivateCallback([this]() { createViews(); });
|
||||
}
|
||||
|
||||
CtfVisualizerTool::~CtfVisualizerTool() = default;
|
||||
|
||||
void CtfVisualizerTool::createViews()
|
||||
{
|
||||
m_traceView = new CtfVisualizerTraceView(nullptr, this);
|
||||
m_traceView->setWindowTitle(tr("Timeline"));
|
||||
|
||||
QMenu *contextMenu = new QMenu(m_traceView);
|
||||
contextMenu->addAction(m_loadJson.get());
|
||||
connect(contextMenu->addAction(tr("Reset Zoom")), &QAction::triggered, this, [this](){
|
||||
m_zoomControl->setRange(m_zoomControl->traceStart(), m_zoomControl->traceEnd());
|
||||
});
|
||||
|
||||
m_traceView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_traceView, &QWidget::customContextMenuRequested,
|
||||
contextMenu, [contextMenu, this](const QPoint &pos) {
|
||||
contextMenu->exec(m_traceView->mapToGlobal(pos));
|
||||
});
|
||||
|
||||
m_perspective.addWindow(m_traceView, Utils::Perspective::OperationType::SplitVertical, nullptr);
|
||||
|
||||
m_statisticsView = new CtfStatisticsView(m_statisticsModel.get());
|
||||
m_statisticsView->setWindowTitle(tr("Statistics"));
|
||||
connect(m_statisticsView, &CtfStatisticsView::eventTypeSelected, [this] (QString title)
|
||||
{
|
||||
int typeId = m_traceManager->getSelectionId(title.toStdString());
|
||||
m_traceView->selectByTypeId(typeId);
|
||||
});
|
||||
connect(m_traceManager.get(), &CtfTraceManager::detailsRequested, m_statisticsView,
|
||||
&CtfStatisticsView::selectByTitle);
|
||||
|
||||
m_perspective.addWindow(m_statisticsView, Utils::Perspective::AddToTab, m_traceView);
|
||||
|
||||
m_perspective.setAboutToActivateCallback(Utils::Perspective::Callback());
|
||||
emit viewsCreated();
|
||||
}
|
||||
|
||||
Timeline::TimelineModelAggregator *CtfVisualizerTool::modelAggregator() const
|
||||
{
|
||||
return m_modelAggregator.get();
|
||||
}
|
||||
|
||||
CtfTraceManager *CtfVisualizerTool::traceManager() const
|
||||
{
|
||||
return m_traceManager.get();
|
||||
}
|
||||
|
||||
Timeline::TimelineZoomControl *CtfVisualizerTool::zoomControl() const
|
||||
{
|
||||
return m_zoomControl.get();
|
||||
}
|
||||
|
||||
void CtfVisualizerTool::loadJson()
|
||||
{
|
||||
if (m_isLoading)
|
||||
return;
|
||||
|
||||
m_isLoading = true;
|
||||
|
||||
QString filename = QFileDialog::getOpenFileName(
|
||||
ICore::mainWindow(), tr("Load Chrome Trace Format File"),
|
||||
"", tr("JSON File (*.json)"));
|
||||
if (filename.isEmpty()) {
|
||||
m_isLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
auto *futureInterface = new QFutureInterface<void>();
|
||||
auto *task = new QFuture<void>(futureInterface);
|
||||
|
||||
QThread *thread = QThread::create([this, filename, futureInterface]() {
|
||||
m_traceManager->load(filename);
|
||||
|
||||
m_modelAggregator->moveToThread(QApplication::instance()->thread());
|
||||
m_modelAggregator->setParent(this);
|
||||
futureInterface->reportFinished();
|
||||
});
|
||||
|
||||
connect(thread, &QThread::finished, this, [this, thread, task, futureInterface]() {
|
||||
// in main thread:
|
||||
if (m_traceManager->isEmpty()) {
|
||||
QMessageBox::warning(Core::ICore::mainWindow(),
|
||||
tr("CTF Visualizer"),
|
||||
tr("The file does not contain any trace data."));
|
||||
} else {
|
||||
m_traceManager->finalize();
|
||||
m_perspective.select();
|
||||
zoomControl()->setTrace(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20);
|
||||
zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20);
|
||||
}
|
||||
thread->deleteLater();
|
||||
delete task;
|
||||
delete futureInterface;
|
||||
m_isLoading = false;
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
m_modelAggregator->setParent(nullptr);
|
||||
m_modelAggregator->moveToThread(thread);
|
||||
|
||||
thread->start();
|
||||
Core::ProgressManager::addTask(*task, tr("Loading CTF File"), CtfVisualizerTaskLoadJson);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
86
src/plugins/ctfvisualizer/ctfvisualizertool.h
Normal file
86
src/plugins/ctfvisualizer/ctfvisualizertool.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ctfvisualizerconstants.h"
|
||||
|
||||
#include <debugger/debuggermainwindow.h>
|
||||
#include <tracing/timelinemodelaggregator.h>
|
||||
#include <tracing/timelinezoomcontrol.h>
|
||||
|
||||
#include <QScopedPointer>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfTraceManager;
|
||||
class CtfStatisticsModel;
|
||||
class CtfStatisticsView;
|
||||
class CtfVisualizerTraceView;
|
||||
|
||||
|
||||
class CtfVisualizerTool : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CtfVisualizerTool();
|
||||
~CtfVisualizerTool();
|
||||
|
||||
Timeline::TimelineModelAggregator *modelAggregator() const;
|
||||
CtfTraceManager *traceManager() const;
|
||||
Timeline::TimelineZoomControl *zoomControl() const;
|
||||
|
||||
void loadJson();
|
||||
|
||||
signals:
|
||||
void viewsCreated();
|
||||
|
||||
private:
|
||||
void createViews();
|
||||
|
||||
void initialize();
|
||||
void finalize();
|
||||
|
||||
Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId,
|
||||
tr("Chrome Trace Format Visualizer")};
|
||||
|
||||
bool m_isLoading;
|
||||
QScopedPointer<QAction> m_loadJson;
|
||||
|
||||
CtfVisualizerTraceView *m_traceView;
|
||||
const QScopedPointer<Timeline::TimelineModelAggregator> m_modelAggregator;
|
||||
const QScopedPointer<Timeline::TimelineZoomControl> m_zoomControl;
|
||||
|
||||
const QScopedPointer<CtfStatisticsModel> m_statisticsModel;
|
||||
CtfStatisticsView *m_statisticsView;
|
||||
|
||||
const QScopedPointer<CtfTraceManager> m_traceManager;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
85
src/plugins/ctfvisualizer/ctfvisualizertraceview.cpp
Normal file
85
src/plugins/ctfvisualizer/ctfvisualizertraceview.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "ctfvisualizertraceview.h"
|
||||
|
||||
#include "ctfvisualizertool.h"
|
||||
|
||||
#include <tracing/timelineformattime.h>
|
||||
#include <tracing/timelineoverviewrenderer.h>
|
||||
#include <tracing/timelinerenderer.h>
|
||||
#include <tracing/timelinetheme.h>
|
||||
|
||||
#include <QQmlContext>
|
||||
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
CtfVisualizerTraceView::CtfVisualizerTraceView(QWidget *parent, CtfVisualizerTool *tool)
|
||||
: QQuickWidget(parent)
|
||||
{
|
||||
setObjectName(QLatin1String("CtfVisualizerTraceView"));
|
||||
|
||||
qmlRegisterType<Timeline::TimelineRenderer>("TimelineRenderer", 1, 0, "TimelineRenderer");
|
||||
qmlRegisterType<Timeline::TimelineOverviewRenderer>("TimelineOverviewRenderer", 1, 0,
|
||||
"TimelineOverviewRenderer");
|
||||
qmlRegisterType<Timeline::TimelineZoomControl>();
|
||||
qmlRegisterType<Timeline::TimelineModel>();
|
||||
qmlRegisterType<Timeline::TimelineNotesModel>();
|
||||
|
||||
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
// Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
|
||||
setMinimumHeight(170);
|
||||
|
||||
Timeline::TimelineTheme::setupTheme(engine());
|
||||
Timeline::TimeFormatter::setupTimeFormatter();
|
||||
|
||||
rootContext()->setContextProperty(QLatin1String("timelineModelAggregator"),
|
||||
tool->modelAggregator());
|
||||
rootContext()->setContextProperty(QLatin1String("zoomControl"),
|
||||
tool->zoomControl());
|
||||
setSource(QUrl(QLatin1String("qrc:/tracing/MainView.qml")));
|
||||
|
||||
// Avoid ugly warnings when reading from null properties in QML.
|
||||
connect(tool->modelAggregator(), &QObject::destroyed, this, [this]{ setSource(QUrl()); });
|
||||
connect(tool->zoomControl(), &QObject::destroyed, this, [this]{ setSource(QUrl()); });
|
||||
}
|
||||
|
||||
CtfVisualizerTraceView::~CtfVisualizerTraceView() = default;
|
||||
|
||||
void CtfVisualizerTraceView::selectByTypeId(int typeId)
|
||||
{
|
||||
QMetaObject::invokeMethod(rootObject(), "selectByTypeId",
|
||||
Q_ARG(QVariant,QVariant::fromValue<int>(typeId)));
|
||||
}
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
||||
|
51
src/plugins/ctfvisualizer/ctfvisualizertraceview.h
Normal file
51
src/plugins/ctfvisualizer/ctfvisualizertraceview.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company,
|
||||
** info@kdab.com, author Tim Henning <tim.henning@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <QQuickWidget>
|
||||
#include <QWidget>
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
class CtfVisualizerTool;
|
||||
|
||||
class CtfVisualizerTraceView : public QQuickWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CtfVisualizerTraceView(QWidget *parent, CtfVisualizerTool *tool);
|
||||
|
||||
~CtfVisualizerTraceView();
|
||||
|
||||
void selectByTypeId(int typeId);
|
||||
};
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CtfVisualizer
|
||||
|
@@ -71,9 +71,9 @@ qtHaveModule(serialport) {
|
||||
}
|
||||
|
||||
qtHaveModule(quick) {
|
||||
SUBDIRS += qmlprofiler perfprofiler
|
||||
SUBDIRS += qmlprofiler perfprofiler ctfvisualizer
|
||||
} else {
|
||||
warning("QmlProfiler and PerfProfiler plugins have been disabled since the Qt Quick module is not available.")
|
||||
warning("QmlProfiler, PerfProfiler and CTF Visualizer plugins have been disabled since the Qt Quick module is not available.")
|
||||
}
|
||||
|
||||
qtHaveModule(help) {
|
||||
|
Reference in New Issue
Block a user