【问题标题】:Debugging python segmentation faults in garbage collection调试垃圾收集中的python分段错误
【发布时间】:2017-02-20 19:16:11
【问题描述】:

我遇到了在 cPython 中进行垃圾收集期间发生的分段错误 (SIGSEGV)。我也遇到过一个进程被 SIGBUS 杀死的例子。我自己的代码主要是 python 和一些非常高级的 Cython。我肯定不会 - 故意和明确地 - 玩弄指针或直接写入内存。


来自核心转储的示例回溯(使用 gdb 提取):

#0  0x00007f8b0ac29471 in subtype_dealloc (self=<Task at remote 0x7f8afc0466d8>)
    at /usr/src/debug/Python-3.5.1/Objects/typeobject.c:1182
#1  0x00007f8b0abe8947 in method_dealloc (im=0x7f8afc883e08) at /usr/src/debug/Python-3.5.1/Objects/classobject.c:198
#2  0x00007f8b0ac285a9 in clear_slots (type=type@entry=0x560219f0fa88, 
    self=self@entry=<Handle at remote 0x7f8afc035948>) at /usr/src/debug/Python-3.5.1/Objects/typeobject.c:1044
#3  0x00007f8b0ac29506 in subtype_dealloc (self=<Handle at remote 0x7f8afc035948>)
    at /usr/src/debug/Python-3.5.1/Objects/typeobject.c:1200
