forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user