Enquête sur les plantages après un fonctionnement de longue durée d'une caméra industrielle - La fuite de handles (partie 1)

· Mis à jour le: · · Développement Windows, Investigation de bug, Caméra industrielle, Fuite de handles, Conception de journalisation

Lorsqu’une application Windows plante soudainement après un fonctionnement de longue durée, le premier réflexe est très souvent de soupçonner une fuite mémoire. Pourtant, dans les faits, il n’est pas rare que la véritable coupable soit une fuite de handles, qui ne se révèle qu’après plusieurs semaines, sous la forme d’une panne secondaire.

Cet article présente le cas d’une application Windows pilotant une caméra industrielle, dont le plantage soudain après environ un mois de fonctionnement continu a fait l’objet d’une enquête. Une fois l’isolement du problème mené à son terme, la cause s’est révélée être une fuite de handles survenant sur le chemin d’échec lié à la reconnexion de la caméra.

Dans cette première partie, nous exposons ce qu’est une fuite de handles, comment nous avons isolé cet incident, et quels journaux conserver pour éviter qu’il ne se reproduise. Dans la seconde partie, Quand une application de pilotage de caméra industrielle plante soudainement après un mois (partie 2) - Qu’est-ce qu’Application Verifier et comment construire un socle de tests des cas anormaux, nous abordons la construction d’un socle de tests des cas anormaux.

Les noms propres et certains champs de journal ont été masqués, mais la façon de penser elle-même est très largement partagée par l’ensemble des applications Windows de pilotage d’équipements.

Table des matières

  1. La conclusion d’abord (en une ligne)
  2. Qu’est-ce qu’une fuite de handles ?
    • 2.1. Ce que l’on entend ici par « handle »
    • 2.2. Pourquoi cela ne se révèle-t-il souvent qu’après un fonctionnement de longue durée ?
    • 2.3. Différence avec une fuite mémoire
  3. Étude de cas : une application de pilotage de caméra industrielle qui plante soudainement après un mois
    • 3.1. Les symptômes observés
    • 3.2. Les indicateurs examinés en premier
    • 3.3. La fuite qui était la cause profonde
  4. Comment nous avons isolé le problème
    • 4.1. Compresser le temps au lieu d’attendre une reproduction à l’échelle du mois
    • 4.2. Lire la pente du Handle Count
    • 4.3. Examiner l’appariement entre create/open et close/dispose
    • 4.4. Pour une fuite de handles, chercher « où elle a fui », pas « où elle a planté »
  5. Les journaux nécessaires pour éviter que cela ne se reproduise
    • 5.1. L’ensemble minimal à conserver en premier
    • 5.2. Les journaux réellement renforcés
    • 5.3. À quelle granularité collecter
  6. Guide de décision sommaire
  7. Résumé
  8. Références

1. La conclusion d’abord (en une ligne)

  • Dans une application de pilotage qui ne plante qu’après un fonctionnement de longue durée, il faut impérativement surveiller le Handle Count, pas seulement Private Bytes
  • Une fuite de handles a tendance à se cacher non pas dans le chemin normal, mais dans les chemins timeout / reconnect / échec intermédiaire / early return
  • La ligne où l’application plante réellement est souvent l’endroit où elle n’a plus pu créer un nouveau handle par la suite, et non l’endroit où la fuite a eu lieu
  • Les journaux à mettre en place en premier sont : le contexte operation/session, le handle count du process, l’appariement open/close des ressources, et les erreurs Win32 / HRESULT / SDK
  • Plutôt que d’attendre une reproduction à l’échelle du mois, il est plus rapide de rejouer des milliers de fois, dans une boucle courte, les chemins de connexion, déconnexion, reconnexion et échec
  • Application Verifier, abordé dans la seconde partie, est assez efficace, mais la base consiste avant tout à pouvoir suivre, avec ses propres journaux, les ruptures de cycle de vie (lifetime)

En résumé, dans ce genre de dossier, ce qu’il faut faire en premier n’est pas de regarder le fait que « ça a planté après une longue période », mais de rendre observables la façon dont les ressources augmentent et les chemins d’échec empruntés.

Une fuite de handles porte le plus souvent déjà le visage d’une panne secondaire au moment où on la découvre. C’est pourquoi se contenter de regarder l’exception au moment du plantage conduit facilement à partir dans une direction complètement erronée.

2. Qu’est-ce qu’une fuite de handles ?

