Folklore des programmeurs et ingénieurs (partie 1)

Folklore des programmeurs et ingénieurs (partie 1)

Il s'agit d'une sélection d'histoires sur Internet sur la façon dont les bugs ont parfois des manifestations complètement incroyables. Peut-être avez-vous aussi quelque chose à dire.

Allergie automobile à la glace à la vanille

Une histoire pour les ingénieurs qui comprennent que l’évidence n’est pas toujours la réponse et que, aussi farfelus que puissent paraître les faits, ils restent les faits. La division Pontiac de General Motors Corporation a reçu une plainte :

C’est la deuxième fois que je vous écris, et je ne vous en veux pas de ne pas répondre, car cela semble fou. Notre famille a pour tradition de manger de la glace tous les soirs après le dîner. Les types de glaces changent à chaque fois, et après le dîner, toute la famille choisit quelle glace acheter, après quoi je vais au magasin. J'ai récemment acheté une Pontiac neuve et depuis, mes déplacements pour acheter de la glace sont devenus un problème. Vous voyez, chaque fois que j'achète de la glace à la vanille et que je reviens du magasin, la voiture ne démarre pas. Si j'apporte d'autres glaces, la voiture démarre sans problème. Je veux poser une question sérieuse, aussi stupide que cela puisse paraître : « Qu'est-ce qui fait que le Pontiac ne démarre pas quand j'apporte de la glace à la vanille, mais démarre facilement quand j'apporte une autre saveur de glace ? »

Comme vous pouvez l’imaginer, le président de la division était sceptique quant à la lettre. Cependant, juste au cas où, j'ai envoyé un ingénieur pour vérifier. Il a été surpris d’avoir rencontré un homme riche et instruit vivant dans une belle région. Ils ont convenu de se rencontrer immédiatement après le dîner afin qu'ils puissent tous les deux aller au magasin acheter des glaces. Ce soir-là, c'était vanille, et quand ils revinrent à la voiture, elle ne démarrait pas.

L'ingénieur revint encore trois soirs. La première fois, la glace était au chocolat. La voiture a démarré. La deuxième fois, il y avait de la glace à la fraise. La voiture a démarré. Le troisième soir, il demanda à prendre de la vanille. La voiture n'a pas démarré.

En raisonnant rationnellement, l'ingénieur a refusé de croire que la voiture était allergique à la glace à la vanille. J'ai donc convenu avec le propriétaire de la voiture qu'il poursuivrait ses visites jusqu'à ce qu'il trouve une solution au problème. Et chemin faisant, il a commencé à prendre des notes : il a noté toutes les informations, heure de la journée, type d'essence, heure d'arrivée et de retour du magasin, etc.

L'ingénieur s'est vite rendu compte que le propriétaire de la voiture passait moins de temps à acheter de la glace à la vanille. La raison en était la disposition des marchandises dans le magasin. La glace à la vanille était la plus populaire et était conservée dans un congélateur séparé à l'avant du magasin pour la rendre plus facile à trouver. Et toutes les autres variétés se trouvaient au fond du magasin, et il fallait beaucoup plus de temps pour trouver la bonne variété et payer.

La question se posait désormais à l’ingénieur : pourquoi la voiture n’a-t-elle pas démarré si moins de temps s’était écoulé depuis l’arrêt du moteur ? Comme le problème était le temps et non la glace à la vanille, l'ingénieur a rapidement trouvé la réponse : il s'agissait d'un sas à gaz. Cela se produisait tous les soirs, mais lorsque le propriétaire de la voiture passait plus de temps à chercher de la glace, le moteur parvenait à refroidir suffisamment et démarrait facilement. Et lorsque l'homme a acheté de la glace à la vanille, le moteur était encore trop chaud et le robinet de gaz n'a pas eu le temps de se dissoudre.

Moralité : Même des problèmes complètement fous sont parfois réels.

crash Bandicoot

C'est douloureux de vivre ça. En tant que programmeur, vous vous habituez à blâmer votre code en premier, deuxième, troisième... et quelque part au dix millième endroit, vous blâmez le compilateur. Et plus bas dans la liste, vous blâmez déjà l’équipement.

Voici mon histoire sur le bug matériel.

Pour le jeu Crash Bandicoot, j'ai écrit du code à charger et à sauvegarder sur une carte mémoire. Pour un développeur de jeux aussi suffisant, c’était comme une promenade dans le parc : je pensais que le travail prendrait plusieurs jours. Cependant, j'ai fini par déboguer le code pendant six semaines. En cours de route, j'ai résolu d'autres problèmes, mais tous les quelques jours, je revenais à ce code pendant quelques heures. C'était une agonie.

Le symptôme ressemblait à ceci : lorsque vous sauvegardez la partie en cours du jeu et accédez à la carte mémoire, tout se passe presque toujours bien... Mais parfois, l'opération de lecture ou d'écriture expire sans raison évidente. Un enregistrement court endommage souvent la carte mémoire. Lorsqu'un joueur tente de sauvegarder, non seulement il ne parvient pas à sauvegarder, mais il détruit également la carte. Merde.

Au bout d'un moment, notre productrice chez Sony, Connie Bus, a commencé à paniquer. Nous n'avons pas pu livrer le jeu avec ce bug, et six semaines plus tard, je n'ai pas compris la cause du problème. Grâce à Connie, nous avons contacté d'autres développeurs PS1 : quelqu'un a-t-il rencontré quelque chose de similaire ? Non. Personne n'a eu de problèmes avec la carte mémoire.

Lorsque vous n'avez aucune idée de débogage, la seule approche qui reste est de « diviser pour mieux régner » : supprimez de plus en plus de code du programme défectueux jusqu'à ce qu'il reste un fragment relativement petit qui cause toujours le problème. Autrement dit, vous coupez le programme morceau par morceau jusqu'à ce que la partie contenant le bug reste.

Mais le fait est qu’il est très difficile de découper des morceaux d’un jeu vidéo. Comment l'exécuter si vous avez supprimé le code qui émule la gravité ? Ou dessiner des personnages ?

Par conséquent, nous devons remplacer des modules entiers par des stubs qui prétendent faire quelque chose d'utile, mais qui font en fait quelque chose de très simple qui ne peut pas contenir d'erreurs. Nous devons écrire de telles béquilles pour que le jeu fonctionne au moins. C'est un processus lent et douloureux.

Bref, je l'ai fait. J'ai supprimé de plus en plus de morceaux de code jusqu'à ce qu'il me reste le code initial qui configure le système pour exécuter le jeu, initialise le matériel de rendu, etc. Bien sûr, à ce stade, je ne pouvais pas créer de menu de sauvegarde et de chargement, car je devrais créer un stub pour tout le code graphique. Mais je pourrais prétendre être un utilisateur utilisant l'écran de sauvegarde et de chargement (invisible) et demander de sauvegarder puis d'écrire sur la carte mémoire.

Cela m'a laissé un petit morceau de code qui présentait toujours le problème ci-dessus - mais cela se produisait toujours de manière aléatoire ! Le plus souvent, tout fonctionnait bien, mais il y avait parfois des problèmes. J'ai supprimé presque tout le code du jeu, mais le bug était toujours présent. C'était déroutant : le code restant ne faisait rien.

