使用Python ctypes时,优化后的共享库出现段错误(icc -O3或-O2)

4 投票
1 回答
1017 浏览
提问于 2025-04-16 16:32

这个情况很奇怪,因为我没有遇到段错误(segfault)

  • 如果共享库是用较低的优化级别编译的(-O0 或 -O1)
  • 如果共享库是用gcc编译的,即使使用了优化标志(-O3)
  • 如果我直接从纯C程序运行代码(而不是通过ctypes)

而且,我在某些机器上也没有遇到段错误。

如果你能在我的代码中找到错误那当然很好,但我还有其他一些更一般的问题:

  1. 这可能是icc或ctypes的错误吗?即使我只能在特定环境中重现这个奇怪的行为,提交bug到问题追踪系统可以吗?
  2. 我尝试调试代码,但由于这个bug只有在代码优化后才会出现,所以当我使用调试器时,看到很多“xxx已定义但未分配(被优化掉了)”。有没有更好的方法来调试优化后的代码?

如何重现这个bug

假设我有一个库的源代码 strange.c 和一个python脚本 run.py,我在运行时遇到了段错误:

icc -O3 -Wall -shared strange.c -o libstrange.so
python run.py

注意,我在我的一台机器上能够重现这个bug

  • uname -m: i868
  • 操作系统: Ubuntu 10.04.2 LTS
  • icc: 12.0.0 20101006
  • Python: 2.6.5
  • Numpy: 1.3.0

但在以下环境中没有遇到段错误:

  • uname -m: i868
  • 操作系统: Ubuntu 10.10
  • icc: 12.0.3 20110309
  • Python: 2.6.6
  • Numpy: 1.3.0

或者在:

  • uname -m: x86_64
  • 操作系统: Scientific Linux SL release 5.5 (Boron)
  • icc: 12.0.0 20101006
  • Python: 2.6.5
  • Numpy: 1.5.0b1

代码

你可以在这里找到代码集(tkf / ctypes_icc / source – Bitbucket)或者在下面找到。 你可以找到一个Makefile和一个shell脚本来运行程序,并检查所有优化标志和编译器(gcc和icc)的退出代码。这个程序的原始版本是我研究的一个模拟程序,但这个程序只是一个没有意义的程序。

strange.c:

typedef struct{
  int num_n;
  double dt, ie, gl, isyn, ssyn, tau1, tau2, lmd1, lmd2, k1_mean, k2_mean;
  double *vi, *v0;
} StrangeStruct;


void
func(double * v0, double * vt, double dt,
     double gl, double isyn, double ie, double isyn_estimate, int num_n)
{
  int i;
  for (i = 0; i < num_n; ++i){
    v0[i] = vt[i] + dt + gl + isyn + ie + isyn_estimate;
  }
}

int
StrangeStruct_func(StrangeStruct *self)
{
  double isyn_estimate;
  isyn_estimate =
    self->ssyn * (self->lmd1 * self->k1_mean - self->lmd2 * self->k2_mean) /
    (self->tau1 - self->tau2);
  func(self->v0, self->vi, self->dt, self->gl, self->isyn,
       self->ie, isyn_estimate, self->num_n);
  return 0;
}

run.py:

from ctypes import POINTER, pointer, c_int, c_double, Structure
import numpy

c_double_p = POINTER(c_double)


class StrangeStruct(Structure):
    _fields_ = [
        ("num_n", c_int),
        ("dt", c_double),
        ("ie", c_double),
        ("gl", c_double),
        ("isyn", c_double),
        ("ssyn", c_double),
        ("tau1", c_double),
        ("tau2", c_double),
        ("lmd1", c_double),
        ("lmd2", c_double),
        ("k1_mean", c_double),
        ("k2_mean", c_double),
        ("vi", c_double_p),
        ("v0", c_double_p),
        ]


StrangeStruct_p = POINTER(StrangeStruct)

ifnet_a2a2 = numpy.ctypeslib.load_library('libstrange.so', '.')
ifnet_a2a2.StrangeStruct_func.restype = c_int
ifnet_a2a2.StrangeStruct_func.argtypes = [StrangeStruct_p]


def func(struct):
    ifnet_a2a2.StrangeStruct_func(pointer(struct))


if __name__ == '__main__':
    ifn = StrangeStruct(
        num_n=100, dt=0.1, gl=0.1, vrest=-60, ie=-3.7, th=-40,
        ssyn=0.5, tau1=3, tau2=1,
        )
    v0 = numpy.zeros(ifn.num_n, dtype=float)
    vi = numpy.zeros(ifn.num_n, dtype=float)
    ifn.v0 = v0.ctypes.data_as(c_double_p)
    ifn.vi = vi.ctypes.data_as(c_double_p)

    func(ifn)

    v0 + vi

1 个回答

3

通常情况下,用gcc和icc编译的程序是不能混合使用的(在这个例子中,python是用gcc编译的)。你可以尝试使用icc的“gcc兼容模式”,这个模式是通过-gcc-version这个选项来设置的。这样可能会让它正常工作,但仍然有可能会遇到问题。

撰写回答