2.1. Ce que l’on entend ici par « handle »

Le handle dont il est question ici est l’identifiant par lequel un processus Windows référence une ressource du système d’exploitation. En font partie, par exemple, les éléments suivants.

Catégorie Exemples
Objets noyau event, mutex, semaphore, thread, process, waitable timer
E/S open sur un file, un pipe, un socket, un device
Fréquent en pilotage d’équipements events internes du SDK de la caméra, objets d’attente liés à l’enregistrement de callbacks, handles liés au thread d’acquisition d’images

Ce qui pose problème en particulier dans les applications de pilotage, c’est le schéma consistant à « oublier de fermer, sur un chemin d’échec intermédiaire, une ressource ouverte temporairement pour une opération donnée ».

Voici typiquement le déroulement.

  • On crée un event à chaque reconnexion
  • L’enregistrement du callback ou le démarrage de l’acquisition échoue en cours de route
  • Le chemin de succès ferme la ressource, mais pas le chemin d’échec
  • Les tests courts habituels n’empruntant que le chemin de succès, cela passe inaperçu

Ce type de fuite se glisse assez couramment, aussi bien en revue de code qu’en exploitation réelle.

2.2. Pourquoi cela ne se révèle-t-il souvent qu’après un fonctionnement de longue durée ?

Une fuite de handles ne casse pas forcément tout de façon spectaculaire en une seule fois. Ce qui est en réalité plus gênant, c’est une fuite à faible pente, où chaque échec ne fait perdre qu’un seul handle.

Fonctionnement normaltimeout / reconnect occasionnelLe chemin d'échec crée un Event HandleCloseHandle n'est jamais appeléLe Handle Count augmente légèrementSe répète des centaines de foisCreateEvent / l'open du SDK échoueCrash / arrêt ailleurs

Si une seule reconnexion ne fait perdre qu’un seul handle, rien ne se produit en quelques minutes. Mais dans une application de pilotage d’équipements qui tourne 24 h/24 et 7 j/7, des conditions limites comme le timeout, la réinitialisation ou la reprise après déconnexion se répètent sans cesse. Le résultat est cette présentation étrange d’un problème qui ne se révèle que plusieurs semaines plus tard.

Ce qui compte ici, c’est que la fuite de handles elle-même ne constitue pas forcément la ligne où l’application plante. Voici les modes de défaillance les plus fréquents.

  • Une API censée créer un nouvel event, fichier ou thread échoue
  • Le SDK ne parvient pas à créer en interne la ressource dont il a besoin, et ne renvoie qu’un code d’échec générique
  • La gestion d’erreur après l’échec est trop légère, et l’application plante en manipulant un handle null ou invalide
  • Les timeouts se multiplient, et le processus finit par être tué par un watchdog ou un contrôleur de niveau supérieur

Autrement dit, le point de plantage est « la dernière victime », pas forcément « le premier coupable ».

2.3. Différence avec une fuite mémoire

Face à un défaut qui survient après un fonctionnement de longue durée, le premier réflexe est de soupçonner une fuite mémoire. C’est naturel en soi, bien sûr, mais il est parfois plus rapide d’examiner une fuite de handles sous un autre angle.

Aspect Fuite mémoire Fuite de handles
Indicateur à surveiller en premier Private Bytes, Commit, Working Set Handle Count
Symptômes typiques Pression mémoire, paging, ralentissement, OOM Échecs de Create* / Open* / d’initialisation interne du SDK, pannes secondaires
Endroit où elle se cache facilement Caches, références conservées, oublis de libération Asymétrie entre create/open et close/dispose
Comment elle se manifeste La mémoire augmente progressivement Le handle count augmente progressivement sans redescendre

C’est pourquoi, lors de l’isolement d’un problème de fonctionnement de longue durée, se contenter de « ne regarder que la mémoire » revient facilement à conduire avec un œil fermé. Surveiller au minimum Handle Count et Thread Count ensemble permet d’y voir beaucoup plus clair.

3. Étude de cas : une application de pilotage de caméra industrielle qui plante soudainement après un mois

3.1. Les symptômes observés

L’incident était simple.

  • Une application Windows pilotant une caméra industrielle tourne 24 h/24 et 7 j/7
  • En temps normal, elle fonctionne sans problème
  • Au bout d’environ un mois, l’application plante brusquement un jour donné
  • Après un redémarrage, elle refonctionne normalement pendant un moment

