Problème d'accès concurrent

3 réponses
Avatar
JKB
Bonjour à tous,

Je suis toujours en train de chercher un bug sournois, sans doute
d'accès concurrent dans un bout de code en C sous Unix.

Le programme en question entre dans un deadlock dans un appel à
pthread_exit(). Je pense à une corruption de mémoire quelque part.
Je pensais d'abord au tas, mais je m'oriente plus vers la pile
aujourd'hui. J'ai testé ce programme sous plusieurs OS avec le même
résultat (ou au moins des résultats similaires) :
- Linux : deadlock ;
- NetBSD : segfault ;
- FreeBSD : segfault.

Les deadlocks et segfaults arrivent toujours aux mêmes endroits du
code mais ne se produisent qu'aléatoirement.

J'ai rajouté du code pour débugguer les accès mémoire. J'ai utilisé
des outils comme valgrind, je n'ai trouvé aucun accès bizarre à la
mémoire. Pire, le code en question peut tourner des jours sous
valgrind sans problème.

En désespoir de cause, j'ai utilisé helgrind. Cet outil me renvoie
(outre des fausses alarmes) une erreur étrange que je ne sais
comment interpréter :

==18981==
---Thread-Announcement------------------------------------------
==18981==
==18981== Thread #1 is the program's root thread
==18981==
==18981==
---Thread-Announcement------------------------------------------
==18981==
==18981== Thread #3 was created
==18981== at 0x7CA598E: clone (clone.S:73)
==18981== by 0x7320179: create_thread (createthread.c:102)
==18981== by 0x7321BE2: pthread_create@@GLIBC_2.2.5
(pthread_create.c:679)
==18981== by 0x4C32B07: pthread_create_WRK (hg_intercepts.c:427)
==18981== by 0x3EAF39: librpl_creation_queue_signaux
(interruptions-conv.c:3443)
==18981== by 0x3EEC4C: librpl_rplinit (rpl-conv.c:347)
==18981== by 0x29A17D: main (init-conv.c:29)
==18981==
==18981==
----------------------------------------------------------------
==18981==
... une fausse alarme puis ...
==18981== Possible data race during write of size 1 at 0xDCAC597 by thread #1
==18981== Locks held: 3, at addresses 0x1A0C560 0x1A0C760 0xC936618
==18981== at 0x4C384DC: mempcpy (vg_replace_strmem.c:1521)
==18981== by 0x401253D: _dl_allocate_tls_init (dl-tls.c:515)
==18981== by 0x7321A50: get_cached_stack (allocatestack.c:254)
==18981== by 0x7321A50: allocate_stack (allocatestack.c:499)
==18981== by 0x7321A50: pthread_create@@GLIBC_2.2.5
(pthread_create.c:539)
==18981== by 0x4C32B07: pthread_create_WRK (hg_intercepts.c:427)
==18981== by 0x3E6F3E: librpl_lancement_thread_signaux
(interruptions-conv.c:1548)
==18981== by 0x3EAEFE: librpl_creation_queue_signaux
(interruptions-conv.c:3422)
==18981== by 0x348761: librpl_instruction_detach
(instructions_d5-conv.c:1325)
==18981== by 0x2E5DE1: librpl_analyse (analyse-conv.c:1074)
==18981== by 0x2F3C19: librpl_evaluation (evaluation-conv.c:764)
==18981== by 0x3ED919: librpl_sequenceur_optimise
(optimisation-conv.c:399)
==18981== by 0x3F3C7B: librpl_rplinit (rpl-conv.c:5186)
==18981== by 0x29A17D: main (init-conv.c:29)
==18981==
==18981== This conflicts with a previous write of size 1 by thread #3
==18981== Locks held: none
==18981== at 0x7321392: start_thread (pthread_create.c:265)
==18981== by 0x7CA59BE: clone (clone.S:105)
==18981== Address 0xdcac597 is in a rw- anonymous segment
==18981==

Si je comprends bien, j'ai deux threads qui essaient d'accéder
en même temps à la même donnée en écriture (qui plus est, j'ai
l'impression qu'on parle ici de la pile du thread). Cela ressemble
étrangement à mon problème. En effet, gdb m'indique :

