0

Retrouver la fonction à l’origine d’un Non Yielding IOCP Listener

twitterlinkedinmail

Petit mémento pour retrouver la fonction à l’origine d’un SOS non yielding ou un IOCP non yielding. On va en profiter pour rappeler les conditions de détection des deux phénomènes. On part sur une version >= 2005.

Pour mémoire, cette condition de “non-relâche” (non-yielding) d’un scheduler ou d’un IO Completion Port a été implémentée en SQL Server 2000 SP3 à la demande du support, pour permettre de diagnostiquer plus facilement les cas où une fonction bloque un scheduler (qui fonctionne depuis SQL 7.0 dans un mode coopératif rappelons-le). Depuis SQL Server 2005, un thread système a la charge de détecter ces conditions, le Scheduler Monitor.

Erreur 17883, non-yielding Scheduler :

Depuis SQL Server 2005, le Scheduler Monitor se réveille toutes les 5 secondes et lance un check simple (un genre de LooksAlive). Si l’ensemble des hypothèses suivantes sont vérifiées:

– Le scheduler n’est pas idle (il travaille quoi…)
– Le nombre de yields n’a pas évolué depuis le dernier contrôle
– Le worker actif n’est pas déjà dans une exception ou en train de générer un minidump.

… alors le scheduler est considéré comme potentiellement bloqué, et un contrôle un peu plus poussé va suivre (genre de isAlive). Si le worker accumule 10 secondes sans avoir bougé (donc consécutivement à 2 checks simples), le Scheduler Monitor va vérifier si la somme des temps (kernel + user) accumulés par le worker depuis le début de son exécution:

– Est inférieure ou égale à 5% au temps total instance (reporté dans sys.dm_os_sys_info): désigne typiquement les cas où le worker est coincé en attente du retour d’une API externe (exemple typique, WriteFileGather() sur un filesystem encrypté ou compressé, le filter driver de l’antivirus qui retarde l’IO, ou les outils d’encryption 3rd party, le chemin vers la baie qui est en échec …)
– Est supérieure ou égale à 40% du temps total instance [1]: désigne typiquement les cas où le worker est dans une boucle infinie (cas fréquents avec de l’exécution SQLCLR).
– Et que tous les schedulers ne sont pas à 100% de charge (donc qu’il y a des cycles sous le coude)

Alors le scheduler est définitivement considéré comme bloqué et le callback de capture va se mettre en place. Au bout de 60 secondes où la condition de non-yield est maintenue, un minidump (*.mdmp) contenant la stack de tous les threads est capturé par SQLDumper sous ~MSSQL\Log. Il ne contient pas tout l’espace mémoire alloué, ce qui serait en 64 bits franchement impraticable, c’est pour ça que ça s’appelle un minidump. Ca veut dire aussi qu’on ne verra pas nécessairement tout dans le dump.

Il est important de noter que le minidump ne sera pris qu’une seule fois, même si la condition persiste au delà des 60 secondes. Il faudra soit utiliser le TF 1262 ou redémarrer l’instance pour réarmer la condition. En outre, si à l’installation vous avez coché les cases ‘Watson’ (error reporting), le minidump sera envoyé à un des serveurs de reporting de MS.

[1] Note: si la valeur se trouve entre 5 et 40%, on considère que le worker est victime de facteurs externes (pagination, autres processes avec une priorité plus élevée) et un minidump dans ce cas ne sert à rien.

Dans l’ERRORLOG, une belle stack trace:

  1. 2011-07-24 22:17:45.850 Server       * BEGIN STACK DUMP:  
  2. 2011-07-24 22:17:45.850 Server       *   07/24/11 22:17:45 spid 2388  
  3. 2011-07-24 22:17:45.850 Server       *  
  4. 2011-07-24 22:17:45.850 Server       * Non-yielding Scheduler   
  5. 2011-07-24 22:17:45.850 Server       Stack Signature for the dump is 0x00000000000003B6  
  6. 2011-07-24 22:17:53.380 Server       External dump process return code 0×20000001.  
  7. (...)  

Erreur  17887, non yielding IOCP  Listener:

Un IO Completion Port est un outil fourni par l’API win32 pour gérer efficacement les entrées sorties asynchrones (cf article sur les IO asynchrones). Dans SQL Server il sert à gérer les demandes de connexion.