La première difficulté est que « le temps avant le plantage est long ». Attendre un mois pour chaque tentative de reproduction est extrêmement pénible pour une investigation.

Ce qui rendait les choses encore plus délicates, c’est que l’endroit du plantage n’était pas exactement le même à chaque fois. Tantôt juste après le début d’une reconnexion, tantôt au démarrage de l’acquisition d’images, tantôt après l’échec d’un appel au SDK.

Avec cette présentation, on peut au départ suspecter n’importe lequel des éléments suivants.

  • Une instabilité du côté du SDK de la caméra
  • Une panne transitoire causée par la communication ou une déconnexion du périphérique
  • Une fuite mémoire
  • Une race condition autour des threads
  • Un échec d’initialisation qui n’apparaît pas dans les journaux

Autrement dit, on se trouvait dans une situation où il y avait beaucoup trop de « suspects vaguement plausibles ».

3.2. Les indicateurs examinés en premier

La première chose qui a été faite fut donc d’examiner la façon dont l’ensemble des ressources du process augmentait. Dans ce cas précis, les tendances observées étaient à peu près les suivantes.

Indicateur Tendance observée Lecture
Handle Count Augmente peu à peu après un reconnect ou un timeout, et ne redescend pas On suspecte une fuite de handles
Private Bytes Il y a des variations, mais la pente d’augmentation monotone est faible Le coupable n’est pas forcément le tas (heap)
Thread Count Pratiquement stable Une fuite de threads est peu probable
Point de plantage Légèrement différent à chaque fois Une panne secondaire est probable

À ce stade, le champ d’investigation s’était considérablement resserré. Car il était plus naturel de voir la situation non pas comme « ça plante après un mois », mais comme « quelque chose fuit progressivement en cours de route, et cela finit par faire planter l’application après un mois ».

3.3. La fuite qui était la cause profonde

La cause finale était un oubli de fermeture d’un event handle créé sur le chemin d’échec d’initialisation lors de la reconnexion de la caméra.

En simplifiant, le déroulement se présente ainsi.

SDK caméraWindowsApplication de pilotageSDK caméraWindowsApplication de pilotageRetour sur le chemin d'échecCloseHandle n'est jamais appeléloop[Reconnexions répétées]CreateEventEnregistrement du callbackÉchec partiel / timeoutLe Handle Count augmente peu à peuCreateEvent / Open suivantÉchecPlante en tant que panne secondaire

En guise d’illustration du code, voici à quoi ressemble la fuite.

handle = CreateEvent(...)

if (!RegisterCallback(handle))
{
    return Error;   // CloseHandle(handle) est manquant
}

if (!StartAcquisition())
{
    return Error;   // close manque ici aussi
}

...
CloseHandle(handle)

La raison pour laquelle cela passe facilement inaperçu avec des tests courts est également assez facile à comprendre.

  • Un démarrage normal suivi d’un arrêt normal referme bien la ressource
  • L’échec ne se produit que pendant une reconnexion
  • Il n’existe aucun test qui emprunte massivement ce chemin d’échec
  • En production, la fuite s’accumule peu à peu sur plusieurs semaines

Autrement dit, la structure était la suivante : « invisible si l’on ne regarde que le chemin normal, mais qui fuit couramment sur les chemins anormaux ».

L’orientation retenue pour la correction n’a rien de spectaculaire.

  • Rapprocher les responsabilités de create/open et de close/dispose
  • Déplacer la libération vers un finally, un destructeur ou un objet de session, afin qu’elle se produise systématiquement même en cas d’échec intermédiaire
  • Clarifier la propriété (ownership) avant et après l’enregistrement du callback et le démarrage de l’acquisition
  • Exprimer « qui ferme la ressource » à travers la responsabilité du code, et non par des commentaires

Il ne s’agit pas tant d’une technique particulière que d’un travail de rangement consistant à intégrer le cycle de vie des ressources directement dans le code.

4. Comment nous avons isolé le problème

4.1. Compresser le temps au lieu d’attendre une reproduction à l’échelle du mois

Dans ce genre d’investigation, attendre un mois à chaque tentative est une mauvaise approche. Ce qu’il faut faire, c’est emprunter le chemin suspect de nombreuses fois en peu de temps.

Dans ce cas précis, la reproduction a été compressée en faisant tourner une boucle de ce type.

