PerfProfiler: Remove backwards compatibility support

Define a new file extension, new file magic, and remove support for the
old data format. You won't be able to load traces generated with older
Qt Creators in 4.10 anymore and you won't be able to use older
perfparsers with Qt Creator 4.10.

On the flip side, we can move along more quickly now and integrate
features from KDAB's perfparser. This is to be preferred because the
maintenance of two different perfparsers is quite a burden.

Change-Id: I69918eadc3294abfbb0b809a54c3c9f25405409a
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Ulf Hermann
2019-05-03 09:51:07 +02:00
parent c770d3f4a7
commit 61fb66ab53
9 changed files with 112 additions and 169 deletions

View File

@@ -69,7 +69,6 @@ public:
void setNumGuessedFrames(quint8 numGuessedFrames) { m_numGuessedFrames = numGuessedFrames; } void setNumGuessedFrames(quint8 numGuessedFrames) { m_numGuessedFrames = numGuessedFrames; }
quint64 period() const { return m_value; } quint64 period() const { return m_value; }
quint64 weight() const { return m_weight; }
quint64 value() const { return m_value; } quint64 value() const { return m_value; }
quint8 feature() const { return m_feature; } quint8 feature() const { return m_feature; }
@@ -87,7 +86,6 @@ private:
quint32 m_pid = 0; quint32 m_pid = 0;
quint32 m_tid = 0; quint32 m_tid = 0;
quint64 m_value = 0; quint64 m_value = 0;
quint64 m_weight = 0;
quint8 m_origNumGuessedFrames = 0; quint8 m_origNumGuessedFrames = 0;
quint8 m_numGuessedFrames = 0; quint8 m_numGuessedFrames = 0;
quint8 m_feature = PerfEventType::InvalidFeature; quint8 m_feature = PerfEventType::InvalidFeature;
@@ -101,7 +99,6 @@ inline QDataStream &operator>>(QDataStream &stream, PerfEvent &event)
case PerfEventType::Command: case PerfEventType::Command:
case PerfEventType::LocationDefinition: case PerfEventType::LocationDefinition:
case PerfEventType::SymbolDefinition: case PerfEventType::SymbolDefinition:
case PerfEventType::AttributesDefinition49:
case PerfEventType::AttributesDefinition: case PerfEventType::AttributesDefinition:
case PerfEventType::StringDefinition: case PerfEventType::StringDefinition:
case PerfEventType::FeaturesDefinition: case PerfEventType::FeaturesDefinition:
@@ -112,8 +109,6 @@ inline QDataStream &operator>>(QDataStream &stream, PerfEvent &event)
case PerfEventType::ThreadStart: case PerfEventType::ThreadStart:
case PerfEventType::ThreadEnd: case PerfEventType::ThreadEnd:
case PerfEventType::LostDefinition: case PerfEventType::LostDefinition:
case PerfEventType::Sample43:
case PerfEventType::Sample49:
case PerfEventType::Sample: case PerfEventType::Sample:
case PerfEventType::TracePointSample: case PerfEventType::TracePointSample:
case PerfEventType::ContextSwitchDefinition: case PerfEventType::ContextSwitchDefinition:
@@ -147,21 +142,14 @@ inline QDataStream &operator>>(QDataStream &stream, PerfEvent &event)
default: { default: {
qint32 typeIndex; qint32 typeIndex;
stream >> event.m_origFrames >> event.m_origNumGuessedFrames; stream >> event.m_origFrames >> event.m_origNumGuessedFrames;
if (event.m_feature == PerfEventType::Sample43 || event.m_feature == PerfEventType::Sample49 QVector<QPair<qint32, quint64>> values;
|| event.m_feature == PerfEventType::TracePointSample49) { stream >> values;
stream >> typeIndex; if (values.isEmpty()) {
if (event.m_feature != PerfEventType::Sample43) typeIndex = 0;
stream >> event.m_value >> event.m_weight;
} else { } else {
QVector<QPair<qint32, quint64>> values; typeIndex = values.first().first;
stream >> values; event.m_value = values.first().second;
if (values.isEmpty()) { // TODO: support multiple values per event.
typeIndex = 0;
} else {
typeIndex = values.first().first;
event.m_value = values.first().second;
// TODO: support multiple values per event.
}
} }
if (event.m_feature == PerfEventType::TracePointSample) if (event.m_feature == PerfEventType::TracePointSample)
stream >> event.m_traceData; stream >> event.m_traceData;
@@ -185,29 +173,18 @@ inline QDataStream &operator<<(QDataStream &stream, const PerfEvent &event)
case PerfEventType::ContextSwitchDefinition: case PerfEventType::ContextSwitchDefinition:
stream << bool(event.extra()); stream << bool(event.extra());
break; break;
case PerfEventType::Sample43:
case PerfEventType::Sample49:
case PerfEventType::Sample: case PerfEventType::Sample:
case PerfEventType::TracePointSample49: case PerfEventType::TracePointSample: {
case PerfEventType::TracePointSample:
stream << event.m_origFrames << event.m_origNumGuessedFrames; stream << event.m_origFrames << event.m_origNumGuessedFrames;
if (feature == PerfEventType::Sample43 || feature == PerfEventType::Sample49 || QVector<QPair<qint32, quint64>> values;
feature == PerfEventType::TracePointSample49) { values.push_back({ PerfEvent::LastSpecialTypeId - event.typeIndex(), event.m_value });
stream << static_cast<qint32>(PerfEvent::LastSpecialTypeId - event.typeIndex()); stream << values;
if (feature != PerfEventType::Sample43)
stream << event.m_value << event.m_weight;
} else {
QVector<QPair<qint32, quint64>> values;
values.push_back({ PerfEvent::LastSpecialTypeId - event.typeIndex(), event.m_value });
stream << values;
}
if (feature == PerfEventType::TracePointSample if (feature == PerfEventType::TracePointSample)
|| feature == PerfEventType::TracePointSample49) {
stream << event.m_traceData; stream << event.m_traceData;
}
break; break;
}
default: default:
QTC_CHECK(false); QTC_CHECK(false);
} }

