Quelle a été votre chasse aux insectes la plus difficile et comment l'avez-vous trouvée et tuée?

31

Il s'agit d'une question "Partager les connaissances". Je souhaite apprendre de vos succès et / ou échecs.

Informations qui pourraient être utiles ...

Contexte:

  • Contexte: langue, application, environnement, etc.
  • Comment le bug a-t-il été identifié?
  • Qui ou quoi a identifié le bug?
  • Quelle était la complexité de la reproduction du bogue?

La chasse.

  • Quel était ton plan?
  • Quelles difficultés avez-vous rencontrées?
  • Comment le code incriminé a-t-il finalement été trouvé?

La tuerie.

  • Quelle était la complexité du correctif?
  • Comment avez-vous déterminé la portée du correctif?
  • Combien de code a été impliqué dans le correctif?

Autopsie.

  • Quelle était la cause profonde techniquement? dépassement de tampon, etc.
  • Quelle était la cause première de 30 000 pieds?
  • Combien de temps a duré le processus?
  • Le correctif a-t-il affecté certaines fonctionnalités?
  • Quelles méthodes, outils, motivations avez-vous trouvé particulièrement utiles? ... horriblement inutile?
  • Si vous pouviez tout recommencer? ............

Ces exemples sont généraux, ne s'appliquent pas à toutes les situations et peuvent être inutiles. Veuillez assaisonner au besoin.

Rouillé
la source

Réponses:

71

Il s'agissait en fait d'un sous-composant de visionneuse d'images tiers de notre application.

Nous avons constaté que 2 à 3 des utilisateurs de notre application verraient fréquemment le composant visionneuse d'images lever une exception et mourir horriblement. Cependant, nous avons eu des dizaines d'autres utilisateurs qui n'ont jamais vu le problème malgré l'utilisation de l'application pour la même tâche pendant la majeure partie de la journée de travail. Il y avait aussi un utilisateur en particulier qui l'a reçu beaucoup plus fréquemment que les autres.

Nous avons essayé les étapes habituelles:

(1) Leur a fait changer d'ordinateur avec un autre utilisateur qui n'a jamais eu de problème pour exclure l'ordinateur / la configuration. - Le problème les a suivis.

(2) Demandez-leur de se connecter à l'application et de travailler en tant qu'utilisateur qui n'a jamais vu le problème. - Le problème les a TOUJOURS suivis.

(3) L'utilisateur a-t-il signalé l'image qu'il visualisait et a-t-il mis en place un faisceau de test pour répéter la visualisation de cette image des milliers de fois rapidement. Le problème ne s'est pas présenté dans le harnais.

(4) Un développeur était assis avec les utilisateurs et les regardait toute la journée. Ils ont vu les erreurs, mais ne les ont pas remarqués faire quoi que ce soit hors de l'ordinaire pour les causer.

Nous avons eu du mal avec cela pendant des semaines en essayant de comprendre ce que les "utilisateurs d'erreur" avaient en commun que les autres utilisateurs n'avaient pas. Je ne sais pas comment, mais le développeur de l'étape (4) a eu un moment d'eureka en route pour travailler un jour digne de l'Encyclopedia Brown.

Il s'est rendu compte que tous les «utilisateurs d'erreur» étaient gauchers et a confirmé ce fait. Seuls les utilisateurs gauchers ont eu les erreurs, jamais les Righties. Mais comment le fait d'être gaucher pouvait-il provoquer un bug?

Nous l'avons fait s'asseoir et regarder à nouveau les gauchers en prêtant une attention particulière à tout ce qu'ils pourraient faire différemment, et c'est ainsi que nous l'avons trouvé.

