Ctypes - 如何从使用 ctypes 的 Python 代码中获取 C 语言的回溯信息

3

我正在尝试调试一段涉及Python通过ctypes调用C函数的代码。我关心的Python代码行大致如下:

returnValue = cfunction()

其中cfunction是一个C函数。我想知道在cfunction代码中,cfunction从哪里返回。我该如何做?

看起来我可以使用带有python的gdb,但我不确定如何正确使用gdb,以便我可以在上面的行上设置断点并显示C代码中C函数返回的位置。当然,我已经使用-g编译了C代码。

这不是我必须使用gdb来完成的要求,只要我能够使用Linux中的一些免费(像啤酒或演讲)工具来完成它。

(我正在运行python 2.7.6,gdb 7.7,并且使用gcc 4.8.2编译了C代码。)

1个回答

2
如果您在任何合理的调试器(包括gdb)下运行python,无论是通过这种方式启动还是附加到它,它都可以在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)

很抱歉,我认为我大致明白你在做什么,但我仍然不确定如何在我拥有的Python脚本中设置断点。我怀疑我对如何使用调试器一般知之甚少,需要做更多的工作,才能在SO上合理地提出这个问题。 - user327301
@raoulcousins:你想在Python代码中设置断点还是在C代码中?如果是前者,你需要使用pdb而不是C级别的调试器。如果是后者,你需要阅读有关gdb或其他调试器的教程,并/或找到一个漂亮的GUI IDE来包装它,这样你就可以单击代码行旁边的“下一步”按钮并添加断点...但基本思路是b printf。如果我的程序中有一个明确的C函数名为printf,那么该命令将在该函数的开头设置断点。 - abarnert
@raoulcousins:事情可能会变得有些复杂,但对于简单的情况……好吧,如果您可以通过编写“mylib.cfunction()”从ctypes调用函数,通常可以通过编写“b cfunction”从“gdb”断点该函数。 - abarnert
最终,我想知道的是cfunction从哪里返回(它有许多返回点)。我想我可以在cfunction可能返回的每个地方都设置断点,然后从那里开始。 - user327301
@raoulcousins:一旦你到达断点,你可以使用n逐行步进(它“跳过”任何函数调用而不是深入其中),直到到达其中一个return语句。或者你可以直接在return语句上设置断点。但是,你真的需要阅读调试器教程并开始尝试;没有人能够在SO评论中通过对话来教你如何使用调试器。 - abarnert
显示剩余5条评论

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