Crash Investigation Notes

Published 2025-07-07

Updated 2025-07-11

A practical record of recent Qt and C++ crash investigations, including event-loop reentrancy, null pointers, QObject lifetime issues, cross-thread UI access, and thread/timer lifecycle mistakes.

Written by - kok-s0s

ERROR

Qt

C++

Software

Event Loop Abnormality

A crash caused by abusing QCoreApplication::processEvents() in Qt.

QCoreApplication::processEvents() immediately processes all pending events in the current event queue, such as GUI updates, signal-slot calls, and timer events. It is often used to prevent the UI from appearing frozen.

Code Context

In a path validation function, I called this inside a validation loop on the main thread:

QCoreApplication::processEvents();  // process events to avoid UI freezing

The intention was simple: while the user waited for a synchronous and time-consuming validation step, the UI should not freeze. It should still display logs and respond to buttons.

In practice, the program started to crash randomly. The crash point was not the processEvents() line, but a stack similar to this:

[E|07/02 16:55:41.072]Signal[6] arrive, will shutdown system and dump stack info
[E|07/02 16:55:41.073]Write codedump failed, reason:-1
[E|07/02 16:55:41.092]-------------------Back Trace Info-------------------
[E|07/02 16:55:41.093]1         0x7f819d37d0 __kernel_rt_sigreturn + 0
[E|07/02 16:55:41.093]2         0x7f7fe46d78 gsignal + 224
[E|07/02 16:55:41.093]3         0x7f7fe33aac abort + 276
[E|07/02 16:55:41.094]4         0x7f7fe80f40 /lib/aarch64-linux-gnu/libc.so.6(+0x6df40) [0x7f7fe80f40]
[E|07/02 16:55:41.094]5         0x7f7fe88344 /lib/aarch64-linux-gnu/libc.so.6(+0x75344) [0x7f7fe88344]
[E|07/02 16:55:41.094]6         0x7f7fe89c90 /lib/aarch64-linux-gnu/libc.so.6(+0x76c90) [0x7f7fe89c90]
[E|07/02 16:55:41.095]7         0x7f81328490 /lib/aarch64-linux-gnu/libQt5Widgets.so.5(+0x3bf490) [0x7f81328490]
[E|07/02 16:55:41.095]8         0x7f8132972c QAbstractItemView::dataChanged(QModelIndex const&, QModelIndex const&, QVector<int> const&) + 340
[E|07/02 16:55:41.096]9         0x7f8132bc2c /lib/aarch64-linux-gnu/libQt5Widgets.so.5(+0x3c2c2c) [0x7f8132bc2c]
[E|07/02 16:55:41.096]10        0x7f80563608 QObject::event(QEvent*) + 512
[E|07/02 16:55:41.097]11        0x7f81109420 QWidget::event(QEvent*) + 568
[E|07/02 16:55:41.097]12        0x7f811a87b4 QFrame::event(QEvent*) + 44
[E|07/02 16:55:41.098]13        0x7f811ab404 QAbstractScrollArea::event(QEvent*) + 588
[E|07/02 16:55:41.098]14        0x7f8132f89c QAbstractItemView::event(QEvent*) + 308
[E|07/02 16:55:41.099]15        0x7f810c74ac QApplicationPrivate::notify_helper(QObject*, QEvent*) + 92
[E|07/02 16:55:41.099]16        0x7f810d0ad8 QApplication::notify(QObject*, QEvent*) + 336
[E|07/02 16:55:41.100]17        0x7f80535c0c QCoreApplication::notifyInternal2(QObject*, QEvent*) + 380
[E|07/02 16:55:41.100]18        0x7f80538b80 QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) + 360
[E|07/02 16:55:41.101]19        0x7f80591d20 /lib/aarch64-linux-gnu/libQt5Core.so.5(+0x2c7d20) [0x7f80591d20]
[E|07/02 16:55:41.101]20        0x7f7f53494c g_main_context_dispatch + 636
[E|07/02 16:55:41.101]21        0x7f7f534bbc /lib/aarch64-linux-gnu/libglib-2.0.so.0(+0x51bbc) [0x7f7f534bbc]
[E|07/02 16:55:41.102]22        0x7f7f534c5c g_main_context_iteration + 52
[E|07/02 16:55:41.102]23        0x7f805911cc QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 84

This kind of stack is misleading. It makes the crash look like a UI or model bug. After digging deeper, the root cause turned out to be that single processEvents() call.

Reconstructing the Crash

The path validation function looked roughly like this:

bool ProgrammingPage::checkPointPath() {
    ...
    foreach (auto point, m_teaching_points) {
        ...
        QCoreApplication::processEvents();  // avoid UI freezing
    }
    ...
}

If some UI object or model, such as QTableView or QStandardItemModel, is modified or closed during this period, the Qt main event loop may emit signals such as dataChanged(). If the corresponding widget or model has already been destroyed, Qt can crash.

This is the classic problem of event reentrancy plus premature object destruction.

Root Cause

Calling QCoreApplication::processEvents() forces Qt to process another round of the main event loop early, while:

Once something like QAbstractItemView::dataChanged() is triggered while the view or model is already invalid, the program may access a null pointer or dangling pointer and crash directly.

Correct Approach

Avoid processEvents(), especially inside loops or business logic.

If the goal is only to keep the UI responsive, prefer asynchronous scheduling:

// Use QTimer to validate points one by one.
QTimer::singleShot(0, this, &ProgrammingPage::checkNextPoint);

If events must be processed, limit the time window:

QEventLoop loop;
QTimer::singleShot(10, &loop, &QEventLoop::quit);  // process events for only 10 ms
loop.exec();

This avoids running the main event loop without bounds and reduces the chance of triggering UI events too early.

For all UI calls, make sure that:

Lessons

MistakeConsequence
Calling QCoreApplication::processEvents() directly on the main threadThe Qt event loop may unexpectedly emit signals, mutate models, or destroy UI objects, eventually causing a crash.
Not checking whether models or widgets are still validCan trigger crashes around QAbstractItemView::dataChanged() and similar paths.
Not splitting time-consuming work into asynchronous tasksLeads to thread stalls, frozen UI, or crashes.

processEvents() is not a universal lubricant. It is more like a dangerous switch: used carefully, the UI may stay responsive; used poorly, the program can crash.

ScenarioRecommended approach
Preventing UI freezesSplit tasks with QTimer.
Long-running tasksMove work to QThread; let the UI thread only update the interface.
Must wait but do not want to freeze the UIUse QEventLoop + QTimer::singleShot() to control processing time.

Null Pointer Access

Background

Some pointers depend on configuration files or external conditions during initialization. If initialization fails and returns null, later code that accesses the pointer without checking it can easily crash.

Example Stack

[E|06/25 22:02:30.710]1         0x7f7a10e6c0 __kernel_rt_sigreturn + 0
[E|06/25 22:02:30.710]2         0x7f76cc94d8 raise + 176

Root Cause

Correct Approach

Slot Function Accessing a Released Object

Background

After QObject signal-slot connections are established, if the connected object is destroyed but the signal is still emitted, the program may access released memory and crash.

Typical Case

connect(button, &QPushButton::clicked, this, &MyWidget::onClicked);
// If this is deleted earlier but the signal is still emitted, access is invalid.

Root Cause

Correct Approach

Cross-Thread UI Operations

Background

Qt GUI widgets must run on the main thread. Accessing UI from a worker thread causes undefined behavior and may crash immediately.

Typical Case

void* run() {
  ui->label->setText("thread text");  // illegal GUI access
}

Root Cause

Correct Approach

UI Widget Not Initialized or Already Destroyed

Background

If setupUi() is not called, a widget is not added to the layout successfully, or findChild() fails and returns nullptr, later access can crash the program.

Root Cause

Correct Approach

Unhandled Exceptions

Background

Qt does not generally enable C++ exception handling as a GUI control-flow mechanism, especially on the GUI main thread. If an exception is thrown and not caught, the program terminates directly.

Root Cause

Correct Approach

Do not use exceptions as control flow in Qt GUI programs.

If exceptions are necessary, add global exception capture in main:

int main(int argc, char *argv[]) {
  try {
    QApplication app(argc, argv);
    MainWindow w;
    w.show();
    return app.exec();
  } catch (std::exception &e) {
    qCritical() << "Unhandled exception:" << e.what();
    return -1;
  }
}

QTimer and QThread Lifetime Mismatch

Background

When using timers or threads, if the parent object is not bound properly or lifetimes do not match, an object may be destroyed too early. Later events then access a dangling object and crash.

Root Cause

Correct Approach

timer->moveToThread(workerThread);