【问题标题】:python tracing a segmentation faultpython跟踪分段错误
【发布时间】:2011-02-09 11:13:17
【问题描述】:

我正在从 python 开发 C 扩展,但我遇到了一些段错误(在开发过程中是不可避免的......)。

我正在寻找一种方法来显示段错误发生在哪一行代码(这个想法就像跟踪每一行代码),我该怎么做?

【问题讨论】:

    标签: python c debugging


    【解决方案1】:

    如果你在linux上,在gdb下运行python

    gdb python
    (gdb) run /path/to/script.py
    ## wait for segfault ##
    (gdb) backtrace
    ## stack trace of the c code
    

    【讨论】:

    • 如果你已经有一个核心文件,你可以使用gdb python core(或者任何核心文件被调用)。如果您在 OSX 上,核心转储(默认情况下不生成;请参阅 ulimit -c)存储在目录 /cores 中。
    • 我真的希望这是我的第一个答案,因为阅读所有其他内容需要相当多的时间,我宁愿花时间解决我的错误。
    • 如果你在运行像python -m unittest my.module.tests.mytest 这样的Python 单元测试时遇到段错误,那么-m 开关会混淆gdb。因此使用--args 选项调用:gdb --args python -m unittest my.module.tests.mytest
    • 与命令类似:`gdb --args python3.6 -c "import something; something.doStuff();" @Mark 你的回答对我很有帮助,谢谢!
    • 它只是说“没有指定可执行文件”
    【解决方案2】:

    这是一种输出代码运行的每一行 Python 的文件名和行号的方法:

    import sys
    
    def trace(frame, event, arg):
        print("%s, %s:%d" % (event, frame.f_code.co_filename, frame.f_lineno))
        return trace
    
    def test():
        print("Line 8")
        print("Line 9")
    
    sys.settrace(trace)
    test()
    

    输出:

    call, test.py:7
    line, test.py:8
    Line 8
    line, test.py:9
    Line 9
    return, test.py:9
    

    (当然,您可能希望将跟踪输出写入文件。)

    【讨论】:

    • @MadPhysicist:如果这就是你的意思,它不会打印你的 C 代码的行号。 :-) 它将打印调用 C 代码的 Python 代码的行号。
    • 这就是我的意思。我发现原来的问题很有趣,因为我遇到了同样的问题。段错误原来是因为我的 C 代码将 NULL 元素插入到 PyList_Object 中。当我尝试遍历列表时,它在 Python 端表现出来。不确定在这种情况下,python 调试器会有多大帮助。
    • 为了使输出更易于阅读(并且不会显着减慢程序速度),请使用an answer below 中提到的faulthandler 模块)——但即使你想使用跟踪,只需使用 Python 的内置跟踪模块 (python -m trace --trace test.py) -- 然而,这两种方法都不会在 C 代码中显示回溯。
    • 我想补充一点,我只是将它应用于我自己的代码进行调试。 sys.settrace 选项似乎永久处于活动状态,直到您使用 sys.settrace(None) 重置它。请记住这一点,否则您将来执行 python 脚本时会遇到问题,并且会看到您的代码非常慢等。
    【解决方案3】:

    来自 C 扩展的段错误通常是由于在创建对对象的新引用时没有增加引用计数的结果。这使得它们很难追踪,因为只有在从对象中删除最后一个引用之后才会发生段错误,即使这样也经常只有在分配其他对象时才会发生。

    您没有说到目前为止您编写了多少 C 扩展代码,但如果您刚刚开始考虑是否可以使用 ctypes 或 Cython。 Ctypes 可能不够灵活,无法满足您的需求,但您应该能够使用 Cython 链接到几乎任何 C 库,并自动为您维护所有引用计数。

    这并不总是足够的:如果您的 Python 对象和任何底层 C 对象具有不同的生命周期,您仍然会遇到问题,但它确实大大简化了事情。

    【讨论】:

    • 另外,将 NULL 放在不属于它们的地方。
    【解决方案4】:

    我来这里是为了寻找解决同一问题的方法,但其他答案都没有帮助我。有帮助的是faulthandler,您可以使用pip install 在Python 2.7 中安装它。

    faulthandler 仅在 3.3 版中被引入 Python,该版本于 2012 年 9 月发布,这是在此处编写大多数其他答案之后。

    【讨论】:

    • 不错!如果您可以读取 Python 程序的标准输出,则使用起来很简单:import faulthandler; faulthandler.enable()
    【解决方案5】:

    gdb 有一些未记录的 python 扩展。

    从 Python 源代码中获取 Tools/gdb/libpython.py(它不包含在正常安装中)。

    把这个放在sys.path

    然后:

    # gdb /gps/python2.7_x64/bin/python coredump
    ...
    Core was generated by `/usr/bin/python script.py'.
    Program terminated with signal 11, Segmentation fault.
    #0  call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
    ...
    (gdb) python
    >import libpython
    >
    >end
    (gdb) bt
    #0  call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
    #1  PyEval_EvalFrameEx (f=f@entry=
        Frame 0x7f9084d20ad0, 
        for file /usr/lib/python2.7/site-packages/librabbitmq/__init__.py, line 220, 
        in drain_events (self=<Connection(channels={1: <Channel(channel_id=1, connection=<...>, is_open=True, connect_timeout=4, _default_channel=<....(truncated), throwflag=throwflag@entry=0) at Python/ceval.c:2681
    ...
    (gdb) py-list
     218            else:
     219                timeout = float(timeout)
    >220            self._basic_recv(timeout)
     221
     222        def channel(self, channel_id=None):
    

    如您所见,我们现在可以看到与 CPython 调用链对应的 Python 堆栈。

    一些注意事项:

    • 您的 gdb 版本需要大于 7 并且需要使用--with-python 编译
    • gdb 嵌入python(通过链接到libpython),它不会在子shell 中运行它。这意味着它可能不一定与$PATH 上的python 版本匹配。
    • 您需要从与 gdb 链接到的任何匹配的 Python 源的任何版本下载 libpython.py
    • 您可能必须以 root 身份运行 gdb - 如果是这样,您可能需要设置 sys.path 以匹配您正在调试的代码。

    如果您无法将libpython.py 复制到sys.path,那么您可以将其位置添加到sys.path,如下所示:

    (gdb) python
    >import sys
    >sys.path.append('/path/to/containing/dir/')
    >import libpython
    >
    >end
    

    这在python dev docsthe fedora wikithe python wiki 中记录得有些差

    如果您有一个较旧的gdb 或者只是无法使其正常工作,那么 Python 源代码中还有一个 gdbinit,您可以将其复制到 ~/.gdbinit,它添加了一些类似的功能

    【讨论】:

    • 或者,可以启动一个像这样的脚本:gdb --args python3 script.py,然后从 gdb-shell 执行 run 命令。脚本崩溃后,可以执行backtrace命令获取trace。
    【解决方案6】:

    这里还有 3 种选择:

    1:在启用faulthandler 的情况下执行脚本:

    python3 -X faulthandler your_script.py
    

    2:在调试模式下执行脚本 (pdb)

    python3 -m pdb your_script.py
    

    并使用continue 命令执行脚本。

    gdb 工具提供的信息最多,但它们都没有打印我脚本中最后执行的行号。

    3:我最终使用了pytest。为此,我将我的代码包装在一个前缀为 test_ 的函数中,并像这样执行脚本:

    pytest your_script.py
    

    【讨论】:

      猜你喜欢
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-06
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 2015-03-15
      相关资源
      最近更新 更多