View File

@@ -39,21 +39,17 @@ public:
static const qint32 staticClassId = 0x70726674; // 'prft' static const qint32 staticClassId = 0x70726674; // 'prft'
enum Feature { enum Feature {
Sample43, // Only used until QtC/perfparser 4.3
ThreadStart, ThreadStart,
ThreadEnd, ThreadEnd,
Command, Command,
LocationDefinition, LocationDefinition,
SymbolDefinition, SymbolDefinition,
AttributesDefinition49,
StringDefinition, StringDefinition,
LostDefinition, LostDefinition,
FeaturesDefinition, FeaturesDefinition,
Error, Error,
Sample49,
Progress, Progress,
TracePointFormat, TracePointFormat,
TracePointSample49,
AttributesDefinition, AttributesDefinition,
ContextSwitchDefinition, ContextSwitchDefinition,
Sample, Sample,
@@ -63,9 +59,7 @@ public:
static quint64 attributeFeatures() static quint64 attributeFeatures()
{ {
return (1ull << Sample43) | (1ull << Sample49) | (1ull << Sample) return (1ull << Sample) | (1ull << TracePointSample) | (1ull << AttributesDefinition);
| (1ull << TracePointSample49) | (1ull << TracePointSample)
| (1ull << AttributesDefinition49) | (1ull << AttributesDefinition);
} }
static quint64 metaFeatures() static quint64 metaFeatures()
@@ -96,8 +90,10 @@ public:
struct Attribute { struct Attribute {
quint64 config = std::numeric_limits<quint64>::max(); quint64 config = std::numeric_limits<quint64>::max();
quint64 frequencyOrPeriod = std::numeric_limits<quint64>::max();
quint32 type = std::numeric_limits<quint32>::max(); quint32 type = std::numeric_limits<quint32>::max();
qint32 name = -1; qint32 name = -1;
bool usesFrequency = false;
}; };
struct Location { struct Location {
@@ -132,12 +128,8 @@ public:
bool isAttribute() const bool isAttribute() const
{ {
switch (feature()) { switch (feature()) {
case Sample43:
case Sample49:
case Sample: case Sample:
case AttributesDefinition49:
case AttributesDefinition: case AttributesDefinition:
case TracePointSample49:
case TracePointSample: case TracePointSample:
return true; return true;
default: default:
@@ -188,12 +180,14 @@ private:
inline QDataStream &operator>>(QDataStream &stream, PerfEventType::Attribute &attribute) inline QDataStream &operator>>(QDataStream &stream, PerfEventType::Attribute &attribute)
{ {
return stream >> attribute.type >> attribute.config >> attribute.name; return stream >> attribute.type >> attribute.config >> attribute.name
>> attribute.usesFrequency >> attribute.frequencyOrPeriod;
} }
inline QDataStream &operator<<(QDataStream &stream, const PerfEventType::Attribute &attribute) inline QDataStream &operator<<(QDataStream &stream, const PerfEventType::Attribute &attribute)
{ {
return stream << attribute.type << attribute.config << attribute.name; return stream << attribute.type << attribute.config << attribute.name
<< attribute.usesFrequency << attribute.frequencyOrPeriod;
} }
inline QDataStream &operator>>(QDataStream &stream, PerfEventType::Location &location) inline QDataStream &operator>>(QDataStream &stream, PerfEventType::Location &location)

View File

@@ -66,8 +66,7 @@ const char PerfSampleFrequency[] = "-F";
const char PerfSampleCount[] = "-c"; const char PerfSampleCount[] = "-c";
const char PerfStreamMagic[] = "QPERFSTREAM"; const char PerfStreamMagic[] = "QPERFSTREAM";
const char PerfQzfileMagic[] = "QPERFQZFILE"; const char PerfZqfileMagic[] = "PTQFILE4.10";
const char PerfZqfileMagic[] = "QPERFZQFILE";
} // namespace Constants } // namespace Constants
} // namespace PerfProfiler } // namespace PerfProfiler

View File

@@ -613,7 +613,7 @@ void PerfProfilerTool::showLoadTraceDialog()
QString filename = QFileDialog::getOpenFileName( QString filename = QFileDialog::getOpenFileName(
ICore::mainWindow(), tr("Load Trace File"), ICore::mainWindow(), tr("Load Trace File"),
"", tr("Trace File (*.ptr)")); "", tr("Trace File (*.ptq)"));
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
@@ -633,11 +633,11 @@ void PerfProfilerTool::showSaveTraceDialog()
QString filename = QFileDialog::getSaveFileName( QString filename = QFileDialog::getSaveFileName(
ICore::mainWindow(), tr("Save Trace File"), ICore::mainWindow(), tr("Save Trace File"),
"", tr("Trace File (*.ptr)")); "", tr("Trace File (*.ptq)"));
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
if (!filename.endsWith(".ptr")) if (!filename.endsWith(".ptq"))
filename += ".ptr"; filename += ".ptq";
setToolActionsEnabled(false); setToolActionsEnabled(false);
m_traceManager->saveToTraceFile(filename); m_traceManager->saveToTraceFile(filename);

View File

@@ -33,12 +33,11 @@
namespace PerfProfiler { namespace PerfProfiler {
namespace Internal { namespace Internal {
Q_STATIC_ASSERT(sizeof(Constants::PerfStreamMagic) == sizeof(Constants::PerfQzfileMagic));
Q_STATIC_ASSERT(sizeof(Constants::PerfStreamMagic) == sizeof(Constants::PerfZqfileMagic)); Q_STATIC_ASSERT(sizeof(Constants::PerfStreamMagic) == sizeof(Constants::PerfZqfileMagic));
PerfProfilerTraceFile::PerfProfilerTraceFile(QObject *parent) : PerfProfilerTraceFile::PerfProfilerTraceFile(QObject *parent) :
Timeline::TimelineTraceFile(parent), m_messageSize(0), Timeline::TimelineTraceFile(parent), m_messageSize(0),
m_dataStreamVersion(-1), m_compressed(false), m_mangledPackets(false) m_dataStreamVersion(-1), m_compressed(false)
{ {
// This should result in either a direct or a queued connection, depending on where the signal // This should result in either a direct or a queued connection, depending on where the signal
// comes from. readMessages() should always run in the GUI thread. // comes from. readMessages() should always run in the GUI thread.
@@ -166,97 +165,83 @@ void PerfProfilerTraceFile::readMessages(const QByteArray &buffer)
QDataStream dataStream(buffer); QDataStream dataStream(buffer);
dataStream.setVersion(m_dataStreamVersion); dataStream.setVersion(m_dataStreamVersion);
while (!dataStream.atEnd()) { PerfEvent event;
PerfEvent event; dataStream >> event;
dataStream >> event;
const qint64 timestamp = event.timestamp(); const qint64 timestamp = event.timestamp();
if (timestamp > 0) if (timestamp > 0)
event.setTimestamp(adjustTimestamp(timestamp)); event.setTimestamp(adjustTimestamp(timestamp));
qint32 id = -1; qint32 id = -1;
switch (event.feature()) { switch (event.feature()) {
case PerfEventType::LocationDefinition: { case PerfEventType::LocationDefinition: {
PerfEventType location(PerfEventType::LocationDefinition); PerfEventType location(PerfEventType::LocationDefinition);
dataStream >> id >> location; dataStream >> id >> location;
traceManager->setEventType(id, std::move(location)); traceManager->setEventType(id, std::move(location));
break; break;
}
case PerfEventType::SymbolDefinition: {
PerfProfilerTraceManager::Symbol symbol;
dataStream >> id >> symbol;
traceManager->setSymbol(id, symbol);
break;
}
case PerfEventType::AttributesDefinition49:
case PerfEventType::AttributesDefinition: {
PerfEventType attributes(PerfEventType::AttributesDefinition49);
dataStream >> id >> attributes;
if (event.feature() == PerfEventType::AttributesDefinition) {
bool usesFrequency;
quint64 frequencyOrPeriod;
dataStream >> usesFrequency >> frequencyOrPeriod;
}
traceManager->setEventType(PerfEvent::LastSpecialTypeId - id, std::move(attributes));
break;
}
case PerfEventType::StringDefinition: {
QByteArray string;
dataStream >> id >> string;
traceManager->setString(id, string);
break;
}
case PerfEventType::FeaturesDefinition: {
PerfFeatures features;
dataStream >> features;
break;
}
case PerfEventType::Error: {
qint32 errorCode;
QString message;
dataStream >> errorCode >> message;
break;
}
case PerfEventType::Progress: {
float progress;
dataStream >> progress;
break;
}
case PerfEventType::TracePointFormat: {
PerfProfilerTraceManager::TracePoint tracePoint;
dataStream >> id >> tracePoint;
traceManager->setTracePoint(id, tracePoint);
break;
}
case PerfEventType::Command: {
PerfProfilerTraceManager::Thread thread;
dataStream >> thread;
traceManager->setThread(thread.tid, thread);
break;
}
case PerfEventType::ThreadStart:
case PerfEventType::ThreadEnd:
case PerfEventType::LostDefinition:
case PerfEventType::ContextSwitchDefinition:
if (acceptsSamples())
traceManager->appendEvent(std::move(event));
break;
case PerfEventType::Sample43:
case PerfEventType::Sample49:
case PerfEventType::Sample:
case PerfEventType::TracePointSample49:
case PerfEventType::TracePointSample:
if (acceptsSamples())
traceManager->appendSampleEvent(std::move(event));
break;
}
if (!m_mangledPackets) {
if (!dataStream.atEnd())
qWarning() << "Read only part of message";
break;
}
} }
case PerfEventType::SymbolDefinition: {
PerfProfilerTraceManager::Symbol symbol;
dataStream >> id >> symbol;
traceManager->setSymbol(id, symbol);
break;
}
case PerfEventType::AttributesDefinition: {
PerfEventType attributes(PerfEventType::AttributesDefinition);
dataStream >> id >> attributes;
traceManager->setEventType(PerfEvent::LastSpecialTypeId - id, std::move(attributes));
break;
}
case PerfEventType::StringDefinition: {
QByteArray string;
dataStream >> id >> string;
traceManager->setString(id, string);
break;
}
case PerfEventType::FeaturesDefinition: {
PerfFeatures features;
dataStream >> features;
break;
}
case PerfEventType::Error: {
qint32 errorCode;
QString message;
dataStream >> errorCode >> message;
break;
}
case PerfEventType::Progress: {
float progress;
dataStream >> progress;
break;
}
case PerfEventType::TracePointFormat: {
PerfProfilerTraceManager::TracePoint tracePoint;
dataStream >> id >> tracePoint;
traceManager->setTracePoint(id, tracePoint);
break;
}
case PerfEventType::Command: {
PerfProfilerTraceManager::Thread thread;
dataStream >> thread;
traceManager->setThread(thread.tid, thread);
break;
}
case PerfEventType::ThreadStart:
case PerfEventType::ThreadEnd:
case PerfEventType::LostDefinition:
case PerfEventType::ContextSwitchDefinition:
if (acceptsSamples())
traceManager->appendEvent(std::move(event));
break;
case PerfEventType::Sample:
case PerfEventType::TracePointSample:
if (acceptsSamples())
traceManager->appendSampleEvent(std::move(event));
break;
}
if (!dataStream.atEnd())
qWarning() << "Read only part of message";
} }
void PerfProfilerTraceFile::readFromDevice() void PerfProfilerTraceFile::readFromDevice()
@@ -266,19 +251,18 @@ void PerfProfilerTraceFile::readFromDevice()
if (m_device->bytesAvailable() < magicSize + static_cast<int>(sizeof(qint32))) if (m_device->bytesAvailable() < magicSize + static_cast<int>(sizeof(qint32)))
return; return;
QVarLengthArray<char> magic(magicSize); QByteArray magic(magicSize, 0);
m_device->read(magic.data(), magicSize); m_device->read(magic.data(), magicSize);
if (strncmp(magic.data(), Constants::PerfStreamMagic, magicSize) == 0) { if (strncmp(magic.data(), Constants::PerfStreamMagic, magicSize) == 0) {
m_compressed = false; m_compressed = false;
m_mangledPackets = false;
} else if (strncmp(magic.data(), Constants::PerfQzfileMagic, magicSize) == 0) {
m_compressed = true;
m_mangledPackets = true;
} else if (strncmp(magic.data(), Constants::PerfZqfileMagic, magicSize) == 0) { } else if (strncmp(magic.data(), Constants::PerfZqfileMagic, magicSize) == 0) {
m_compressed = true; m_compressed = true;
m_mangledPackets = false;
} else { } else {
fail(tr("Invalid data format")); fail(tr("Invalid data format. The trace file's identification string is \"%1\"."
"An acceptable trace file should have \"%2\". You cannot read trace files "
"generated with older versions of Qt Creator.")
.arg(QString::fromLatin1(magic))
.arg(QString::fromLatin1(Constants::PerfZqfileMagic)));
return; return;
} }
@@ -287,7 +271,10 @@ void PerfProfilerTraceFile::readFromDevice()
if (m_dataStreamVersion < 0 if (m_dataStreamVersion < 0
|| m_dataStreamVersion > QDataStream::Qt_DefaultCompiledVersion) { || m_dataStreamVersion > QDataStream::Qt_DefaultCompiledVersion) {
fail(tr("Invalid data format")); fail(tr("Invalid data format. The trace file was written with data stream version %1. "
"We can read at most version %2. Please use a newer version of Qt.")
.arg(m_dataStreamVersion)
.arg(qint32(QDataStream::Qt_DefaultCompiledVersion)));
return; return;
} }
} }
@@ -305,8 +292,6 @@ void PerfProfilerTraceFile::readFromDevice()
m_messageSize = 0; m_messageSize = 0;
if (!m_compressed) if (!m_compressed)
emit messagesAvailable(buffer); emit messagesAvailable(buffer);
else if (m_mangledPackets)
emit messagesAvailable(qUncompress(buffer));
else else
emit blockAvailable(qUncompress(buffer)); emit blockAvailable(qUncompress(buffer));
@@ -460,7 +445,7 @@ void PerfProfilerTraceFile::writeToDevice()
{ {
CompressedDataStream bufferStream(m_device.data()); CompressedDataStream bufferStream(m_device.data());
const quint8 feature = PerfEventType::AttributesDefinition49; const quint8 feature = PerfEventType::AttributesDefinition;
qint32 id = 0; qint32 id = 0;
for (const PerfEventType &attribute : traceManager->attributes()) { for (const PerfEventType &attribute : traceManager->attributes()) {
if (!attribute.isAttribute()) if (!attribute.isAttribute())

View File

@@ -71,7 +71,6 @@ protected:
quint32 m_messageSize; quint32 m_messageSize;
qint32 m_dataStreamVersion; qint32 m_dataStreamVersion;
bool m_compressed; bool m_compressed;
bool m_mangledPackets;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -179,7 +179,7 @@ void PerfProfilerTraceManager::clearTypeStorage()
const Timeline::TraceEventType &PerfProfilerEventTypeStorage::get(int typeId) const const Timeline::TraceEventType &PerfProfilerEventTypeStorage::get(int typeId) const
{ {
static const PerfEventType emptyAttribute(PerfEventType::AttributesDefinition49); static const PerfEventType emptyAttribute(PerfEventType::AttributesDefinition);
static const PerfEventType emptyLocation(PerfEventType::LocationDefinition); static const PerfEventType emptyLocation(PerfEventType::LocationDefinition);
if (typeId >= 0) { if (typeId >= 0) {

View File

@@ -167,8 +167,6 @@ QVariantMap PerfTimelineModel::details(int index) const
const int guessedFrames = -frame.numSamples; const int guessedFrames = -frame.numSamples;
if (guessedFrames > 0) if (guessedFrames > 0)
result.insert(tr("Guessed"), tr("%n frames", nullptr, guessedFrames)); result.insert(tr("Guessed"), tr("%n frames", nullptr, guessedFrames));
if (const int sampleWeight = weight(index))
result.insert(tr("Weight"), sampleWeight);
if (const int sampleValue = value(index)) if (const int sampleValue = value(index))
result.insert(tr("Value"), sampleValue); result.insert(tr("Value"), sampleValue);
if (attribute.type == PerfEventType::TypeTracepoint) { if (attribute.type == PerfEventType::TypeTracepoint) {
@@ -406,13 +404,11 @@ void PerfTimelineModel::addSample(const PerfEvent &event, qint64 resourceDelta,
{ {
static const int intMax = std::numeric_limits<int>::max(); static const int intMax = std::numeric_limits<int>::max();
const int value = static_cast<int>(qMin(static_cast<quint64>(intMax), event.value())); const int value = static_cast<int>(qMin(static_cast<quint64>(intMax), event.value()));
const int weight = static_cast<int>(qMin(static_cast<quint64>(intMax), event.weight()));
const int id = TimelineModel::insert(event.timestamp(), 1, event.typeIndex()); const int id = TimelineModel::insert(event.timestamp(), 1, event.typeIndex());
StackFrame sample = StackFrame::sampleFrame(); StackFrame sample = StackFrame::sampleFrame();
sample.numSamples = event.numGuessedFrames() > 0 ? -event.numGuessedFrames() : 1; sample.numSamples = event.numGuessedFrames() > 0 ? -event.numGuessedFrames() : 1;
sample.value = value; sample.value = value;
sample.weight = weight;
sample.resourcePeak = m_resourceBlocks.currentTotal(); sample.resourcePeak = m_resourceBlocks.currentTotal();
sample.resourceDelta = resourceDelta; sample.resourceDelta = resourceDelta;
sample.resourceGuesses = resourceGuesses; sample.resourceGuesses = resourceGuesses;
@@ -617,11 +613,6 @@ int PerfTimelineModel::value(int index) const
return m_data[index].value; return m_data[index].value;
} }
int PerfTimelineModel::weight(int index) const
{
return m_data[index].weight;
}
bool PerfTimelineModel::handlesTypeId(int typeId) const bool PerfTimelineModel::handlesTypeId(int typeId) const
{ {
return m_locationStats.contains(typeId); return m_locationStats.contains(typeId);

View File

@@ -89,7 +89,6 @@ public:
} }
int value(int index) const; int value(int index) const;
int weight(int index) const;
QHash<qint32, QVariant> extraData(int index) const { return m_extraData.value(index); } QHash<qint32, QVariant> extraData(int index) const { return m_extraData.value(index); }
@@ -106,7 +105,6 @@ private:
int displayRowCollapsed = MaximumSpecialRow; int displayRowCollapsed = MaximumSpecialRow;
int displayRowExpanded = MaximumSpecialRow; int displayRowExpanded = MaximumSpecialRow;
int value = 0; int value = 0;
int weight = 0;
qint64 resourcePeak = 0; qint64 resourcePeak = 0;
qint64 resourceDelta = 0; qint64 resourceDelta = 0;