分段错误 - python C-extension中的核心转储

rui*_*991 4 c python linux python-c-extension

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

我在 python 中调用函数是这样的:

import cutil
cutil.c_euclidean_dist(2,[1.0,1,0],[0,0])
Run Code Online (Sandbox Code Playgroud)

它运行良好,返回正确的结果。但是如果我这样做超过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
Run Code Online (Sandbox Code Playgroud)

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");
} 
Run Code Online (Sandbox Code Playgroud)

错误消息:

segmentation fault - core dump:
Run Code Online (Sandbox Code Playgroud)

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 ()
Run Code Online (Sandbox Code Playgroud)

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

Py_DECREF(seq_a);
Py_DECREF(seq_b);  
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ler 5

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

为了解决这个问题,我将引用GNU Radio 的 GDB/Python 调试迷你教程

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

ulimit -c unlimited
Run Code Online (Sandbox Code Playgroud)

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

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

marcus> find -type f -cmin 5 -name 'core.[0-9]*'
Run Code Online (Sandbox Code Playgroud)

./build/python/core.22608

因为这将找到在最后 _5 min_utes 内更改/创建的所有 _f_iles,具有匹配的名称。

将 GDB 与核心转储一起使用

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

gdb programname coredump
Run Code Online (Sandbox Code Playgroud)

IE

gdb /usr/bin/python2 build/python/core.22608
Run Code Online (Sandbox Code Playgroud)

很多信息可能会滚动。

最后,您会看到 GDB 提示:

(gdb) 
Run Code Online (Sandbox Code Playgroud)

获取回溯

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

 (gdb)bt
Run Code Online (Sandbox Code Playgroud)

[...] 跳过,

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

(gdb) py-bt
Run Code Online (Sandbox Code Playgroud)

如果我们得到 undefined 命令错误,我们必须停在这里并确保安装了 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 实现例程中)

...