【问题标题】:segmentation fault - core dump in python C-extension分段错误-python C 扩展中的核心转储
【发布时间】:2015-06-06 10:37:48
【问题描述】:

我正在为 python 编写一个 c 扩展。正如您在下面看到的,代码的目的是计算两个向量的欧几里得距离。 第一个参数 n 是向量的维度, 第二个,第三个参数是浮点数的两个列表。

我在 python 中这样调用函数:

import cutil
cutil.c_euclidean_dist(2,[1.0,1,0],[0,0])

它运行良好,返回正确的结果。 但如果我这样做超过 100 次(维度为 1*1000),则会导致分段错误 - 核心转储:

#!/usr/bin/env python
#coding:utf-8
import cutil
import science
import time
a = []
b = []
d = 0.0 
for x in range(2500):
    a.append([float(i+x) for i in range(1000)])
    b.append([float(i-x) for i in range(1000)])

t1 = time.time()
for x in range(500):
    d += cutil.c_euclidean_dist(1000,a[x],b[x])
print time.time() - t1
print d

C 代码在这里:

#include <python2.7/Python.h>
#include <math.h>

static PyObject* cutil_euclidean_dist(PyObject* self, PyObject* args) {
    PyObject *seq_a, *seq_b;
    int n;
    float * array_a,* array_b;
    PyObject *item;

    PyArg_ParseTuple(args,"iOO", &n , &seq_a, &seq_b);
    if (!PySequence_Check(seq_a) || !PySequence_Check(seq_b)) {
       PyErr_SetString(PyExc_TypeError, "expected sequence");
       return NULL;
    }

    array_a =(float *)malloc(sizeof(float)*n);
    array_b =(float *)malloc(sizeof(float)*n);  

    if (NULL == array_a || NULL == array_b){
        PyErr_SetString(PyExc_TypeError, "malloc failed!");
        Py_DECREF(seq_a);
        Py_DECREF(seq_b);
        return NULL;
    }

    int i;
    for(i=0;i<n;i++){
        item = PySequence_GetItem(seq_a,i);

        if (!PyFloat_Check(item)) {
            free(array_a);  /* free up the memory before leaving */
            free(array_b);
            Py_DECREF(seq_a);
            Py_DECREF(seq_b);
            Py_DECREF(item);
            PyErr_SetString(PyExc_TypeError, "expected sequence of float");
            return NULL;
        }
        array_a[i] = PyFloat_AsDouble(item);

        Py_DECREF(item);

        item = PySequence_GetItem(seq_b,i);
        if(!PyFloat_Check(item)) {
            free(array_a);
            free(array_b);
            Py_DECREF(seq_a);
            Py_DECREF(seq_b);
            Py_DECREF(item);
            PyErr_SetString(PyExc_TypeError, "expected sequence of float"); 
            return NULL;
        }
        array_b[i] = PyFloat_AsDouble(item);
        Py_DECREF(item);
    }

    double sum = 0;
    for(i=0;i<n;i++){
        double delta = array_a[i] - array_b[i];
        sum += delta * delta;
    }

    free(array_a);
    free(array_b);
    Py_DECREF(seq_a);
    Py_DECREF(seq_b);

    return Py_BuildValue("d",sqrt(sum));
}

static PyMethodDef cutil_methods[] = {
    {"c_euclidean_dist",(PyCFunction)cutil_euclidean_dist,METH_VARARGS,NULL},
    {NULL,NULL,0,NULL}
};

PyMODINIT_FUNC initcutil(void) {
    Py_InitModule3("cutil", cutil_methods, "liurui's c extension for python");
} 

错误信息:

segmentation fault - core dump:

c-extension编译成cutil.so,不知道怎么看dump。 但是我翻了很多遍我的C代码,没有发现任何问题..

可能是内存问题

应该是一段很简单的C代码,有什么问题? 我需要你的帮助~非常感谢!

这里是 gdb /usr/bin/python2.7 ./core:的结果:

root@ubuntu:/home/rrg/workspace/opencvTest/test# gdb /usr/bin/python2.7 ./core 
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 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".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/python2.7...Reading symbols from /usr/lib/debug//usr/bin/python2.7...done.
done.

