QmlProfiler: Fix crash at startup if file creation fails

If opening the temporary file for QmlProfilerEventStorage fails in it's
destructor, it was crashing, because the error handler tried to emit a
signal on an object that wasn't even half constructed yet.

Can be tested by forcing the return value of TraceStashFile::open to
`false`.

Delay the construction of the event storage a bit and do not set the
error handler in its constructor, since nobody could connect to the
signal that was sent yet anyway. Also add some checks before calling the
error handler.

Change-Id: I0fc1207aac447090e12f6b2342e156312e0d1d1b
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Eike Ziller
2024-12-16 10:11:57 +01:00
parent 8b6f7b4ea7
commit 012dd52fa3
2 changed files with 16 additions and 12 deletions

View File

@@ -130,7 +130,8 @@ public:
void clear()
{
file.remove();
if (!file.fileName().isEmpty())
file.remove();
stream.setDevice(nullptr);
}

View File

@@ -88,12 +88,9 @@ public:
int resolveStackTop();
};
QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) :
Timeline::TimelineTraceManager(
std::make_unique<QmlProfilerEventStorage>(
std::bind(&Timeline::TimelineTraceManager::error, this, std::placeholders::_1)),
std::make_unique<QmlProfilerEventTypeStorage>(), parent),
d(new QmlProfilerModelManagerPrivate)
QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent)
: Timeline::TimelineTraceManager({}, std::make_unique<QmlProfilerEventTypeStorage>(), parent)
, d(new QmlProfilerModelManagerPrivate)
{
setNotesModel(new QmlProfilerNotesModel(this));
d->textMarkModel = new Internal::QmlProfilerTextMarkModel(this);
@@ -103,6 +100,10 @@ QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) :
this, &QmlProfilerModelManager::setTypeDetails);
connect(d->detailsRewriter, &Internal::QmlProfilerDetailsRewriter::eventDetailsChanged,
this, &QmlProfilerModelManager::typeDetailsFinished);
auto storage = new QmlProfilerEventStorage(QmlProfilerEventStorage::ErrorHandler());
storage->setErrorHandler([this](const QString &message) { emit error(message); });
std::unique_ptr<Timeline::TraceEventStorage> storagePtr(storage);
swapEventStorage(storagePtr);
}
QmlProfilerModelManager::~QmlProfilerModelManager()
@@ -486,7 +487,7 @@ QmlProfilerEventStorage::QmlProfilerEventStorage(
const std::function<void (const QString &)> &errorHandler)
: m_file("qmlprofiler-data"), m_errorHandler(errorHandler)
{
if (!m_file.open())
if (!m_file.open() && m_errorHandler)
errorHandler(Tr::tr("Cannot open temporary trace file to store events."));
}
@@ -506,13 +507,13 @@ void QmlProfilerEventStorage::clear()
{
m_size = 0;
m_file.clear();
if (!m_file.open())
if (!m_file.open() && m_errorHandler)
m_errorHandler(Tr::tr("Failed to reset temporary trace file."));
}
void QmlProfilerEventStorage::finalize()
{
if (!m_file.flush())
if (!m_file.flush() && m_errorHandler)
m_errorHandler(Tr::tr("Failed to flush temporary trace file."));
}
@@ -534,13 +535,15 @@ bool QmlProfilerEventStorage::replay(
case Timeline::TraceStashFile<QmlEvent>::ReplaySuccess:
return true;
case Timeline::TraceStashFile<QmlEvent>::ReplayOpenFailed:
m_errorHandler(Tr::tr("Could not re-open temporary trace file."));
if (m_errorHandler)
m_errorHandler(Tr::tr("Could not re-open temporary trace file."));
break;
case Timeline::TraceStashFile<QmlEvent>::ReplayLoadFailed:
// Happens if the loader rejects an event. Not an actual error
break;
case Timeline::TraceStashFile<QmlEvent>::ReplayReadPastEnd:
m_errorHandler(Tr::tr("Read past end in temporary trace file."));
if (m_errorHandler)
m_errorHandler(Tr::tr("Read past end in temporary trace file."));
break;
}
return false;