forked from qt-creator/qt-creator
QmlProfiler: Move type resolution logic into trace client
This way we will be able to use server-provided type IDs for more efficient lookup of event types. Change-Id: I37cd592a7829e5f36c6cfc04e400013d1dc37155 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
@@ -43,13 +43,9 @@ class QmlProfilerDataModel::QmlProfilerDataModelPrivate
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void rewriteType(int typeIndex);
|
void rewriteType(int typeIndex);
|
||||||
int resolveType(const QmlEventType &type);
|
|
||||||
int resolveStackTop();
|
int resolveStackTop();
|
||||||
|
|
||||||
QVector<QmlEventType> eventTypes;
|
QVector<QmlEventType> eventTypes;
|
||||||
QHash<QmlEventType, int> eventTypeIds;
|
|
||||||
|
|
||||||
QStack<QmlTypedEvent> rangesInProgress;
|
|
||||||
|
|
||||||
QmlProfilerModelManager *modelManager;
|
QmlProfilerModelManager *modelManager;
|
||||||
int modelId;
|
int modelId;
|
||||||
@@ -141,8 +137,15 @@ void QmlProfilerDataModel::setEventTypes(const QVector<QmlEventType> &types)
|
|||||||
{
|
{
|
||||||
Q_D(QmlProfilerDataModel);
|
Q_D(QmlProfilerDataModel);
|
||||||
d->eventTypes = types;
|
d->eventTypes = types;
|
||||||
for (int id = 0; id < types.count(); ++id)
|
}
|
||||||
d->eventTypeIds[types[id]] = id;
|
|
||||||
|
int QmlProfilerDataModel::addEventType(const QmlEventType &type)
|
||||||
|
{
|
||||||
|
Q_D(QmlProfilerDataModel);
|
||||||
|
int typeIndex = d->eventTypes.length();
|
||||||
|
d->eventTypes.append(type);
|
||||||
|
d->rewriteType(typeIndex);
|
||||||
|
return typeIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerDataModel::addEvent(const QmlEvent &event)
|
void QmlProfilerDataModel::addEvent(const QmlEvent &event)
|
||||||
@@ -159,8 +162,6 @@ void QmlProfilerDataModel::clear()
|
|||||||
d->file.open();
|
d->file.open();
|
||||||
d->eventStream.setDevice(&d->file);
|
d->eventStream.setDevice(&d->file);
|
||||||
d->eventTypes.clear();
|
d->eventTypes.clear();
|
||||||
d->eventTypeIds.clear();
|
|
||||||
d->rangesInProgress.clear();
|
|
||||||
d->detailsRewriter->clearRequests();
|
d->detailsRewriter->clearRequests();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,26 +171,6 @@ bool QmlProfilerDataModel::isEmpty() const
|
|||||||
return d->file.pos() == 0;
|
return d->file.pos() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static uint qHash(const QmlEventType &type)
|
|
||||||
{
|
|
||||||
return qHash(type.location.filename) ^
|
|
||||||
((type.location.line & 0xfff) | // 12 bits of line number
|
|
||||||
((type.message << 12) & 0xf000) | // 4 bits of message
|
|
||||||
((type.location.column << 16) & 0xff0000) | // 8 bits of column
|
|
||||||
((type.rangeType << 24) & 0xf000000) | // 4 bits of rangeType
|
|
||||||
((type.detailType << 28) & 0xf0000000)); // 4 bits of detailType
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static bool operator==(const QmlEventType &type1,
|
|
||||||
const QmlEventType &type2)
|
|
||||||
{
|
|
||||||
return type1.message == type2.message && type1.rangeType == type2.rangeType &&
|
|
||||||
type1.detailType == type2.detailType && type1.location.line == type2.location.line &&
|
|
||||||
type1.location.column == type2.location.column &&
|
|
||||||
// compare filename last as it's expensive.
|
|
||||||
type1.location.filename == type2.location.filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerDataModel::QmlProfilerDataModelPrivate::rewriteType(int typeIndex)
|
void QmlProfilerDataModel::QmlProfilerDataModelPrivate::rewriteType(int typeIndex)
|
||||||
{
|
{
|
||||||
QmlEventType &type = eventTypes[typeIndex];
|
QmlEventType &type = eventTypes[typeIndex];
|
||||||
@@ -207,80 +188,6 @@ void QmlProfilerDataModel::QmlProfilerDataModelPrivate::rewriteType(int typeInde
|
|||||||
detailsRewriter->requestDetailsForLocation(typeIndex, type.location);
|
detailsRewriter->requestDetailsForLocation(typeIndex, type.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
int QmlProfilerDataModel::QmlProfilerDataModelPrivate::resolveType(const QmlEventType &type)
|
|
||||||
{
|
|
||||||
QHash<QmlEventType, int>::ConstIterator it = eventTypeIds.constFind(type);
|
|
||||||
|
|
||||||
int typeIndex = -1;
|
|
||||||
if (it != eventTypeIds.constEnd()) {
|
|
||||||
typeIndex = it.value();
|
|
||||||
} else {
|
|
||||||
typeIndex = eventTypes.size();
|
|
||||||
eventTypeIds[type] = typeIndex;
|
|
||||||
eventTypes.append(type);
|
|
||||||
rewriteType(typeIndex);
|
|
||||||
}
|
|
||||||
return typeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
int QmlProfilerDataModel::QmlProfilerDataModelPrivate::resolveStackTop()
|
|
||||||
{
|
|
||||||
if (rangesInProgress.isEmpty())
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
QmlTypedEvent &typedEvent = rangesInProgress.top();
|
|
||||||
int typeIndex = typedEvent.event.typeIndex();
|
|
||||||
if (typeIndex >= 0)
|
|
||||||
return typeIndex;
|
|
||||||
|
|
||||||
typeIndex = resolveType(typedEvent.type);
|
|
||||||
typedEvent.event.setTypeIndex(typeIndex);
|
|
||||||
eventStream << typedEvent.event;
|
|
||||||
modelManager->dispatch(typedEvent.event, eventTypes[typeIndex]);
|
|
||||||
return typeIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerDataModel::addTypedEvent(const QmlEvent &event, const QmlEventType &type)
|
|
||||||
{
|
|
||||||
Q_D(QmlProfilerDataModel);
|
|
||||||
|
|
||||||
// RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore,
|
|
||||||
// all ranges are perfectly nested. This is why we can defer the type resolution until either
|
|
||||||
// the range ends or a child range starts. With only the information in RangeStart we wouldn't
|
|
||||||
// be able to uniquely identify the event type.
|
|
||||||
Message rangeStage = type.rangeType == MaximumRangeType ? type.message : event.rangeStage();
|
|
||||||
switch (rangeStage) {
|
|
||||||
case RangeStart:
|
|
||||||
d->resolveStackTop();
|
|
||||||
d->rangesInProgress.push(QmlTypedEvent({event, type}));
|
|
||||||
break;
|
|
||||||
case RangeEnd: {
|
|
||||||
int typeIndex = d->resolveStackTop();
|
|
||||||
QTC_ASSERT(typeIndex != -1, break);
|
|
||||||
QmlEvent appended = event;
|
|
||||||
appended.setTypeIndex(typeIndex);
|
|
||||||
d->eventStream << appended;
|
|
||||||
d->modelManager->dispatch(appended, d->eventTypes[typeIndex]);
|
|
||||||
d->rangesInProgress.pop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RangeData:
|
|
||||||
d->rangesInProgress.top().type.data = type.data;
|
|
||||||
break;
|
|
||||||
case RangeLocation:
|
|
||||||
d->rangesInProgress.top().type.location = type.location;
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
QmlEvent appended = event;
|
|
||||||
int typeIndex = d->resolveType(type);
|
|
||||||
appended.setTypeIndex(typeIndex);
|
|
||||||
d->eventStream << appended;
|
|
||||||
d->modelManager->dispatch(appended, d->eventTypes[typeIndex]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerDataModel::replayEvents(qint64 rangeStart, qint64 rangeEnd,
|
void QmlProfilerDataModel::replayEvents(qint64 rangeStart, qint64 rangeEnd,
|
||||||
QmlProfilerModelManager::EventLoader loader) const
|
QmlProfilerModelManager::EventLoader loader) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,11 +47,11 @@ public:
|
|||||||
|
|
||||||
const QVector<QmlEventType> &eventTypes() const;
|
const QVector<QmlEventType> &eventTypes() const;
|
||||||
void setEventTypes(const QVector<QmlEventType> &types);
|
void setEventTypes(const QVector<QmlEventType> &types);
|
||||||
|
int addEventType(const QmlEventType &type);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
void addEvent(const QmlEvent &event);
|
void addEvent(const QmlEvent &event);
|
||||||
void addTypedEvent(const QmlEvent &event, const QmlEventType &type);
|
|
||||||
void replayEvents(qint64 startTime, qint64 endTime,
|
void replayEvents(qint64 startTime, qint64 endTime,
|
||||||
QmlProfilerModelManager::EventLoader loader) const;
|
QmlProfilerModelManager::EventLoader loader) const;
|
||||||
void finalize();
|
void finalize();
|
||||||
|
|||||||
@@ -30,9 +30,30 @@
|
|||||||
#include <qmldebug/qmlenginecontrolclient.h>
|
#include <qmldebug/qmlenginecontrolclient.h>
|
||||||
#include <qmldebug/qdebugmessageclient.h>
|
#include <qmldebug/qdebugmessageclient.h>
|
||||||
#include <qmldebug/qpacketprotocol.h>
|
#include <qmldebug/qpacketprotocol.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
|
|
||||||
|
inline static uint qHash(const QmlEventType &type)
|
||||||
|
{
|
||||||
|
return qHash(type.location.filename) ^
|
||||||
|
((type.location.line & 0xfff) | // 12 bits of line number
|
||||||
|
((type.message << 12) & 0xf000) | // 4 bits of message
|
||||||
|
((type.location.column << 16) & 0xff0000) | // 8 bits of column
|
||||||
|
((type.rangeType << 24) & 0xf000000) | // 4 bits of rangeType
|
||||||
|
((type.detailType << 28) & 0xf0000000)); // 4 bits of detailType
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool operator==(const QmlEventType &type1,
|
||||||
|
const QmlEventType &type2)
|
||||||
|
{
|
||||||
|
return type1.message == type2.message && type1.rangeType == type2.rangeType &&
|
||||||
|
type1.detailType == type2.detailType && type1.location.line == type2.location.line &&
|
||||||
|
type1.location.column == type2.location.column &&
|
||||||
|
// compare filename last as it's expensive.
|
||||||
|
type1.location.filename == type2.location.filename;
|
||||||
|
}
|
||||||
|
|
||||||
class QmlProfilerTraceClientPrivate {
|
class QmlProfilerTraceClientPrivate {
|
||||||
public:
|
public:
|
||||||
QmlProfilerTraceClientPrivate(QmlProfilerTraceClient *_q, QmlDebug::QmlDebugConnection *client,
|
QmlProfilerTraceClientPrivate(QmlProfilerTraceClient *_q, QmlDebug::QmlDebugConnection *client,
|
||||||
@@ -50,6 +71,9 @@ public:
|
|||||||
|
|
||||||
void sendRecordingStatus(int engineId);
|
void sendRecordingStatus(int engineId);
|
||||||
bool updateFeatures(ProfileFeature feature);
|
bool updateFeatures(ProfileFeature feature);
|
||||||
|
int resolveType(const QmlEventType &type);
|
||||||
|
int resolveStackTop();
|
||||||
|
void processCurrentEvent();
|
||||||
|
|
||||||
QmlProfilerTraceClient *q;
|
QmlProfilerTraceClient *q;
|
||||||
QmlProfilerDataModel *model;
|
QmlProfilerDataModel *model;
|
||||||
@@ -63,8 +87,76 @@ public:
|
|||||||
|
|
||||||
// Reuse the same event, so that we don't have to constantly reallocate all the data.
|
// Reuse the same event, so that we don't have to constantly reallocate all the data.
|
||||||
QmlTypedEvent currentEvent;
|
QmlTypedEvent currentEvent;
|
||||||
|
QHash<QmlEventType, int> eventTypeIds;
|
||||||
|
QStack<QmlTypedEvent> rangesInProgress;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int QmlProfilerTraceClientPrivate::resolveType(const QmlEventType &type)
|
||||||
|
{
|
||||||
|
QHash<QmlEventType, int>::ConstIterator it = eventTypeIds.constFind(type);
|
||||||
|
|
||||||
|
int typeIndex = -1;
|
||||||
|
if (it != eventTypeIds.constEnd()) {
|
||||||
|
typeIndex = it.value();
|
||||||
|
} else {
|
||||||
|
typeIndex = model->addEventType(type);
|
||||||
|
eventTypeIds[type] = typeIndex;
|
||||||
|
}
|
||||||
|
return typeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QmlProfilerTraceClientPrivate::resolveStackTop()
|
||||||
|
{
|
||||||
|
if (rangesInProgress.isEmpty())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
QmlTypedEvent &typedEvent = rangesInProgress.top();
|
||||||
|
int typeIndex = typedEvent.event.typeIndex();
|
||||||
|
if (typeIndex >= 0)
|
||||||
|
return typeIndex;
|
||||||
|
|
||||||
|
typeIndex = resolveType(typedEvent.type);
|
||||||
|
typedEvent.event.setTypeIndex(typeIndex);
|
||||||
|
model->addEvent(typedEvent.event);
|
||||||
|
return typeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlProfilerTraceClientPrivate::processCurrentEvent()
|
||||||
|
{
|
||||||
|
// RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore,
|
||||||
|
// all ranges are perfectly nested. This is why we can defer the type resolution until either
|
||||||
|
// the range ends or a child range starts. With only the information in RangeStart we wouldn't
|
||||||
|
// be able to uniquely identify the event type.
|
||||||
|
Message rangeStage = currentEvent.type.rangeType == MaximumRangeType ?
|
||||||
|
currentEvent.type.message : currentEvent.event.rangeStage();
|
||||||
|
switch (rangeStage) {
|
||||||
|
case RangeStart:
|
||||||
|
resolveStackTop();
|
||||||
|
rangesInProgress.push(currentEvent);
|
||||||
|
break;
|
||||||
|
case RangeEnd: {
|
||||||
|
int typeIndex = resolveStackTop();
|
||||||
|
QTC_ASSERT(typeIndex != -1, break);
|
||||||
|
currentEvent.event.setTypeIndex(typeIndex);
|
||||||
|
model->addEvent(currentEvent.event);
|
||||||
|
rangesInProgress.pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RangeData:
|
||||||
|
rangesInProgress.top().type.data = currentEvent.type.data;
|
||||||
|
break;
|
||||||
|
case RangeLocation:
|
||||||
|
rangesInProgress.top().type.location = currentEvent.type.location;
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
int typeIndex = resolveType(currentEvent.type);
|
||||||
|
currentEvent.event.setTypeIndex(typeIndex);
|
||||||
|
model->addEvent(currentEvent.event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QmlProfilerTraceClientPrivate::sendRecordingStatus(int engineId)
|
void QmlProfilerTraceClientPrivate::sendRecordingStatus(int engineId)
|
||||||
{
|
{
|
||||||
QmlDebug::QPacket stream(q->connection()->currentDataStreamVersion());
|
QmlDebug::QPacket stream(q->connection()->currentDataStreamVersion());
|
||||||
@@ -96,6 +188,8 @@ QmlProfilerTraceClient::~QmlProfilerTraceClient()
|
|||||||
|
|
||||||
void QmlProfilerTraceClient::clearData()
|
void QmlProfilerTraceClient::clearData()
|
||||||
{
|
{
|
||||||
|
d->eventTypeIds.clear();
|
||||||
|
d->rangesInProgress.clear();
|
||||||
if (d->recordedFeatures != 0) {
|
if (d->recordedFeatures != 0) {
|
||||||
d->recordedFeatures = 0;
|
d->recordedFeatures = 0;
|
||||||
emit recordedFeaturesChanged(0);
|
emit recordedFeaturesChanged(0);
|
||||||
@@ -152,7 +246,7 @@ void QmlProfilerTraceClient::setRequestedFeatures(quint64 features)
|
|||||||
d->currentEvent.type.message = DebugMessage;
|
d->currentEvent.type.message = DebugMessage;
|
||||||
d->currentEvent.type.rangeType = MaximumRangeType;
|
d->currentEvent.type.rangeType = MaximumRangeType;
|
||||||
d->currentEvent.type.detailType = type;
|
d->currentEvent.type.detailType = type;
|
||||||
d->model->addTypedEvent(d->currentEvent.event, d->currentEvent.type);
|
d->processCurrentEvent();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
d->messageClient.reset();
|
d->messageClient.reset();
|
||||||
@@ -211,7 +305,7 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
|
|||||||
emit traceFinished(d->currentEvent.event.timestamp(),
|
emit traceFinished(d->currentEvent.event.timestamp(),
|
||||||
d->currentEvent.event.numbers<QList<int>, qint32>());
|
d->currentEvent.event.numbers<QList<int>, qint32>());
|
||||||
} else if (d->updateFeatures(d->currentEvent.type.feature())) {
|
} else if (d->updateFeatures(d->currentEvent.type.feature())) {
|
||||||
d->model->addTypedEvent(d->currentEvent.event, d->currentEvent.type);
|
d->processCurrentEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user