diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2018-01-25 11:30:08 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2018-01-26 14:52:23 +0000 |
commit | a24a2af95e22cce759da46bf9a2e8a2687d3611d (patch) | |
tree | e9bc30793b9426297465e7e27ca2fd8e16dcdabd | |
parent | af54011bd5d3d52d3cee9a3340da6939da9468c7 (diff) |
Buffer non-pipe perf.data in a temporary file when streamed
This is mainly for testing. I don't want to special case things for this
all the time.
Change-Id: Ifc0621065fe0d5dd86fbebc70f89cfdc8a0ee43c
Reviewed-by: Milian Wolff <milian.wolff@kdab.com>
-rw-r--r-- | app/main.cpp | 121 | ||||
-rw-r--r-- | app/perfdata.cpp | 10 | ||||
-rw-r--r-- | app/perfdata.h | 4 | ||||
-rw-r--r-- | app/perfheader.cpp | 1 | ||||
-rw-r--r-- | tests/auto/perfdata/tst_perfdata.cpp | 3 |
5 files changed, 110 insertions, 29 deletions
diff --git a/app/main.cpp b/app/main.cpp index 361a029..c5d6039 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -27,6 +27,7 @@ #include "perfunwind.h" #include <QAbstractSocket> +#include <QTemporaryFile> #include <QCommandLineParser> #include <QCoreApplication> #include <QDebug> @@ -53,7 +54,8 @@ enum ErrorCodes { HeaderError, DataError, MissingData, - InvalidOption + InvalidOption, + BufferingError }; class PerfTcpSocket : public QTcpSocket { @@ -242,35 +244,34 @@ int main(int argc, char *argv[]) PerfHeader header(infile.data()); PerfAttributes attributes; PerfFeatures features; - PerfData data(infile.data(), &unwind, &header, &attributes); + PerfData data(&unwind, &header, &attributes); features.setArchitecture(parser.value(arch).toLatin1()); - QObject::connect(&header, &PerfHeader::finished, [&]() { - unwind.setByteOrder(static_cast<QSysInfo::Endian>(header.byteOrder())); - if (!header.isPipe()) { - const qint64 filePos = infile->pos(); - if (!attributes.read(infile.data(), &header)) { - qWarning() << "Failed to read attributes"; - qApp->exit(DataError); - return; - } - if (!features.read(infile.data(), &header)) { - qWarning() << "Failed to read features"; - qApp->exit(DataError); - return; - } - infile->seek(filePos); + auto readFileHeader = [&]() { + const qint64 filePos = infile->pos(); + if (!attributes.read(infile.data(), &header)) { + qWarning() << "Failed to read attributes"; + qApp->exit(DataError); + return; + } + if (!features.read(infile.data(), &header)) { + qWarning() << "Failed to read features"; + qApp->exit(DataError); + return; + } + infile->seek(filePos); - // first send features, as it may contain better event descriptions - unwind.features(features); + // first send features, as it may contain better event descriptions + unwind.features(features); - const auto& attrs = attributes.attributes(); - for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) { - unwind.attr(PerfRecordAttr(it.value(), {it.key()})); - } + const auto& attrs = attributes.attributes(); + for (auto it = attrs.begin(), end = attrs.end(); it != end; ++it) { + unwind.attr(PerfRecordAttr(it.value(), {it.key()})); } + }; + auto readData = [&]() { const QByteArray &featureArch = features.architecture(); unwind.setArchitecture(PerfRegisterInfo::archByName(featureArch)); @@ -280,11 +281,85 @@ int main(int argc, char *argv[]) return; } + data.setSource(infile.data()); QObject::connect(infile.data(), &QIODevice::aboutToClose, &data, &PerfData::finishReading); QObject::connect(&data, &PerfData::finished, infile.data(), [&](){ infile->disconnect(); }); QObject::connect(infile.data(), &QIODevice::readyRead, &data, &PerfData::read); if (infile->bytesAvailable() > 0) data.read(); + }; + + auto writeBytes = [](QIODevice *target, const char *data, qint64 length) { + qint64 pos = 0; + while (pos < length) { + const qint64 written = target->write(data + pos, length - pos); + if (written < 0) + return false; + pos += written; + } + return true; + }; + + QScopedPointer<QIODevice> tempfile; + auto bufferSequentialData = [&](){ + QByteArray buffer(1 << 25, Qt::Uninitialized); + const qint64 read = infile->read(buffer.data(), buffer.length()); + if (read < 0) { + qWarning() << "Failed to read from input."; + qApp->exit(BufferingError); + return; + } + + if (!writeBytes(tempfile.data(), buffer.data(), read)) { + qWarning() << "Failed to write buffer file."; + qApp->exit(BufferingError); + return; + } + }; + + QObject::connect(&header, &PerfHeader::finished, [&]() { + unwind.setByteOrder(static_cast<QSysInfo::Endian>(header.byteOrder())); + if (!header.isPipe()) { + if (infile->isSequential()) { + qWarning() << "Reading a non-pipe perf.data from a stream requires buffering."; + tempfile.reset(new QTemporaryFile); + if (!tempfile->open(QIODevice::ReadWrite)) { + qWarning() << "Failed to open buffer file."; + qApp->exit(BufferingError); + return; + } + + // We've checked this when parsing the header. + Q_ASSERT(header.size() <= std::numeric_limits<int>::max()); + const QByteArray fakeHeader(static_cast<int>(header.size()), 0); + if (!writeBytes(tempfile.data(), fakeHeader.data(), fakeHeader.length())) { + qWarning() << "Failed to write fake header to buffer file."; + qApp->exit(BufferingError); + return; + } + + QObject::connect(infile.data(), &QIODevice::readyRead, bufferSequentialData); + QObject::connect(infile.data(), &QIODevice::aboutToClose, [&]() { + infile->disconnect(); + infile.swap(tempfile); + if (!infile->reset()) { + qWarning() << "Cannot reset buffer file."; + qApp->exit(BufferingError); + return; + } + readFileHeader(); + readData(); + }); + + if (infile->bytesAvailable() > 0) + bufferSequentialData(); + } else { + readFileHeader(); + readData(); + } + } else { + readData(); + } }); QObject::connect(&header, &PerfHeader::error, []() { diff --git a/app/perfdata.cpp b/app/perfdata.cpp index ba1d05e..ce67043 100644 --- a/app/perfdata.cpp +++ b/app/perfdata.cpp @@ -28,12 +28,16 @@ static const int intMax = std::numeric_limits<int>::max(); -PerfData::PerfData(QIODevice *source, PerfUnwind *destination, const PerfHeader *header, - PerfAttributes *attributes) : - m_source(source), m_destination(destination), m_header(header), m_attributes(attributes) +PerfData::PerfData(PerfUnwind *destination, const PerfHeader *header, PerfAttributes *attributes) : + m_source(nullptr), m_destination(destination), m_header(header), m_attributes(attributes) { } +void PerfData::setSource(QIODevice *source) +{ + m_source = source; +} + PerfData::ReadStatus PerfData::processEvents(QDataStream &stream) { const quint16 headerSize = PerfEventHeader::fixedLength(); diff --git a/app/perfdata.h b/app/perfdata.h index f6b9db3..13d31a0 100644 --- a/app/perfdata.h +++ b/app/perfdata.h @@ -463,8 +463,8 @@ class PerfData : public QObject { Q_OBJECT public: - PerfData(QIODevice *source, PerfUnwind *destination, const PerfHeader *header, - PerfAttributes *attributes); + PerfData(PerfUnwind *destination, const PerfHeader *header, PerfAttributes *attributes); + void setSource(QIODevice *source); public slots: void read(); diff --git a/app/perfheader.cpp b/app/perfheader.cpp index b34e31b..d08d6c3 100644 --- a/app/perfheader.cpp +++ b/app/perfheader.cpp @@ -101,6 +101,7 @@ void PerfHeader::read() } disconnect(m_source, &QIODevice::readyRead, this, &PerfHeader::read); + m_source = nullptr; emit finished(); } diff --git a/tests/auto/perfdata/tst_perfdata.cpp b/tests/auto/perfdata/tst_perfdata.cpp index 7ab1971..45bb5ea 100644 --- a/tests/auto/perfdata/tst_perfdata.cpp +++ b/tests/auto/perfdata/tst_perfdata.cpp @@ -66,7 +66,8 @@ static void process(PerfUnwind *unwind, QIODevice *input) { PerfHeader header(input); PerfAttributes attributes; - PerfData data(input, unwind, &header, &attributes); + PerfData data(unwind, &header, &attributes); + data.setSource(input); QSignalSpy spy(&data, SIGNAL(finished())); QObject::connect(&header, &PerfHeader::finished, &data, [&](){ |