À un moment donné, probablement vers trois heures du matin, une pensée m'est venue. Les opérations de lecture et d'écriture (entrée/sortie) impliquent des temps d'exécution précis. Lorsque vous travaillez avec un disque dur, une carte mémoire ou un module Bluetooth, le code de bas niveau responsable de la lecture et de l'écriture le fait en fonction des impulsions d'horloge.

A l'aide d'une horloge, un appareil qui n'est pas directement connecté au processeur est synchronisé avec le code qui s'exécute sur le processeur. L'horloge détermine le débit en bauds, c'est-à-dire la vitesse à laquelle les données sont transmises. S'il y a confusion avec les timings, alors soit le matériel, soit le logiciel, ou les deux, sont également confus. Et c’est très mauvais, car les données peuvent être endommagées.

Et si quelque chose dans notre code perturbait les horaires ? J'ai vérifié tout ce qui concerne cela dans le code du programme de test et j'ai remarqué que nous réglions la minuterie programmable de PS1 sur 1 kHz (1000 ticks par seconde). C'est beaucoup : par défaut, lorsque la console démarre, elle tourne à 100 Hz. Et la plupart des jeux utilisent cette fréquence.

Andy, le développeur du jeu, a réglé la minuterie sur 1 kHz afin que les mouvements soient calculés avec plus de précision. Andy a tendance à aller trop loin, et si nous imitons la gravité, nous le faisons aussi précisément que possible !

Mais que se passerait-il si l’accélération du minuteur affectait d’une manière ou d’une autre la synchronisation globale du programme, et donc l’horloge qui régule le débit en bauds de la carte mémoire ?

J'ai commenté le code de la minuterie. L'erreur ne s'est plus reproduite. Mais cela ne signifie pas que nous l’avons réparé, car la panne s’est produite de manière aléatoire. Et si j'avais juste de la chance ?

Quelques jours plus tard, j'ai réexpérimenté le programme de test. Le bug ne s'est pas reproduit. Je suis revenu à la base de code complète du jeu et j'ai modifié le code de sauvegarde et de chargement afin que la minuterie programmable soit réinitialisée à sa valeur d'origine (100 Hz) avant d'accéder à la carte mémoire, puis réinitialisée à 1 kHz. Il n'y a plus eu d'accidents.

Mais pourquoi est-ce arrivé?

Je suis revenu au programme de test. J'ai essayé de trouver un modèle d'apparition d'une erreur avec une minuterie de 1 kHz. Finalement, j'ai remarqué que l'erreur se produit lorsque quelqu'un joue avec une manette PS1. Puisque je le ferais rarement moi-même, pourquoi aurais-je besoin d'un contrôleur pour tester le code de sauvegarde et de chargement ? - Je n'ai même pas remarqué cette dépendance. Mais un jour, un de nos artistes attendait que je termine mes tests - je jurais probablement à ce moment-là - et il a fait tournoyer nerveusement le contrôleur dans ses mains. Une erreur est survenue. "Attends quoi?!" Eh bien, recommence !

Lorsque j'ai réalisé que ces deux événements étaient interconnectés, j'ai pu facilement reproduire l'erreur : j'ai commencé à enregistrer sur la carte mémoire, j'ai déplacé le contrôleur et j'ai détruit la carte mémoire. Pour moi, cela ressemblait à un bug matériel.

Je suis venu voir Connie et lui ai parlé de ma découverte. Elle a relayé l'information à l'un des ingénieurs qui ont conçu la PS1. "Impossible", a-t-il répondu, "cela ne peut pas être un problème matériel." J'ai demandé à Connie d'organiser une conversation pour nous.

L'ingénieur m'a appelé et nous nous sommes disputés dans son anglais approximatif et mon japonais (extrêmement) approximatif. Finalement, j'ai dit : « Laissez-moi simplement envoyer mon programme de test de 30 lignes où le déplacement du contrôleur provoque un bug. » Il a accepté. Il a dit que c'était une perte de temps et qu'il était terriblement occupé à travailler sur un nouveau projet, mais qu'il céderait parce que nous étions un développeur très important pour Sony. J'ai nettoyé mon programme de test et je le lui ai envoyé.

Le lendemain soir (nous étions à Los Angeles et lui à Tokyo), il m'a appelé et s'est excusé timidement. C'était un problème matériel.

Je ne sais pas exactement quel était le bug, mais d'après ce que j'ai entendu au siège de Sony, si vous réglez la minuterie sur une valeur suffisamment élevée, cela interférait avec les composants de la carte mère à proximité du cristal de la minuterie. L'un d'eux était un contrôleur de débit en bauds pour la carte mémoire, qui définissait également le débit en bauds des contrôleurs. Je ne suis pas ingénieur, donc j'ai peut-être raté quelque chose.

Mais l’essentiel est qu’il y a eu des interférences entre les composants de la carte mère. Et lors de la transmission simultanée de données via le port du contrôleur et le port de la carte mémoire avec une minuterie fonctionnant à 1 kHz, des bits ont été perdus, des données ont été perdues et la carte a été endommagée.

Mauvaises vaches

Dans les années 1980, mon mentor Sergueï a écrit le logiciel du SM-1800, un clone soviétique du PDP-11. Ce micro-ordinateur vient d'être installé dans une gare ferroviaire près de Sverdlovsk, un important nœud de transport en URSS. Le nouveau système a été conçu pour acheminer le trafic de wagons et de marchandises. Mais il contenait un bug ennuyeux qui entraînait des plantages et des plantages aléatoires. Les chutes se produisaient toujours lorsque quelqu'un rentrait chez lui le soir. Mais malgré une enquête approfondie le lendemain, l'ordinateur a fonctionné correctement dans tous les tests manuels et automatiques. Cela indique généralement une condition de concurrence critique ou un autre bug compétitif qui se produit dans certaines conditions. Fatigué des appels tard dans la nuit, Sergei a décidé d'aller au fond des choses et, tout d'abord, de comprendre quelles conditions dans la gare de triage ont conduit à la panne informatique.

Tout d’abord, il a collecté des statistiques sur toutes les chutes inexpliquées et a créé un graphique par date et heure. Le schéma était évident. Après avoir observé pendant quelques jours supplémentaires, Sergei s'est rendu compte qu'il pouvait facilement prédire l'heure des futures pannes du système.

Il a vite appris que des perturbations ne se produisaient que lorsque la station triait des trains de bovins en provenance du nord de l’Ukraine et de l’ouest de la Russie à destination d’un abattoir voisin. C'était en soi étrange, car l'abattoir était approvisionné par des fermes situées beaucoup plus près, au Kazakhstan.

La centrale nucléaire de Tchernobyl a explosé en 1986 et les retombées radioactives ont rendu les zones environnantes inhabitables. De vastes zones du nord de l’Ukraine, de la Biélorussie et de l’ouest de la Russie ont été contaminées. Soupçonnant des niveaux élevés de radiations dans les wagons à l'arrivée, Sergei a développé une méthode pour tester cette théorie. Il était interdit à la population d'avoir des dosimètres, alors Sergei s'est inscrit auprès de plusieurs militaires à la gare. Après plusieurs verres de vodka, il parvient à convaincre un soldat de mesurer le niveau de radiation dans l'un des wagons suspects. Il s’est avéré que le niveau était plusieurs fois supérieur aux valeurs normales.

