PerfProfiler: Add error handling to PerfDataReader

If we fail to write to the buffer files or the perfparser process we
should report a failure.

Change-Id: Ib9fc29ff2609d915320371aa73a52128e7861fa8
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Ulf Hermann
2019-01-07 14:30:39 +01:00
parent dec9b7eb04
commit 4c6a497c89
3 changed files with 43 additions and 41 deletions

View File

@@ -318,26 +318,35 @@ QStringList PerfDataReader::collectArguments(const QString &executableDirPath,
return arguments; return arguments;
} }
static bool checkedWrite(QIODevice *device, const QByteArray &input)
{
qint64 written = 0;
const qint64 size = input.size();
while (written < size) {
const qint64 bytes = device->write(input.constData() + written, size - written);
if (bytes < 0)
return false;
written += bytes;
}
return true;
}
void PerfDataReader::writeChunk() void PerfDataReader::writeChunk()
{ {
if (!m_buffer.isEmpty()) { if (!m_buffer.isEmpty()) {
if (m_input.bytesToWrite() < s_maxBufferSize) { if (m_input.bytesToWrite() < s_maxBufferSize) {
QScopedPointer<TempFile> file(m_buffer.takeFirst()); std::unique_ptr<Utils::TemporaryFile> file(m_buffer.takeFirst());
file->reset(); file->reset();
const QByteArray data(file->readAll()); const QByteArray data(file->readAll());
for (qint64 i = 0, end = data.length(); i < end;) { if (!checkedWrite(&m_input, data)) {
const qint64 written = m_input.write(data.constData() + i, end - i); m_input.disconnect();
if (written >= 0) { m_input.kill();
i += written; emit finished();
} else { QMessageBox::warning(Core::ICore::mainWindow(),
m_input.disconnect(); tr("Cannot send data to Perf data parser"),
m_input.kill(); tr("The perf data parser doesn't accept further input. "
emit finished(); "Your trace is incomplete."));
QMessageBox::warning(Core::ICore::mainWindow(),
tr("Cannot send data to Perf data parser"),
tr("The perf data parser doesn't accept further input. "
"Your trace is incomplete."));
}
} }
} }
} else if (m_dataFinished) { } else if (m_dataFinished) {
@@ -360,25 +369,23 @@ void PerfDataReader::clear()
PerfProfilerTraceFile::clear(); PerfProfilerTraceFile::clear();
} }
void PerfDataReader::feedParser(const QByteArray &input) bool PerfDataReader::feedParser(const QByteArray &input)
{ {
if (m_buffer.isEmpty()) { if (!m_buffer.isEmpty()) {
if (m_input.isOpen() && m_input.bytesToWrite() < s_maxBufferSize) { auto *file = m_buffer.last();
m_input.write(input); if (file->pos() < s_maxBufferSize)
return; return checkedWrite(file, input);
} } else if (m_input.isOpen() && m_input.bytesToWrite() < s_maxBufferSize) {
TempFile *file = new TempFile; return checkedWrite(&m_input, input);
m_buffer.append(file);
connect(file, &QIODevice::bytesWritten, this, &PerfDataReader::writeChunk);
file->write(input);
} else {
TempFile *file = m_buffer.last();
if (file->pos() > s_maxBufferSize) {
file = new TempFile;
m_buffer.append(file);
}
file->write(input);
} }
auto file = std::make_unique<Utils::TemporaryFile>("perfdatareader");
connect(file.get(), &QIODevice::bytesWritten, this, &PerfDataReader::writeChunk);
if (!file->open() || !checkedWrite(file.get(), input))
return false;
m_buffer.append(file.release());
return true;
} }
QStringList PerfDataReader::findTargetArguments(const ProjectExplorer::RunConfiguration *rc) const QStringList PerfDataReader::findTargetArguments(const ProjectExplorer::RunConfiguration *rc) const

View File

@@ -54,7 +54,7 @@ public:
QStringList findTargetArguments(const ProjectExplorer::RunConfiguration *rc) const; QStringList findTargetArguments(const ProjectExplorer::RunConfiguration *rc) const;
void clear(); void clear();
void feedParser(const QByteArray &input); bool feedParser(const QByteArray &input);
// Trigger after delay has passed // Trigger after delay has passed
void triggerRecordingStateChange(bool recording); void triggerRecordingStateChange(bool recording);
@@ -81,12 +81,6 @@ protected:
private: private:
static const int s_maxBufferSize = 1 << 29; static const int s_maxBufferSize = 1 << 29;
class TempFile : public Utils::TemporaryFile
{
public:
TempFile() : Utils::TemporaryFile("perfdatareader") { open(); }
};
QStringList collectArguments(const QString &executableDirPath, QStringList collectArguments(const QString &executableDirPath,
const ProjectExplorer::Kit *kit) const; const ProjectExplorer::Kit *kit) const;
void writeChunk(); void writeChunk();
@@ -94,7 +88,7 @@ private:
bool m_recording; bool m_recording;
bool m_dataFinished; bool m_dataFinished;
QProcess m_input; QProcess m_input;
QQueue<TempFile *> m_buffer; QQueue<Utils::TemporaryFile *> m_buffer;
qint64 m_localProcessStart; qint64 m_localProcessStart;
qint64 m_localRecordingEnd; qint64 m_localRecordingEnd;
qint64 m_localRecordingStart; qint64 m_localRecordingStart;

View File

@@ -225,8 +225,9 @@ void PerfProfilerRunner::start()
appendMessage(QString::fromLocal8Bit(recorder->readAllStandardError()), appendMessage(QString::fromLocal8Bit(recorder->readAllStandardError()),
Utils::StdErrFormat); Utils::StdErrFormat);
}); });
connect(recorder, &DeviceProcess::readyReadStandardOutput, this, [reader, recorder] { connect(recorder, &DeviceProcess::readyReadStandardOutput, this, [this, reader, recorder] {
reader->feedParser(recorder->readAllStandardOutput()); if (!reader->feedParser(recorder->readAllStandardOutput()))
reportFailure(tr("Failed to transfer perf data to perfparser"));
}); });
} }