forked from qt-creator/qt-creator
Tracing: Generalize event filtering
This allows us to not only filter by ranges, but potentially also by other criteria. Change-Id: I7349ceeabbb2781473a3a4c803dab1006b7b8e50 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -365,7 +365,7 @@ void TimelineTraceManager::clearAll()
|
|||||||
clearTypeStorage();
|
clearTypeStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime)
|
void TimelineTraceManager::restrictByFilter(TraceEventFilter filter)
|
||||||
{
|
{
|
||||||
if (d->notesModel)
|
if (d->notesModel)
|
||||||
d->notesModel->stash();
|
d->notesModel->stash();
|
||||||
@@ -374,9 +374,8 @@ void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime)
|
|||||||
setVisibleFeatures(0);
|
setVisibleFeatures(0);
|
||||||
|
|
||||||
QFutureInterface<void> future;
|
QFutureInterface<void> future;
|
||||||
replayEvents(startTime, endTime,
|
replayEvents(filter(std::bind(&TimelineTraceManagerPrivate::dispatch, d,
|
||||||
std::bind(&TimelineTraceManagerPrivate::dispatch, d, std::placeholders::_1,
|
std::placeholders::_1, std::placeholders::_2)),
|
||||||
std::placeholders::_2),
|
|
||||||
[this]() {
|
[this]() {
|
||||||
initialize();
|
initialize();
|
||||||
}, [this]() {
|
}, [this]() {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class TRACING_EXPORT TimelineTraceManager : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(const TraceEvent &, const TraceEventType &)> TraceEventLoader;
|
typedef std::function<void(const TraceEvent &, const TraceEventType &)> TraceEventLoader;
|
||||||
|
typedef std::function<TraceEventLoader(TraceEventLoader)> TraceEventFilter;
|
||||||
typedef std::function<void()> Initializer;
|
typedef std::function<void()> Initializer;
|
||||||
typedef std::function<void()> Finalizer;
|
typedef std::function<void()> Finalizer;
|
||||||
typedef std::function<void()> Clearer;
|
typedef std::function<void()> Clearer;
|
||||||
@@ -99,15 +100,14 @@ protected:
|
|||||||
virtual void clearEventStorage();
|
virtual void clearEventStorage();
|
||||||
virtual void clearTypeStorage();
|
virtual void clearTypeStorage();
|
||||||
|
|
||||||
void restrictToRange(qint64 startTime, qint64 endTime);
|
void restrictByFilter(TraceEventFilter filter);
|
||||||
|
|
||||||
void addEvent(const TraceEvent &event);
|
void addEvent(const TraceEvent &event);
|
||||||
void addEventType(const TraceEventType &type);
|
void addEventType(const TraceEventType &type);
|
||||||
virtual const TraceEventType &lookupType(int typeId) const = 0;
|
virtual const TraceEventType &lookupType(int typeId) const = 0;
|
||||||
|
|
||||||
virtual TimelineTraceFile *createTraceFile() = 0;
|
virtual TimelineTraceFile *createTraceFile() = 0;
|
||||||
virtual void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader,
|
virtual void replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer,
|
||||||
Initializer initializer, Finalizer finalizer,
|
|
||||||
ErrorHandler errorHandler, QFutureInterface<void> &future) const = 0;
|
ErrorHandler errorHandler, QFutureInterface<void> &future) const = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -163,9 +163,10 @@ void FlameGraphModel::restrictToFeatures(quint64 visibleFeatures)
|
|||||||
clear();
|
clear();
|
||||||
|
|
||||||
QFutureInterface<void> future;
|
QFutureInterface<void> future;
|
||||||
m_modelManager->replayQmlEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(),
|
const auto filter = m_modelManager->rangeFilter(m_modelManager->traceStart(),
|
||||||
std::bind(&FlameGraphModel::loadEvent, this,
|
m_modelManager->traceEnd());
|
||||||
std::placeholders::_1, std::placeholders::_2),
|
m_modelManager->replayQmlEvents(filter(std::bind(&FlameGraphModel::loadEvent, this,
|
||||||
|
std::placeholders::_1, std::placeholders::_2)),
|
||||||
std::bind(&FlameGraphModel::beginResetModel, this),
|
std::bind(&FlameGraphModel::beginResetModel, this),
|
||||||
std::bind(&FlameGraphModel::finalize, this),
|
std::bind(&FlameGraphModel::finalize, this),
|
||||||
[this](const QString &message) {
|
[this](const QString &message) {
|
||||||
|
|||||||
@@ -146,13 +146,12 @@ const QmlEventType &QmlProfilerModelManager::eventType(int typeId) const
|
|||||||
return d->eventTypes.at(typeId);
|
return d->eventTypes.at(typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd,
|
void QmlProfilerModelManager::replayEvents(TraceEventLoader loader, Initializer initializer,
|
||||||
TraceEventLoader loader, Initializer initializer,
|
|
||||||
Finalizer finalizer, ErrorHandler errorHandler,
|
Finalizer finalizer, ErrorHandler errorHandler,
|
||||||
QFutureInterface<void> &future) const
|
QFutureInterface<void> &future) const
|
||||||
{
|
{
|
||||||
replayQmlEvents(rangeStart, rangeEnd, static_cast<QmlEventLoader>(loader), initializer,
|
replayQmlEvents(static_cast<QmlEventLoader>(loader), initializer, finalizer, errorHandler,
|
||||||
finalizer, errorHandler, future);
|
future);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isStateful(const QmlEventType &type)
|
static bool isStateful(const QmlEventType &type)
|
||||||
@@ -164,81 +163,19 @@ static bool isStateful(const QmlEventType &type)
|
|||||||
return message == PixmapCacheEvent || message == MemoryAllocation;
|
return message == PixmapCacheEvent || message == MemoryAllocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerModelManager::replayQmlEvents(qint64 rangeStart, qint64 rangeEnd,
|
void QmlProfilerModelManager::replayQmlEvents(QmlEventLoader loader,
|
||||||
QmlEventLoader loader, Initializer initializer,
|
Initializer initializer, Finalizer finalizer,
|
||||||
Finalizer finalizer, ErrorHandler errorHandler,
|
ErrorHandler errorHandler,
|
||||||
QFutureInterface<void> &future) const
|
QFutureInterface<void> &future) const
|
||||||
{
|
{
|
||||||
if (initializer)
|
if (initializer)
|
||||||
initializer();
|
initializer();
|
||||||
|
|
||||||
QStack<QmlEvent> stack;
|
|
||||||
bool crossedRangeStart = false;
|
|
||||||
|
|
||||||
const auto result = d->file.replay([&](const QmlEvent &event) {
|
const auto result = d->file.replay([&](const QmlEvent &event) {
|
||||||
if (future.isCanceled())
|
if (future.isCanceled())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const QmlEventType &type = d->eventTypes[event.typeIndex()];
|
loader(event, d->eventTypes[event.typeIndex()]);
|
||||||
|
|
||||||
// No restrictions: load all events
|
|
||||||
if (rangeStart == -1 || rangeEnd == -1) {
|
|
||||||
loader(event, type);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double-check if rangeStart has been crossed. Some versions of Qt send dirty data.
|
|
||||||
qint64 adjustedTimestamp = event.timestamp();
|
|
||||||
if (event.timestamp() < rangeStart && !crossedRangeStart) {
|
|
||||||
if (type.rangeType() != MaximumRangeType) {
|
|
||||||
if (event.rangeStage() == RangeStart)
|
|
||||||
stack.push(event);
|
|
||||||
else if (event.rangeStage() == RangeEnd)
|
|
||||||
stack.pop();
|
|
||||||
return true;
|
|
||||||
} else if (isStateful(type)) {
|
|
||||||
adjustedTimestamp = rangeStart;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!crossedRangeStart) {
|
|
||||||
for (QmlEvent stashed : stack) {
|
|
||||||
stashed.setTimestamp(rangeStart);
|
|
||||||
loader(stashed, d->eventTypes[stashed.typeIndex()]);
|
|
||||||
}
|
|
||||||
stack.clear();
|
|
||||||
crossedRangeStart = true;
|
|
||||||
}
|
|
||||||
if (event.timestamp() > rangeEnd) {
|
|
||||||
if (type.rangeType() != MaximumRangeType) {
|
|
||||||
if (event.rangeStage() == RangeEnd) {
|
|
||||||
if (stack.isEmpty()) {
|
|
||||||
QmlEvent endEvent(event);
|
|
||||||
endEvent.setTimestamp(rangeEnd);
|
|
||||||
loader(endEvent, d->eventTypes[event.typeIndex()]);
|
|
||||||
} else {
|
|
||||||
stack.pop();
|
|
||||||
}
|
|
||||||
} else if (event.rangeStage() == RangeStart) {
|
|
||||||
stack.push(event);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (isStateful(type)) {
|
|
||||||
adjustedTimestamp = rangeEnd;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adjustedTimestamp != event.timestamp()) {
|
|
||||||
QmlEvent adjusted(event);
|
|
||||||
adjusted.setTimestamp(adjustedTimestamp);
|
|
||||||
loader(adjusted, type);
|
|
||||||
} else {
|
|
||||||
loader(event, type);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -370,6 +307,22 @@ void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newStrin
|
|||||||
emit typeDetailsChanged(typeId);
|
emit typeDetailsChanged(typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QmlProfilerModelManager::restrictByFilter(QmlProfilerModelManager::QmlEventFilter filter)
|
||||||
|
{
|
||||||
|
return Timeline::TimelineTraceManager::restrictByFilter([filter](TraceEventLoader loader) {
|
||||||
|
const auto filteredQmlLoader = filter([loader](const QmlEvent &event,
|
||||||
|
const QmlEventType &type) {
|
||||||
|
loader(event, type);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [filteredQmlLoader](const Timeline::TraceEvent &event,
|
||||||
|
const Timeline::TraceEventType &type) {
|
||||||
|
filteredQmlLoader(static_cast<const QmlEvent &>(event),
|
||||||
|
static_cast<const QmlEventType &>(type));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const Timeline::TraceEventType &QmlProfilerModelManager::lookupType(int typeIndex) const
|
const Timeline::TraceEventType &QmlProfilerModelManager::lookupType(int typeIndex) const
|
||||||
{
|
{
|
||||||
return eventType(typeIndex);
|
return eventType(typeIndex);
|
||||||
@@ -404,7 +357,7 @@ void QmlProfilerModelManager::addEvent(const QmlEvent &event)
|
|||||||
void QmlProfilerModelManager::restrictToRange(qint64 start, qint64 end)
|
void QmlProfilerModelManager::restrictToRange(qint64 start, qint64 end)
|
||||||
{
|
{
|
||||||
d->isRestrictedToRange = (start != -1 || end != -1);
|
d->isRestrictedToRange = (start != -1 || end != -1);
|
||||||
TimelineTraceManager::restrictToRange(start, end);
|
restrictByFilter(rangeFilter(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QmlProfilerModelManager::isRestrictedToRange() const
|
bool QmlProfilerModelManager::isRestrictedToRange() const
|
||||||
@@ -412,6 +365,78 @@ bool QmlProfilerModelManager::isRestrictedToRange() const
|
|||||||
return d->isRestrictedToRange;
|
return d->isRestrictedToRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QmlProfilerModelManager::QmlEventFilter
|
||||||
|
QmlProfilerModelManager::rangeFilter(qint64 rangeStart, qint64 rangeEnd) const
|
||||||
|
{
|
||||||
|
return [rangeStart, rangeEnd, this] (QmlEventLoader loader) {
|
||||||
|
QStack<QmlEvent> stack;
|
||||||
|
bool crossedRangeStart = false;
|
||||||
|
|
||||||
|
return [=](const QmlEvent &event, const QmlEventType &type) mutable {
|
||||||
|
|
||||||
|
// No restrictions: load all events
|
||||||
|
if (rangeStart == -1 || rangeEnd == -1) {
|
||||||
|
loader(event, type);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-check if rangeStart has been crossed. Some versions of Qt send dirty data.
|
||||||
|
qint64 adjustedTimestamp = event.timestamp();
|
||||||
|
if (event.timestamp() < rangeStart && !crossedRangeStart) {
|
||||||
|
if (type.rangeType() != MaximumRangeType) {
|
||||||
|
if (event.rangeStage() == RangeStart)
|
||||||
|
stack.push(event);
|
||||||
|
else if (event.rangeStage() == RangeEnd)
|
||||||
|
stack.pop();
|
||||||
|
return true;
|
||||||
|
} else if (isStateful(type)) {
|
||||||
|
adjustedTimestamp = rangeStart;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!crossedRangeStart) {
|
||||||
|
for (auto stashed : stack) {
|
||||||
|
stashed.setTimestamp(rangeStart);
|
||||||
|
loader(stashed, eventType(stashed.typeIndex()));
|
||||||
|
}
|
||||||
|
stack.clear();
|
||||||
|
crossedRangeStart = true;
|
||||||
|
}
|
||||||
|
if (event.timestamp() > rangeEnd) {
|
||||||
|
if (type.rangeType() != MaximumRangeType) {
|
||||||
|
if (event.rangeStage() == RangeEnd) {
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
QmlEvent endEvent(event);
|
||||||
|
endEvent.setTimestamp(rangeEnd);
|
||||||
|
loader(endEvent, type);
|
||||||
|
} else {
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
} else if (event.rangeStage() == RangeStart) {
|
||||||
|
stack.push(event);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (isStateful(type)) {
|
||||||
|
adjustedTimestamp = rangeEnd;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adjustedTimestamp != event.timestamp()) {
|
||||||
|
QmlEvent adjusted(event);
|
||||||
|
adjusted.setTimestamp(adjustedTimestamp);
|
||||||
|
loader(adjusted, type);
|
||||||
|
} else {
|
||||||
|
loader(event, type);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Timeline::TimelineTraceFile *QmlProfilerModelManager::createTraceFile()
|
Timeline::TimelineTraceFile *QmlProfilerModelManager::createTraceFile()
|
||||||
{
|
{
|
||||||
return new Internal::QmlProfilerTraceFile(this);
|
return new Internal::QmlProfilerTraceFile(this);
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class QMLPROFILER_EXPORT QmlProfilerModelManager : public Timeline::TimelineTrac
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(const QmlEvent &, const QmlEventType &)> QmlEventLoader;
|
typedef std::function<void(const QmlEvent &, const QmlEventType &)> QmlEventLoader;
|
||||||
|
typedef std::function<QmlEventLoader(QmlEventLoader)> QmlEventFilter;
|
||||||
|
|
||||||
explicit QmlProfilerModelManager(QObject *parent = nullptr);
|
explicit QmlProfilerModelManager(QObject *parent = nullptr);
|
||||||
~QmlProfilerModelManager() override;
|
~QmlProfilerModelManager() override;
|
||||||
@@ -64,8 +65,7 @@ public:
|
|||||||
void addEventTypes(const QVector<QmlEventType> &types);
|
void addEventTypes(const QVector<QmlEventType> &types);
|
||||||
const QmlEventType &eventType(int typeId) const;
|
const QmlEventType &eventType(int typeId) const;
|
||||||
|
|
||||||
void replayQmlEvents(qint64 rangeStart, qint64 rangeEnd, QmlEventLoader loader,
|
void replayQmlEvents(QmlEventLoader loader, Initializer initializer, Finalizer finalizer,
|
||||||
Initializer initializer, Finalizer finalizer,
|
|
||||||
ErrorHandler errorHandler, QFutureInterface<void> &future) const;
|
ErrorHandler errorHandler, QFutureInterface<void> &future) const;
|
||||||
|
|
||||||
void finalize() override;
|
void finalize() override;
|
||||||
@@ -81,20 +81,22 @@ public:
|
|||||||
void restrictToRange(qint64 start, qint64 end);
|
void restrictToRange(qint64 start, qint64 end);
|
||||||
bool isRestrictedToRange() const;
|
bool isRestrictedToRange() const;
|
||||||
|
|
||||||
|
QmlEventFilter rangeFilter(qint64 start, qint64 end) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void typeDetailsChanged(int typeId);
|
void typeDetailsChanged(int typeId);
|
||||||
void typeDetailsFinished();
|
void typeDetailsFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void detailsChanged(int typeId, const QString &newString);
|
void detailsChanged(int typeId, const QString &newString);
|
||||||
|
void restrictByFilter(QmlEventFilter filter);
|
||||||
|
|
||||||
void clearEventStorage() override;
|
void clearEventStorage() override;
|
||||||
void clearTypeStorage() override;
|
void clearTypeStorage() override;
|
||||||
|
|
||||||
const Timeline::TraceEventType &lookupType(int typeId) const override;
|
const Timeline::TraceEventType &lookupType(int typeId) const override;
|
||||||
Timeline::TimelineTraceFile *createTraceFile() override;
|
Timeline::TimelineTraceFile *createTraceFile() override;
|
||||||
void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader,
|
void replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer,
|
||||||
Initializer initializer, Finalizer finalizer,
|
|
||||||
ErrorHandler errorHandler, QFutureInterface<void> &future) const override;
|
ErrorHandler errorHandler, QFutureInterface<void> &future) const override;
|
||||||
|
|
||||||
class QmlProfilerModelManagerPrivate;
|
class QmlProfilerModelManagerPrivate;
|
||||||
|
|||||||
@@ -102,9 +102,10 @@ void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features)
|
|||||||
|
|
||||||
clear();
|
clear();
|
||||||
QFutureInterface<void> future;
|
QFutureInterface<void> future;
|
||||||
m_modelManager->replayQmlEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(),
|
auto filter = m_modelManager->rangeFilter(m_modelManager->traceStart(),
|
||||||
std::bind(&QmlProfilerStatisticsModel::loadEvent, this,
|
m_modelManager->traceEnd());
|
||||||
std::placeholders::_1, std::placeholders::_2),
|
m_modelManager->replayQmlEvents(filter(std::bind(&QmlProfilerStatisticsModel::loadEvent, this,
|
||||||
|
std::placeholders::_1, std::placeholders::_2)),
|
||||||
std::bind(&QmlProfilerStatisticsModel::beginResetModel, this),
|
std::bind(&QmlProfilerStatisticsModel::beginResetModel, this),
|
||||||
[this]() {
|
[this]() {
|
||||||
finalize();
|
finalize();
|
||||||
|
|||||||
@@ -712,7 +712,7 @@ void QmlProfilerTraceFile::saveQtd(QIODevice *device)
|
|||||||
|
|
||||||
QStack<QmlEvent> stack;
|
QStack<QmlEvent> stack;
|
||||||
qint64 lastProgressTimestamp = traceStart();
|
qint64 lastProgressTimestamp = traceStart();
|
||||||
modelManager()->replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
|
modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) {
|
||||||
if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) {
|
if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) {
|
||||||
stack.push(event);
|
stack.push(event);
|
||||||
return;
|
return;
|
||||||
@@ -858,7 +858,7 @@ void QmlProfilerTraceFile::saveQzt(QIODevice *device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qint64 lastProgressTimestamp = traceStart();
|
qint64 lastProgressTimestamp = traceStart();
|
||||||
modelManager()->replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
|
modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) {
|
||||||
Q_UNUSED(type);
|
Q_UNUSED(type);
|
||||||
bufferStream << event;
|
bufferStream << event;
|
||||||
// 32MB buffer should be plenty for efficient compression
|
// 32MB buffer should be plenty for efficient compression
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ void QmlProfilerTraceClientTest::testMessageReceived()
|
|||||||
traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected);
|
traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected);
|
||||||
|
|
||||||
QFutureInterface<void> future;
|
QFutureInterface<void> future;
|
||||||
modelManager.replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
|
modelManager.replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) {
|
||||||
qint64 timestamp;
|
qint64 timestamp;
|
||||||
qint32 message;
|
qint32 message;
|
||||||
qint32 rangeType;
|
qint32 rangeType;
|
||||||
|
|||||||
Reference in New Issue
Block a user