Comment puis-je résoudre une erreur de segmentation lorsque je travaille avec des Ctypes Python et C ++?

Disons que j’ai les deux signatures de fonction suivantes en C ++:

BYTE* init( BYTE* Options, BYTE* Buffer ) 

et:

 int next( BYTE* interface, BYTE* Buffer ) 

L’idée est que j’initialise d’abord une classe d’ Interface en C ++, puis que j’appelle ensuite la fonction next depuis Python, avec une référence à cette classe.

La première fonction renvoie un pointeur BYTE à l’interface via:

 Interface* interface; // initialize stuff return((BYTE*) interface); 

Je l’appelle en Python comme ceci:

 class Foo: def init(self, data): # left out: setting options_ptr buf = (c_ubyte * len(data.bytes)).from_buffer_copy(data.bytes) init_fun = getattr(self.dll, '?init@@YAPAEPAE0HH@Z') init_fun.restype = POINTER(c_ubyte) self.interface_ptr = init_fun(options_ptr, buf) # this works fine! def next(self, data): # create buf from other data buf = (c_ubyte * len(data.bytes)).from_buffer_copy(data.bytes) next_fun = getattr(self.dll, '?next@@YAHPAE0HN@Z') ret = next_fun(self.interface_ptr, buf) # I randomly get segmentation faults here 

J’appelle cela de l’extérieur avec, par exemple:

 foo = Foo() foo.init(some_data) foo.next(some_other_data) # ... foo.next(some_additional_data) 

Maintenant, quand je le lance, j’obtiens des erreurs de segmentation:

 [1] 24712 segmentation fault python -u test.py 

Parfois, cela se produit après le premier appel à .next() , parfois après le onzième appel à .next() totalement aléatoire.

Il existe un code de test C ++ pour l’API qui fonctionne de la manière suivante:

 BYTE Buffer[500000]; UTIN BufSize=0; BYTE* Interface; # not shown here: fill buffer with something Interface = init(Buffer); while(true) { # not shown here: fill buffer with other data int ret = next(Interface, Buffer); } 

Maintenant, comme je ne peux pas montrer le code exact, car il est beaucoup plus gros et exclusif, la question est: comment puis-je résoudre un tel problème de segmentation? Je peux rompre lorsque l’exception est levée (lors du débogage avec VS2012), mais cela rompt ici:

Clairement, cela n’est pas utile car rien n’est fait avec aucun tampon à la ligne indiquée. Et les valeurs des variables sont aussi cryptiques:

Dans mon cas, les data sont un object BitSsortingng . Le problème pourrait-il se passer si le code C ++ effectue les opérations de mémoire sur la mémoire tampon? Ou que certaines données sont récupérées par Python quand elles sont encore nécessaires?

Plus généralement, comment puis-je m’assurer que les erreurs de segmentation ne se produisent pas lorsque je travaille avec Ctypes? Je sais que l’API DLL sous-jacente fonctionne correctement et ne plante pas.


Mise à jour: lorsque je buf une variable d’instance, par exemple self._buf , une erreur de segmentation se produit, mais elle se rompt à un emplacement différent lors du débogage:

J’ai eu quelques incompréhensions, ce qui a conduit aux problèmes:

  • Lorsque vous créez un object Ctypes dans Python et que vous le transmettez à une fonction C, et que cet object Python n’est plus nécessaire, il est (probablement) mis au rebut et ne se trouve plus dans la stack de mémoire où C s’attend à ce qu’il soit.

    Par conséquent, faites du tampon une variable d’instance, par exemple self._buf .

  • Les fonctions C s’attendent à ce que les données soient mutables. Si les fonctions C ne copient pas les données ailleurs, mais travaillent directement sur la mémoire tampon, celle-ci doit être mutable. La documentation Ctypes spécifie ceci:

    L’affectation d’une nouvelle valeur aux instances des types de pointeurs c_char_p, c_wchar_p et c_void_p modifie l’emplacement mémoire auquel elles pointent, pas le contenu du bloc mémoire (bien sûr, car les chaînes Python sont immuables).

    Veillez toutefois à ne pas les transmettre aux fonctions qui attendent des pointeurs sur la mémoire mutable. Si vous avez besoin de blocs de mémoire mutables, ctypes a une fonction create_ssortingng_buffer() qui les crée de différentes manières. Le contenu actuel du bloc de mémoire est accessible (ou modifié) avec la propriété raw ; si vous voulez y accéder en tant que chaîne terminée par NUL , utilisez la propriété value :

    Alors, j’ai fait quelque chose comme ça:

  self._buf = create_ssortingng_buffer(500000) self._buf.value = startdata.bytes 
  • Le tampon doit être utilisé en Python comme un tableau normal, comme indiqué dans l’exemple de code, où il est rempli et où les données sont manipulées. Donc, pour ma méthode .next() , j’ai fait ceci:
  self._buf.value = nextdata.bytes 

Maintenant, mon programme fonctionne comme prévu.