OuiNonDémarrageOuverture de la caméraDémarrage de l'acquisitiontimeout / déconnexion simulésReconnexionReprise de l'acquisitionRépéter N fois ?Vérifier les écarts à la fin

Le point clé est de consacrer le temps aux opérations de cycle de vie aux frontières, plutôt qu’au temps normal où « les images arrivent ».

Concrètement, voici les scénarios qui se révèlent efficaces.

  • Faire tourner massivement open -> start -> stop -> close
  • Provoquer délibérément des timeouts pour déclencher des reconnexions
  • Forcer un échec juste après l’enregistrement du callback
  • Introduire des interruptions de déconnexion, des interruptions de reconnexion et des races de shutdown

Il n’est pas nécessaire de reproduire à la perfection un mois d’exploitation réelle. Au contraire, emprunter des milliers de fois la frontière de cycle de vie (lifetime edge) que l’on soupçonne rapproche bien davantage de la cause.

4.2. Lire la pente du Handle Count

Dans une investigation de fuite de handles, se contenter de regarder les valeurs absolues peut prêter à confusion. Ce qui compte, c’est de savoir si le compteur redescend bien après une opération censée le faire redescendre, et de combien il augmente pour combien d’opérations.

Pour l’approche, l’ordre suivant est généralement le plus lisible.

  1. Établir la baseline après le warm-up
  2. Enregistrer le Handle Count après chaque reconnect / start-stop / close
  3. Observer l’écart cycle par cycle
  4. Observer aussi la pente agrégée sur plusieurs cycles

Voici, par exemple, la façon de la lire.

leakSlope =
    (currentHandleCount - baselineHandleCount)
    / reconnectCount

Savoir si une valeur absolue de 2000 est élevée ou faible varie selon l’application. En revanche, si l’on constate +1 par reconnexion, sans jamais redescendre, c’est très suspect.

L’astuce ici est de ne pas regarder Handle Count isolément, mais de consigner au moins les éléments suivants en parallèle.

  • Handle Count
  • Private Bytes
  • Thread Count
  • ReconnectCount
  • La phase actuelle

Cela permet de savoir assez rapidement si « c’est la mémoire qui augmente », si « ce sont les threads qui augmentent », ou si « les ressources ne redescendent pas à chaque reconnexion ».

4.3. Examiner l’appariement entre create/open et close/dispose

Même une fois établi que le Handle Count global du process est suspect, cela seul ne permet pas de remonter jusqu’à l’endroit de la fuite. Ce dont on a besoin ensuite, ce sont des journaux qui montrent le cycle de vie des ressources par paires.

À titre d’illustration, voici le type de journal structuré concerné.

CameraSession session=421 cameraId=CAM01 phase=ReconnectStart reason=FrameTimeout handleCount=1824 privateBytesMB=418

CameraResource session=421 resourceId=evt-884 kind=Event name=FrameReady action=Create osHandle=0x00000ABC handleCount=1825

CameraResource session=421 resourceId=evt-884 kind=Event name=FrameReady action=Close osHandle=0x00000ABC handleCount=1824

Ce qui compte ici, c’est de ne pas se fier uniquement à osHandle. Les valeurs de handle Windows pouvant être réutilisées ultérieurement, il est plus facile de suivre la trace en faisant porter au moins les éléments suivants dans les journaux.

  • sessionId
  • resourceId
  • kind
  • action(Create/Open/Register/Close/Dispose/Unregister)
  • osHandle
  • phase

En procédant ainsi, il devient plus facile de repérer un flux « à un seul poumon » où l’on a un Create sans Close correspondant.

4.4. Pour une fuite de handles, chercher « où elle a fui », pas « où elle a planté »

Ce point est particulièrement important.

Une fuite de handles se présente souvent sous cette forme.

  • La ligne du plantage : échec de CreateEvent
  • La véritable fuite : CloseHandle manquait sur le chemin d’échec depuis plusieurs jours déjà

Autrement dit, l’API qui a fini par échouer en dernier est la sortie du dommage, pas forcément l’entrée de la cause.

L’ordre d’investigation à privilégier est donc le suivant.

  1. Regarder quelle ressource continue d’augmenter
  2. Regarder à quelle frontière d’opération elle ne redescend pas
  3. Rechercher l’endroit où l’appariement entre create/open et close/dispose est rompu
  4. Lire le point de plantage en dernier

Suivre cet ordre permet de bien moins se perdre en cours de route.

5. Les journaux nécessaires pour éviter que cela ne se reproduise