Cette condition a été ajoutée avec la version SQL Server 2005 pour détecter les cas où l’IOCP est bloqué et ne peut plus gérer de nouvelles demandes de connexion. L’ensemble des hypothèses suivantes doivent être réunies pour valider la condition d’IOCP bloqué:

– Un IOCP est actif
– Pas de nouvelle demande n’a été postée sur le port depuis le dernier check (toutes les 5 secondes)
– La situation persiste depuis au moins 10 secondes (donc deux checks basiques comme pour l’erreur 17883).
– SQL Server n’est ni sous pression CPU ni sous pression mémoire.

Dans ces conditions, un minidump est capturé exactement comme pour l’erreur 17883. Et là aussi une belle stack trace :

  1. 2013-08-08 16:05:41.93 Server      Using 'dbghelp.dll' version '4.0.5'  
  2. 2013-08-08 16:05:42.08 Server      **Dump thread - spid = 0, EC = 0x0000000000000000  
  3. 2013-08-08 16:05:42.09 Server      ***Stack Dump being sent to K:\MSSQL10_50.MSSQLSERVER\MSSQL\LOG\SQLDump0001.txt  
  4. 2013-08-08 16:05:42.09 Server      * *******************************************************************************  
  5. 2013-08-08 16:05:42.09 Server      *  
  6. 2013-08-08 16:05:42.09 Server      * BEGIN STACK DUMP:  
  7. 2013-08-08 16:05:42.09 Server      *   08/08/13 16:05:42 spid 1996  
  8. 2013-08-08 16:05:42.09 Server      *  
  9. 2013-08-08 16:05:42.09 Server      * Non-yielding IOCP Listener  
  10. 2013-08-08 16:05:42.09 Server      *  
  11. 2013-08-08 16:05:42.09 Server      * *******************************************************************************  
  12. 2013-08-08 16:05:42.09 Server      * -------------------------------------------------------------------------------  
  13. 2013-08-08 16:05:42.09 Server      * Short Stack Dump  
  14. 2013-08-08 16:05:42.32 Server      Stack Signature for the dump is 0x0000000000000056  
  15. 2013-08-08 16:06:04.10 Server      External dump process return code 0x20000001.  
  16. External dump process returned no errors.  
  17.   
  18. 2013-08-08 16:06:04.10 Logon       Error: 17803, Severity: 20, State: 17.  
  19. 2013-08-08 16:06:04.10 Logon       There was a memory allocation failure during connection establishment. Reduce nonessential  
  20. memory loador increase system memory. The connection has been closed. [CLIENT: 194.26.22.110]  

Une ligne est particulièrement intéressante:

  1. 2013-08-08 16:06:04.13 Server      IO Completion Listener (0xce8) Worker 0x00000000006AE1A0 appears to be non-yielding on  
  2. Node 0. Approx CPU Used: kernel 46 ms, user 9609 ms, Interval: 14999.  
  3. 2013-08-08 16:06:59.65 spid88  

0xce8 désigne le ThreadID de notre bloquant.

Exemple avec une 17887:

Pour ouvrir le minidump, il faut utiliser windbg et déclarer les symboles dans la bonne version de SQL Server soit en déclarant le serveur de symboles de MS comme décrit dans l’article sur SATA, soit en récupérant le PDB de sqlservr comme décrit par Paul Randal ici.

La particularité du minidump dans ce cas, est qu’il contient outre la stack de tous les threads, une copie de la structure CONTEXT du thread qui bloque au moment précis où l’exception est levée, qui sera accessible dans le debugger via la commande .cxr. CONTEXT contient la valeur des registres du CPU sur lequel notre bloqueur était en exécution, et nous informe sur la stack exacte impliquée dans notre problème, car parfois la situation peut se débloquer entre le moment où l’exception est détectée et le minidump généré. La stack du thread ne serait alors plus valide, voire plus disponible du tout. Ca permet aussi de vérifier si le worker n’a pas progressé au dernier moment.