warning: core file may not match specified executable file.
[New LWP 13787]
[New LWP 13789]
[New LWP 13790]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `python py.py'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000005398b3 in list_dealloc.16846 (op=0x7f688b2faa28) at ../Objects/listobject.c:309
309 ../Objects/listobject.c: no such file or directory
#0  0x00000000005398b3 in list_dealloc.16846 (op=0x7f688b2faa28) at ../Objects/listobject.c:309
#1  0x00000000004fdb96 in insertdict_by_entry (value=<optimized out>, ep=0x1777fa8, hash=<optimized out>, key='b', mp=0x7f68a8dbb168) at ../Objects/dictobject.c:519
#2  insertdict (value=<optimized out>, hash=<optimized out>, key='b', mp=0x7f68a8dbb168) at ../Objects/dictobject.c:556
#3  dict_set_item_by_hash_or_entry (value=<optimized out>, ep=0x0, hash=<optimized out>, key='b', 
    op={'a': None, 'x': None, 'c': None, 'b': None, 'd': <float at remote 0x4480b30>, '__builtins__': <module at remote 0x7f68a8de6b08>, 'science': <module at remote 0x7f68a8ce4088>, '__package__': None, 'i': 999, 'cutil': <module at remote 0x7f68a8cdfbb0>, 'time': <module at remote 0x7f68a640ea28>, '__name__': '__main__', 't1': <float at remote 0xd012708>, '__doc__': None}) at ../Objects/dictobject.c:765
#4  PyDict_SetItem (
    op=op@entry={'a': None, 'x': None, 'c': None, 'b': None, 'd': <float at remote 0x4480b30>, '__builtins__': <module at remote 0x7f68a8de6b08>, 'science': <module at remote 0x7f68a8ce4088>, '__package__': None, 'i': 999, 'cutil': <module at remote 0x7f68a8cdfbb0>, 'time': <module at remote 0x7f68a640ea28>, '__name__': '__main__', 't1': <float at remote 0xd012708>, '__doc__': None}, key=key@entry='b', 
    value=<optimized out>) at ../Objects/dictobject.c:818
#5  0x000000000055a9e1 in _PyModule_Clear (m=<optimized out>) at ../Objects/moduleobject.c:139
#6  0x00000000004f2ad4 in PyImport_Cleanup () at ../Python/import.c:473
#7  0x000000000042fa89 in Py_Finalize () at ../Python/pythonrun.c:459
#8  0x000000000046ac10 in Py_Main (argc=<optimized out>, argv=0x7fff3958d058) at ../Modules/main.c:665
#9  0x00007f68a8665ec5 in __libc_start_main (main=0x46ac3f <main>, argc=2, argv=0x7fff3958d058, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fff3958d048)
    at libc-start.c:287
#10 0x000000000057497e in _start ()

编辑: 我在最后返回附近评论最后两句话:

Py_DECREF(seq_a);
Py_DECREF(seq_b);  

然后它似乎运作良好。我觉得非常非常奇怪…… 这两句话的目的是为了释放(或释放)这两个pyobject,为什么没有我认为必要的两句话就可以很好地工作?

【问题讨论】:

  • 与您的问题无关,但DO not cast the return value of malloc() 并且如果malloc()s 中的任何一个成功,您将在` if (NULL == array_a || NULL == array_b)`中发生内存泄漏
  • 为什么你一拿到东西就减价,文件上说你必须这样做吗?
  • @iharob 对于第一个问题,我明白了,不会从 malloc() 中转换指针。
  • @iharob 第二个,文档没有要求我这样做,我自己做的,因为我认为如果我不立即使用 decref() 会出现内存泄漏将“项目”设置为其他“对象”后
  • @plaes 你能帮我解决这个问题吗?我真的需要你的帮助~

标签: python c linux python-c-extension


【解决方案1】:

c-extension编译成cutil.so,不知道怎么看dump。

为了解决这个问题,我将引用GNU Radio's GDB/Python debugging mini-tutorial

幸运的是,有一个称为核心转储的功能,它允许将程序的状态存储在一个文件中,以便以后进行分析。通常,该功能被禁用;您可以通过以下方式启用它:

ulimit -c unlimited

请注意,这仅适用于从您使用 ulimit 的 shell 生成的进程。这里发生的情况是核心转储的最大大小设置为无限制(在大多数情况下,原始值为 0)。

现在,核心转储文件位于崩溃程序的当前执行目录中。在我们的例子中,它是 build/python/,但由于所有核心转储都应该有一个类似 core. 的名称,我们可以使用一点查找魔法:

marcus> find -type f -cmin 5 -name 'core.[0-9]*'

./build/python/core.22608

因为这会找到所有 _f_ile,在最后 _5 分钟_utes 内更改/创建,名称匹配。

将 GDB 与核心转储一起使用

找到 build/python/core.22608, 我们现在可以启动 GDB:

gdb programname coredump

gdb /usr/bin/python2 build/python/core.22608

很多信息可能会滚动。

最后,您会看到 GDB 提示符:

(gdb) 

获取回溯

通常,您只会得到一个回溯(或更短,bt)。回溯只是被调用函数的层次结构。

 (gdb)bt

[...] 跳过,

第 2 帧和后面的帧绝对看起来像是 Python 实现的一部分——这听起来很糟糕,因为 GDB 本身不知道如何调试 python,但幸运的是,有一个扩展可以做到这一点。所以我们可以尝试使用py-bt:

(gdb) py-bt

如果我们得到一个未定义的命令错误,我们必须在这里停下来并确保安装了python开发包(Redhatoids上的python-devel,Debianoids上的python2.7-dev);对于某些系统,您应该将 /usr/share/doc/{python-devel,python2.7-dev}/gdbinit[.gz] 的内容附加到您的 ~/.gdbinit 中,然后重新启动 gdb。

py-bt 的输出现在清楚地说明了哪些 python 行对应于哪个堆栈帧(跳过那些对 python 隐藏的堆栈帧,因为它们在外部库或 python 实现例程中)

...

【讨论】:

  • 我运行 gdb /usr/bin/python2.7 ./core ,它给出了一个警告:核心文件可能与指定的可执行文件不匹配。但它仍然给出了一些堆栈信息,我复制了上面的堆栈信息,你能帮我找出问题吗?
  • 如果您不确定是否使用了正确的可执行文件,您也可以尝试通过gdb --args python mypythonfile.py 运行该程序,因为这样可以确保使用正确的可执行文件
  • @user3978288:没问题;虽然如果你支持或接受我的回答,我会很高兴:)
【解决方案2】:

感谢以上帮助我的两位好心人。

问题似乎解决了。

评论这两行:

Py_DECREF(seq_a); 
Py_DECREF(seq_b);

有关更多详细信息,请阅读 C-API 上的 python 官方文档

我猜原因是从 argv 得到的 seq_a seq_b 是“借来的引用”而不是真正的引用,所以我们不需要 decref()。

但正如官方文档所说,如果您使用 incref() 将借用的引用转换为真实的引用,那么您应该调用 decref()

你也可以搜索“python c-extension引用计数”了解更多详情

【讨论】:

    猜你喜欢
    • 2015-02-27
    • 1970-01-01
    • 2022-01-14
    • 2017-02-25
    • 2016-07-12
    • 2018-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多