如何在运行时调试共享库?

48

有谁能告诉我如何在共享库上进行运行时调试?

我需要对我的共享库中的一个函数进行运行时调试,但它被另一个程序调用。 我该如何做类似于dbx的共享库调试?

我正在AIX上使用dbx。 相比我要做的事情,gdb是否更好?

6个回答

39

你只需要使用可执行文件调用 gdb(无论是你自己的还是第三方的都可以)。以下是一个示例,我调试ls命令并在(共享)c库中设置断点。该示例使用支持延迟(挂起)断点的 gdb 6.8,这使得操作变得容易:

gdb /bin/ls
GNU gdb 6.8-debian
Copyright (C) 2008 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"...
(no debugging symbols found)
(gdb) b write
Function "write" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (write) pending.
(gdb) r
Starting program: /bin/ls
(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]
(no debugging symbols found)
(no debugging symbols found)
[New Thread 0x7f98d2d23780 (LWP 7029)]
[Switching to Thread 0x7f98d2d23780 (LWP 7029)]

Breakpoint 1, 0x00007f98d2264bb0 in write () from /lib/libc.so.6
(gdb)

正如你所见,gdb会自动管理可执行文件使用的所有线程。在这里,你不需要为线程做任何特殊处理。断点将在任何线程中起作用。

或者,如果你想要将调试器附加到已经运行的应用程序上(我在这里使用tail -f /tmp/ttt作为示例):

ps ux | grep tail
lothar    8496  0.0  0.0   9352   804 pts/3    S+   12:38   0:00 tail -f /tmp/ttt
lothar    8510  0.0  0.0   5164   840 pts/4    S+   12:39   0:00 grep tail

gdb
GNU gdb 6.8-debian
Copyright (C) 2008 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"...
(no debugging symbols found)
(gdb) attach 8496
Attaching to program: /usr/bin/tail, process 8496
Reading symbols from /lib/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/librt.so.1
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
[New Thread 0x7f24853f56e0 (LWP 8496)]
Loaded symbols for /lib/libpthread.so.0
Reading symbols from /lib/ld-linux-x86-64.so.2...
(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
(no debugging symbols found)
0x00007f2484d2bb50 in nanosleep () from /lib/libc.so.6
(gdb) b write
Breakpoint 1 at 0x7f2484d57bb0
(gdb) c
Continuing.
[Switching to Thread 0x7f24853f56e0 (LWP 8496)]

Breakpoint 1, 0x00007f2484d57bb0 in write () from /lib/libc.so.6
(gdb)

我在AIX上使用了dbx。 如果你说gdb可以处理多线程应用程序,那么dbx就非常糟糕了。我一直使用它,真是太傻了。 - debugger
我不了解dbx,但它应该具有类似的功能。 - lothar
当尝试调试Oracle的SQLPlus时,这种方法将无法工作。 - Kapil Vyas

13

通常,调试共享库的过程与调试可执行文件的过程基本相同 - 主要区别在于,在共享库加载到内存之前,您可能无法设置断点。您需要将调试器附加到主可执行文件上。

如果您正在调试一个不属于您但在插件架构中使用您模块的应用程序,则仍然使用相同的方法。请确保(像往常一样)为您的共享库提供了调试信息。在Windows中,您可以生成.pdb文件。使用gcc,我认为您需要指定特殊的编译器标志(-g?)以确保提供调试信息。您需要将调试器附加到第三方应用程序上。


如果主可执行文件不是我的,而是第三方的......但我正在编写一个将被第三方使用的模块......那么我该如何调试呢? - debugger
你仍然可以使用gdb启动可执行文件(或附加到进程)。在库加载后,gdb可以轻松设置断点。 - lothar
但是,当您在共享库代码中设置断点并且主可执行文件调用它时,主可执行文件会被阻塞吗? - debugger
它将以与您在主可执行文件中设置的任何其他断点完全相同的方式工作。 - 1800 INFORMATION
如果主可执行文件中有线程,那么我该如何调试共享库代码呢?这并不简单。我尝试设置断点到我的函数,但它们从未停止。我甚至可以看到我的符号,并且我有日志证实我的共享库代码正在执行。 - debugger

5

以下是对 lothar 回答的进一步说明:

我正在使用 python 和 python 的单元测试库 unittest 运行位于 Linux 上的动态库 test.so(由 test.c 编译而来)上的测试,测试脚本为 tests/test_pwmbasic.py。(我现在才意识到命名方式有点单调)

~/my/test/path/
    tests/
        __init__.py
        test_pwmbasic.py
    test.c
    test.so

我希望从test_pwmbasic.py的刺激中调试test.so中的内容。这是我使它工作的方法...
$ cd ~/my/test/path
$ gdb $(which python)
   ... gdb blah ...
(gdb) b test.c:179
(gdb) run
>>> from tests.test_pwmbasic import *
>>> import unittest
>>> unittest.main()
   ... unittest blah ...
Breakpoint 1, pwmTest_setDutyCycles (dutyCycles=0x7ffff7ece910) at ./test.c:179
(gdb) print pwm_errorCode
$1 = PWM_ERROR_NONE

现在我想要和gdb结婚

注意:test.c还包括../pwm.c,因此我也可以在该库内设置断点

(gdb) b pwm.c:123

1

我很久没有在AIX上使用dbx了,我也遇到了这个问题。但对我来说,安装gdb并不是一个选项。

dbx  /path/to/your/program
(dbx) run [args to your program]
(dbx) set $ignoreonbptrap           # I kept hitting a trace/bpt trap
(dbx) set $deferevents              # allows setting bp in not loaded shared library
(dbx) set $repeat                   # useful, repeat commands with <enter> tjust like gdb
(dbx) stop in MySharedLibraryFunc   # defers breakpoint
(dbx) cont

1

我记得通过创建一个使用它的模拟应用程序来测试共享库。如果你愿意做很多工作,你可以创建第二个模拟共享库,只收集有关第三方应用程序如何使用该库的信息,然后让你的模拟应用程序重放这些信息。

当然,不要怀疑printf和fprintf调用的威力。


1
你可以尝试静态编译和链接库来调试它。
如果你的 bug 只在编译为共享库时出现,那么这可能会给你一些线索。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接