Non seulement le bétail émettait beaucoup de rayonnement, mais son niveau était si élevé qu'il entraînait une perte aléatoire de bits dans la mémoire du SM-1800, situé dans un bâtiment à côté de la station.

Il y avait une pénurie alimentaire en URSS et les autorités ont décidé de mélanger la viande de Tchernobyl avec de la viande provenant d'autres régions du pays. Cela a permis de réduire le niveau global de radioactivité sans perdre de précieuses ressources. Ayant appris cela, Sergei a immédiatement rempli les documents d'émigration. Et les pannes informatiques se sont arrêtées d’elles-mêmes lorsque le niveau de rayonnement a diminué au fil du temps.

À travers les tuyaux

Il était une fois Movietech Solutions créé un logiciel pour les cinémas, conçu pour la comptabilité, la vente de billets et la gestion générale. La version DOS de l'application phare était très populaire parmi les chaînes de cinéma de petite et moyenne taille en Amérique du Nord. Il n'est donc pas surprenant que lorsqu'une version de Windows 95 a été annoncée, intégrée aux derniers écrans tactiles et bornes libre-service, et équipée de toutes sortes d'outils de reporting, elle soit également rapidement devenue populaire. Le plus souvent, la mise à jour s'est déroulée sans problème. Le personnel informatique local a installé de nouveaux équipements, migré les données et les activités ont continué. Sauf quand ça ne durait pas. Lorsque cela se produisait, la société envoyait James, surnommé « le nettoyeur ».

Bien que son surnom suggère un type néfaste, le nettoyeur n’est qu’une combinaison d’instructeur, d’installateur et de touche-à-tout. James passait quelques jours sur le site du client à assembler tous les composants, puis passait quelques jours supplémentaires à enseigner au personnel comment utiliser le nouveau système, à résoudre tous les problèmes matériels qui survenaient et, essentiellement, à aider le logiciel à ses débuts.

Il n'est donc pas surprenant qu'en ces temps mouvementés, James arrive au bureau le matin, et avant de pouvoir atteindre son bureau, il soit accueilli par le manager, rempli de caféine au-delà d'habitude.

"J'ai bien peur que vous deviez vous rendre à Annapolis, en Nouvelle-Écosse, le plus tôt possible." Tout leur système est tombé en panne et après une nuit de travail avec leurs ingénieurs, nous ne parvenons pas à comprendre ce qui s'est passé. Il semble que le réseau soit en panne sur le serveur. Mais seulement après que le système ait fonctionné pendant plusieurs minutes.

— Ils ne sont pas revenus à l’ancien système ? - James répondit complètement sérieusement, même si mentalement il écarquilla les yeux de surprise.

— Exactement : leur informaticien a « changé de priorités » et a décidé de repartir avec son ancien serveur. James, ils ont installé le système sur six sites et ont juste payé pour un support premium, et leur entreprise est désormais gérée comme elle l'était dans les années 1950.

James se redressa légèrement.

- C'est une autre affaire. Bon, commençons.

Lorsqu'il est arrivé à Annapolis, la première chose qu'il a faite a été de trouver le premier cinéma du client qui avait un problème. Sur la carte prise à l'aéroport, tout semblait correct, mais la zone autour de l'adresse souhaitée semblait suspecte. Pas de ghetto, mais qui rappelle le film noir. Alors que James se garait en bordure du centre-ville, une prostituée s'est approchée de lui. Compte tenu de la taille d’Annapolis, c’était probablement le seul de toute la ville. Son apparence a immédiatement fait penser au célèbre personnage qui proposait du sexe contre de l'argent sur grand écran. Non, pas à propos de Julia Roberts, mais à propos de Jon Voight [allusion au film "Midnight Cowboy" - env. voie].

Après avoir renvoyé la prostituée, James se rendit au cinéma. Les environs se sont améliorés, mais ils donnent toujours l'impression d'être délabrés. Non pas que James soit trop inquiet. Il est déjà allé dans des endroits misérables. Et c'était au Canada, où même les agresseurs sont assez polis pour dire « merci » après avoir pris votre portefeuille.

L'entrée latérale du cinéma se trouvait dans une ruelle sombre. James se dirigea vers la porte et frappa. Bientôt, il craqua et s'ouvrit légèrement.

-Es-tu un nettoyeur ? - une voix rauque venait de l'intérieur.

- Oui, c'est moi... Je suis venu pour tout réparer.

James entra dans le hall du cinéma. N'ayant apparemment pas d'autre choix, le personnel a commencé à distribuer des billets papier aux visiteurs. Cela rendait l’information financière difficile, sans parler des détails plus intéressants. Mais le personnel a accueilli James avec soulagement et l'a immédiatement emmené dans la salle des serveurs.

À première vue, tout allait bien. James s'est connecté au serveur et a vérifié les endroits suspects habituels. Aucun problème. Cependant, par prudence, James a arrêté le serveur, remplacé la carte réseau et restauré le système. Elle a immédiatement commencé à travailler pleinement. Le personnel a recommencé à vendre des billets.

James a appelé Mark et l'a informé de la situation. Il n'est pas difficile d'imaginer que James veuille rester dans les parages et voir si quelque chose d'inattendu se produit. Il descendit les escaliers et commença à demander aux employés ce qui s'était passé. Il est évident que le système ne fonctionne plus. Ils l'ont éteint et rallumé, tout a fonctionné. Mais au bout de 10 minutes, le système est tombé en panne.

Juste à ce moment-là, quelque chose de similaire s’est produit. Soudain, le système de billetterie a commencé à générer des erreurs. Le personnel soupira et attrapa les tickets papier, et James se précipita vers la salle des serveurs. Tout avait l'air bien avec le serveur.

Puis un des employés est entré.

— Le système fonctionne à nouveau.

James était perplexe car il n'avait rien fait. Plus précisément, rien qui ferait fonctionner le système. Il s'est déconnecté, a décroché son téléphone et a appelé la ligne d'assistance de son entreprise. Bientôt, le même employé entra dans la salle des serveurs.

- Le système est en panne.

James jeta un coup d'œil au serveur. Un motif intéressant et familier de formes multicolores dansait sur l'écran - des tuyaux se tordant et s'entrelaçant de manière chaotique. Nous avons tous vu cet économiseur d'écran à un moment donné. C’était magnifiquement rendu et littéralement hypnotisant.


James appuya sur un bouton et le motif disparut. Il s'est précipité vers la billetterie et a rencontré en chemin un employé qui revenait vers lui.

— Le système fonctionne à nouveau.

Si vous pouvez faire un facepalm mental, c'est exactement ce que James a fait. Économiseur d'écran. Il utilise OpenGL. Et donc, pendant le fonctionnement, il consomme toutes les ressources du processeur du serveur. En conséquence, chaque appel au serveur se termine par un timeout.

James est retourné dans la salle des serveurs, s'est connecté et a remplacé l'économiseur d'écran par les magnifiques tuyaux par un écran vide. Autrement dit, au lieu d'un économiseur d'écran qui consomme 100 % des ressources du processeur, j'en ai installé un autre qui ne consomme pas de ressources. Ensuite, j'ai attendu 10 minutes pour vérifier ma supposition.

Lorsque James est arrivé au cinéma suivant, il se demandait comment expliquer à son manager qu'il venait de parcourir 800 km pour désactiver l'économiseur d'écran.

Crash pendant une certaine phase de la lune