Alors on y va, on lance windbg,  et on charge le minidump File -> Open Crash Dump …

  1. Loading Dump File [E:\SQLDump0001.mdmp]  
  2. Comment: 'Stack Trace'  
  3. Comment: 'Non-yielding IOCP Listener'  
  4. User Mini Dump File: Only registers, stack and portions of memory are available  
  5.   
  6. Symbol search path is: SRV*e:\symbols*http://msdl.microsoft.com/download/symbols;E:\Symbols;E:\DENALI\MSSQL11.DENALI\MSSQL\Binn  
  7. Executable search path is:  
  8. Windows 7 Version 7601 (Service Pack 1) MP (2 procs) Free x64  
  9. Product: Server, suite: Enterprise TerminalServer SingleUserTS  
  10. Machine Name:  
  11. Debug session time: Thu Aug  8 15:06:03.000 2013 (UTC + 2:00)  
  12. System Uptime: 20 days 20:56:11.890  
  13. Process Uptime: 8 days 3:28:59.000  
  14. ................................................................  
  15. .....................................................  
  16. Loading unloaded module list  
  17. .....................................................................  
  18. This dump file has an exception of interest stored in it.  
  19. The stored exception information can be accessed via .ecxr.  
  20. (a44.7cc): Unknown exception - code 00000000 (first/second chance not available)  
  21. ntdll!NtWaitForSingleObject+0xa:  
  22. 00000000`77c3135a c3              ret  

On va aller chercher la dernière stack de notre worker bloquant. On prend le contexte courant du thread 0xce8 (ThreadID = 3304):

  1. 0:006> ~~[0xce8]s  
  2. sqlservr!BufferPartition::Steal+0x2d:  
  3. 00000000`00a4da41 0f85b5100900    jne     sqlservr!BufferPartition::Steal+0x2f (00000000`00adeafc) [br=0]  

Et on affiche la stack:

  1. 0:005> k  
  2. Child-SP          RetAddr           Call Site  
  3. 00000000`067aeb30 00000000`00a4dc19 sqlservr!BufferPartition::Steal+0x2d  
  4. 00000000`067aeb70 00000000`00a4e730 sqlservr!BPool::Steal+0x16f  
  5. 00000000`067aec40 00000000`00a4e330 sqlservr!SQLSinglePageAllocator::AllocatePages+0x30  
  6. 00000000`067aec70 00000000`00a4e537 sqlservr!MemoryNode::AllocatePagesInternal+0xfc  
  7. 00000000`067aef10 00000000`00a4e7e1 sqlservr!MemoryClerkInternal::AllocatePages+0xaa  
  8. 00000000`067aef60 00000000`00a4e77b sqlservr!CVarPageMgr::PviNewVarPage+0x41  
  9. 00000000`067aefd0 00000000`00a36fe9 sqlservr!CVarPageMgr::PbAllocate+0x161  
  10. 00000000`067af010 00000000`00e3d5cd sqlservr!CMemObj::Alloc+0x49  
  11. 00000000`067af050 00000000`00e3d39c sqlservr!CErrorReportingManager::CwchFormatToBuffer+0x253  
  12. 00000000`067af1b0 00000000`00e6bba1 sqlservr!CErrorReportingManager::CwchCallFormatToBuffer+0x18  
  13. 00000000`067af1f0 00000000`02acecc2 sqlservr!CLoginClientInfo::CLoginClientInfo+0x61  
  14. 00000000`067af270 00000000`0107e3a4 sqlservr!ReportLoginFailureBySNIConn+0x5a  
  15. 00000000`067af360 00000000`026cb3a3 sqlservr!TDSSNIClient::AcceptCommon+0x3f2  
  16. 00000000`067af660 00000000`00a7daf2 sqlservr!Smux::ReadDone+0x413  
  17. 00000000`067af700 00000000`00a7dc11 sqlservr!SNIReadDone+0xa2  
  18. 00000000`067af770 00000000`00a3bbd8 sqlservr!SOS_Node::ListenOnIOCompletionPort+0x263  
  19. 00000000`067af900 00000000`00a3b8ba sqlservr!SOS_Task::Param::Execute+0x12a  
  20. 00000000`067afa10 00000000`00a3b6ff sqlservr!SOS_Scheduler::RunTask+0x96  
  21. 00000000`067afa70 00000000`00f58fb6 sqlservr!SOS_Scheduler::ProcessTasks+0x128  
  22. 00000000`067afae0 00000000`00f59175 sqlservr!SchedulerManager::WorkerEntryPoint+0x2b6  
  23. 00000000`067afbc0 00000000`00f59839 sqlservr!SystemThread::RunWorker+0xcc  
  24. 00000000`067afc00 00000000`00f59502 sqlservr!SystemThreadDispatcher::ProcessWorker+0x2db  
  25. 00000000`067afcb0 00000000`755937d7 sqlservr!SchedulerManager::ThreadEntryPoint+0x173  
  26. 00000000`067afd50 00000000`75593894 msvcr80!endthreadex+0x47  
  27. 00000000`067afd80 00000000`77ad652d msvcr80!endthreadex+0x104  
  28. 00000000`067afdb0 00000000`77c0c521 kernel32!BaseThreadInitThunk+0xd  
  29. 00000000`067afde0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d  

On voit qu’on est bien sur un bon problème mémoire, d’après les derniers appels (SQLSinglePageAllocator::AllocatePages(), BPool:Steal(), BufferPartition::Steal() ). Plus haut dans la stack, on voit les appels vers SNI (connexions réseau): SOS_Node::ListenOnIOCompletionPort(), SNIReadDone(), Smux::ReadDone(), TDSSNIClient::AcceptCommon() … On ne peut pas vraiment en dire plus à ce niveau là.

Quand au contenu de la structure CONTEXT au moment de l’exception, il va falloir faire une petite gymnastique pour retrouver le bon offset. On sait juste que la structure CONTEXT est copiée dans une autre structure appelée g_copiedStackInfo par SQLDumper. On va chercher où se trouve cette structure dans le minidump en utilisant la commande explore (‘x’):

  1. 0:005> x sqlservr!g_copiedStackInfo  
  2. 00000000`04036900 sqlservr!g_copiedStackInfo = <no type information>  

Si on regarde dans winnt.h la définition de CONTEXT:

  1. typedef struct _CONTEXT  
  2. {  
  3. DWORD   ContextFlags;  
  4.   
  5. /* These are selected by CONTEXT_DEBUG_REGISTERS */  
  6. DWORD   Dr0;  
  7. DWORD   Dr1;  
  8. DWORD   Dr2;  
  9. DWORD   Dr3;  
  10. DWORD   Dr6;  
  11. DWORD   Dr7;  
  12.   
  13. /* These are selected by CONTEXT_FLOATING_POINT */  
  14. FLOATING_SAVE_AREA FloatSave;  
  15.   
  16. /* These are selected by CONTEXT_SEGMENTS */  
  17. DWORD   SegGs;  
  18. DWORD   SegFs;  
  19. DWORD   SegEs;  
  20. DWORD   SegDs;  
  21.   
  22. /* These are selected by CONTEXT_INTEGER */  
  23. DWORD   Edi;  
  24. DWORD   Esi;  
  25. DWORD   Ebx;  
  26. DWORD   Edx;  
  27. DWORD   Ecx;  
  28. DWORD   Eax;  
  29.   
  30. /* These are selected by CONTEXT_CONTROL */  
  31. DWORD   Ebp;  
  32. DWORD   Eip;  
  33. DWORD   SegCs;  
  34. DWORD   EFlags;  
  35. DWORD   Esp;  
  36. DWORD   SegSs;  
  37.   
  38. BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];  
  39. } CONTEXT;  

Chaque propriété est un DWORD, donc un dd pour lire le contenu doit suffire:

  1. 0:005> dd sqlservr!g_copiedStackInfo  
  2. 00000000`04036900  00000001 00000000 069ef240 00000000  
  3. 00000000`04036910  000015e0 00000000 00000000 00000000  
  4. 00000000`04036920  00000000 00000000 00000000 00000000  
  5. 00000000`04036930  00000000 00000000 00000000 00000000  
  6. 00000000`04036940  00000000 00000000 00000000 00000000  
  7. 00000000`04036950  0010000b 00001fa0 00000033 00000000  
  8. 00000000`04036960  002b0000 00000297 00000000 00000000  
  9. 00000000`04036970  00000000 00000000 00000000 00000000  

Chaque ligne représente une structure CONTEXT. Il faut trouver laquelle est la bonne, malheureusement je n’ai pas les symboles privés donc je ne vais pas pouvoir résoudre le contenu de g_copiedStackInfo, comme me l’indique ce message laconique de windbg:

  1. 0:005> dt 00000000`04036900 CONTEXT Rip Rsp Rbp  
  2. *************************************************************************  
  3. ***                                                                   ***  
  4. ***                                                                   ***  
  5. ***    Either you specified an unqualified symbol, or your debugger   ***  
  6. ***    doesn't have full symbol information.  Unqualified symbol      ***  
  7. ***    resolution is turned off by default. Please either specify a   ***  
  8. ***    fully qualified symbol module!symbolname, or enable resolution ***  
  9. ***    of unqualified symbols by typing ".symopt- 100". Note that   ***  
  10. ***    enabling unqualified symbol resolution with network symbol     ***  
  11. ***    server shares in the symbol path may cause the debugger to     ***  
  12. ***    appear to hang for long periods of time when an incorrect      ***  
  13. ***    symbol name is typed or the network symbol server is down.     ***  
  14. ***                                                                   ***  
  15. ***    For some commands to work properly, your symbol path           ***  
  16. ***    must point to .pdb files that have full type information.      ***  
  17. ***                                                                   ***  
  18. ***    Certain .pdb files (such as the public OS symbols) do not      ***  
  19. ***    contain the required information.  Contact the group that      ***  
  20. ***    provided you with these symbols if you need this command to    ***  
  21. ***    work.                                                          ***  
  22. ***                                                                   ***  
  23. ***    Type referenced: CONTEXT                                       ***  
  24. ***                                                                   ***  
  25. *************************************************************************  
  26. Symbol CONTEXT not found.  

Donc il va falloir charger les contextes  un par un pour voir s’il y a qq chose dedans:

  1. 0:005> .cxr sqlservr!g_copiedStackInfo  
  2. rax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000  
  3. rdx=0000000000000000 rsi=0000000003b82080 rdi=0000000003b80800  
  4. rip=0000000000070fc0 rsp=000000000007ffc7 rbp=0000000003b80800  
  5. r8=00000000069ef240  r9=00000000008b1080 r10=0000000003b82080  
  6. r11=0000000000000001 r12=0000000000007628 r13=aaaaaaaaaaaaaaab  
  7. r14=00000000008b0080 r15=000000000000005e  
  8. iopl=0         nv up di pl nz na pe nc  
  9. cs=0000  ss=0000  ds=0000  es=0000  fs=0000  gs=0000             efl=00000000  
  10. 00000000`00070fc0 0000            add     byte ptr [rax],al ds:00000000`00000000=??  
  11. 0:005> kc  
  12. *** Stack trace for last set context - .thread/.cxr resets it  
  13. Call Site  
  14. 0x0  

Nada. Frame suivante (qui se situe à l’adresse de base + 0x10 => ( 00000000`04036910 – 00000000`04036900 ) ):

  1. 0:005> .cxr sqlservr!g_copiedStackInfo+0x10  
  2. rax=0000000000000000 rbx=0000000003b80800 rcx=0000000000000000  
  3. rdx=000000000007ffc7 rsi=00000000069ef240 rdi=00000000008b1080  
  4. rip=0000000000070fc0 rsp=0000000003b82080 rbp=0000000003b80800  
  5. r8=0000000003b82080  r9=0000000000000001 r10=0000000000007628  
  6. r11=aaaaaaaaaaaaaaab r12=00000000008b0080 r13=000000000000005e  
  7. r14=0000000000070fc0 r15=0000000161ffd000  
  8. iopl=1         ov dn ei ng nz na pe nc  
  9. cs=0000  ss=0010  ds=0000  es=0000  fs=0000  gs=000b             efl=00001fa0  
  10. 00000000`00070fc0 0000            add     byte ptr [rax],al ds:00000000`00000000=??  
  11. 0:005> kc  
  12. *** Stack trace for last set context - .thread/.cxr resets it  
  13. Call Site  
  14. 0x0  
  15. 0x0  

Toujours rien. Allons voir à l’adresse suivante:

  1. 0:005> .cxr sqlservr!g_copiedStackInfo+0x20  
  2. rax=000000000007ffc7 rbx=0000000003b80800 rcx=0000000003b80800  
  3. rdx=0000000003b82080 rsi=0000000003b82080 rdi=0000000000000001  
  4. rip=0000000000a7edb0 rsp=00000000069ef240 rbp=00000000008b1080  
  5. r8=0000000000007628  r9=aaaaaaaaaaaaaaab r10=00000000008b0080  
  6. r11=000000000000005e r12=0000000000070fc0 r13=0000000161ffd000  
  7. r14=0000000000070fc0 r15=0000000000070fcf  
  8. iopl=0         nv up ei ng nz ac po cy  
  9. cs=0033  ss=002b  ds=0000  es=0000  fs=0000  gs=0000             efl=00000297  
  10. sqlservr!BPool::MayGrow+0x3b:  
  11. 00000000`00a7edb0 ??              ???  
  12. 0:005> kc  
  13. *** Stack trace for last set context - .thread/.cxr resets it  
  14. Call Site  
  15. sqlservr!BPool::MayGrow  
  16. sqlservr!BPool::ReplenishFreeList  
  17. sqlservr!BPool::Steal  
  18. sqlservr!SQLSinglePageAllocator::AllocatePages  
  19. sqlservr!MemoryNode::AllocatePagesInternal  
  20. sqlservr!MemoryClerkInternal::AllocatePages  
  21. sqlservr!CVarPageMgr::PviNewVarPage  
  22. sqlservr!CVarPageMgr::PbAllocate  
  23. sqlservr!CMemObj::Alloc  
  24. sqlservr!CErrorReportingManager::CwchFormatToBuffer  
  25. sqlservr!CErrorReportingManager::CwchCallFormatToBuffer  
  26. sqlservr!CLoginClientInfo::CLoginClientInfo  
  27. sqlservr!ReportLoginFailureBySNIConn  
  28. sqlservr!TDSSNIClient::AcceptCommon  
  29. sqlservr!Smux::ReadDone  
  30. sqlservr!SNIReadDone  
  31. sqlservr!SOS_Node::ListenOnIOCompletionPort  
  32. sqlservr!SOS_Task::Param::Execute  
  33. sqlservr!SOS_Scheduler::RunTask  
  34. sqlservr!SOS_Scheduler::ProcessTasks  
  35. sqlservr!SchedulerManager::WorkerEntryPoint  
  36. sqlservr!SystemThread::RunWorker  
  37. sqlservr!SystemThreadDispatcher::ProcessWorker  
  38. sqlservr!SchedulerManager::ThreadEntryPoint  
  39. msvcr80!endthreadex  
  40. msvcr80!endthreadex  
  41. kernel32!BaseThreadInitThunk  
  42. ntdll!RtlUserThreadStart  

Cette fois on la tient. Et si  on compare les deux stacks (celle obtenue via le ThreadID 0xce8 et celle contenue dans la struct CONTEXT), on voit un décalage:

  1. sqlservr!BPool::MayGrow  
  2. sqlservr!BPool::ReplenishFreeList  

Et BPool:MayGrow est une indication que SQL Server demande une allocation supplémentaire pour ajuster Target et Committed. C’est là que ça coince.

Le fichier bugcheck nous renvoie un état de la mémoire sur la machine au moment du problème:

  1. Memory  
  2. MemoryLoad = 94%  
  3. Total Physical = 4095 MB  
  4. Available Physical = 219 MB  
  5. Total Page File = 8189 MB  
  6. Available Page File = 2072 MB  
  7. Total Virtual = 8388607 MB  
  8. Available Virtual = 8380979 MB  
  9. **Dump thread - spid = 0, EC = 0x0000000000000000  
  10. ***Stack Dump being sent to K:\MSSQL10_50.MSSQLSERVER\MSSQL\LOG\SQLDump0001.txt  

6Gb de page file utilisé, 200Mb de libre, MemoryLoad 94%. On n’est pas loin d’une OOM condition.

Et pourquoi donc on en est arrivé là s’il vous plaît ?

Parce que MAX SERVER MEMORY est à sa valeur par défaut !!

Vilaine habitude. Il faut toujours setter max server memory sur 64 bits.

A+ David B.

Références:
How to diagnose and correct errors 17883, 17884, 17887, and 17888 – Bob Dorr Microsoft SQL Server CSS, Sameer Tejani SQL Server Development SQLOS Team.
How to analyze Non-Yielding scheduler or Non-yielding IOCP Listener dumps: Karthick P.K

Continuez votre lecture sur le blog :

twitterlinkedinmail

David Baffaleuf

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.