我正在为python编写一个c-extension。如下所示,代码的目的是计算两个向量的euclidean-dist。 第一个参数是向量的维数, 第二个,第三个参数是浮动的两个列表。
我在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,我不知道如何查看转储。 但我多次查看我的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 ()
编辑: 我评论最后一次回归附近的最后2个句子:
Py_DECREF(seq_a);
Py_DECREF(seq_b);
然后它似乎运作良好。我觉得很奇怪...... 这句话的目的是释放(或释放)两个pyobject,为什么它没有我认为必要的两个句子呢?
答案 0 :(得分:4)
c-extension编译为cutil.so,我不知道如何查看转储。
要解决此问题,我将引用GNU Radio's GDB/Python debugging mini-tutorial:
幸运的是,有一项称为核心转储的功能,它允许将程序状态存储在一个文件中,以便以后进行分析。通常,该功能被禁用;你可以通过以下方式启用它:
ulimit -c unlimited
请注意,这仅适用于从您使用ulimit的shell中生成的进程。这里发生的是核心转储的最大大小设置为unlimited(在大多数情况下,原始值为0)。
现在,核心转储文件位于崩溃程序的当前执行目录中。在我们的例子中,那是build / python /,但由于所有的核心转储都应该有像core这样的名字,我们可以使用一点点魔法:
marcus> find -type f -cmin 5 -name 'core.[0-9]*'
./构建/蟒/ core.22608
因为这会找到所有_f_iles,在最后_5 min_utes内更改/创建,名称匹配。
将GDB与核心转储一起使用
找到了build / python / core.22608, 我们现在可以启动GDB:
gdb programname coredump
即
gdb /usr/bin/python2 build/python/core.22608
许多信息可能会滚动。
最后,您受到了GDB提示的欢迎:
(gdb)
获得回溯
通常,您只是获得一个回溯(或更短,bt)。回溯只是被调用的函数的层次结构。
(gdb)bt
[...]跳过,
第2帧和第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实现例程中)
...
答案 1 :(得分:0)
感谢上面帮助我的2个善良的人。
问题似乎已经解决了。
评论2行:
Py_DECREF(seq_a);
Py_DECREF(seq_b);
有关详细信息,请参阅C-API上的python官方文档
我想原因是来自argv的seq_a seq_b是一个&#34;借来的参考&#34;而不是真正的参考,所以我们不需要递减()。
但正如官方文档所说,如果你使用incrementf()将借来的refference转换成真正的引用,那么你应该调用decref()
你也可以搜索&#34; python c-extension参考计数&#34;更多细节