schroedinger:[~/cvs/test] > gdb -p 17494
...
Attaching to process 17494
[New LWP 17497]
[New LWP 17498]
[Thread debugging using libthread_db enabled]
Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f2a0402367d in pthread_join (threadid=139818318202624,
thread_return=thread_return@entry=0x0) at pthread_join.c:90
90 pthread_join.c: Aucun fichier ou dossier de ce type.
(gdb) thread 3
[Switching to thread 3 (Thread 0x7f29fd308700 (LWP 17498))]
#0 __lll_lock_wait () at
../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
135 ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S: Aucun
fichier ou dossier de ce type.
(gdb) bt
#0 __lll_lock_wait () at
../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
#1 0x00007f2a04024bf6 in __GI___pthread_mutex_lock (
mutex=0x7f2a06938970 <_rtld_global+2352>)
at ../nptl/pthread_mutex_lock.c:115
#2 0x00007f2a037159ff in __GI___dl_iterate_phdr (callback=0x7f2a033f0db0,
data=0x7f29fd307830) at dl-iteratephdr.c:41
#3 0x00007f2a033f212e in _Unwind_Find_FDE ()
from /lib/x86_64-linux-gnu/libgcc_s.so.1
#4 0x00007f2a033eeb23 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
...
#9 __pthread_exit (value=value@entry=0x0) at pthread_exit.c:28
#10 0x00005623af93e3ff in thread_surveillance_signaux (argument=0x7f2a0690e010)
at interruptions-conv.c:201
...

Pour helgrind :
#1 est le thread racine (ici le thread issu du lancement de main())
#3 est un thread créé traditionnellement par pthread_create() :

ligne 1548 de interruptions.c :
if (pthread_create(&((*s_etat_processus).thread_signaux), &attributs,
thread_signaux, s_etat_processus) != 0)

s_etat_processus est parfaitement initialisé; attributs est défini
sur la pile, initialisé et le thread est déclaré comme
PTHREAD_CREATE_JOINABLE. Le thread lancé se contente d'une pile
réduite (très peu de variables sur la pile et un simple appel à une
function).

Je puis fournir le code source si nécessaire.

#1 appelle bien pthread_create(). Comment se fait-il qu'avant le
lancement du thread #3, je me prenne une telle erreur ? Et surtout
comment comprendre ce qu'il se passe ici. Voire comment connaître le
responsable ?

Tout avis, suggestion ou conseil pour débugguer ce truc sera le
bienvenu.

Bien cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
=> http://loubardes.de-charybde-en-scylla.fr

3 réponses

Avatar
JKB
Rhohh... Désolé pour le bruit, je crois que j'ai trouvé.
Je libérais un sémaphore avec un sem_close() alors que je
l'utilisais encore dans la fin dans thread concurrent, qui lui était
DETACHED et terminait un peu quand il voulait.
Mon problème n'avais donc rien à voir avec ce que helgrind
renvoyait.
Enfin, je crois les doigts, mon algo vient de faire 1220000 là où ce
matin, il était infoutu d'en faire le dixième sans partir en
deadlock.
Cordialement,
JKB
--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
=> http://loubardes.de-charybde-en-scylla.fr
Avatar
Erwan David
JKB écrivait :
Rhohh... Désolé pour le bruit, je crois que j'ai trouvé.
Je libérais un sémaphore avec un sem_close() alors que je
l'utilisais encore dans la fin dans thread concurrent, qui lui était
DETACHED et terminait un peu quand il voulait.
Mon problème n'avais donc rien à voir avec ce que helgrind
renvoyait.
Enfin, je crois les doigts, mon algo vient de faire 1220000 là où ce
matin, il était infoutu d'en faire le dixième sans partir en
deadlock.
Cordialement,

9 chances sur 10 que ça te provoque un use_after_free et donc une
corruption mémoire comme tu le supposais.
--
Les simplifications c'est trop compliqué
Avatar
JKB
Le Thu, 03 Aug 2017 17:31:10 +0200,
Erwan David écrivait :
JKB écrivait :
Rhohh... Désolé pour le bruit, je crois que j'ai trouvé.
Je libérais un sémaphore avec un sem_close() alors que je
l'utilisais encore dans la fin dans thread concurrent, qui lui était
DETACHED et terminait un peu quand il voulait.
Mon problème n'avais donc rien à voir avec ce que helgrind
renvoyait.
Enfin, je crois les doigts, mon algo vient de faire 1220000 là où ce
matin, il était infoutu d'en faire le dixième sans partir en
deadlock.
Cordialement,

9 chances sur 10 que ça te provoque un use_after_free et donc une
corruption mémoire comme tu le supposais.

On a beau savoir qu'il y a une corruption de la mémoire. Lorsqu'un
programme est méchamment multithreadé, c'est toujours la misère à
corriger. Ça faisait plusieurs mois que j'étais dessus sans trouver
et je commençais à désespérer.
Enfin, ça m'a permis de corriger tout un tas d'autres erreurs
beaucoup plus mineures...
JKB
--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
=> http://loubardes.de-charybde-en-scylla.fr