Histoire vraie. Un jour, un bug logiciel est survenu qui dépendait de la phase de la lune. Il existait une petite routine couramment utilisée dans divers programmes du MIT pour calculer l'approximation de la vraie phase de la Lune. GLS a intégré cette routine dans un programme LISP qui, lors de l'écriture d'un fichier, produirait une ligne avec un horodatage de près de 80 caractères. Il était très rare que la première ligne d’un message soit trop longue et mène à la ligne suivante. Et lorsque le programme a lu ce fichier plus tard, il a juré. La longueur de la première ligne dépend de la date et de l'heure exactes, ainsi que de la longueur de la spécification de phase au moment de l'impression de l'horodatage. Autrement dit, le bug dépendait littéralement de la phase de la lune !

Première édition papier Fichier de jargon (Steele-1983) contenait un exemple d'une telle ligne qui conduisait au bug décrit, mais le compositeur l'a « corrigé ». Cela a depuis été décrit comme un « bug de phase lunaire ».

Soyez toutefois prudent avec les hypothèses. Il y a quelques années, des ingénieurs du CERN (Centre européen de recherche nucléaire) ont rencontré des erreurs lors d'expériences menées au Grand collisionneur électron-positon. Étant donné que les ordinateurs traitent activement l’énorme quantité de données générées par cet appareil avant de montrer le résultat aux scientifiques, beaucoup ont émis l’hypothèse que le logiciel était d’une manière ou d’une autre sensible à la phase de la lune. Plusieurs ingénieurs désespérés sont allés au fond de la vérité. L'erreur est due à un léger changement dans la géométrie de l'anneau de 27 km de long dû à la déformation de la Terre lors du passage de la Lune ! Cette histoire est entrée dans le folklore de la physique sous le nom de « La vengeance de Newton sur la physique des particules » et constitue un exemple du lien entre les lois les plus simples et les plus anciennes de la physique et les concepts scientifiques les plus avancés.

La chasse d'eau arrête le train

Le meilleur bug matériel dont j'ai jamais entendu parler concernait un train à grande vitesse en France. Le bug a entraîné un freinage d'urgence du train, mais seulement s'il y avait des passagers à bord. Dans chacun de ces cas, le train a été mis hors service, contrôlé, mais rien n'a été trouvé. Il a ensuite été renvoyé dans la file d'attente et s'est immédiatement arrêté.

Lors d'un des contrôles, un mécanicien voyageant à bord du train s'est rendu aux toilettes. Il a vite disparu, BOUM ! Arrêt d'urgence.

L'ingénieur a contacté le chauffeur et lui a demandé :

— Que faisais-tu juste avant de freiner ?

- Bon, j'ai ralenti dans la descente...

C'était étrange, car en fonctionnement normal, le train ralentit des dizaines de fois lors des descentes. Le train a continué et lors de la descente suivante, le conducteur a averti :

- Je vais ralentir.

Rien ne s'est passé.

— Qu'avez-vous fait lors du dernier freinage ? - a demandé au chauffeur.

- Eh bien... j'étais dans les toilettes...

- Eh bien, alors va aux toilettes et fais ce que tu as fait quand nous redescendrons !

L’ingénieur est allé aux toilettes et, lorsque le conducteur a prévenu : « Je ralentis », il a tiré la chasse d’eau. Bien entendu, le train s’est arrêté immédiatement.

Ils pouvaient désormais reproduire le problème et devaient en trouver la cause.

Au bout de deux minutes, ils remarquèrent que le câble de commande à distance du frein moteur (le train avait un moteur à chaque extrémité) était déconnecté de la paroi de l'armoire électrique et reposait sur le relais qui commandait le solénoïde de la prise des toilettes... Lorsque le relais a été allumé, cela a créé des interférences dans le câble de frein et la protection du système contre les pannes comprenait simplement un freinage d'urgence.

La passerelle qui détestait FORTRAN

Il y a quelques mois, nous avons remarqué que les connexions réseau sur le continent [c'était à Hawaï] devenaient très, très lentes. Cela peut durer 10 à 15 minutes, puis se reproduire soudainement. Après un certain temps, mon collègue s'est plaint auprès de moi du fait que les connexions réseau sur le continent à tous ne marche pas. Il avait du code FORTRAN qui devait être copié sur une machine située sur le continent, mais il ne pouvait pas le faire car « le réseau n'a pas résisté assez longtemps pour que le téléchargement FTP puisse se terminer ».

Oui, il s'est avéré que des pannes de réseau se sont produites lorsqu'un collègue a tenté de transférer par FTP un fichier contenant le code source en FORTRAN vers une machine située sur le continent. Nous avons essayé d'archiver le fichier : il a ensuite été copié sans problème (mais la machine cible n'avait pas de décompresseur, donc le problème n'a pas été résolu). Finalement, nous avons « divisé » le code FORTRAN en très petits morceaux et les avons envoyés un par un. La plupart des fragments ont été copiés sans problème, mais quelques morceaux n'ont pas réussi, ou sont passés après nombreux tentatives.

Lorsque nous avons examiné les passages problématiques, nous avons découvert qu'ils avaient quelque chose en commun : ils contenaient tous des blocs de commentaires qui commençaient et se terminaient par des lignes composées d'un C majuscule (comme un collègue préférait commenter en FORTRAN). Nous avons envoyé un e-mail à des experts en réseau sur le continent pour leur demander de l'aide. Bien sûr, ils voulaient voir des échantillons de nos fichiers qui ne pouvaient pas être transférés via FTP... mais nos lettres ne leur sont pas parvenues. Finalement, nous avons trouvé un simple décrisà quoi ressemblent les fichiers non transférables. Cela a fonctionné :) [Oserais-je ajouter ici un exemple d'un des commentaires FORTRAN problématiques ? Cela n’en vaut probablement pas la peine !]

