9
votes

DELPHI: La section critique de débogage suspendue en rapportant une pile d'appels de threads d'exécution sur le verrouillage "Échec"

Je cherche un moyen de déboguer une section critique rare Delphi 7 (TCriticalSection) Hang / impasse. Dans ce cas, si un thread attend une section critique pendant plus de 10 secondes, je voudrais produire un rapport avec la trace de la pile du fil à la fois verrouillant la section critique et le fil qui n'a pas pu être capable. Pour verrouiller la section critique après avoir attendu 10 secondes. C'est correct alors si une exception est soulevée ou si l'application se termine.

Je préférerais continuer à utiliser des sections critiques plutôt que d'utiliser d'autres primitives de synchronisation, si possible, mais peut basculer si nécessaire (comme pour obtenir une fonctionnalité de délai d'attente).

Si l'outil / la méthode fonctionne au moment de l'exécution de l'IDE, c'est un bonus, car cela est difficile à reproduire à la demande. Dans le cas rare, je peux dupliquer l'impasse à l'intérieur de l'EDI, si j'essaie de faire une pause pour commencer à déboguer, l'IDE siège simplement de ne rien faire et ne peut jamais dans un état où je peux voir des threads ou des piles d'appel. Je peux réinitialiser le programme en cours d'exécution, cependant.

Mise à jour: Dans ce cas, je ne traite que d'une section critique et de 2 threads, ce n'est donc probablement pas un problème de commande de verrouillage. Je crois qu'il y a une tentative imbriquée inappropriée d'entrer dans la serrure sur deux threads différents, ce qui entraîne une impasse.


0 commentaires

5 Réponses :


1
votes

Si vous voulez pouvoir attendre quelque chose avec un délai d'attente, vous pouvez essayer de remplacer votre section critique avec un signal TEvent. Vous pouvez dire à attendre sur l'événement, donnez-lui une longueur de temporisation et vérifiez le code de résultat. Si le signal a été défini, vous pouvez continuer. Sinon, s'il a expiré, vous soulevez une exception.

Au moins, c'est comme ça que je le ferais dans D2010. Je ne suis pas sûr si Delphi 7 a Tevent, mais c'est probablement.


0 commentaires

9
votes

Vous devez créer et utiliser votre propre classe d'objet de verrouillage. Il peut être mis en œuvre en utilisant des sections critiques ou mutex, selon que vous voulez déboguer ou non.

Créer votre propre classe a un avantage supplémentaire: Vous pouvez mettre en œuvre une hiérarchie de verrouillage et soulever une exception quand elle est violée. Serrures se produisent lorsque les blocages ne sont pas prises dans le même ordre, à chaque fois. L'attribution d'un niveau de verrouillage à chaque serrure permet de vérifier que les verrous sont prises dans l'ordre correct. Vous pouvez mémoriser le niveau de verrouillage actuel dans un threadvar et les verrouillages ne doivent être prises qui ont un niveau de verrouillage inférieur, sinon vous soulevez une exception. Cela attraper toutes les violations, même en l'absence de blocage se produit, il devrait donc accélérer votre mise au point beaucoup.

pour obtenir la trace de la pile des fils, il y a beaucoup de questions ici sur Stack Overflow traiter cette question.

mise à jour

Vous écrivez:

Dans ce cas, je ne traite que d'une section critique et 2 fils, c'est donc probablement pas un problème de commande de verrouillage. Je crois qu'il y a une tentative imbriquée inappropriée d'entrer dans la serrure sur deux threads différents, ce qui entraîne une impasse.

Ce ne peut pas être l'histoire. Il n'y a pas moyen de l'impasse avec deux fils et une seule section critique seul sous Windows, car les sections critiques peuvent être acquises il récursive par un fil. Il doit y avoir un autre mécanisme de blocage en jeu, comme par exemple le SendMessage () appel.

Mais si vous êtes vraiment affaire avec deux fils seulement, puis l'un d'entre eux doit être le principal / VCL / thread GUI. Dans ce cas, vous devriez être en mesure d'utiliser le MadExcept « vérification de gel thread principal » fonctionnalité . Il va essayer d'envoyer un message au thread principal, et l'échec après un temps personnalisable est écoulé sans que le message en cours de traitement. Si votre thread principal bloque sur la section critique, et l'autre thread bloque sur un message de traitement des appels alors MadExcept devrait être en mesure de rattraper cela et vous donner une trace de la pile pour les deux fils.


2 commentaires

MakedXcept peut également être invité à prendre une décharge de thread à tout moment, c'est peut-être idéal pour cela.


madexcept ressemble à la meilleure option. Merci!



4
votes

Ce n'est pas un anwer direct à votre question, mais quelque chose que j'ai rencontré récemment qui m'avait été souples (et quelques collègues) pendant un moment.

C'était un thread intermittent accroché, impliquant une section critique et une fois que nous connaissions la cause, c'était très évident et nous a donné à tous un moment "d'oh". Cependant, il a pris une chassage sérieuse pour trouver (ajouter de plus en plus de journalisation de traces pour identifier la déclaration incriminée) et c'est pourquoi je pensais en parler.

Il était également sur une entrée critique Entrée. Un autre fil avait effectivement acquis cette section critique. Une serrure morte en tant que telle ne semblait pas être la cause, car il n'y avait qu'une seule section critique impliquée, il ne pouvait donc pas avoir de problèmes d'acquérir des serrures dans un ordre différent. Le fil tenant la section critique doit simplement avoir continué puis libéré le verrou, permettant à l'autre thread de l'acquérir.

En fin de compte, il s'est avéré que le fil tenant la serrure accédait à l'itemindex d'un (IIRC) ComboBox, assez inoffreux, il semblerait. Malheureusement, l'obtention de cet itemindex est dépendant du traitement des messages. Et le fil en attente de la serrure était le fil d'application principal ... (Juste au cas où quelqu'un se demande: le thread principal fait tout le traitement du message ...)

