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:
Ulf Hermann
2018-05-07 17:08:12 +02:00
parent 7e9b6f62c6
commit 95d7be1b28
8 changed files with 119 additions and 91 deletions

View File

@@ -365,7 +365,7 @@ void TimelineTraceManager::clearAll()
clearTypeStorage();
}
void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime)
void TimelineTraceManager::restrictByFilter(TraceEventFilter filter)
{
if (d->notesModel)
d->notesModel->stash();
@@ -374,9 +374,8 @@ void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime)
setVisibleFeatures(0);
QFutureInterface<void> future;
replayEvents(startTime, endTime,
std::bind(&TimelineTraceManagerPrivate::dispatch, d, std::placeholders::_1,
std::placeholders::_2),
replayEvents(filter(std::bind(&TimelineTraceManagerPrivate::dispatch, d,
std::placeholders::_1, std::placeholders::_2)),
[this]() {
initialize();
}, [this]() {

View File

@@ -43,6 +43,7 @@ class TRACING_EXPORT TimelineTraceManager : public QObject
Q_OBJECT
public:
typedef std::function<void(const TraceEvent &, const TraceEventType &)> TraceEventLoader;
typedef std::function<TraceEventLoader(TraceEventLoader)> TraceEventFilter;
typedef std::function<void()> Initializer;
typedef std::function<void()> Finalizer;
typedef std::function<void()> Clearer;
@@ -99,15 +100,14 @@ protected:
virtual void clearEventStorage();
virtual void clearTypeStorage();
void restrictToRange(qint64 startTime, qint64 endTime);
void restrictByFilter(TraceEventFilter filter);
void addEvent(const TraceEvent &event);
void addEventType(const TraceEventType &type);
virtual const TraceEventType &lookupType(int typeId) const = 0;
virtual TimelineTraceFile *createTraceFile() = 0;
virtual void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader,
Initializer initializer, Finalizer finalizer,
virtual void replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler, QFutureInterface<void> &future) const = 0;
private:

View File

@@ -163,9 +163,10 @@ void FlameGraphModel::restrictToFeatures(quint64 visibleFeatures)
clear();
QFutureInterface<void> future;
m_modelManager->replayQmlEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(),
std::bind(&FlameGraphModel::loadEvent, this,
std::placeholders::_1, std::placeholders::_2),
const auto filter = m_modelManager->rangeFilter(m_modelManager->traceStart(),
m_modelManager->traceEnd());
m_modelManager->replayQmlEvents(filter(std::bind(&FlameGraphModel::loadEvent, this,
std::placeholders::_1, std::placeholders::_2)),
std::bind(&FlameGraphModel::beginResetModel, this),
std::bind(&FlameGraphModel::finalize, this),
[this](const QString &message) {

View File

@@ -146,13 +146,12 @@ const QmlEventType &QmlProfilerModelManager::eventType(int typeId) const
return d->eventTypes.at(typeId);
}
void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd,
TraceEventLoader loader, Initializer initializer,
void QmlProfilerModelManager::replayEvents(TraceEventLoader loader, Initializer initializer,
Finalizer finalizer, ErrorHandler errorHandler,
QFutureInterface<void> &future) const
{
replayQmlEvents(rangeStart, rangeEnd, static_cast<QmlEventLoader>(loader), initializer,
finalizer, errorHandler, future);
replayQmlEvents(static_cast<QmlEventLoader>(loader), initializer, finalizer, errorHandler,
future);
}
static bool isStateful(const QmlEventType &type)
@@ -164,81 +163,19 @@ static bool isStateful(const QmlEventType &type)
return message == PixmapCacheEvent || message == MemoryAllocation;
}
void QmlProfilerModelManager::replayQmlEvents(qint64 rangeStart, qint64 rangeEnd,
QmlEventLoader loader, Initializer initializer,
Finalizer finalizer, ErrorHandler errorHandler,
void QmlProfilerModelManager::replayQmlEvents(QmlEventLoader loader,
Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler,
QFutureInterface<void> &future) const
{
if (initializer)
initializer();
QStack<QmlEvent> stack;
bool crossedRangeStart = false;
const auto result = d->file.replay([&](const QmlEvent &event) {
if (future.isCanceled())
return false;
const QmlEventType &type = 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);
}
loader(event, d->eventTypes[event.typeIndex()]);
return true;
});
@@ -370,6 +307,22 @@ void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newStrin
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
{
return eventType(typeIndex);
@@ -404,7 +357,7 @@ void QmlProfilerModelManager::addEvent(const QmlEvent &event)
void QmlProfilerModelManager::restrictToRange(qint64 start, qint64 end)
{
d->isRestrictedToRange = (start != -1 || end != -1);
TimelineTraceManager::restrictToRange(start, end);
restrictByFilter(rangeFilter(start, end));
}
bool QmlProfilerModelManager::isRestrictedToRange() const
@@ -412,6 +365,78 @@ bool QmlProfilerModelManager::isRestrictedToRange() const
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()
{
return new Internal::QmlProfilerTraceFile(this);

View File

@@ -50,6 +50,7 @@ class QMLPROFILER_EXPORT QmlProfilerModelManager : public Timeline::TimelineTrac
Q_OBJECT
public:
typedef std::function<void(const QmlEvent &, const QmlEventType &)> QmlEventLoader;
typedef std::function<QmlEventLoader(QmlEventLoader)> QmlEventFilter;
explicit QmlProfilerModelManager(QObject *parent = nullptr);
~QmlProfilerModelManager() override;
@@ -64,8 +65,7 @@ public:
void addEventTypes(const QVector<QmlEventType> &types);
const QmlEventType &eventType(int typeId) const;
void replayQmlEvents(qint64 rangeStart, qint64 rangeEnd, QmlEventLoader loader,
Initializer initializer, Finalizer finalizer,
void replayQmlEvents(QmlEventLoader loader, Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler, QFutureInterface<void> &future) const;
void finalize() override;
@@ -81,20 +81,22 @@ public:
void restrictToRange(qint64 start, qint64 end);
bool isRestrictedToRange() const;
QmlEventFilter rangeFilter(qint64 start, qint64 end) const;
signals:
void typeDetailsChanged(int typeId);
void typeDetailsFinished();
private:
void detailsChanged(int typeId, const QString &newString);
void restrictByFilter(QmlEventFilter filter);
void clearEventStorage() override;
void clearTypeStorage() override;
const Timeline::TraceEventType &lookupType(int typeId) const override;
Timeline::TimelineTraceFile *createTraceFile() override;
void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader,
Initializer initializer, Finalizer finalizer,
void replayEvents(TraceEventLoader loader, Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler, QFutureInterface<void> &future) const override;
class QmlProfilerModelManagerPrivate;

View File

@@ -102,9 +102,10 @@ void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features)
clear();
QFutureInterface<void> future;
m_modelManager->replayQmlEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(),
std::bind(&QmlProfilerStatisticsModel::loadEvent, this,
std::placeholders::_1, std::placeholders::_2),
auto filter = m_modelManager->rangeFilter(m_modelManager->traceStart(),
m_modelManager->traceEnd());
m_modelManager->replayQmlEvents(filter(std::bind(&QmlProfilerStatisticsModel::loadEvent, this,
std::placeholders::_1, std::placeholders::_2)),
std::bind(&QmlProfilerStatisticsModel::beginResetModel, this),
[this]() {
finalize();

View File

@@ -712,7 +712,7 @@ void QmlProfilerTraceFile::saveQtd(QIODevice *device)
QStack<QmlEvent> stack;
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) {
stack.push(event);
return;
@@ -858,7 +858,7 @@ void QmlProfilerTraceFile::saveQzt(QIODevice *device)
}
qint64 lastProgressTimestamp = traceStart();
modelManager()->replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) {
Q_UNUSED(type);
bufferStream << event;
// 32MB buffer should be plenty for efficient compression

View File

@@ -70,7 +70,7 @@ void QmlProfilerTraceClientTest::testMessageReceived()
traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected);
QFutureInterface<void> future;
modelManager.replayQmlEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
modelManager.replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) {
qint64 timestamp;
qint32 message;
qint32 rangeType;