Archive:Development/Tutorials/Debugging/How to create useful crash reports (zh CN)

    From KDE TechBase

    Template:I18n/Language Navigation Bar (zh CN)

    简介

    本文说明如何重现有用的 KDE 应用程序当机(crash)的回溯(backtrace)。首先,提供一些基本信息。然后,我们将说明在几个发行版中如何准备您的 KDE 套件,以获得回溯。这对大多数人就已足够。其他部分是关于如何用 GNU Debugger 和 Valgrind 建立回溯,这在某些情况下非常有用。

    如何建立有用的当机回报

    一份良好的 Bugzilla 当机回报由两部分组成:如何重现当机的描述和当机的回溯。如果缺少其中一个要素,开发人员会很难(但不是不可能)解决问题。

    说明不要只有「它当掉了」(it crashed)。尝试描述当机之前的一切情况。按下一个按钮、打开一个特定的网站或档案,才发生问题?这些小细节可能看起来没有什么用,但对开发者来说可能是有用的,所以请把它写下来。

    一份更有深刻见解的文章,如何写出好的错误的描述可以在此链接找到,请在回报错误之前阅读它。

    不要把回溯放在错误(bug)回报的附件。相反地,简单地贴上回报。这种方式开发者更容易寻找重复的回报,因为附件不会被搜寻到。

    如果您贴上回溯到回报,请确定一定从回溯删除所有

    (no debugging symbols found)
    

    ,不要只有一行或两行,因为会增加阅读上的困难。

    即使是直接贴上回溯优于增加一个附件,但请不要贴上其他东西,如日志(Valgrind、strace或终端输出)或范例数据(邮件、HTML档案等)。这些项目请使用附件。

    回溯

    回溯是不可缺少的。他们对你来说可能看起来毫无意义,但实际上它们含有丰富的有用信息。回溯说明当机之前呼叫哪些函式,使开发者可以追踪哪个函式开始出问题。拥有良好的回溯有一个缺点:函式库和可执行文件占用更多的硬盘空间比其优化的部分。这就是为什么许多发行版选择安装时拿掉档案,结果造成无用的回溯

    (no debugging symbols found)
    Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    [Thread debugging using libthread_db enabled]
    [New Thread -1233848624 (LWP 12212)]
    [New Thread -1255081072 (LWP 12820)]
    [New Thread -1240921200 (LWP 12819)]
    [New Thread -1266680944 (LWP 12818)]
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    (no debugging symbols found)
    0xffffe410 in __kernel_vsyscall ()
    #0  0xffffe410 in __kernel_vsyscall ()
    #1  0xb6a1210b in ?? () from /lib/tls/i686/cmov/libpthread.so.0
    #2  0xb6a85afe in ?? () from /usr/lib/libX11.so.6
    #3  0x00000003 in ?? ()
    #4  0x082149c0 in ?? ()
    #5  0x00003ffc in ?? ()
    #6  0x00000000 in ?? ()
    

    但不用担心,经过一些修改,您可以建立完整成熟的 KDE 应用程序回溯。

    准备你的KDE套件

    如果你的发行版有除错功能的套件,请安装它们。

    查看回溯很容易发现你缺少哪些除错套件。例如,下一行就是来自回溯:

    #6  0xb7975bdc in ?? () from /usr/lib/libkmailprivate.so.4
    

    ?? 表示 libkmailprivate.so.4 函式库没有除错信息,它可能是放在单独的除错套件。在这种情况下很容易猜测,您需要安装 KMail 的除错套件以得到更好的回溯。

    有时候,你需要安装一个以上的除错套件,才能获得良好的回溯。这取决于发行版如何划分套件。例如说,一些发行版安装 kdepim 除错套件,就可以得到足够的 KMail 当机的除错信息,其他发行版可能还要额外的 KMail 除错套件。

    这里有一份发行版如何获得除错套件的列表:

    • Debian - Debian 提供的 -dbg 套件可以更容易建立有用回溯。只要安装对应的 -dbg 套件。例如 kdepim-dbg 用于 KMail 当机。-dbg 的相依性可以确保加入其他正确的套件(kdelibs-dbg、gdb,等)。
    • FreeBSD ports - 请参阅 KDE on FreeBSD FAQ
    • Gentoo - Gentoo 有自己的文件,说明如何进行。
    • Mandriva - Mandriva 2007.0 和之后的版本,KDE 全部都有额外的除错套件(实际上,所有的套件都有)。只要安装对应的 -debug 套件,像 kdebase-debugkdemultimedia-debug 。不管怎样,你可能想安装 kdelibs-debug
      • 注:-debug套件在不同的仓库。例如,在 main 中所有的套件 ,你会发现除错套件在 debug_main 仓库。
    • Kubuntu/Ubuntu - The Ubuntu 家族要做这些事是很容易的。在套件库中,每一个官方 KDE 模块都有一个后缀 -dbg 的附加套件。经常要安装 kdelibs5-dbg,因为所有的 KDE 应用程序都使用 kdelibs(kdelibs-dbg 用于 KDE 3 应用程序)。然后你应该安装当机应用程序的 -dbg 套件。例如,如果 KOrganizer 当机,你应该安装 kdepim-dbg。如果程序不是来自官方 KDE 模块,也没有 -dbg 套件。您可以从这个程序当机的除错网页的套件列表安装 -dbgsym 套件。
      在 Ubuntu 开发周期, Apport 当机处理程序会打开,并回报当机到 launchpad.net 和显示回溯给您。如果你想要使用 KDE 当机处理程序,在 /etc/defaults/apport 关闭 Apport
      Lucid Lynx(10.04)开始 Kubuntu 将转发上游所有非 kubuntu 特有的错误,并禁用 Apport 因此 DrKonqui 将次作为预设当机处理。
    • openSUSE - 你只需要安装 -debuginfo 套件,例如:kdepimlibs4-debuginfo。您可以在KDE 套件库找到这些套件。此外还有一个专门的openSUSE 除错页面
    • Fedora - Fedora 有自己的文件,说明如何进行。(必须启用 debuginfo 套件库。)

    如果你的发行版没有除错功能的 KDE 套件,你必须从原始码编译 KDE:

    • 如果你使用 KDE 4,在 CMake 阶段,你应该使用 -DCMAKE_BUILD_TYPE=debugfull 参数。如果你想指定自己的 CXXFLAGS,请使用-DCMAKE_BUILD_TYPE=None CMAKE_CXX_FLAGS="-O0 -g" 。您可以根据您的需要更改 CMAKE_CXX_FLAGS。

    接者只是 makemake install,就像你所熟悉的。

    当机!

    当你的应用程序当机的时候。KDE 当机对话框应该会在当机后出现,它会显示回溯分页。

    KDE 当机对话框
    KDE 当机对话框

    单击该分页,并稍等一下。过程可能需要相当内存,因此可能会突然变慢。但结束后应该会改善。例如:

    Using host libthread_db library "/lib/libthread_db.so.1". 
    [Thread debugging using libthread_db enabled] 
    [New Thread -1232783168 (LWP 7604)] 
    [KCrash handler] 
    #6  0x0806be76 in TreeMapItem::parent (this=0x0) 
        at /home/bram/KDE/kde3/kdeaddons/konq-plugins/fsview/treemap.h:285 
    #7  0x08065fea in TreeMapItemList::compareItems (this=0xbfec04a8, item1=0x0, 
        item2=0x0) 
        at /home/bram/KDE/kde3/kdeaddons/konq-plugins/fsview/treemap.cpp:720 
    #8  0xb7281619 in QGList::operator== () from /usr/qt/3/lib/libqt-mt.so.3 
    #9  0x0806d498 in QPtrList<TreeMapItem>::operator== (this=0xbfec04a8, 
        list=@0xbfec0468) at /usr/qt/3/include/qptrlist.h:74 
    #10 0x08062e18 in TreeMapWidget::mousePressEvent (this=0xbfec03ac, 
        e=0xbfebff1c) 
        at /home/bram/KDE/kde3/kdeaddons/konq-plugins/fsview/treemap.cpp:1840 
    #11 0xb7004a63 in QWidget::event () from /usr/qt/3/lib/libqt-mt.so.3 
    #12 0xb6f6bca7 in QApplication::internalNotify () 
       from /usr/qt/3/lib/libqt-mt.so.3 
    #13 0xb6f6ca88 in QApplication::notify () from /usr/qt/3/lib/libqt-mt.so.3 
    #14 0xb7725a84 in KApplication::notify (this=0xbfec055c, receiver=0xbfec03ac, 
        event=0xbfebff1c) 
        at /home/bram/KDE/kde3/kdelibs/kdecore/kapplication.cpp:550 
    #15 0xb6f0bfd2 in QETWidget::translateMouseEvent () 
       from /usr/qt/3/lib/libqt-mt.so.3 
    #16 0xb6f0b8b0 in QApplication::x11ProcessEvent () 
       from /usr/qt/3/lib/libqt-mt.so.3 
    #17 0xb6f1b761 in QEventLoop::processEvents () from /usr/qt/3/lib/libqt-mt.so.3 
    #18 0xb6f82831 in QEventLoop::enterLoop () from /usr/qt/3/lib/libqt-mt.so.3 
    #19 0xb6f826b6 in QEventLoop::exec () from /usr/qt/3/lib/libqt-mt.so.3 
    #20 0xb6f6b72f in QApplication::exec () from /usr/qt/3/lib/libqt-mt.so.3 
    #21 0x0805181e in main (argc=134673960, argv=0xffffffff) 
        at /home/bram/KDE/kde3/kdeaddons/konq-plugins/fsview/main.cpp:55
    

    这样看起来好多了,对不对?它显示内存地址、原始码档案和行号和传递给函式的参数。这样更有助于开发人员找到问题。

    Template:Note (zh CN)

    使用 GDB 检索回溯

    在某些情况下,不可能建立一个 KDE 当机对话框的回溯。这可能是由于应用程序,进入了无限循环,或当机对话框因为某种原因并没有出现。您可以尝试使用gdbGNU Debugger)取得回溯。GDB is widely available through distribution packages.

    可以在不同的情况呼叫 GDB 。您可以从 gdb 内部执行应用程序,或附加 gdb 到一个已执行的行程。后者当应用程序,已经进入了无限循环时,可能是有用的。但是,我们先由在 gdb 内部执行应用程序开始。在 shell,执行:

    $ gdb someKDEapp
    

    GDB 将出现提示。请注意,这不会启动应用程序本身,你应该透过呼叫 run 指令执行它:

    (gdb) run
    

    这将执行应用程序,就像你习惯的一样,你可以像平常一样使用它(它仅消耗更多的内存,并可能觉得缓慢)。现在是时候重现你的当机。当你成功时,应用程序只会关闭,然后您应该回到 GDB 提示。现在是时候执行「backtrace」指令:

    Template:Note (zh CN)

    (gdb) thread apply all backtrace
    

    应该会出现一个可张贴在 KDE Bugzilla 的良好回溯。

    如果要附加到现有的行程中,请在 shell 执行以下指令:

    $ gdb someKDEapp pid
    

    其中pid是你想要附加行程的 ID。一旦附加,而这个行程又是一个无限回圈,在使用「backtrace」指令后将出现一个有用的回溯。你可以使用「continue」指令,让应用程序继续执行,在 gdb 按下 Ctrl+C 能够再次回到命令。

    使用 Valgrind 检索回溯

    当涉及到当机,Valgrind 也是一个建立回溯的有用工具。这不是一个GDB替代品,而是一个补充。

    当您在 Valgrind 执行应用程序时,每一块应用程序读取或写入的内存都会检查。Valgrind 会在标准输出或日志文件中,回报错误的记忆体操作。由于大多数当机是由于无效的内存读取,Valgrind 可用于追踪什么地方发生问题。

    Template:Note (zh CN)

    和 GDB一样,Valgrind 使得应用程序执行变慢,而且消耗很多资源。

    在valgrind 内部启动应用程序:

    $ valgrind --log-file=someKDEapp someKDEapp
    

    现在重现当机。这件事一发生,应用程序和 Valgrind 就会终止。只留下一个名为 someKDEapp.pid 的档案,其中 pid 是代表 Valgrind 行程的 ID。这个档案可能会列出比当机原因更多的错误。这里是对应于上面 GDB 回溯的当机原因:

    ==23292== Invalid read of size 4
    ==23292==    at 0x806BD9E: TreeMapItem::parent() const (treemap.h:285)
    ==23292==    by 0x8065FB9: TreeMapItemList::compareItems(void*, void*) (treemap.cpp:720)
    ==23292==    by 0x50AC618: QGList::operator==(QGList const&) const (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==    by 0x806D3BF: QPtrList<TreeMapItem>::operator==(QPtrList<TreeMapItem> const&) const (qptrlist.h:74)
    ==23292==    by 0x8062DE7: TreeMapWidget::mousePressEvent(QMouseEvent*) (treemap.cpp:1840)
    ==23292==    by 0x4E2FA62: QWidget::event(QEvent*) (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==    by 0x4D96CA6: QApplication::internalNotify(QObject*, QEvent*) (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==    by 0x4D97A87: QApplication::notify(QObject*, QEvent*) (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==    by 0x4809AC3: KApplication::notify(QObject*, QEvent*) (kapplication.cpp:550)
    ==23292==    by 0x4D36FD1: QETWidget::translateMouseEvent(_XEvent const*) (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==    by 0x4D368AF: QApplication::x11ProcessEvent(_XEvent*) (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==    by 0x4D46760: QEventLoop::processEvents(unsigned) (in /usr/qt/3/lib/libqt-mt.so.3.3.8)
    ==23292==  Address 0x2C is not stack'd, malloc'd or (recently) free'd
    

    但可以肯定是,只要把整个日志文件放到当机回报的附件。