Détection de l’application bloquée

J’ai une très grande application Windows complexe (millions + LOC) écrite en C ++. Nous recevons chaque jour une poignée de rapports que l’application a verrouillés et doivent être fermés avec force.

Bien que nous ayons des rapports exhaustifs sur les plantages en place, je souhaiterais les développer pour inclure ces scénarios de blocage – même avec une forte journalisation en place, nous n’avons pas été en mesure de localiser certaines de ces causes. Nous pouvons clairement voir où l’activité s’est arrêtée – mais pas pourquoi elle s’est arrêtée, même en évaluant la sortie de tous les threads.

Le problème est la détection d’un blocage. Jusqu’à présent, le mieux que je puisse trouver est un thread de surveillance (car nous avons des preuves que les threads d’arrière-plan continuent à exécuter des problèmes sans problème) qui pient périodiquement la fenêtre principale avec un message personnalisé et confirme qu’il est géré dans un mode en temps opportun. Cela ne capturerait que les blocages des threads de l’interface graphique, mais cela semble être la majorité de ces threads. Si une réponse n’était pas reçue dans un délai configurable, nous capturions une mémoire et un vidage de la stack, et donnions à l’utilisateur la possibilité de continuer à attendre ou à redémarrer l’application.

Est-ce que quelqu’un connaît une meilleure façon de faire cela qu’un tel sondage périodique de la fenêtre principale de cette manière? Cela semble douloureusement maladroit, mais je n’ai pas vu d’alternatives fonctionnant sur nos plates-formes – Windows XP et Windows 2003 Server. Je vois que Vista dispose de bien meilleurs outils pour cela, mais malheureusement cela ne nous aidera pas.

Il suffit de dire que nous avons effectué des diagnostics approfondis à ce sujet et que nous n’avons obtenu qu’un succès limité. Notez que la connexion de Windbg en temps réel n’est pas une option, car nous n’obtenons les rapports que quelques heures ou quelques jours après l’incident. Nous pourrions récupérer un fichier mémoire et des fichiers journaux, mais rien de plus.

Toutes les suggestions au-delà de ce que je planifie ci-dessus seraient appréciées.

La réponse est simple: SendMessageTimeout !

En utilisant cette API, vous pouvez envoyer un message à une fenêtre et attendre un délai avant de continuer. Si l’application répond avant l’expiration du délai, elle est toujours en cours d’exécution, sinon elle est bloquée.

Une option consiste à exécuter votre programme sous votre propre “débogueur” tout le temps. Certains programmes, tels que GetRight, le font pour la protection contre la copie, mais vous pouvez également le faire pour détecter les blocages. Essentiellement, vous incluez dans votre programme du code à attacher à un processus via l’API de débogage, puis utilisez cette API pour rechercher périodiquement des blocages. Lorsque le programme démarre pour la première fois, il vérifie si un débogueur lui est associé et, sinon, il exécute une autre copie de lui-même et s’y attache. Ainsi, la première instance ne fait rien d’autre que le débogueur et la seconde ” un.

La façon dont vous recherchez les blocages est une autre question, mais l’access à l’API de débogage devrait permettre de vérifier de manière raisonnablement efficace si la stack a été modifiée ou non (sans charger tous les symboles). Cependant, il se peut que vous n’ayez besoin de le faire que toutes les quelques minutes, même si ce n’est pas efficace, cela peut être correct.

C’est une solution quelque peu extrême, mais qui devrait être efficace. Il serait également très facile d’activer et de désactiver ce comportement – un commutateur de ligne de commande fera l’affaire ou #define si vous préférez. Je suis sûr qu’il y a du code qui fait des choses comme ça, donc vous n’avez probablement pas à le faire à partir de zéro.

Une suggestion:

En supposant que le problème est dû au locking, vous pouvez vider vos états de mutex et de sémaphore à partir d’un fil de surveillance. Avec un peu de travail (en traçant votre graphe d’appel), vous pouvez déterminer comment vous êtes arrivé à une impasse, quels chemins d’appel se bloquent mutuellement, etc.

Bien qu’une parsing de crashdump semble fournir une solution pour identifier le problème, dans mon expérience, cela porte rarement beaucoup de fruits, car elle manque de détails non ambigus suffisants sur ce qui s’est passé juste avant le crash. Même avec l’outil que vous proposez, cela fournirait peu plus que des preuves indirectes de ce qui s’est passé. Je parie que la cause est des données partagées non protégées, donc une trace de locking ne l’affiche pas.

La manière la plus productive de trouver ceci dans mon expérience est de distiller la logique de l’application dans son essence et d’identifier où les conflits doivent se produire. Combien y a-t-il de threads? Combien sont des interfaces graphiques? A combien de points les threads interagissent-ils? Oui, c’est une bonne vérification de bureau. Des interactions suspectes peuvent être identifiées en un jour ou deux, puis il suffit de convaincre un petit groupe de sceptiques que l’interaction est correcte.