#4  0x00007f8b0ac8caad in PyEval_EvalFrameEx (
    f=f@entry=Frame 0x56021a01ff08, for file /usr/lib64/python3.5/asyncio/base_events.py, line 1239, in _run_once (self=<_UnixSelectorEventLoop(_coroutine_wrapper_set=False, _current_handle=None, _ready=<collections.deque at remote 0x7f8afd39a250>, _closed=False, _task_factory=None, _selector=<EpollSelector(_map=<_SelectorMapping(_selector=<...>) at remote 0x7f8afc868748>, _epoll=<select.epoll at remote 0x7f8b0b1b8e58>, _fd_to_key={4: <SelectorKey at remote 0x7f8afcac8a98>, 6: <SelectorKey at remote 0x7f8afcac8e08>, 7: <SelectorKey at remote 0x7f8afcac8e60>, 8: <SelectorKey at remote 0x7f8afc873048>, 9: <SelectorKey at remote 0x7f8afc873830>, 10: <SelectorKey at remote 0x7f8afc873af0>, 11: <SelectorKey at remote 0x7f8afc87b620>, 12: <SelectorKey at remote 0x7f8afc87b7d8>, 13: <SelectorKey at remote 0x7f8afc889af0>, 14: <SelectorKey at remote 0x7f8afc884678>, 15: <SelectorKey at remote 0x7f8afc025eb8>, 16: <SelectorKey at remote 0x7f8afc889db0>, 17: <SelectorKey at remote 0x7f8afc01a258>, 18: <SelectorKey at remote 0x7f8afc...(truncated), 
    throwflag=throwflag@entry=0) at /usr/src/debug/Python-3.5.1/Python/ceval.c:1414

在扫荡期间(我认为):

#1  0x00007f0a0624c63f in visit_decref (op=, data=<optimized out>)
    at /usr/src/debug/Python-3.5.1/Modules/gcmodule.c:373
#2  0x00007f0a0624b813 in subtract_refs (containers=<optimized out>)
    at /usr/src/debug/Python-3.5.1/Modules/gcmodule.c:398
#3  collect (generation=generation@entry=2, n_collected=n_collected@entry=0x7f09d75708c8, 
    n_uncollectable=n_uncollectable@entry=0x7f09d75708d0, nofail=nofail@entry=0)
    at /usr/src/debug/Python-3.5.1/Modules/gcmodule.c:951

而且在 malloc 期间也有一次:

#0  _PyObject_Malloc (ctx=0x0, nbytes=56) at /usr/src/debug/Python-3.4.3/Objects/obmalloc.c:1159
1159                if ((pool->freeblock = *(block **)bp) != NULL) {
(gdb) bt
#0  _PyObject_Malloc (ctx=0x0, nbytes=56) at /usr/src/debug/Python-3.4.3/Objects/obmalloc.c:1159

还有 SIGBUS 跟踪(看起来它是在 cPython 从另一个错误中恢复时发生的):

#0  malloc_printerr (ar_ptr=0x100101f0100101a, ptr=0x7f067955da60 <generations+32>, str=0x7f06785a2b8c "free(): invalid size", action=3) at malloc.c:5009
5009        set_arena_corrupt (ar_ptr);
(gdb) bt
#0  malloc_printerr (ar_ptr=0x100101f0100101a, ptr=0x7f067955da60 <generations+32>, str=0x7f06785a2b8c "free(): invalid size", action=3) at malloc.c:5009
#1  _int_free (av=0x100101f0100101a, p=<optimized out>, have_lock=0) at malloc.c:3842
Python Exception <type 'exceptions.RuntimeError'> Type does not have a target.:

这些回溯来自 Fedora 24 和 Python 3.5.1,一些来自 Centos 7 和 Python 3.4.3。所以我排除了以下问题:

  • 内存不好(可能是,但很巧合的是,我的笔记本电脑和(虚拟)服务器出现了相同的问题,并且没有显示其他工作正常)。
  • 操作系统或 cPython 或其组合中的问题。

所以 - 像往常一样 - 它一定是我自己的代码中的东西。它是(计算)“任务”的某些并发的线程代码和运行异步循环的线程的混合体。代码库还有其他运行良好的“工作负载”。 '服务'这个'工作负载'的代码对我来说很突出,我使用(threading.RLock)锁来序列化一些请求并且我正在序列化并写入磁盘。

任何关于如何找到根本原因的建议将不胜感激!

我尝试过的事情:

  • 将使用的外部依赖项剥离到最低限度(即 cloudpickle):没有区别
  • 转储所有对象类型的计数,如垃圾收集器在显式收集之前和之后看到的,并查看在崩溃之前是否有对象类型被跟踪并且不在“成功”的垃圾收集中:没有。
  • 使用 GDB 处理核心转储:在 C 和 Python 回溯方面并不一致。
  • 以 MALLOC_CHECK_=2 运行:除了进程已以退出代码 -11 退出之外,我没有看到任何错误消息。

编辑 1

有东西:https://gist.github.com/frensjan/dc9bc784229fec844403c9d9528ada66

最值得注意的是:

==19072== Invalid write of size 8
==19072==    at 0x47A3B7: subtype_dealloc (typeobject.c:1157)
==19072==    by 0x4E0696: PyEval_EvalFrameEx (ceval.c:1388)
==19072==    by 0x58917B: gen_send_ex (genobject.c:104)
==19072==    by 0x58917B: _PyGen_Send (genobject.c:158)
...
==19072==    by 0x4E2A6A: call_function (ceval.c:4262)
==19072==    by 0x4E2A6A: PyEval_EvalFrameEx (ceval.c:2838)
==19072==  Address 0x8 is not stack'd, malloc'd or (recently) free'd

==19072== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==19072==  Access not within mapped region at address 0x8
==19072==    at 0x47A3B7: subtype_dealloc (typeobject.c:1157)
==19072==    by 0x4E0696: PyEval_EvalFrameEx (ceval.c:1388)
==19072==    by 0x58917B: gen_send_ex (genobject.c:104)
==19072==    by 0x58917B: _PyGen_Send (genobject.c:158)

但是 valgrind 发现的错误指向与我之前得到的核心转储相同的位置,离我的代码不远……不太确定该怎么处理。

环境:在 Centos 7 上使用 ./configure --without-pymalloc 构建的 Python 3.4.5+。使用 valgrind --tool=memcheck --dsymutil=yes --track-origins=yes --show-leak-kinds=all --trace-children=yes python ... 运行 python

非常感谢任何帮助!

【问题讨论】:

  • This question 展示了如何在调试模式下编译 python 并在 valgrind 下运行它的方法。如果你幸运的话,这可以为你指明正确的方向。
  • 感谢@voo!得到了一些东西:valgrind output 但 valgrind 发现的错误指向与我之前得到的核心转储相同的位置,离我的代码很远......不太确定该怎么做。环境:在 Centos 7 上使用./configure --without-pymalloc 构建的 Python 3.4.5+。使用valgrind --tool=memcheck --dsymutil=yes --track-origins=yes --show-leak-kinds=all --trace-children=yes python ... 运行 python

标签: python c multithreading garbage-collection memory-corruption


【解决方案1】:

我相当确信我遇到的问题是由于 CPython 中的 issue 26617 而在 7bfa6f2 中修复的。

我已经通过在 7bfa6f2 之前的提交版本重现问题(运行到 SIGSEGV)来检查这一点,并且无法通过提交的版本重现它7bfa6f2。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 2015-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-21
    相关资源
    最近更新 更多