5.1. L’ensemble minimal à conserver en premier

Ce qui a été efficace dans cette investigation n’était pas simplement d’augmenter le volume des journaux. C’était d’ajouter méthodiquement des « informations permettant de remonter plus tard jusqu’à la cause ».

Au minimum, voici ce qu’il est souhaitable de conserver.

Catégorie Champs minimaux souhaités Raison
Contexte de l’opération cameraId, sessionId, operationId, reconnectCount, phase Pour relier l’événement à quelle opération, et à quelle occurrence
Ressources du process handleCount, privateBytes, workingSet, threadCount Pour isoler en premier lieu ce qui augmente
Cycle de vie des ressources action, resourceId, kind, osHandle, owner Pour suivre les paires create/open et close/dispose
Résultat des appels externes win32Error, HRESULT, sdkError, timeoutMs Pour comparer plus tard les types d’échec
Transitions d’état OpenStart, OpenDone, ReconnectStart, ReconnectDone, ShutdownStart, etc. Pour savoir au milieu de quelle phase les choses se sont dégradées
Environnement d’exécution pid, tid, buildVersion, machineName Pour faire correspondre dumps, symboles et livrables

Nous ne prétendons pas que cela suffise. Mais sans au moins cela, on se retrouve facilement avec des journaux qui ne conservent rien de plus que le fait que « ça a planté ».

5.2. Les journaux réellement renforcés

Dans ce cas, les journaux ont été renforcés dans les directions suivantes.

  1. Heartbeat périodique
    • Émission de Handle Count / Private Bytes / Thread Count / ReconnectCount toutes les 1 à 5 minutes
  2. Journaux de frontière par session de caméra
    • OpenStart
    • CallbackRegistered
    • AcquisitionStart
    • TimeoutDetected
    • ReconnectStart
    • ReconnectDone
    • CloseStart
    • CloseDone
  3. Journaux de cycle de vie des ressources
    • Create/Open/Register et Close/Dispose/Unregister pour les events, threads, files, timers et tokens d’enregistrement du SDK
  4. Normalisation des erreurs
    • Ne pas s’arrêter au seul message d’exception ; émettre en même temps win32Error, HRESULT, sdkError et phase

Ce qui importe, c’est de ne pas changer la forme des journaux entre le succès et l’échec. Si le cas anormal seul adopte un format différent, l’agrégation devient difficile par la suite.

5.3. À quelle granularité collecter

Une erreur fréquente ici consiste à se dire « pour l’instant, je log tout en INFO ». Mais en procédant ainsi, on se retrouve face à un mur de journaux au moment de les relire. C’est assez pénible.

En termes de granularité, la répartition suivante est globalement réaliste.

  • Surveillance périodique
    • Handle Count, Private Bytes, Thread Count, ReconnectCount
  • Frontières d’opération
    • start / done / fail de la session
  • Frontières de ressources
    • create/open/register et close/dispose/unregister
  • Détails en cas d’anomalie
    • code d’erreur, stack, déclencheur de capture de dump

Un journal détaillé pour chaque frame n’est généralement pas nécessaire. Ce qui est en réalité efficace face à un défaut de longue durée, ce sont des journaux qui permettent de lire « quelle responsabilité a ouvert la ressource, et quelle responsabilité l’a fermée ».

6. Guide de décision sommaire

  • Plante seulement après quelques jours à quelques semaines
    • Mettre en place en premier un heartbeat pour Handle Count / Private Bytes / Thread Count
  • Il y a des retry / reconnect / shutdown
    • Construire d’abord un harnais de test qui fait tourner massivement uniquement ces frontières
  • Usage important de SDK natifs / P/Invoke / Win32
    • Il vaut vraiment la peine d’appliquer Application Verifier, abordé dans la seconde partie
  • Une interface graphique cohabite aussi
    • En plus de Handle Count, il est préférable de surveiller aussi GDI Objects / USER Objects
  • L’exception au moment du plantage seule ne dit rien
    • Il est plus rapide de mettre d’abord en ordre des journaux structurés de cycle de vie pour operation / session / resource

Ce dernier point est particulièrement important. Dans l’investigation de bug, ce n’est souvent pas la technique d’analyse elle-même qui décide de l’issue, mais le fait d’avoir mis les choses sous une forme observable.

7. Résumé