Finalement, nous avons réussi à comprendre. Une nouvelle passerelle a été récemment installée entre notre partie du campus et le réseau continental. Il avait d’énormes difficultés à transmettre des paquets contenant des bits répétés de C majuscule ! Seuls quelques-uns de ces paquets pourraient utiliser toutes les ressources de la passerelle et empêcher la plupart des autres paquets de passer. Nous nous sommes plaints auprès du fabricant de la passerelle... et ils nous ont répondu : « Ah oui, vous êtes confronté à un bug de C répété ! Nous le connaissons déjà. Nous avons finalement résolu le problème en achetant une nouvelle passerelle chez un autre fabricant (pour la défense du premier, l'impossibilité de transférer les programmes FORTRAN peut être un avantage pour certains !).

Des temps difficiles

Il y a quelques années, alors que je travaillais à la création d'un système ETL en Perl pour réduire les coûts des essais cliniques de phase 40, j'avais besoin de traiter environ 000 1 dates. Deux d’entre eux n’ont pas réussi le test. Cela ne m'a pas trop dérangé car ces dates étaient extraites de données fournies par les clients qui étaient souvent, disons, surprenantes. Mais lorsque j'ai vérifié les données originales, il s'est avéré que ces dates étaient le 2011er janvier 1 et le 2007er janvier 30. Je pensais que le bug était contenu dans le programme que je venais d'écrire, mais il s'est avéré que cela faisait déjà XNUMX ans. vieux. Cela peut paraître mystérieux à ceux qui ne connaissent pas l’écosystème logiciel. En raison de la décision de longue date d'une autre entreprise de gagner de l'argent, mon client m'a payé pour corriger un bug qu'une entreprise avait introduit par accident et l'autre volontairement. Pour que vous compreniez de quoi je parle, je dois parler de la société qui a ajouté la fonctionnalité qui a fini par devenir un bug, ainsi que de quelques autres événements intéressants qui ont contribué au mystérieux bug que j'ai corrigé.

Au bon vieux temps, les ordinateurs Apple réinitialisaient parfois spontanément leur date au 1er janvier 1904. La raison était simple : il utilisait une « horloge système » alimentée par batterie pour suivre la date et l’heure. Que s'est-il passé lorsque la batterie est morte ? Les ordinateurs ont commencé à suivre la date en fonction du nombre de secondes écoulées depuis le début d'une époque. Par époque, nous entendions la date d'origine de référence, et pour les Macintosh, c'était le 1er janvier 1904. Et après que la batterie soit morte, la date actuelle a été réinitialisée à celle spécifiée. Mais pourquoi est-ce arrivé ?

Auparavant, Apple utilisait 32 bits pour stocker le nombre de secondes écoulées depuis la date d'origine. Un bit peut stocker l'une des deux valeurs - 1 ou 0. Deux bits peuvent stocker l'une des quatre valeurs : 00, 01, 10, 11. Trois bits - une valeur sur huit : 000, 001, 010, 011, 100 , 101, 110, 111, etc. Et 32 pourrait stocker l’une des 232 valeurs, soit 4 294 967 296 secondes. Pour les dates Apple, cela équivaut à environ 136 ans, donc les anciens Mac ne peuvent pas gérer les dates après 2040. Et si la batterie du système est déchargée, la date est réinitialisée à 0 seconde depuis le début de l'époque et vous devez régler manuellement la date à chaque fois que vous allumez l'ordinateur (ou jusqu'à ce que vous achetiez une nouvelle batterie).

Cependant, la décision d'Apple de stocker les dates sous forme de secondes depuis l'époque signifiait que nous ne pouvions pas gérer les dates antérieures à l'époque, ce qui avait des conséquences considérables, comme nous le verrons. Apple a introduit une fonctionnalité, pas un bug. Entre autres choses, cela signifiait que le système d'exploitation Macintosh était immunisé contre le « bug du millénaire » (ce qui ne pouvait pas être dit de nombreuses applications Mac qui possédaient leur propre système de date pour contourner les restrictions).

Poursuivre. Nous avons utilisé Lotus 1-2-3, « l'application phare » d'IBM qui a contribué au lancement de la révolution PC, même si les ordinateurs Apple étaient dotés de VisiCalc, qui a fait du succès de l'ordinateur personnel. En toute honnêteté, si le 1-2-3 n’était pas apparu, les PC n’auraient guère décollé et l’histoire des ordinateurs personnels aurait pu évoluer de manière très différente. Lotus 1-2-3 a traité à tort 1900 comme une année bissextile. Lorsque Microsoft a publié sa première feuille de calcul, Multiplan, il a conquis une petite part de marché. Et lorsqu'ils ont lancé le projet Excel, ils ont décidé non seulement de copier le schéma de dénomination des lignes et des colonnes de Lotus 1-2-3, mais également d'assurer la compatibilité des bogues en traitant délibérément 1900 comme une année bissextile. Ce problème existe encore aujourd'hui. Donc, dans 1-2-3, c'était un bug, mais dans Excel, c'était une décision consciente de garantir que tous les utilisateurs 1-2-3 puissent importer leurs tableaux dans Excel sans modifier les données, même si c'était faux.

Mais il y avait un autre problème. Tout d'abord, Microsoft a publié Excel pour Macintosh, qui ne reconnaissait pas les dates antérieures au 1er janvier 1904. Et dans Excel, le 1er janvier 1900 était considéré comme le début de l'ère. Par conséquent, les développeurs ont apporté une modification afin que leur programme reconnaisse le type d’ère et stocke les données en lui-même conformément à l’époque souhaitée. Microsoft a même écrit un article explicatif à ce sujet. Et cette décision a conduit à mon bug.