Nous aurions peut-être pensé à cela beaucoup plus tôt si cela avait été un peu plus évident dès le début que le VCL était impliqué. Cependant, il a commencé dans un code associé à l'UI et que la participation de VCL n'est devenu apparent qu'après l'ajout d'instrumentation (tracing d'entrée - sortie) le long de l'arborescence d'appel et de retour à tous les événements déclenchés et à leurs manutentionnaires jusqu'au code de l'UI.

J'espère que cette histoire sera utile à quelqu'un face à un accrocher mystérieux.


0 commentaires

3
votes

Utilisez mutex au lieu de la section critique. Il y a une petite différence entre les mutiles et les sections critiques - les sections critiques sont plus efficaces, tandis que les mutiles sont plus flexibles. Votre peut facilement basculer entre les mutiles et les sections critiques, en utilisant, par exemple des mutexes dans la version de débogage.

pour la section critique que nous utilisons: p> xxx pré>

la même avec mutex: p >

function AcquireLock(Lock: THandle; TimeOut: LongWord): Boolean;
begin
  Result:= WaitForSingleObject(Lock, Timeout) = WAIT_OBJECT_0;
end;


0 commentaires

2
votes

Vous pouvez également utiliser des sections critiques avec le tryenterRCriticalSection API au lieu de Entriticalsection .

Si vous utilisez tryenterRCriticalSection et l'acquisition de verrouillage échoue , l'API renvoie false et vous pouvez faire face à l'échec de quelque manière que ce soit que vous voyez en forme, au lieu de simplement verrouiller le fil.

quelque chose comme xxx

Notez que delphi's tcrriticalsection utilise Entriticalsection Donc, à moins que vous ne répondiez cette classe, vous devrez faire votre propre classe ou vous devrez traiter avec l'initialisation / désinitialisation de la section critique.


0 commentaires