Pour une application qui ne plante qu’après un fonctionnement de longue durée, il faut surveiller non seulement la mémoire, mais aussi le Handle Count. Une fuite de handles a tendance à se cacher non pas dans le chemin normal, mais dans le chemin d’échec des cas anormaux, et le point de plantage est le plus souvent la sortie d’une panne secondaire, et non l’endroit où la fuite a eu lieu. En matière de lecture des symptômes, tout se ramène finalement à ces trois points.

Pour la prévention, il faut rapprocher les responsabilités de create/open et de close/dispose, conserver des journaux porteurs de contexte par session / opération, et enregistrer à la fois les ressources du process et le cycle de vie des ressources. Côté tests, plutôt que d’attendre une reproduction à l’échelle du mois, faire tourner timeout / reconnect / shutdown dans des boucles courtes, et retenir comme critère d’acceptation non seulement « ça ne casse pas », mais aussi « c’est traçable quand ça casse ». Ce qui a fonctionné dans ce cas, c’est cette combinaison. Dans la seconde partie, nous utilisons Application Verifier pour faire remonter par anticipation des modes de défaillance difficiles à déclencher, comme l’épuisement de la mémoire ou les anomalies de handles.

Dans une application de pilotage, il est important que le chemin normal fonctionne, mais pouvoir dire « ce qui s’est passé » quand elle casse compte énormément en exploitation de long terme.

Une fuite de handles est exactement le type de défaut où cette différence se paie. Plutôt que de ne regarder qu’au moment où elle se produit, l’examiner à travers la façon dont elle augmente, les frontières et les paires de responsabilités la rend bien plus facile à suivre.

Seconde partie : Quand une application de pilotage de caméra industrielle plante soudainement après un mois (partie 2) - Qu’est-ce qu’Application Verifier et comment construire un socle de tests des cas anormaux

8. Références

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Ces études de cas présentent une démarche proche d’analyse, de priorisation ou de refonte.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Qu'est-ce qu'une fuite de handles ?
C'est le fait qu'un processus Windows oublie de fermer un handle référençant une ressource du système d'exploitation - event, mutex, file, socket, etc. -, ce qui fait augmenter continuellement le Handle Count. Le schéma le plus fréquent consiste à ouvrir temporairement une ressource pour une opération donnée, puis à oublier de la fermer sur un chemin d'échec intermédiaire tel qu'un timeout, une reconnexion ou un early return ; comme les tests courts habituels n'empruntent presque toujours que le chemin de succès, ce genre de fuite passe facilement inaperçu.
Comment distinguer une fuite mémoire d'une fuite de handles ?
Les indicateurs à surveiller ne sont pas les mêmes. Pour une fuite mémoire, ce sont Private Bytes et Commit qui augmentent progressivement, tandis que pour une fuite de handles, c'est le Handle Count qui augmente progressivement sans jamais redescendre. Lors de l'isolement d'un problème de fonctionnement de longue durée, se contenter de surveiller la mémoire revient à conduire avec un œil fermé : il est essentiel de suivre aussi le Handle Count et le Thread Count en parallèle. Si une interface graphique cohabite dans le même processus, il faut également surveiller les GDI Objects et les USER Objects.
Pourquoi une fuite de handles ne fait-elle planter l'application qu'après un fonctionnement de longue durée ?
Parce qu'une fuite à faible pente - un seul handle perdu par échec occasionnel - ne provoque rien de visible en quelques minutes ; mais dans une application fonctionnant 24 h/24 et 7 j/7, des conditions limites comme les timeouts ou les reconnexions se répètent sans cesse et la fuite s'accumule sur plusieurs semaines. Elle finit par se révéler sous forme de panne secondaire, au moment où une API censée créer un nouvel event, un fichier ou un thread échoue à son tour. Il est important de retenir que le point de plantage est le plus souvent la dernière victime, et non l'endroit où la fuite a eu lieu.
Comment mener l'investigation d'une fuite de handles ?
Sans attendre une reproduction qui prend un mois, compressez le temps en rejouant des milliers de fois, dans une boucle courte, les frontières suspectes du cycle de vie des ressources - open -> start -> stop -> close, timeout, reconnexion, etc. Établissez une baseline après la période de warm-up, observez l'écart et la pente du Handle Count après chaque cycle, recherchez à l'aide de journaux structurés portant sessionId, resourceId et action les endroits où l'appariement entre create/open et close/dispose est rompu, puis lisez le point de plantage en dernier : c'est l'ordre qui permet le moins de se perdre.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog