Как исправить ошибку в функции Python "classify_argument" в ../src/x86/ffi64.c:158 - PullRequest
2 голосов
/ 04 апреля 2019

Intro

Я занимаюсь разработкой программы на Python.Он использует библиотеку C, которая связана с python с помощью SWIG.Библиотека C является неким королем TCP-сервера, который обрабатывает клиентские соединения в отдельных потоках C.Эти потоки получают данные от клиентов, а затем выполняют базовую обработку и анализ входящих данных.После этого проанализированные данные отправляются в python через «функцию обратного вызова Python».Результат этого "обратного вызова функции Python" затем обрабатывается в C и отправляется обратно клиенту.

Были некоторые ошибки, которые были вызваны ошибками в моем коде (двойное освобождение памяти, оценка указателя NULL), которые я решил.

Но теперь я столкнулся с ошибкой в ​​коде интерпретатора Python.И я не могу понять, как ее решить.

Отладка

Я получил обратную трассировку с использованием gdb (стековые кадры от 5 до 11 опущены для ясности):

#0  classify_argument (type=0x55, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
#1  0x00007ffff4b4212c in examine_argument (type=<optimized out>, classes=classes@entry=0x7fffeb522c60, in_return=in_return@entry=false, pngpr=pngpr@entry=0x7fffeb522c58, pnsse=pnsse@entry=0x7fffeb522c5c) at ../src/x86/ffi64.c:314
#2  0x00007ffff4b4296f in ffi_closure_unix64_inner (closure=0x7ffff7ff20f0, rvalue=0x7fffe4024820, reg_args=0x7fffeb522cc0, argp=0x7fffeb522d90 "") at ../src/x86/ffi64.c:615
#3  0x00007ffff4b42de4 in ffi_closure_unix64 () at ../src/x86/unix64.S:229
#4  0x00007ffff5fd0b21 in MyCCodeConnectionHandler (parameter=0x7ffff7ff20f0, connection=0x7fffe4024820, event=SERVER_CONNECTION_CLOSED) at my_code/server.c:586
...
#12 0x00007ffff7bc4184 in start_thread (arg=0x7fffeb523700) at pthread_create.c:312
#13 0x00007ffff71e403d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

Когда я пытаюсь напечатать type-> type переменную в GDB, я вижу:

(gdb) print type->type
Cannot access memory at address 0x5f

Кажется, что type аргумент функции classify_argument недействительно, так как я знаю, что адреса от 0x0000000000000000 до 0x000000000000FFFF используются для обнаружения нулевого указателя.И это является причиной SegFault.

Я отлаживал другие вызовы функции classify_argument , используя точки останова, и он показал мне, что аргумент type получает другие виды значений:

classify_argument (type=0x7ffff6662ee8, classes=0x1, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
classify_argument (type=0x7ffff65e7e10, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
classify_argument (type=0x7ffff65e7e10, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158

и не было ошибок по умолчанию.

Код и вспомогательная информация

Вот фрагмент ../src/x86/ffi64.c:

155 static size_t
156 classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
157        size_t byte_offset)
158 {
159   switch (type->type)
160     {
161     case FFI_TYPE_UINT8:
162     case FFI_TYPE_SINT8:
163     case FFI_TYPE_UINT16:
164     case FFI_TYPE_SINT16:
165     case FFI_TYPE_UINT32:
166     case FFI_TYPE_SINT32:
167     case FFI_TYPE_UINT64:
168     case FFI_TYPE_SINT64:
169     case FFI_TYPE_POINTER:
170       {
171   size_t size = byte_offset + type->size;
172 
173   if (size <= 4)
174     {
175       classes[0] = X86_64_INTEGERSI_CLASS;
176       return 1;
177     }
178   else if (size <= 8)
179     {
180       classes[0] = X86_64_INTEGER_CLASS;

Я знаю о блокировке GIL, которую необходимо получить перед вызовом кода Python из потоков C.Вот фрагмент my_code / server.c

...
static void
MyCCodeConnectionHandler(void* parameter, ConnectionStruct *connection, ConnectionEvent event)
{
    if(parameter != NULL) { //parameter is a pointer to Python callback
        PyGILState_STATE gstate;
        gstate = PyGILState_Ensure();
        ((void (*)(long, long))parameter)((long)connection, event);
        PyGILState_Release(gstate);
    }
}
...

paramter Аргумент имеет следующее определение CTYPES в Python

ConnectionHandlerFuncType = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_long)

И преобразован с использованием этого кода Python передпередано в C

def py_callback(connection, event):
  # some code here

ctype_function_wrapper = ConnectionHandlerFuncType(py_callback)
cfunction_pointer = ctypes.cast(ctype_function_wrapper, ctypes.c_void_p).value
# cfunction_pointer is passed as parameter arg to MyCCodeConnectionHandler

Пожалуйста, помогите мне решить эту ошибку.

1 Ответ

0 голосов
/ 25 апреля 2019

Директора решили мою проблему.Кажется, что это единственный правильный способ сделать обратные вызовы в SWIG.Мой способ передачи обратного вызова на уровень C. был неправильным.

Вот как теперь выглядит мой обратный вызов C:

static void
MyCCodeConnectionHandler(void* parameter, ConnectionStruct *connection, ConnectionEvent event)
{
    //parameter contains (ConnectionCallbackDirector *) value casted to (void *)
    if(parameter != NULL) {
        PyGILState_STATE gstate = PyGILState_Ensure();
        ((ConnectionCallbackDirector *)parameter)->run(connection, event);
        PyGILState_Release(gstate);
    }
}

Код директора:

class ConnectionCallbackDirector {
  public:
    virtual ~ConnectionCallbackDirector() {}
    virtual void run(ConnectionStruct *, ConnectionEvent) {}
};

Оба из нихЯ поместил в файл SWIG * .i и enabled director feature for SWIG module and ConnectionCallbackDirector class.

. В Python я написал этот класс, унаследованный от ConnectionCallbackDirector

class ConnectionCallback(my_c_lib.ConnectionCallbackDirector):

  def __init__(self, pycallback):
    my_c_lib.ConnectionCallbackDirector.__init__(self)
    self._pycallback = pycallback

  def run(self, connection, event):
    return self._pycallback(connection, event)

И передал объект, созданный из класса ConnectionCallback, вКод С.

Теперь все хорошо!

...