Mon système ETL recevait des feuilles de calcul Excel de clients créées sous Windows, mais pouvant également être créées sur un Mac. Par conséquent, le début de l’ère indiquée dans le tableau pourrait être soit le 1er janvier 1900, soit le 1er janvier 1904. Comment le savoir ? Le format de fichier Excel affiche les informations nécessaires, mais l'analyseur que j'ai utilisé ne les a pas affichées (c'est maintenant le cas) et a supposé que vous connaissiez l'époque d'une table spécifique. J'aurais probablement pu passer plus de temps à comprendre le format binaire Excel et à envoyer un correctif à l'auteur de l'analyseur, mais j'avais beaucoup plus à faire pour le client, j'ai donc rapidement écrit une heuristique pour déterminer l'époque. Elle était simple.

Dans Excel, la date du 5 juillet 1998 peut être représentée au format "07-05-98" (système américain inutile), "Jul 5, 98", "July 5, 1998", "5-Jul-98" ou un autre format, un autre format inutile (ironiquement, l'un des formats que ma version d'Excel ne proposait pas était ISO 8601). Cependant, dans le tableau, la date non formatée a été stockée sous la forme « 35981 » pour l'époque 1900 ou « 34519 » pour l'époque 1904 (les nombres représentent le nombre de jours depuis l'époque). J'ai simplement utilisé un simple analyseur pour extraire l'année de la date formatée, puis j'ai utilisé l'analyseur Excel pour extraire l'année de la date non formatée. Si les deux valeurs différaient de 4 ans, alors je savais que j'utilisais un système avec l'époque-1904.

Pourquoi n'ai-je pas simplement utilisé des dates formatées ? Parce que le 5 juillet 1998 peut être formaté comme « 98 juillet » avec le jour du mois perdu. Nous avons reçu des tableaux de tellement d'entreprises qui les ont créés de tellement de manières différentes que c'était à nous (dans ce cas, moi) de déterminer les dates. De plus, si Excel réussit, nous devrions le faire aussi !

Au même moment, j'ai rencontré 39082. Permettez-moi de vous rappeler que Lotus 1-2-3 considérait 1900 comme une année bissextile, et cela était fidèlement répété dans Excel. Et comme cela ajoute un jour à l’année 1900, de nombreuses fonctions de calcul de date pourraient être erronées pour ce jour précis. Autrement dit, 39082 aurait pu être le 1er janvier 2011 (sur Mac) ou le 31 décembre 2006 (sur Windows). Si mon « analyseur d’année » a extrait l’année 2011 de la valeur formatée, alors tout va bien. Mais comme l'analyseur Excel ne sait pas quelle époque est utilisée, la valeur par défaut est l'époque 1900, renvoyant l'année 2006. Mon application a constaté que la différence était de 5 ans, l'a considérée comme une erreur, l'a enregistrée et a renvoyé une valeur non formatée.

Pour contourner ce problème, j'ai écrit ceci (pseudocode) :

diff = formatted_year - parsed_year
if 0 == diff
    assume 1900 date system
if 4 == diff
    assume 1904 date system
if 5 == diff and month is December and day is 31
    assume 1904 date system

Et puis les 40 000 dates ont été analysées correctement.

Au milieu de gros travaux d'impression

Au début des années 1980, mon père travaillait chez Storage Technology, une division aujourd'hui disparue qui créait des lecteurs de bande et des systèmes pneumatiques pour l'alimentation de bandes à grande vitesse.

Ils ont repensé les disques afin qu'ils puissent avoir un disque central « A » connecté à sept disques « B », et le petit système d'exploitation en RAM qui contrôlait le disque « A » pouvait déléguer les opérations de lecture et d'écriture à tous les disques « B ».

A chaque démarrage du lecteur « A », il fallait insérer une disquette dans le lecteur périphérique connecté à « A » afin de charger le système d'exploitation dans sa mémoire. C'était extrêmement primitif : la puissance de calcul était fournie par un microcontrôleur 8 bits.

Le public cible de ces équipements était constitué d'entreprises disposant de très grands entrepôts de données - banques, chaînes de vente au détail, etc. - qui devaient imprimer de nombreuses étiquettes d'adresse ou relevés bancaires.

Un client a eu un problème. Au milieu d'un travail d'impression, un lecteur particulier « A » pourrait cesser de fonctionner, provoquant le blocage de l'ensemble du travail. Pour rétablir le fonctionnement du disque, le personnel a dû tout redémarrer. Et si cela se produisait au milieu d'une tâche de six heures, une énorme quantité de temps informatique coûteux était perdue et le calendrier de l'ensemble de l'opération était perturbé.

Des techniciens ont été envoyés par Storage Technologies. Mais malgré tous leurs efforts, ils n'ont pas réussi à reproduire le bug dans des conditions de test : il semblait se produire au milieu de gros travaux d'impression. Le problème n'était pas le matériel, ils ont remplacé tout ce qu'ils pouvaient : RAM, microcontrôleur, lecteur de disquette, chaque partie imaginable du lecteur de bande – le problème a persisté.

Ensuite, les techniciens ont appelé le siège et ont appelé l'expert.

L'expert a pris une chaise et une tasse de café, s'est assis dans la salle informatique (à l'époque il y avait des salles dédiées aux ordinateurs) et a regardé le personnel faire la queue pour un gros travail d'impression. L’expert attendait qu’un échec se produise – et il s’est produit. Tout le monde regardait l’Expert, mais il n’avait aucune idée de la raison pour laquelle cela s’était produit. Il a donc ordonné que le travail soit à nouveau mis en file d'attente et que tout le personnel et les techniciens soient retournés au travail.

L'expert s'est de nouveau assis sur la chaise et a commencé à attendre un échec. Environ six heures se sont écoulées et la panne s'est produite. L'Expert n'avait encore une fois aucune idée, sauf que tout se passait dans une pièce remplie de monde. Il ordonna de relancer la mission, se rassit et attendit.

Au troisième échec, l'expert a remarqué quelque chose. L'échec s'est produit lorsque le personnel a changé les bandes sur un lecteur étranger. De plus, la panne s'est produite dès qu'un des employés a traversé un certain carrelage au sol.

Le plancher surélevé était constitué de tuiles d'aluminium posées à une hauteur de 6 à 8 pouces. De nombreux fils d'ordinateurs passaient sous le plancher surélevé pour empêcher quiconque de marcher accidentellement sur un câble important. Les carreaux ont été posés de manière très serrée pour empêcher les débris de pénétrer sous le plancher surélevé.

L'expert s'est rendu compte qu'un des carreaux était déformé. Lorsqu'un employé marchait sur son coin, les bords du carreau frottaient contre les carreaux adjacents. Les pièces en plastique qui reliaient les carreaux frottaient également avec elles, ce qui provoquait des microdécharges statiques générant des interférences radiofréquences.

Aujourd’hui, la RAM est bien mieux protégée contre les interférences radio. Mais à l’époque, ce n’était pas le cas. L'expert s'est rendu compte que ces interférences perturbaient la mémoire, et avec elle le fonctionnement du système d'exploitation. Il a appelé le service d'assistance, commandé de nouveaux carreaux, les a installés lui-même et le problème a disparu.

C'est la marée haute !

L'histoire s'est déroulée dans une salle de serveurs, au quatrième ou cinquième étage d'un bureau à Portsmouth (je crois), dans le quartier des docks.

Un jour, le serveur Unix contenant la base de données principale est tombé en panne. Ils l'ont redémarré, mais il a continué joyeusement à tomber encore et encore. Nous avons décidé d'appeler quelqu'un du service d'assistance.

Le gars du support... Je pense qu'il s'appelait Mark, mais ça n'a pas d'importance... Je ne pense pas le connaître. Cela n'a pas vraiment d'importance. Restons avec Mark, d'accord ? Super.

Ainsi, quelques heures plus tard, Mark est arrivé (il n'y a pas loin de Leeds à Portsmouth, vous savez), a allumé le serveur et tout a fonctionné sans problème. Foutu support typique, le client en est très contrarié. Mark parcourt les fichiers journaux et ne trouve rien de fâcheux. Alors Mark remonte dans le train (ou quel que soit le mode de transport par lequel il est arrivé, ça aurait pu être une vache boiteuse d'après ce que je sais... de toute façon, ça n'a pas d'importance, d'accord ?) et retourne à Leeds, après avoir gaspillé le jour.

Le soir même, le serveur plante à nouveau. L'histoire est la même... le serveur ne se lève pas. Mark essaie d'aider à distance, mais le client ne parvient pas à démarrer le serveur.

Un autre train, un bus, une meringue au citron ou une autre connerie, et Mark est de retour à Portsmouth. Regardez, le serveur démarre sans aucun problème ! Miracle. Mark passe plusieurs heures à vérifier que tout est en ordre avec le système d'exploitation ou le logiciel et part pour Leeds.

Vers le milieu de la journée, le serveur plante (allez-y doucement !). Cette fois, il semble raisonnable de faire appel à du personnel d'assistance matérielle pour remplacer le serveur. Mais non, après environ 10 heures, ça tombe aussi.

La situation s'est répétée pendant plusieurs jours. Le serveur fonctionne, plante au bout de 10 heures environ et ne démarre pas pendant les 2 heures suivantes. Ils ont vérifié le refroidissement, les fuites de mémoire, ils ont tout vérifié, mais n'ont rien trouvé. Puis les accidents ont cessé.

La semaine s'est déroulée sans soucis... tout le monde était content. Heureux jusqu'à ce que tout recommence. L'image est la même. 10 heures de travail, 2-3 heures d'arrêt...

Et puis quelqu'un (je crois qu'ils m'ont dit que cette personne n'avait rien à voir avec l'informatique) a dit :

"C'est la marée !"

L'exclamation fut accueillie par des regards vides, et la main de quelqu'un hésita probablement devant le bouton d'appel de sécurité.

"Il cesse de fonctionner avec la marée."

Cela semble être un concept complètement étranger aux travailleurs du support informatique, qui sont peu susceptibles de lire le Tide Yearbook en s'asseyant pour prendre un café. Ils ont expliqué que cela ne pouvait en aucun cas être lié à la marée, car le serveur fonctionnait depuis une semaine sans panne.

« La semaine dernière, la marée était basse, mais cette semaine elle est haute. »

Un peu de terminologie pour ceux qui n'ont pas de permis yacht. Les marées dépendent du cycle lunaire. Et lorsque la Terre tourne, toutes les 12,5 heures, l’attraction gravitationnelle du Soleil et de la Lune crée un raz-de-marée. Au début du cycle de 12,5 heures, il y a une marée haute, au milieu du cycle il y a un reflux et à la fin il y a à nouveau une marée haute. Mais à mesure que l’orbite de la Lune change, la différence entre marée basse et marée haute change également. Lorsque la Lune est entre le Soleil et la Terre ou du côté opposé de la Terre (pleine lune ou pas de lune), nous obtenons les marées Syzygyn - les marées hautes les plus hautes et les marées basses les plus basses. À la demi-lune, nous obtenons des marées en quadrature – les marées les plus basses. La différence entre les deux extrêmes diminue considérablement. Le cycle lunaire dure 28 jours : syzygien - quadrature - syzygien - quadrature.

Lorsqu'on a expliqué aux techniciens l'essence des forces de marée, ils ont immédiatement pensé qu'ils devaient appeler la police. Et assez logique. Mais il s'avère que le mec avait raison. Deux semaines plus tôt, un destroyer était amarré non loin du bureau. Chaque fois que la marée le faisait monter à une certaine hauteur, le poste radar du navire se retrouvait au niveau du sol de la salle des serveurs. Et le radar (ou l’équipement de guerre électronique, ou tout autre jouet militaire) a créé le chaos dans les ordinateurs.

Mission de vol pour la fusée

J'ai été chargé de porter un grand système de contrôle et de surveillance de lancement de fusée (environ 400 2.5.1 lignes) vers de nouvelles versions du système d'exploitation, du compilateur et du langage. Plus précisément, de Solaris 7 à Solaris 83, et du système de développement Verdix Ada (VADS), écrit en Ada 95, au système Rational Apex Ada, écrit en Ada XNUMX. VADS a été acheté par Rational et son produit a été obsolète, bien que Rational ait essayé d'implémenter des versions compatibles de packages spécifiques à VADS pour faciliter la transition vers le compilateur Apex.

Trois personnes m'ont aidé à compiler le code proprement. Cela a pris deux semaines. Et puis j’ai travaillé seul pour faire fonctionner le système. En bref, c'était la pire architecture et implémentation d'un système logiciel que j'ai jamais rencontrée, il a donc fallu encore deux mois pour terminer le port. Le système a ensuite été soumis à des tests, qui ont duré plusieurs mois supplémentaires. J'ai immédiatement corrigé les bugs trouvés lors des tests, mais leur nombre a rapidement diminué (le code source était un système de production, donc sa fonctionnalité fonctionnait de manière assez fiable, il me suffisait de supprimer les bugs apparus lors de l'adaptation au nouveau compilateur). Finalement, lorsque tout fonctionnait comme il se doit, j'ai été transféré vers un autre projet.

Et le vendredi précédant Thanksgiving, le téléphone a sonné.

Le lancement de la fusée devait être testé dans environ trois semaines et lors des tests en laboratoire du compte à rebours, la séquence de commandes a été bloquée. Dans la vraie vie, cela annulerait le test, et si le blocage survenait quelques secondes après le démarrage du moteur, plusieurs actions irréversibles se produiraient dans les systèmes auxiliaires, ce qui nécessiterait une préparation longue – et coûteuse – de la fusée. Cela n'aurait pas commencé, mais beaucoup de gens auraient été très mécontents de la perte de temps et de beaucoup, beaucoup d'argent. Ne laissez personne vous dire que le ministère de la Défense dépense de l'argent de manière imprudente : je n'ai jamais rencontré un responsable de passation de marchés qui n'ait pas mis le budget en premier ou en second, suivi du calendrier.

Au cours des mois précédents, ce défi de compte à rebours avait été exécuté des centaines de fois sous de nombreuses variantes, avec seulement quelques ratés mineurs. La probabilité que cela se produise était donc très faible, mais ses conséquences étaient très importantes. Multipliez ces deux facteurs et vous comprendrez que les informations prédisaient une semaine de vacances gâchée pour moi et des dizaines d'ingénieurs et de managers.

Et l'attention a été portée sur moi en tant que personne ayant porté le système.

Comme pour la plupart des systèmes critiques en matière de sécurité, de nombreux paramètres étaient enregistrés, il était donc assez facile d'identifier les quelques lignes de code exécutées avant la panne du système. Et bien sûr, ils n’avaient absolument rien d’inhabituel : les mêmes expressions avaient été exécutées avec succès des milliers de fois au cours du même essai.

Nous avons appelé les gens d'Apex chez Rational parce que ce sont eux qui ont développé le compilateur et que certaines des routines qu'ils ont développées ont été appelées dans le code suspect. Ils (et tout le monde) ont été impressionnés par la nécessité de s’attaquer à la racine d’un problème d’importance littéralement nationale.

Comme il n’y avait rien d’intéressant dans les revues, nous avons décidé d’essayer de reproduire le problème dans un laboratoire local. Ce n’était pas une tâche facile puisque l’événement se produisait environ une fois tous les 1000 XNUMX essais. Une raison présumée était qu'un appel à une fonction mutex développée par le fournisseur (faisant partie du package de migration VADS) Unlock n'a pas conduit au déverrouillage. Le thread de traitement qui a appelé la fonction traitait les messages de pulsation, qui arrivaient nominalement toutes les secondes. Nous avons augmenté la fréquence à 10 Hz, soit 10 fois par seconde, et avons commencé à courir. Environ une heure plus tard, le système s'est verrouillé. Dans le journal, nous avons vu que la séquence des messages enregistrés était la même que lors du test échoué. Nous avons effectué plusieurs autres passages, le système était systématiquement bloqué 45 à 90 minutes après le départ, et à chaque fois le journal contenait le même itinéraire. Même si nous exécutions techniquement un code différent – ​​la fréquence des messages était différente – le comportement du système était le même, nous étions donc convaincus que ce scénario de chargement provoquait le même problème.

Nous devions maintenant déterminer où exactement le blocage s'était produit dans la séquence d'expressions.

Cette implémentation du système utilisait le système de tâches Ada et l'utilisait incroyablement mal. Les tâches sont une construction de haut niveau exécutable simultanément dans Ada, quelque chose comme des threads d'exécution, uniquement intégrées au langage lui-même. Lorsque deux tâches doivent communiquer, elles « fixent un rendez-vous », échangent les données nécessaires, puis arrêtent le rendez-vous et reprennent leurs exécutions indépendantes. Cependant, le système a été mis en œuvre différemment. Après qu'une tâche cible ait été rendez-vous, cette tâche cible a rencontré une autre tâche, qui a ensuite rencontré une troisième tâche, et ainsi de suite jusqu'à ce qu'un certain traitement soit terminé. Après cela, tous ces rendez-vous étaient terminés et chaque tâche devait retourner à son exécution. Autrement dit, nous avions affaire au système d'appel de fonction le plus cher au monde, qui arrêtait tout le processus « multitâche » pendant qu'il traitait une partie des données d'entrée. Et avant, cela ne posait pas de problèmes uniquement parce que le débit était très faible.

J'ai décrit ce mécanisme de tâche car lorsqu'un rendez-vous était demandé ou censé se terminer, un « changement de tâche » pouvait se produire. Autrement dit, le processeur pourrait commencer à traiter une autre tâche prête à être exécutée. Il s'avère que lorsqu'une tâche est prête à rencontrer une autre tâche, une tâche complètement différente peut commencer à s'exécuter et finalement le contrôle revient au premier rendez-vous. Et d'autres événements peuvent survenir et provoquer un changement de tâche ; un de ces événements est un appel à une fonction système, telle que l'impression ou l'exécution d'un mutex.

Pour comprendre quelle ligne de code était à l'origine du problème, je devais trouver un moyen d'enregistrer la progression d'une séquence d'instructions sans déclencher de changement de tâche, ce qui empêcherait un crash. Je n'ai donc pas pu en profiter Put_Line()pour éviter d'effectuer des opérations d'E/S. Je pourrais définir une variable de compteur ou quelque chose de similaire, mais comment puis-je voir sa valeur si je ne peux pas l'afficher à l'écran ?

De plus, lors de l'examen du journal, il s'est avéré que, malgré le gel du traitement des messages de battement de cœur, qui bloquait toutes les opérations d'E/S du processus et empêchait l'exécution d'autres traitements, d'autres tâches indépendantes continuaient d'être exécutées. Autrement dit, le travail n’était pas entièrement bloqué, mais seulement une chaîne (critique) de tâches.

C’était l’indice nécessaire pour évaluer l’expression bloquante.

J'ai créé un package Ada contenant une tâche, un type énuméré et une variable globale de ce type. Les littéraux énumérables étaient liés à des expressions spécifiques de la séquence problématique (par ex. Incrementing_Buffer_Index, Locking_Mutex, Mutex_Unlocked), puis y avez inséré des expressions d'affectation qui attribuaient l'énumération correspondante à une variable globale. Étant donné que le code objet de tout cela stockait simplement une constante en mémoire, le changement de tâche suite à son exécution était extrêmement improbable. Nous nous méfiions principalement des expressions susceptibles de basculer la tâche, car le blocage se produisait lors de l'exécution plutôt que de revenir lors du basculement de la tâche (pour plusieurs raisons).

La tâche de suivi s'exécutait simplement en boucle et vérifiait périodiquement si la valeur de la variable globale avait changé. À chaque modification, la valeur était enregistrée dans un fichier. Puis une petite attente et un nouveau chèque. J'ai écrit la variable dans le fichier car la tâche n'a été exécutée que lorsque le système l'a sélectionnée pour exécution lors du changement de tâche dans la zone à problème. Quoi qu'il arrive dans cette tâche, cela n'affectera pas les autres tâches bloquées sans rapport.

Il était prévu que lorsque le système atteignait le point d'exécuter le code problématique, la variable globale serait réinitialisée lors du passage à chaque expression suivante. Ensuite, quelque chose se produira qui fera basculer la tâche, et comme sa fréquence d'exécution (10 Hz) est inférieure à celle de la tâche de surveillance, le moniteur pourrait capturer la valeur de la variable globale et l'écrire. Dans une situation normale, je pourrais obtenir une séquence répétitive d'un sous-ensemble d'énumérations : les dernières valeurs de la variable au moment du changement de tâche. Lors du blocage, la variable globale ne devrait plus changer et la dernière valeur écrite indiquera quelle expression n'a pas été complétée.

J'ai exécuté le code avec suivi. Il s'est figé. Et la surveillance a fonctionné comme sur des roulettes.

Le journal contenait la séquence attendue, qui était interrompue par une valeur indiquant qu'un mutex avait été appelé Unlock, et la tâche n'est pas terminée - comme c'est le cas pour des milliers d'appels précédents.

Les ingénieurs d'Apex analysaient fébrilement leur code à ce moment-là et trouvèrent un endroit dans le mutex où, théoriquement, un verrouillage pourrait se produire. Mais sa probabilité était très faible, puisque seule une certaine séquence d'événements se produisant à un moment donné pouvait conduire à un blocage. La loi de Murphy, les gars, c'est la loi de Murphy.

Pour protéger le morceau de code dont j'avais besoin, j'ai remplacé les appels de fonction mutex (construits au-dessus de la fonctionnalité mutex du système d'exploitation) par un petit package mutex natif Ada pour contrôler l'accès mutex à ce morceau.

Je l'ai inséré dans le code et j'ai exécuté le test. Sept heures plus tard, le code fonctionnait toujours.

Mon code a été soumis à Rational, où ils l'ont compilé, désassemblé et vérifié qu'il n'utilisait pas la même approche que celle utilisée dans les fonctions mutex problématiques.

Ce fut la révision de code la plus chargée de ma carrière 🙂 Il y avait une dizaine d'ingénieurs et de managers dans la salle avec moi, dix autres personnes étaient en conférence téléphonique - et ils ont tous examiné environ 20 lignes de code.

Le code a été révisé, de nouveaux fichiers exécutables ont été assemblés et soumis à des tests de régression formels. Quelques semaines plus tard, le test du compte à rebours a réussi et la fusée a décollé.

D'accord, c'est bien beau, mais quel est le but de l'histoire ?

C'était un problème absolument dégoûtant. Des centaines de milliers de lignes de code, une exécution parallèle, plus d'une douzaine de processus en interaction, une mauvaise architecture et une mauvaise implémentation, des interfaces pour systèmes embarqués et des millions de dollars dépensés. Pas de pression, n'est-ce pas.

Je n'étais pas le seul à travailler sur ce problème, même si j'étais sous le feu des projecteurs lors du portage. Mais même si je l'ai fait, cela ne signifie pas que j'ai compris les centaines de milliers de lignes de code, ni même que je les ai survolées. Le code et les journaux ont été analysés par des ingénieurs dans tout le pays, mais lorsqu'ils m'ont fait part de leurs hypothèses sur les causes de l'échec, il ne m'a fallu qu'une demi-minute pour les réfuter. Et quand on me demandait d’analyser des théories, je le confiais à quelqu’un d’autre, car il était évident pour moi que ces ingénieurs faisaient fausse route. Cela semble présomptueux ? Oui, c'est vrai, mais j'ai rejeté les hypothèses et les demandes pour une autre raison.

J'ai compris la nature du problème. Je ne savais pas exactement où cela se passait ni pourquoi, mais je savais ce qui se passait.

Au fil des années, j’ai accumulé beaucoup de connaissances et d’expériences. J'ai été l'un des pionniers de l'utilisation d'Ada et j'ai compris ses avantages et ses inconvénients. Je sais comment les bibliothèques d'exécution Ada gèrent les tâches et gèrent l'exécution parallèle. Et je comprends la programmation de bas niveau au niveau de la mémoire, des registres et de l'assembleur. En d’autres termes, j’ai une connaissance approfondie de mon domaine. Et je les ai utilisés pour trouver la cause du problème. Je n'ai pas seulement contourné le bug, j'ai compris comment le trouver dans un environnement d'exécution très sensible.

De telles histoires de lutte contre le code ne sont pas très intéressantes pour ceux qui ne connaissent pas les caractéristiques et les conditions d'une telle lutte. Mais ces histoires nous aident à comprendre ce qu’il faut faire pour résoudre des problèmes vraiment difficiles.

Pour résoudre des problèmes vraiment difficiles, vous devez être plus qu’un simple programmeur. Vous devez comprendre le « destin » du code, comment il interagit avec son environnement et comment l'environnement lui-même fonctionne.

Et puis vous aurez votre propre semaine de vacances gâchée.

A suivre.

Source: habr.com

Ajouter un commentaire