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();
|
||||
}
|
||||
|
||||
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]() {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user