Il s'est avéré que le bogue ne s'est produit que si vous avez déplacé la souris vers la colonne de pixels la plus à droite dans la visionneuse d'images pendant le chargement d'une nouvelle image (erreur de débordement car le fournisseur avait un calcul unique pour l'événement survolé).

Apparemment, en attendant le chargement de l'image suivante, les utilisateurs ont tous naturellement déplacé leur main (et donc la souris) vers le clavier.

Le seul utilisateur qui a eu l'erreur le plus fréquemment était l'un de ces types d'ADD qui compulsivement déplaçait sa souris avec beaucoup d'impatience en attendant que la page suivante se charge, elle déplaçait donc la souris vers la droite beaucoup plus rapidement et frappait le chronométrer juste donc elle l'a fait quand l'événement de charge s'est produit. Jusqu'à ce que nous obtenions un correctif du vendeur, nous lui avons simplement dit de lâcher la souris après avoir cliqué sur (document suivant) et de ne pas le toucher avant qu'il ne soit chargé.

Il était désormais connu dans la légende de l'équipe de développement comme "The Left Handed Bug"

JohnFx
la source
14
C'est la chose la plus perverse dont j'ai jamais entendu parler.
Nathan Taylor
9
Cela a fait un héros du gars qui l'a résolu, cependant.
JohnFx
2
Wow, maintenant c'est un sacré bug!
Mitchel Sellers,
3
Super trouvaille! Bonne histoire.
Toon Krijthe
11
Comme si nous, les gauchers, ne sommes pas déjà suffisamment traités comme des citoyens de seconde zone. Maintenant, nous devons également nous retrouver avec plus que notre juste part de bogues logiciels ... gee, merci! : p
Dan Moulding
11

Cela remonte à longtemps (la fin des années 80).

L'entreprise pour laquelle j'ai travaillé a écrit un package de CAO (en FORTRAN) qui fonctionnait sur différentes stations de travail Unix (HP, Sun, Silcon Graphics, etc.). Nous avons utilisé notre propre format de fichier pour stocker les données et lorsque le package a été démarré, l'espace disque était rare, il y avait donc beaucoup de décalage de bits utilisé pour stocker plusieurs indicateurs dans les en-têtes d'entité.

Le type de l'entité (ligne, arc, texte, etc.) a été multiplié par 4096 (je pense) lors de son stockage. De plus, cette valeur a été annulée pour indiquer un élément supprimé. Donc, pour obtenir le type, nous avions du code qui:

type = record[1] MOD 4096

Sur chaque machine sauf une, cela donnait ± 1 (pour une ligne), ± 2 (pour un arc) etc. et nous pouvions alors vérifier le panneau pour voir s'il était supprimé.

Sur une machine (HP, je pense), nous avons eu un problème étrange où la gestion des éléments supprimés était vissée.

C'était dans les jours précédant les débogueurs IDE et visuels, j'ai donc dû insérer des instructions de trace et la journalisation pour essayer de détecter le problème.

J'ai finalement découvert que c'était parce que tandis que tous les autres fabricants mis en œuvre MODafin que -4096 MOD 4096résultat -1HP mis en œuvre mathématiquement correctement afin que -4096 MOD 4096résultat -4097.

J'ai fini par devoir parcourir l'intégralité de la base de code en enregistrant le signe de la valeur et en le rendant positif avant d'effectuer le MODpuis en multipliant le résultat par la valeur du signe.

Cela a pris plusieurs jours.

ChrisF
la source
3
Il y a probablement eu des chasses d'insectes plus difficiles au fil des ans, mais celle-ci est restée dans mon esprit depuis bien plus de 20 ans!
ChrisF
7

Wow, bonne lecture ici!

Ma plus difficile a été il y a des années lorsque Turbo Pascal était gros, bien qu'il ait pu être l'un des premiers IDE C ++ de cette époque. En tant que développeur unique (et troisième dans cette startup), j'avais écrit quelque chose comme un programme de CAO simplifié et convivial pour les vendeurs. C'était génial à l'époque, mais a développé un crash aléatoire désagréable. Il était impossible de se reproduire, mais cela s'est produit assez souvent pour que je parte à la chasse aux bogues.

Ma meilleure stratégie consistait à effectuer une seule étape dans le débogueur. Le bogue ne s'est produit que lorsque l'utilisateur a entré suffisamment de dessin et doit peut-être être dans un certain mode ou état de zoom, il y avait donc beaucoup de réglages et d'effacement fastidieux des points d'arrêt, fonctionnant normalement pendant une minute pour entrer dans un dessin, puis parcourir une grande partie du code. Les points d'arrêt qui sautaient un certain nombre de fois puis se cassaient étaient particulièrement utiles. Cet exercice a dû être répété plusieurs fois.

Finalement, je l'ai réduit à un endroit où un sous-programme était appelé, recevant un 2 mais de l'intérieur, il a vu un certain nombre de charabia. J'aurais pu comprendre cela plus tôt, mais je n'étais pas entré dans ce sous-programme, en supposant qu'il avait obtenu ce qui lui avait été donné. Aveuglé en supposant que la plus simple des choses allait bien!

Il s'est avéré bourrer un int 16 bits sur la pile, mais le sous-programme attendait 32 bits. Ou quelque chose comme ça. Le compilateur n'a pas automatiquement rempli toutes les valeurs sur 32 bits, ou n'a pas effectué de vérification de type suffisante. C'était insignifiant à réparer, juste une partie d'une ligne, presque aucune pensée requise. Mais pour y arriver, il a fallu trois jours de chasse et de remise en question de l'évidence.

J'ai donc une expérience personnelle avec cette anecdote sur le consultant coûteux qui arrive, après un certain temps, fait un tapotement quelque part et facture 2000 $. Les dirigeants exigent une ventilation, et c'est 1 $ pour le robinet, 1999 $ pour savoir où taper. Sauf dans mon cas, il était temps, pas d'argent.

Leçons apprises: 1) utiliser les meilleurs compilateurs, où "le meilleur" est défini comme incluant la vérification d'autant de problèmes que l'informatique sait comment vérifier, et 2) remettre en question les choses évidentes simples, ou au moins vérifier leur bon fonctionnement.

