$ gdb /usr/bin/netease-cloud-musicGNU gdb (Debian 7.12-6) 7.12.0.20161007-gitCopyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from /usr/bin/netease-cloud-music...(no debugging symbols found)...done.(gdb) attach 245374Attaching to program: /usr/bin/netease-cloud-music, process 245374ptrace: No such process.(gdb) attach 24374Attaching to program: /usr/bin/netease-cloud-music, process 24374[New LWP 24375][New LWP 24377][New LWP 24378][New LWP 24379][New LWP 24380][New LWP 24381][New LWP 24382][New LWP 24383][New LWP 24384][New LWP 24385][New LWP 24386][New LWP 24387][New LWP 24388][New LWP 24389][New LWP 24390][New LWP 24391][New LWP 24392][New LWP 24393][New LWP 24394][New LWP 24395][New LWP 24396][New LWP 24397][New LWP 24398][New LWP 24399][New LWP 24400][New LWP 24401][New LWP 24403][New LWP 24404][New LWP 24405][New LWP 24406][New LWP 24407][New LWP 24408][New LWP 24409][New LWP 24410][New LWP 24411][New LWP 24412][New LWP 24413][New LWP 24414][New LWP 24427][New LWP 24432][New LWP 24433][Thread debugging using libthread_db enabled]Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".0x00007fbb983b46ad in read () at ../sysdeps/unix/syscall-template.S:8484 ../sysdeps/unix/syscall-template.S: No such file or directory.
现在看一下调用栈:
(gdb) bt#0 0x00007fbb983b46ad in read () at ../sysdeps/unix/syscall-template.S:84#1 0x00007fbb0c141a8b in Dtk::Core:PrivateSignal) (this=this@entry=0x55ef77514728, _t1=) at .moc/moc_qsocketnotifier.cpp:135#5 0x00007fbb995d8a42 in QSocketNotifier::event(QEvent*) (this=0x55ef77514728, e=0x7fff381a5e30) at kernel/qsocketnotifier.cpp:260#6 0x00007fbb9dad328c in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=, receiver=0x55ef77514728, e=0x7fff381a5e30) at kernel/qapplication.cpp:3804#7 0x00007fbb9dad837f in QApplication::notify(QObject*, QEvent*) (this=0x55ef774a4030, receiver=0x55ef77514728, e=0x7fff381a5e30) at kernel/qapplication.cpp:3561#8 0x00007fbb995a12a8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55ef77514728, event=event@entry=0x7fff381a5e30) at kernel/qcoreapplication.cpp:1015#9 0x00007fbb995f410d in QCoreApplication::sendEvent(QObject*, QEvent*) (event=0x7fff381a5e30, receiver=) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:225#10 0x00007fbb995f410d in socketNotifierSourceDispatch(GSource*, GSourceFunc, gpointer) (source=0x55ef774d9370) at kernel/qeventdispatcher_glib.cpp:100#11 0x00007fbb9b519887 in g_main_dispatch (context=0x55ef77386130) at ../../../../glib/gmain.c:3148#12 0x00007fbb9b519887 in g_main_context_dispatch (context=context@entry=0x55ef77386130) at ../../../../glib/gmain.c:3813#13 0x00007fbb9b519ab8 in g_main_context_iterate (context=context@entry=0x55ef77386130, block=block@entry=1, dispatch=dispatch@entry=1, self=) at ../../../../glib/gmain.c:3886#14 0x00007fbb9b519b5c in g_main_context_iteration (context=0x55ef77386130, may_block=may_block@entry=1) at ../../../../glib/gmain.c:3947#15 0x00007fbb995f3c7f in QEventDispatcherGlib::processEvents(QFlagsrocessEventsFlag>) (this=0x55ef774ddec0, flags=...) at kernel/qeventdispatcher_glib.cpp:417#16 0x00007fbb9959f57a in QEventLoop::exec(QFlagsrocessEventsFlag>) (this=this@entry=0x7fff381a6030, flags=..., flags@entry=...) at kernel/qeventloop.cpp:204#17 0x00007fbb995a762d in QCoreApplication::exec() () at kernel/qcoreapplication.cpp:1285#18 0x000055ef7575330c in main ()
/* This is a "normal" system call stub: if there is an error, it returns -1 and sets errno. */T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) retT_PSEUDO_END (SYSCALL_SYMBOL)
(gdb) bt#0 0x00007fbb983b46ad in read () at ../sysdeps/unix/syscall-template.S:84#1 0x00007fbb0c141a8b in Dtk::Core:PrivateSignal) (this=this@entry=0x55ef77514728, _t1=) at .moc/moc_qsocketnotifier.cpp:135#5 0x00007fbb995d8a42 in QSocketNotifier::event(QEvent*) (this=0x55ef77514728, e=0x7fff381a57f0) at kernel/qsocketnotifier.cpp:260#6 0x00007fbb9dad328c in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=, receiver=0x55ef77514728, e=0x7fff381a57f0) at kernel/qapplication.cpp:3804#7 0x00007fbb9dad837f in QApplication::notify(QObject*, QEvent*) (this=0x55ef774a4030, receiver=0x55ef77514728, e=0x7fff381a57f0) at kernel/qapplication.cpp:3561#8 0x00007fbb995a12a8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55ef77514728, event=event@entry=0x7fff381a57f0) at kernel/qcoreapplication.cpp:1015#9 0x00007fbb995f410d in QCoreApplication::sendEvent(QObject*, QEvent*) (event=0x7fff381a57f0, receiver=) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:225#10 0x00007fbb995f410d in socketNotifierSourceDispatch(GSource*, GSourceFunc, gpointer) (source=0x55ef774d9370) at kernel/qeventdispatcher_glib.cpp:100#11 0x00007fbb9b519887 in g_main_dispatch (context=0x55ef77386130) at ../../../../glib/gmain.c:3148#12 0x00007fbb9b519887 in g_main_context_dispatch (context=context@entry=0x55ef77386130) at ../../../../glib/gmain.c:3813#13 0x00007fbb9b519ab8 in g_main_context_iterate (context=context@entry=0x55ef77386130, block=block@entry=0, dispatch=dispatch@entry=1, self=) at ../../../../glib/gmain.c:3886#14 0x00007fbb9b519b5c in g_main_context_iteration (context=0x55ef77386130, may_block=0) at ../../../../glib/gmain.c:3947#15 0x00007fbb8f5a641a in () at /usr/lib/x86_64-linux-gnu/qcef/libcef.so#16 0x00007fbb8f5cd59a in () at /usr/lib/x86_64-linux-gnu/qcef/libcef.so#17 0x00007fbb8f4dc41a in () at /usr/lib/x86_64-linux-gnu/qcef/libcef.so#18 0x00007fbb995cc85c in QtPrivate:SlotObjectBase::call(QObject*, void**) (a=0x7fff381a5ab0, r=0x55ef7764e700, this=PrivateSignal) (this=this@entry=0x55ef7764e700) at .moc/moc_qtimer.cpp:198#22 0x00007fbb995d8e18 in QTimer::timerEvent(QTimerEvent*) (this=0x55ef7764e700, e=) at kernel/qtimer.cpp:247#23 0x00007fbb995cd2ab in QObject::event(QEvent*) (this=0x55ef7764e700, e=) at kernel/qobject.cpp:1278#24 0x00007fbb9dad328c in QApplicationPrivate::notify_helper(QObject*, QEvent*) (this=, receiver=0x55ef7764e700, e=0x7fff381a5e00) at kernel/qapplication.cpp:3804#25 0x00007fbb9dad837f in QApplication::notify(QObject*, QEvent*) (this=0x55ef774a4030, receiver=0x55ef7764e700, e=0x7fff381a5e00) at kernel/qapplication.cpp:3561#26 0x00007fbb995a12a8 in QCoreApplication::notifyInternal2(QObject*, QEvent*) (receiver=0x55ef7764e700, event=event@entry=0x7fff381a5e00) at kernel/qcoreapplication.cpp:1015#27 0x00007fbb995f2c1e in QCoreApplication::sendEvent(QObject*, QEvent*) (event=0x7fff381a5e00, receiver=) at ../../include/QtCore/../../src/corelib/kernel/qcoreapplication.h:225#28 0x00007fbb995f2c1e in QTimerInfoList::activateTimers() (this=0x55ef774fdf90) at kernel/qtimerinfo_unix.cpp:637#29 0x00007fbb995f3141 in timerSourceDispatch(GSource*, GSourceFunc, gpointer) (source=) at kernel/qeventdispatcher_glib.cpp:176#30 0x00007fbb9b519887 in g_main_dispatch (context=0x55ef77386130) at ../../../../glib/gmain.c:3148#31 0x00007fbb9b519887 in g_main_context_dispatch (context=context@entry=0x55ef77386130) at ../../../../glib/gmain.c:3813#32 0x00007fbb9b519ab8 in g_main_context_iterate (context=context@entry=0x55ef77386130, block=block@entry=1, dispatch=dispatch@entry=1, self=) at ../../../../glib/gmain.c:3886#33 0x00007fbb9b519b5c in g_main_context_iteration (context=0x55ef77386130, may_block=may_block@entry=1) at ../../../../glib/gmain.c:3947#34 0x00007fbb995f3c7f in QEventDispatcherGlib::processEvents(QFlagsrocessEventsFlag>) (this=0x55ef774ddec0, flags=...) at kernel/qeventdispatcher_glib.cpp:417#35 0x00007fbb9959f57a in QEventLoop::exec(QFlagsrocessEventsFlag>) (this=this@entry=0x7fff381a6030, flags=..., flags@entry=...) at kernel/qeventloop.cpp:204#36 0x00007fbb995a762d in QCoreApplication::exec() () at kernel/qcoreapplication.cpp:1285#37 0x000055ef7575330c in main ()
现在来看, 已经可以打印出源文件的行号了:
Dtk::Core:FileSystemWatcherPrivate::_q_readFromInotify() (this=0x55ef775146f0) at filesystem/dfilesystemwatcher_linux.cpp:144
在 dtkcore 项目里, 我们来看一下第144号是什么:
void DFileSystemWatcherPrivate::_q_readFromInotify(){ Q_Q(DFileSystemWatcher);// qDebug() << "QInotifyFileSystemWatcherEngine::readFromInotify"; int buffSize = 0; ioctl(inotifyFd, FIONREAD, (char *) &buffSize); QVarLengthArray buffer(buffSize); buffSize = read(inotifyFd, buffer.data(), buffSize); char *at = buffer.data(); char * const end = at + buffSize; QList eventList; QHash pathForId; /// only save event: IN_MOVE_TO QMap cookieToFilePath; QMap cookieToFileName; QSet hasMoveFromByCookie; while (at < end) { inotify_event *event = reinterpret_cast(at); QString path;
/*! Constructs a new file system watcher object with the given \a parent.*/DFileSystemWatcher:FileSystemWatcher(QObject *parent) : QObject(parent) , DObject(){ int fd = -1;#ifdef IN_CLOEXEC fd = inotify_init1(IN_CLOEXEC);#endif if (fd == -1) { fd = inotify_init(); } if (fd != -1) d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this));}
修改一下 inotify 句柄被初始化时的方式, 改为非阻塞式的.
/*! Constructs a new file system watcher object with the given \a parent.*/DFileSystemWatcher:FileSystemWatcher(QObject *parent) : QObject(parent) , DObject(){ int fd = -1;#ifdef IN_CLOEXEC fd = inotify_init1(IN_CLOEXEC | O_NONBLOCK);#endif if (fd == -1) { fd = inotify_init1(O_NONBLOCK); } if (fd != -1) d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this));}
有关 inotify_init1() 这个接口的详细信息可以读取一下手册:
$ man inotify$ man inotify_init
修改完之后, 为 dtkcore 生成新的 deb 包, 然后安装, 重新验证.
验证数十次之后, 搜狗输入法输入时导致主窗口冻结的问题再没有出现过.
问题
很容易导致播放器窗口没有响应. 这个问题在开发过程中, 在我的机器上并没有遇到过.
现在使用 gdb 来跟踪一下调用栈.
而 24376 和 24415 这两个是 renderer 进程, 不需要关注它们.
Dtk::Core:
这里并没有打印出具体调用了 dtkcore 里面的那个函数, 为此, 可以安装 dtkcore 的 debug 包:
Dtk::Core:
buffSize = read(inotifyFd, buffer.data(), buffSize);
读取 inotify 文件句柄时, 被阻塞了.
原来是 QFileSystemWatcher 提供的接口太少. 这个问题在几年前开发云音乐客户端时
我也有遇到过, 当时的做法是写了一个类似的文件系统监控接口, 只是功能更全面了些:
在 DFileSystemWatcher 类里面找一下 inotify_init, 定位到了以下代码:
验证数十次之后, 搜狗输入法输入时导致主窗口冻结的问题再没有出现过.
另外, 这个输入法的问题, 在我经常使用的 openbox 环境里面, 并不能重现.
DFilesystemWatcher监控一个配置文件的变化. 在这个桌面环境里, 所有的 Qt 进程都会
自动载入这个插件. 但问题在于, 这个监控的操作, 是在 UI 线程里做的.
在这个线程里做了耗时的操作, 导致鼠标/键盘事件不能及时在 MessageLoop 里面被处理掉.
常见的解决方式就是把 I/O 相关的操作, 都放到后台的工作线程里. chrome 浏览器就是
典型的例子, 上面 gdb 有显示, 在主进程里, 有三四十个线程, 其中的很大一部分
就是 I/O 工作线程, 用于完成 http 请求, DNS 请求, SQLite 读写操作, 以及本地的缓存文件等.