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:
Tim Henning
2019-08-29 11:45:45 +02:00
parent 41361047b4
commit 7fec418205
22 changed files with 22816 additions and 2 deletions

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View 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
}

View 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

View 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

View 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 &current, 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,9 @@
QTC_PLUGIN_NAME = CtfVisualizer
QTC_LIB_DEPENDS += \
utils \
tracing
QTC_PLUGIN_DEPENDS += \
debugger \
qtsupport

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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) {