Depuis lors, tous les bugs difficiles ont été vraiment difficiles, comme je le sais, pour vérifier les choses simples de manière plus approfondie que ce qui semble nécessaire.

La leçon 2 s'applique également au bogue électronique le plus difficile que j'ai jamais résolu, également avec une correction triviale, mais plusieurs EE intelligentes étaient bloquées depuis des mois. Mais ce n'est pas un forum électronique, donc je n'en dirai pas plus.

DarenW
la source
Veuillez poster le bug électronique ailleurs et un lien ici!
tgkprog
6

La condition de course aux données de mise en réseau de l'enfer

J'écrivais un client / serveur de mise en réseau (Windows XP / C #) pour travailler avec une application similaire sur une très ancienne station de travail (Encore 32/77) écrite par un autre développeur.

L'application a essentiellement consisté à partager / manipuler certaines données sur l'hôte pour contrôler le processus hôte exécutant le système avec notre interface utilisateur tactile multi-écrans sophistiquée basée sur PC.

Il l'a fait avec une structure à 3 couches. Le processus de communication a lu / écrit des données vers / depuis l'hôte, a effectué toutes les conversions de format nécessaires (endianness, format à virgule flottante, etc.) et a écrit / lu les valeurs vers / depuis une base de données. La base de données a servi d'intermédiaire de données entre les communications et les interfaces utilisateur tactiles. L'interface tactile de l'interface utilisateur a généré des interfaces d'écran tactile en fonction du nombre de moniteurs connectés au PC (il l'a détecté automatiquement).

Dans le laps de temps donné, un paquet de valeurs entre l'hôte et notre PC ne pouvait envoyer que 128 valeurs maximum sur le fil à la fois avec une latence maximale de ~ 110 ms par aller-retour (UDP a été utilisé avec une connexion Ethernet x-over directe entre les ordinateurs). Ainsi, le nombre de variables autorisées en fonction du nombre variable d'écrans tactiles connectés était sous contrôle strict. De plus, l'hôte (bien qu'ayant une architecture multiprocesseur assez complexe avec un bus de mémoire partagée utilisé pour l'informatique en temps réel) avait environ 1 / 100e de la puissance de traitement de mon téléphone portable, il a donc été chargé de faire le moins de traitement possible et c'est le serveur / client devait être écrit en assembleur pour garantir cela (l'hôte exécutait une simulation en temps réel complète qui ne pouvait pas être affectée par notre programme).

Le problème était. Certaines valeurs, lorsqu'elles sont modifiées sur l'écran tactile, ne prennent pas uniquement la valeur nouvellement entrée, mais alternent de manière aléatoire entre cette valeur et la valeur précédente. Cela et seulement sur quelques valeurs spécifiques sur quelques pages spécifiques avec une certaine combinaison de pages a jamais présenté le symptôme. Nous avons presque complètement manqué le problème jusqu'à ce que nous commencions à l'exécuter à travers le processus d'acceptation initial du client


Pour cerner le problème, j'ai choisi l'une des valeurs oscillantes:

  • J'ai vérifié l'application Touchscreen, elle oscillait
  • J'ai vérifié la base de données, oscillant
  • J'ai vérifié l'application de communication en oscillant

Ensuite, j'ai éclaté Wireshark et commencé à décoder manuellement les captures de paquets. Résultat:

  • Pas oscillant mais les paquets ne semblaient pas corrects, il y avait trop de données.

J'ai parcouru chaque détail du code de communication cent fois sans trouver de défaut / erreur.

Enfin, j'ai commencé à envoyer des e-mails à l'autre développeur pour lui demander en détail comment sa fin fonctionnait pour voir s'il manquait quelque chose. Puis je l'ai trouvé.

Apparemment, lorsqu'il a envoyé des données, il n'a pas vidé le tableau de données avant la transmission.Il a donc essentiellement écrasé le dernier tampon utilisé avec les nouvelles valeurs écrasant l'ancienne, mais les anciennes valeurs non écrasées étaient toujours transmises.

Donc, si une valeur était à la position 80 du tableau de données et que la liste des valeurs demandées est passée à moins de 80 mais que la même valeur était contenue dans la nouvelle liste, les deux valeurs existeraient dans le tampon de données pour ce tampon spécifique à tout moment. temps donné.

La valeur lue dans la base de données dépend de la tranche de temps à laquelle l'interface utilisateur a demandé la valeur.


La solution était douloureusement simple. Lisez le nombre d'éléments entrants sur le tampon de données (il était en fait contenu dans le cadre du protocole de paquet) et ne lisez pas le tampon au-delà de ce nombre d'éléments.


Leçons apprises:

  • Ne tenez pas la puissance de calcul moderne pour acquise. Il fut un temps où les ordinateurs ne prenaient pas en charge Ethernet et où le vidage d'une baie pouvait être considéré comme coûteux. Si vous voulez vraiment voir jusqu'où nous en sommes, imaginez un système qui n'a pratiquement aucune forme d'allocation dynamique de mémoire. IE, le processus exécutif devait pré-allouer toute la mémoire pour tous les programmes dans l'ordre et aucun programme ne pouvait se développer au-delà de cette frontière. IE, allouer plus de mémoire à un programme sans recompiler tout le système pourrait provoquer un crash massif. Je me demande si les gens parleront un jour des jours de la pré-collecte des ordures.

  • Lors de la mise en réseau avec des protocoles personnalisés (ou de la gestion de la représentation des données binaires en général), assurez-vous de lire la spécification jusqu'à ce que vous compreniez chaque fonction de chaque valeur envoyée à travers le canal. Je veux dire, lisez-le jusqu'à ce que vos yeux vous fassent mal. Les gens manipulent des données en manipulant des bits ou des octets individuels ont des façons très intelligentes et efficaces de faire les choses. Manquer le moindre détail pourrait briser le système.

Le temps global pour réparer était de 2-3 jours avec la plupart de ce temps passé à travailler sur d'autres choses quand j'ai été frustré par cela.

SideNote: L'ordinateur hôte en question ne prend pas en charge Ethernet par défaut. La carte pour le conduire a été fabriquée sur mesure et mise à niveau et la pile de protocoles n'existait pratiquement pas. Le développeur avec lequel je travaillais était un enfer de programmeur, il a non seulement implémenté une version allégée d'UDP et une fausse pile Ethernet mimimale (le processeur n'était pas assez puissant pour gérer une pile Ethernet complète) sur le système pour ce projet mais il l'a fait en moins d'une semaine. Il avait également été l'un des chefs d'équipe du projet d'origine qui avait conçu et programmé le système d'exploitation en premier lieu. Disons simplement que tout ce qu'il a eu à partager sur les ordinateurs / la programmation / l'architecture, peu importe combien de temps ou combien je suis déjà nouveau, j'écouterais chaque mot.

Plie d'Evan
la source
5

L'arrière-plan

  • Dans une application WCF critique, pilotant un site Web et fournissant un traitement transactionnel principal.
  • Application grand volume (centaines d'appels par seconde)
  • Plusieurs instances multiples du serveur
  • des centaines de tests unitaires réussis et d'innombrables attaques QA

L'insecte

  • Une fois déplacé en production, le serveur fonctionnerait correctement pendant une durée aléatoire, puis commencerait à se dégrader rapidement et à porter le boîtier CPU à 100%.

Comment je l'ai trouvé

Au début, j'étais sûr que c'était un problème de performance normal, alors je crée une journalisation élaborée. Les performances vérifiées à chaque appel ont parlé aux utilisateurs de la base de données de l'utilisation et surveillé les serveurs pour détecter les problèmes. 1 semaine

Ensuite, j'étais sûr d'avoir un problème de contention de thread. J'ai vérifié mes blocages tentés de créer la situation créer des outils pour tenter de créer la situation en débogage. Avec la frustration croissante de la direction, je me suis tourné vers mes pairs pour savoir comment suggérer des choses, du redémarrage du projet à partir de zéro à la limitation du serveur à un seul thread. 1,5 semaines

Ensuite, j'ai regardé le blog de Tess Ferrandez, j'ai créé un fichier de vidage utilisateur et l'ai annulé avec windebug la prochaine fois que le serveur a fait un vidage. J'ai trouvé que tous mes threads étaient bloqués dans la fonction dictionary.add.

Le long le petit dictionnaire court qui gardait juste la trace du journal dans lequel écrire les erreurs de threads x n'était pas synchronisé.

rediffusion
la source
3

Nous avions une application qui parlait à un périphérique matériel qui, dans certains cas, ne fonctionnerait pas correctement si le périphérique était physiquement débranché jusqu'à ce qu'il ait été rebranché et réinitialisé deux fois.

Le problème s'est avéré qu'une application en cours d'exécution au démarrage faisait parfois des erreurs de segmentation lorsqu'elle tentait de lire à partir d'un système de fichiers qui n'avait pas encore été monté (par exemple, si un utilisateur la configurait pour lire à partir d'un volume NFS). Au démarrage, l'application enverrait des ioctls au pilote pour initialiser le périphérique, puis lirait les paramètres de configuration et enverrait plus d'ioctls pour mettre le périphérique dans le bon état.

Un bogue dans le pilote provoquait l'écriture d'une valeur non valide sur le périphérique lors de l'appel d'initialisation, mais la valeur a été remplacée par des données valides une fois que les appels ont été effectués pour mettre le périphérique dans un état spécifique.

L'appareil lui-même avait une batterie et détecterait s'il perdait de l'énergie de la carte mère, et écrivait un indicateur dans la mémoire volatile indiquant qu'il avait perdu de l'énergie, il entrerait alors dans un état spécifique la prochaine fois qu'il serait allumé, et un instruction devait être envoyée pour effacer le drapeau.

Le problème était que si l'alimentation était coupée une fois que les ioctls avaient été envoyés pour initialiser le périphérique (et écrit la valeur non valide sur le périphérique) mais avant que des données valides puissent être envoyées. Lorsque l'appareil était rallumé, il verrait que le drapeau avait été défini et essaierait de lire les données non valides envoyées par le pilote en raison de l'initialisation incomplète. Cela mettrait l'appareil dans un état invalide où l'indicateur de mise hors tension avait été effacé mais l'appareil ne recevrait plus d'instructions jusqu'à ce qu'il ait été réinitialisé par le conducteur. La deuxième réinitialisation signifierait que l'appareil n'essayait pas de lire les données non valides qui y avaient été stockées et recevrait des instructions de configuration correctes, lui permettant d'être mis dans le bon état (en supposant que l'application envoyant les ioctls ne se soit pas trompée ).

En fin de compte, il a fallu environ deux semaines pour déterminer l'ensemble exact des circonstances à l'origine du problème.

Cercerilla
la source
2

Pour un projet universitaire, nous écrivions un système de nœuds P2P distribués qui partage des fichiers, cette prise en charge de la multidiffusion pour se détecter mutuellement, plusieurs anneaux de nœuds et un serveur de noms afin qu'un nœud soit attribué à un client.

Écrit en C ++, nous avons utilisé POCO pour cela car il permet une bonne programmation IO, Socket et Thread.


Il y a eu deux bugs qui nous ont ennuyés et nous ont fait perdre beaucoup de temps, vraiment logique:

Au hasard, un ordinateur partageait son adresse IP locale au lieu de son adresse IP distante.

Cela a amené les clients à se connecter au nœud sur le même PC ou les nœuds à se connecter entre eux.

Comment avons-nous identifié cela? Lorsque nous avons amélioré la sortie dans le serveur de noms, nous avons découvert à un moment ultérieur, lorsque nous avons redémarré les ordinateurs, que notre script pour déterminer l'adresse IP à fournir était incorrect. Au hasard, le périphérique lo a été répertorié en premier au lieu du périphérique eth0 ... Vraiment stupide. Alors maintenant, nous avons codé en dur pour le requist de eth0 car il est partagé entre tous les ordinateurs de l'université ...


Et maintenant un plus ennuyeux:

Au hasard, le flux de paquets se mettrait en pause de manière aléatoire.
Lorsque le prochain client se connecte, il continue ...

Cela s'est produit vraiment au hasard et comme plus d'un ordinateur est impliqué, il est devenu plus ennuyeux de déboguer ce problème, les ordinateurs de l'université ne nous permettent pas d'exécuter Wireshark sur ceux-ci, nous devons donc deviner si le problème était du côté de l'envoi ou de la réception. côté.

Avec beaucoup de sortie dans le code, nous avons simplement supposé que l'envoi des commandes allait bien,
cela nous a laissé nous demander où était le vrai problème ... Il semblait que la façon dont POCO interroge est incorrecte et que nous devrions plutôt vérifier les caractères disponibles sur la prise entrante.

Nous avons supposé que cela fonctionnait comme des tests plus simples dans un prototype impliquant moins de paquets ne causait pas ce problème, donc cela nous a fait supposer que la déclaration de sondage fonctionnait mais ... Ce n'était pas le cas. :-(


Leçons apprises:

  • Ne faites pas d'hypothèses stupides comme l'ordre des périphériques réseau.

  • Les frameworks ne font pas toujours leur travail (implémentation ou documentation) correctement.

  • Fournissez suffisamment de sortie dans le code, si cela n'est pas autorisé, assurez-vous de consigner les détails étendus dans un fichier.

  • Lorsque le code n'a pas été testé unitaire (parce que c'est trop difficile), ne supposez pas que les choses fonctionnent.

Tamara Wijsman
la source
1
Résoudre les problèmes de mise en réseau sans Wirehark (ou un outil similaire) est héroïque dans / de iteslf.
Evan Plaice
2

Je suis toujours sur ma chasse aux insectes la plus difficile. C'est l'un de ceux qui sont parfois là et parfois ce ne sont pas des bugs. C'est pourquoi je suis ici, à 6 h 10 le lendemain.

Contexte:

  • Contexte: langue, application, environnement, etc.
    • PHP OS Commerce
  • Comment le bug a-t-il été identifié?
    • Ordre aléatoire qui fonctionne à mi-chemin les problèmes d'échec et de redirection aléatoires
  • Qui ou quoi a identifié le bug?
    • Client, et le problème de redirection était évident
  • Quelle était la complexité de la reproduction du bogue?
    • Je n'ai pas pu reproduire, mais le client a pu.

La chasse.

  • Quel était ton plan?
    • Ajouter un code de débogage, remplir l'ordre, analyser les données, répéter
  • Quelles difficultés avez-vous rencontrées?
    • Manque de problèmes reproductibles et code horrible
  • Comment le code incriminé a-t-il finalement été trouvé?
    • beaucoup de code incriminé a été trouvé ... mais pas exactement ce dont j'avais besoin.

La tuerie.

  • Quelle était la complexité du correctif?
    • très
  • Comment avez-vous déterminé la portée du correctif?
    • il n'y avait aucune portée ... c'était partout
  • Combien de code a été impliqué dans le correctif?
    • Tout? Je ne pense pas qu'il y ait eu un fichier intact

Autopsie.

  • Quelle était la cause profonde techniquement? dépassement de tampon, etc.
    • mauvaise pratique de codage
  • Quelle était la cause première de 30 000 pieds?
    • Je préfère ne pas dire...
  • Combien de temps a duré le processus?
    • éternité et un jour
  • Le correctif a-t-il affecté certaines fonctionnalités?
    • fonctionnalité? ou est-ce un bug?
  • Quelles méthodes, outils, motivations avez-vous trouvé particulièrement utiles? ... horriblement inutile?
  • Si vous pouviez tout recommencer? ............
    • ctrl + a Suppr
WalterJ89
la source
Si la raison était une «mauvaise pratique de codage», vous voudrez peut-être discuter avec votre patron si c'est le bon moment pour réviser les pratiques de codage de votre équipe et peut-être introduire un examen par les pairs?
2

J'ai dû corriger des trucs de concurrence déroutants au dernier semestre, mais le bug qui se démarque toujours le plus pour moi était dans un jeu basé sur du texte que j'écrivais dans l'assemblage PDP-11 pour une affectation aux devoirs. Il était basé sur le jeu de la vie de Conway et pour une raison étrange, une grande partie des informations à côté de la grille étaient constamment écrasées par des informations qui n'auraient pas dû être là. La logique était également assez simple, donc c'était très déroutant. Après l'avoir parcouru plusieurs fois pour redécouvrir que toute la logique est correcte, j'ai soudain remarqué quel était le problème. Cette chose:.

Dans PDP-11, ce petit point à côté d'un nombre le fait en base 10 au lieu de 8. Il était à côté d'un nombre qui délimitait une boucle qui était censée être limitée à la grille, dont la taille était définie avec les mêmes nombres mais en base 8.

Il se démarque toujours pour moi en raison de la quantité de dommages causés par un minuscule ajout de 4 pixels. Alors quelle est la conclusion? Ne codez pas dans l'assemblage PDP-11.

EpsilonVector
la source
2

Le programme cadre principal a cessé de fonctionner sans raison

Je viens de poster ceci à une autre question. Voir l'article ici

Cela s'est produit parce qu'ils ont installé une version plus récente du compilateur sur le châssis principal.

Mise à jour du 11/06/13: (La réponse d'origine a été supprimée par OP)

J'ai hérité de cette application de cadre principal. Un jour, hors du bleu clair, il a cessé de fonctionner. C'est ça ... pouf ça vient de s'arrêter.

Mon travail consistait à le faire fonctionner le plus rapidement possible. Le code source n'avait pas été modifié depuis deux ans, mais tout à coup, il venait de s'arrêter. J'ai essayé de compiler le code et il s'est cassé sur la ligne XX. J'ai regardé la ligne XX et je ne pouvais pas dire ce qui ferait casser la ligne XX. J'ai demandé les spécifications détaillées de cette application et il n'y en avait pas. La ligne XX n'était pas la coupable.

J'ai imprimé le code et j'ai commencé à le réviser de haut en bas. J'ai commencé à créer un organigramme de ce qui se passait. Le code était si compliqué que je pouvais à peine le comprendre. J'ai renoncé à essayer de l'organigramme. J'avais peur de faire des changements sans savoir comment ce changement affecterait le reste du processus, d'autant plus que je n'avais aucun détail sur ce que faisait la demande.

J'ai donc décidé de commencer en haut du code source et d'ajouter des freins blancs et de ligne pour rendre le code plus lisible. J'ai remarqué, dans certains cas, qu'il y avait des conditions qui combinaient des ET et des OU et qu'il n'était pas clairement possible de distinguer entre quelles données étaient ET et quelles données étaient OU. J'ai donc commencé à mettre des parenthèses autour des conditions ET et OU pour les rendre plus lisibles.

Alors que je descendais lentement pour le nettoyer, j'enregistrais périodiquement mon travail. À un moment donné, j'ai essayé de compiler le code et une chose étrange s'est produite. L'erreur avait dépassé la ligne de code d'origine et était maintenant plus bas. J'ai donc continué, en séparant les conditions ET et OU avec des parens. Quand j'ai fini de le nettoyer, ça a marché. Allez comprendre.

J'ai alors décidé de visiter le magasin des opérations et de leur demander s'ils avaient récemment installé de nouveaux composants sur le châssis principal. Ils ont dit oui, nous avons récemment mis à jour le compilateur. Hmmmm.

Il s'avère que l'ancien compilateur évaluait l'expression de gauche à droite. La nouvelle version du compilateur a également évalué les expressions de gauche à droite, mais le code ambigu signifie que la combinaison peu claire de AND et OR n'a pas pu être résolue.

Leçon que j'ai apprise de ceci ... TOUJOURS, TOUJOURS, TOUJOURS utiliser des parenthèses pour séparer les conditions ET et les conditions OU lorsqu'elles sont utilisées en conjonction les unes avec les autres.

Michael Riley - AKA Gunny
la source
le message vers lequel votre lien pointe a été supprimé - cela vous dérangerait-il de mettre à jour la réponse?
gnat
1
@gnat - Trouvé sur archive.org :)
Michael Riley - AKA Gunny
1

Contexte:

  • Contexte: Web Server (C ++) qui permet aux clients de s'enregistrer eux-mêmes
  • Bogue: lors de la demande de la page, il ne répondait tout simplement pas, l'ensemble de la batterie de serveurs, et les processus étaient tués (et relancés) car ils prenaient trop de temps (seulement quelques secondes sont autorisées) pour servir la page
  • Certains utilisateurs se sont plaints, mais c'était extrêmement sporadique, donc la plupart du temps inaperçu (les gens ont tendance à appuyer sur "Actualiser" lorsqu'une page n'est pas diffusée). Nous avons cependant remarqué les vidages de mémoire;)
  • En fait, nous n'avons jamais réussi à nous reproduire dans nos environnements locaux, le bogue est apparu plusieurs fois dans les systèmes de test, mais n'a jamais été détecté lors des tests de performances ??

La chasse.

  • Plan: Eh bien, comme nous avions des vidages de mémoire et des journaux, nous voulions les analyser. Comme cela affectait l'ensemble de la ferme et que nous avions eu des problèmes de bases de données dans le passé, nous soupçonnions la base de données (une seule base de données pour plusieurs serveurs)
  • Difficultés: Un vidage complet du serveur est énorme, et donc ils sont effacés assez fréquemment (pour ne pas manquer d'espace), nous avons donc dû être rapides à en saisir un quand il s'est produit ... Nous avons persisté. Les vidages ont montré diverses piles (jamais aucun élément de base de données, tant pour cela), il a échoué lors de la préparation de la page elle-même (pas dans les calculs précédents), et a confirmé ce que les journaux ont montré, la préparation de la page prenait parfois longtemps, même bien que ce soit juste un moteur de modèle de base avec des données précalculées (MVC traditionnel)
  • Pour y arriver: Après quelques échantillons supplémentaires et quelques réflexions, nous avons réalisé que le temps était pris pour lire les données du disque dur (le modèle de page). Puisqu'il s'agissait de l'ensemble de la ferme, nous avons d'abord recherché des tâches planifiées (crontab, lots) mais les horaires ne correspondaient jamais d'une occurrence à une autre ... J'ai finalement pensé que cela arrivait toujours quelques jours avant l'activation d'une nouvelle version du logiciel et j'ai eu un AhAh! moment ... il a été causé par la distribution du logiciel! La livraison de plusieurs centaines de mégaoctets (compressés) peut réduire les performances du disque: / Bien sûr, la distribution est automatisée et l'archive est envoyée à tous les serveurs à la fois (multidiffusion).

La tuerie.

  • Correction de la complexité: passage aux modèles compilés
  • Code affecté: aucun, un simple changement dans le processus de construction

Autopsie.

  • Cause profonde: problème opérationnel ou manque de planification prospective :)
  • Calendrier: il a fallu des mois pour rechercher, quelques jours pour corriger et tester, quelques semaines pour les tests et le déploiement de l'assurance qualité et des performances - pas pressé, car nous savions que le déploiement du correctif déclencherait le bogue ... et rien sinon ... un peu pervers vraiment!
  • Effets secondaires néfastes: impossibilité de changer de modèle au moment de l'exécution maintenant qu'ils sont cuits dans le code fourni, nous n'avons cependant pas beaucoup utilisé la fonctionnalité, car généralement, le changement de modèle signifie que vous avez plus de données à verser. L'utilisation de css est largement suffisant pour les "petits" changements de mise en page.
  • Méthodes, outils: gdb+ suivi! Il nous a juste fallu du temps pour soupçonner le disque, puis identifier la cause des pics d'activité sur le graphique de surveillance ...
  • La prochaine fois: traitez tous les IO comme indésirables!
Matthieu M.
la source
1

Le plus dur n'a jamais été tué car il n'a jamais pu être reproduit autrement que dans l'environnement de production complet avec l'usine en fonctionnement.

Le plus fou que j'ai tué:

Les dessins impriment du charabia!

Je regarde le code et je ne vois rien. Je retire un travail de la file d'attente de l'imprimante et je l'examine, ça a l'air bien. (C'était à l'époque du dos, PCL5 avec HPGl / 2 intégré - en fait, très bon pour tracer des dessins et pas de maux de tête pour créer une image raster dans une mémoire limitée.) Je la dirige vers une autre imprimante qui devrait la comprendre, elle s'imprime très bien .

Faites reculer le code, le problème est toujours là.

Enfin, je crée manuellement un fichier simple et l'envoie à l'imprimante - charabia. Il s'avère que ce n'était pas du tout mon bug, mais l'imprimante elle-même. La société de maintenance l'avait mise à jour vers la dernière version lorsqu'elle réparait autre chose et cette dernière version avait un bug. Leur faire comprendre qu'ils avaient supprimé des fonctionnalités essentielles et devaient les renvoyer à une version antérieure était plus difficile que de trouver le bogue lui-même.

Celui qui était encore plus vexant mais comme c'était seulement sur ma boîte, je ne mettrais pas en première place:

Borland Pascal, code DPMI pour gérer certaines API non prises en charge. Exécutez-le, parfois cela a fonctionné, généralement il a explosé en essayant de gérer un pointeur invalide. Cependant, cela n'a jamais produit un mauvais résultat, comme on pourrait s'y attendre en tapant sur un pointeur.

Débogage - si je parcourais le code en une seule étape, cela fonctionnerait toujours correctement, sinon il était tout aussi instable qu'auparavant. L'inspection a toujours montré les bonnes valeurs.

Le coupable: il y en avait deux.

1) Le code de bibliothèque de Borland avait un bug majeur: les pointeurs en mode réel étaient stockés dans des variables de pointeur en mode protégé. Le problème est que la plupart des pointeurs en mode réel ont des adresses de segment non valides en mode protégé et lorsque vous essayez de copier le pointeur, il l'a chargé dans une paire de registres, puis l'a enregistré.

2) Le débogueur ne dirait jamais rien sur une telle charge invalide en mode pas à pas. Je ne sais pas ce qu'il a fait en interne mais ce qui a été présenté à l'utilisateur avait l'air tout à fait correct. Je soupçonne qu'il n'a pas réellement exécuté l'instruction mais l'a simulée à la place.

Loren Pechtel
la source
1

C'est juste un bug très simple que j'ai transformé en quelque sorte en cauchemar pour moi.

Contexte: Je travaillais sur la création de mon propre système d'exploitation. Le débogage est très difficile (les instructions de trace sont tout ce que vous pouvez avoir, et parfois même pas)

Bogue: Au lieu de faire deux commutateurs de threads en mode utilisateur, ce serait plutôt une erreur de protection générale.

La chasse aux bogues: j'ai probablement passé une semaine ou deux à essayer de résoudre ce problème. Insérer des instructions de trace partout. Examen du code d'assemblage généré (à partir de GCC). Imprimer chaque valeur que je pouvais.

Le problème: quelque part au début de la chasse aux bogues, j'avais placé une hltinstruction dans le crt0. Le crt0 est essentiellement ce qui amorce un programme utilisateur à utiliser dans un système d'exploitation. Cette hltinstruction provoque un GPF lorsqu'elle est exécutée à partir du mode utilisateur. Je l'ai placé là et je l'ai pratiquement oublié. (à l'origine, le problème était dû à un débordement de tampon ou à une erreur d'allocation de mémoire)

Le correctif: supprimez l' hltinstruction :) Après l'avoir supprimée, tout s'est bien passé.

Ce que j'ai appris: lorsque vous essayez de déboguer un problème, ne perdez pas de vue les correctifs que vous essayez. Faites des différences régulières avec la dernière version de contrôle de source stable et voyez ce que vous avez changé récemment quand rien d'autre ne fonctionne

Earlz
la source