summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2018-01-25 11:30:08 +0100
committerUlf Hermann <ulf.hermann@qt.io>2018-01-26 14:52:23 +0000
commita24a2af95e22cce759da46bf9a2e8a2687d3611d (patch)
treee9bc30793b9426297465e7e27ca2fd8e16dcdabd
parentaf54011bd5d3d52d3cee9a3340da6939da9468c7 (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.cpp121
-rw-r--r--app/perfdata.cpp10
-rw-r--r--app/perfdata.h4
-rw-r--r--app/perfheader.cpp1
-rw-r--r--tests/auto/perfdata/tst_perfdata.cpp3
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, [&](){