Ctypes - 从使用ctypes的Python代码获取C语言回溯

3 投票
1 回答
698 浏览
提问于 2025-04-20 23:48

我正在调试一段代码,这段代码是用Python调用一个C语言的函数,使用的是ctypes库。我关注的那行Python代码大概是这样的:

returnValue = cfunction()

这里的cfunction就是那个C语言的函数。我想知道在cfunction的代码中,它是从哪里返回的。我该怎么做呢?

看起来我可以在Python中使用gdb这个调试工具,但我不太确定怎么正确使用gdb,以便在上面那行代码设置一个断点,并显示C语言代码中cfunction是从哪里返回的。当然,我已经用-g选项编译了C代码。

其实我并不一定要用gdb,只要能用一些免费的工具(无论是免费软件还是开源软件)在Linux上做到这一点就可以。

(我使用的是Python 2.7.6,gdb 7.7,C代码是用gcc 4.8.2编译的。)

1 个回答

2

如果你在任何合适的调试工具下运行 python(包括 gdb),无论是直接启动还是附加到正在运行的程序上,它都可以在 C 代码中设置断点。这些 C 代码可能是 Python 的一部分,也可能是作为扩展模块加载的,或者通过 `ctypes` 加载的,或者其他方式加载的。设置好断点后,你可以打印出调用栈,逐行调试,或者进出函数,等等,想做什么都可以。这就像正常的调试会话一样。(当然,你的 Python 可能没有调试符号,但只要你的 .so 文件有调试符号,那就足够了,对吧?)

举个例子(使用 lldb,不过我想我用的命令都是和 gdb 兼容的…):

$ lldb python3
Current executable set to 'python3' (x86_64).
(lldb) run
Process 6828 launched: '/Library/Frameworks/Python.framework/Versions/3.4/bin/python3' (x86_64)
Process 6828 stopped
* thread #1: tid = 0x8cf0d1, 0x00007fff5fc01028 dyld`_dyld_start, stop reason = exec
    frame #0: 0x00007fff5fc01028 dyld`_dyld_start
dyld`_dyld_start:
-> 0x7fff5fc01028:  popq   %rdi
   0x7fff5fc01029:  pushq  $0x0
   0x7fff5fc0102b:  movq   %rsp, %rbp
   0x7fff5fc0102e:  andq   $-0x10, %rsp
(lldb) c
Process 6828 resuming
Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 00:54:21)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> libc = ctypes.CDLL('/usr/lib/libc.dylib')
Process 6828 stopped
* thread #1: tid = 0x8cf0d1, 0x00007fff8a7149aa libsystem_kernel.dylib`__select + 10, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff8a7149aa libsystem_kernel.dylib`__select + 10
libsystem_kernel.dylib`__select + 10:
-> 0x7fff8a7149aa:  jae    0x7fff8a7149b4            ; __select + 20
   0x7fff8a7149ac:  movq   %rax, %rdi
   0x7fff8a7149af:  jmpq   0x7fff8a71119a            ; cerror
   0x7fff8a7149b4:  ret
(lldb) b printf
Breakpoint 1: where = libsystem_c.dylib`printf, address = 0x00007fff875cf8a8
(lldb) c
Process 6828 resuming
>>> libc.printf('spam')
Process 6828 stopped
* thread #1: tid = 0x8cf0d1, 0x00007fff875cf8a8 libsystem_c.dylib`printf, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00007fff875cf8a8 libsystem_c.dylib`printf
libsystem_c.dylib`printf:
-> 0x7fff875cf8a8:  pushq  %rbp
   0x7fff875cf8a9:  movq   %rsp, %rbp
   0x7fff875cf8ac:  pushq  %r15
   0x7fff875cf8ae:  pushq  %r14
(lldb) bt
* thread #1: tid = 0x8cf0d1, 0x00007fff875cf8a8 libsystem_c.dylib`printf, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x00007fff875cf8a8 libsystem_c.dylib`printf
    frame #1: 0x0000000101a545e7 _ctypes.so`ffi_call_unix64 + 79
    frame #2: 0x0000000101a5549f _ctypes.so`ffi_call + 575
    frame #3: 0x0000000101a4f81f _ctypes.so`_ctypes_callproc + 879
    frame #4: 0x0000000101a4772a _ctypes.so`PyCFuncPtr_call + 314
    frame #5: 0x000000010000da08 Python`PyObject_Call + 104
    frame #6: 0x00000001000e1c3f Python`PyEval_EvalFrameEx + 16975
    frame #7: 0x00000001000e665d Python`PyEval_EvalCodeEx + 2349
    frame #8: 0x00000001000e671f Python`PyEval_EvalCode + 63
    frame #9: 0x000000010010f5ba Python`PyRun_InteractiveOneObject + 474
    frame #10: 0x000000010010f93e Python`PyRun_InteractiveLoopFlags + 110
    frame #11: 0x00000001001113e1 Python`PyRun_AnyFileExFlags + 161
    frame #12: 0x000000010012867f Python`Py_Main + 3535
    frame #13: 0x0000000100000e32 Python
    frame #14: 0x0000000100000c84 Python
(lldb)

撰写回答