Commit 0924ee5e authored by Ulf Hermann's avatar Ulf Hermann

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's avatarJoerg Bornemann <joerg.bornemann@qt.io>
parent ebd6d269
......@@ -43,13 +43,9 @@ class QmlProfilerDataModel::QmlProfilerDataModelPrivate
{
public:
void rewriteType(int typeIndex);
int resolveType(const QmlEventType &type);
int resolveStackTop();
QVector<QmlEventType> eventTypes;
QHash<QmlEventType, int> eventTypeIds;
QStack<QmlTypedEvent> rangesInProgress;
QmlProfilerModelManager *modelManager;
int modelId;
......@@ -141,8 +137,15 @@ void QmlProfilerDataModel::setEventTypes(const QVector<QmlEventType> &types)
{
Q_D(QmlProfilerDataModel);
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)
......@@ -159,8 +162,6 @@ void QmlProfilerDataModel::clear()
d->file.open();
d->eventStream.setDevice(&d->file);
d->eventTypes.clear();
d->eventTypeIds.clear();
d->rangesInProgress.clear();
d->detailsRewriter->clearRequests();
}
......@@ -170,26 +171,6 @@ bool QmlProfilerDataModel::isEmpty() const
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)
{
QmlEventType &type = eventTypes[typeIndex];
......@@ -207,80 +188,6 @@ void QmlProfilerDataModel::QmlProfilerDataModelPrivate::rewriteType(int typeInde
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,
QmlProfilerModelManager::EventLoader loader) const
{
......
......@@ -47,11 +47,11 @@ public:
const QVector<QmlEventType> &eventTypes() const;
void setEventTypes(const QVector<QmlEventType> &types);
int addEventType(const QmlEventType &type);
void clear();
bool isEmpty() const;
void addEvent(const QmlEvent &event);
void addTypedEvent(const QmlEvent &event, const QmlEventType &type);
void replayEvents(qint64 startTime, qint64 endTime,
QmlProfilerModelManager::EventLoader loader) const;
void finalize();
......
......@@ -30,9 +30,30 @@
#include <qmldebug/qmlenginecontrolclient.h>
#include <qmldebug/qdebugmessageclient.h>
#include <qmldebug/qpacketprotocol.h>
#include <utils/qtcassert.h>
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 {
public:
QmlProfilerTraceClientPrivate(QmlProfilerTraceClient *_q, QmlDebug::QmlDebugConnection *client,
......@@ -50,6 +71,9 @@ public:
void sendRecordingStatus(int engineId);
bool updateFeatures(ProfileFeature feature);
int resolveType(const QmlEventType &type);
int resolveStackTop();
void processCurrentEvent();
QmlProfilerTraceClient *q;
QmlProfilerDataModel *model;
......@@ -63,8 +87,76 @@ public:
// Reuse the same event, so that we don't have to constantly reallocate all the data.
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)
{
QmlDebug::QPacket stream(q->connection()->currentDataStreamVersion());
......@@ -96,6 +188,8 @@ QmlProfilerTraceClient::~QmlProfilerTraceClient()
void QmlProfilerTraceClient::clearData()
{
d->eventTypeIds.clear();
d->rangesInProgress.clear();
if (d->recordedFeatures != 0) {
d->recordedFeatures = 0;
emit recordedFeaturesChanged(0);
......@@ -152,7 +246,7 @@ void QmlProfilerTraceClient::setRequestedFeatures(quint64 features)
d->currentEvent.type.message = DebugMessage;
d->currentEvent.type.rangeType = MaximumRangeType;
d->currentEvent.type.detailType = type;
d->model->addTypedEvent(d->currentEvent.event, d->currentEvent.type);
d->processCurrentEvent();
});
} else {
d->messageClient.reset();
......@@ -211,7 +305,7 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
emit traceFinished(d->currentEvent.event.timestamp(),
d->currentEvent.event.numbers<QList<int>, qint32>());
} else if (d->updateFeatures(d->currentEvent.type.feature())) {
d->model->addTypedEvent(d->currentEvent.event, d->currentEvent.type);
d->processCurrentEvent();
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment