QProgressDialog::setValue 产生随机堆栈溢出

Posted

技术标签:

【中文标题】QProgressDialog::setValue 产生随机堆栈溢出【英文标题】:QProgressDialog::setValue produces random stack overflows 【发布时间】:2020-04-02 10:16:54 【问题描述】:

我正在开发一个无法共享源代码的大型项目。在那个项目中,我们在 windows 中(不是在 ubuntu 中)遇到了一些难以重现的堆栈溢出错误,并且调试器的堆栈跟踪显示设置为窗口模式的进度对话框正在递归调用 QProgressDialog::setValue(int)。

我创建了一个 QProgressDialog 的这个小 example 来尝试重现错误。尽管使用此示例(在 Windows 或 Ubuntu 中都没有)我没有遇到堆栈溢出错误,但当我通过在调试器中添加断点来查看 QProgressDailog::setValue 时,我看到它也递归调用自身(在两个操作系统中) ,只是没有足够的时间产生堆栈溢出。最令人印象深刻的是,有时它确实只调用了一次 QPrigressDialog::setValue。

查看调用堆栈或窗口的示例:

Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 653 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 663 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 663 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 663 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 663 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 663 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
Qt5Widgets.dll!QProgressDialog::setValue(int progress) Line 663 C++
[Inline Frame] Qt5Core.dll!QtPrivate::QSlotObjectBase::call(QObject *) Line 398 C++
Qt5Core.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 626    C++
Qt5Core.dll!QObject::event(QEvent * e) Line 1339    C++
Qt5Widgets.dll!QWidget::event(QEvent * event) Line 9094 C++
Qt5Widgets.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3687 C++
Qt5Widgets.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3639   C++
Qt5Core.dll!QCoreApplication::notifyInternal2(QObject * receiver, QEvent * event) Line 1075 C++
[Inline Frame] Qt5Core.dll!QCoreApplication::sendEvent(QObject *) Line 1470 C++
Qt5Core.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1815 C++
qwindows.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 82 C++
Qt5Core.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 525 C++
qwindows.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 75    C++
[Inline Frame] Qt5Core.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag>) Line 138   C++
Qt5Core.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 225 C++
Qt5Core.dll!QCoreApplication::exec() Line 1383  C++
progress_bar.exe!main(int argc, char * * argv) Line 9   C++
[External Code] 

查看 ubuntu 上的调用堆栈示例:

#0  0x00007ffff76fa330 in QProgressDialog::setValue(int) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#1  0x00007ffff60a3dec in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<int>, void, void (QProgressDialog::*)(int)>::call(void (QProgressDialog::*)(int), QProgressDialog*, void**) ()
    at /home/apalomer/programming_workspace/progress_bar_example/build/libthreadedworker.so
#2  0x00007ffff60a3ba4 in void QtPrivate::FunctionPointer<void (QProgressDialog::*)(int)>::call<QtPrivate::List<int>, void>(void (QProgressDialog::*)(int), QProgressDialog*, void**) ()
    at /home/apalomer/programming_workspace/progress_bar_example/build/libthreadedworker.so
#3  0x00007ffff60a3759 in QtPrivate::QSlotObject<void (QProgressDialog::*)(int), QtPrivate::List<int>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) ()
    at /home/apalomer/programming_workspace/progress_bar_example/build/libthreadedworker.so
#4  0x00007ffff6ef30c2 in QObject::event(QEvent*) (this=0x555555b74610, e=<optimized out>) at kernel/qobject.cpp:1247
#5  0x00007ffff751775b in QWidget::event(QEvent*) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#6  0x00007ffff74d883c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#7  0x00007ffff74e0104 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#8  0x00007ffff6ec38d8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x555555b74610, event=event@entry=0x7fffc80037a0) at kernel/qcoreapplication.cpp:1024
#9  0x00007ffff6ec604d in QCoreApplication::sendEvent(QObject*, QEvent*) (event=0x7fffc80037a0, receiver=<optimized out>) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:233
#10 0x00007ffff6ec604d in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (receiver=receiver@entry=0x0, event_type=event_type@entry=0, data=0x55555576fef0)
    at kernel/qcoreapplication.cpp:1699
#11 0x00007ffff6ec65d8 in QCoreApplication::sendPostedEvents(QObject*, int) (receiver=receiver@entry=0x0, event_type=event_type@entry=0) at kernel/qcoreapplication.cpp:1553
#12 0x00007ffff6f1d263 in postEventSourceDispatch(GSource*, GSourceFunc, gpointer) (s=0x5555558952a0) at kernel/qeventdispatcher_glib.cpp:276
#13 0x00007ffff4221417 in g_main_context_dispatch () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#14 0x00007ffff4221650 in  () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#15 0x00007ffff42216dc in g_main_context_iteration () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#16 0x00007ffff6f1c88f in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (this=0x5555558d2b80, flags=...) at kernel/qeventdispatcher_glib.cpp:423
#17 0x00007ffff76fa4c7 in QProgressDialog::setValue(int) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#18 0x00007ffff60a3dec in QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<int>, void, void (QProgressDialog::*)(int)>::call(void (QProgressDialog::*)(int), QProgressDialog*, void**) ()
    at /home/apalomer/programming_workspace/progress_bar_example/build/libthreadedworker.so
#19 0x00007ffff60a3ba4 in void QtPrivate::FunctionPointer<void (QProgressDialog::*)(int)>::call<QtPrivate::List<int>, void>(void (QProgressDialog::*)(int), QProgressDialog*, void**) ()
    at /home/apalomer/programming_workspace/progress_bar_example/build/libthreadedworker.so
#20 0x00007ffff60a3759 in QtPrivate::QSlotObject<void (QProgressDialog::*)(int), QtPrivate::List<int>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) ()
    at /home/apalomer/programming_workspace/progress_bar_example/build/libthreadedworker.so
#21 0x00007ffff6ef30c2 in QObject::event(QEvent*) (this=0x555555b74610, e=<optimized out>) at kernel/qobject.cpp:1247
#22 0x00007ffff751775b in QWidget::event(QEvent*) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#23 0x00007ffff74d883c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#24 0x00007ffff74e0104 in QApplication::notify(QObject*, QEvent*) () at /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#25 0x00007ffff6ec38d8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x555555b74610, event=event@entry=0x7fffc8002ef0) at kernel/qcoreapplication.cpp:1024
#26 0x00007ffff6ec604d in QCoreApplication::sendEvent(QObject*, QEvent*) (event=0x7fffc8002ef0, receiver=<optimized out>) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:233
#27 0x00007ffff6ec604d in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (receiver=receiver@entry=0x0, event_type=event_type@entry=0, data=0x55555576fef0)
    at kernel/qcoreapplication.cpp:1699
#28 0x00007ffff6ec65d8 in QCoreApplication::sendPostedEvents(QObject*, int) (receiver=receiver@entry=0x0, event_type=event_type@entry=0) at kernel/qcoreapplication.cpp:1553
#29 0x00007ffff6f1d263 in postEventSourceDispatch(GSource*, GSourceFunc, gpointer) (s=0x5555558952a0) at kernel/qeventdispatcher_glib.cpp:276
#30 0x00007ffff4221417 in g_main_context_dispatch () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#31 0x00007ffff4221650 in  () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#32 0x00007ffff42216dc in g_main_context_iteration () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0
#33 0x00007ffff6f1c88f in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (this=0x5555558d2b80, flags=...) at kernel/qeventdispatcher_glib.cpp:423
#34 0x00007ffff6ec190a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (this=this@entry=0x7fffffffcfb0, flags=..., flags@entry=...) at kernel/qeventloop.cpp:212
#35 0x00007ffff6eca9b4 in QCoreApplication::exec() () at kernel/qcoreapplication.cpp:1297
#36 0x0000555555554cb4 in main ()

关于如何解决此递归的任何想法?我应该将其移至错误报告吗?尤其是当我手动单步执行程序时会发生这种情况。

我已经在 Windows 10、Visual Studio 16.5.2、MSVC 19.25.28612.0 和 Qt 5.14.2 以及 Qt 5.14.1、Qt 5.9.5 中对此进行了测试。另外,我在 Ubuntu 18.04、g++ 7.5.0 和 Qt 5.9.5 中尝试过。

【问题讨论】:

为什么要求关闭这个问题?我提供了一个示例和一种方法来重现不良行为。 查看结束原因以及您的问题文本,我认为这可能是事实,您提供了示例链接而不是示例本身。 【参考方案1】:

根据我从您的示例代码中收集到的信息:

    有一个工作线程每 200 毫秒向主事件队列发布事件。 (由于工作线程发出的信号在排队连接上,这将在接收者的线程事件循环中发布一个事件) QProgressDialog 源代码清楚地表明,当配置为模态模式时,它会在其 setValue 方法中调用 QCoreApplication::processEvents。

因此,为了引发堆栈溢出,一个线程应该能够在对话框开始处理它们之前在对话框的线程事件循环中发布足够的事件。

一旦它开始处理这个事件链,它就会在自身上递归,并可能导致堆栈溢出。如果您使用调试器单步执行主线程,则可以解释为什么其他线程能够在主偶数循环中累积如此多的事件。

我不知道这可能是 Qt 项目的错误,但您仍然可以在他们的 IRC 频道上询问或打开问题并查看它的位置。

尽管如此,一个可能的临时解决方案是避免来自工作线程的突发信号以避免陷入这种陷阱(例如每秒触发一次状态更新,而不是在最终用户不明显的短时间间隔内)。

【讨论】:

你是绝对正确的。我已经能够在我在帖子中分享的项目的modification 中始终如一地产生问题。我也有 reported 它作为 Qt 的一个错误,看看他们是否真的认为这是一个问题。 @apalomer 我想您可能想更好地解释在您的 QTBUG 中发生此堆栈溢出的环境。 Thiago 已经关闭了它,因为他认为您正在直接从另一个线程更新 QProgressDialog(而不是通过排队信号)。

以上是关于QProgressDialog::setValue 产生随机堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章