it-swarm-fr.com

Quelle était votre chasse au bug la plus difficile et comment l'avez-vous trouvé et de le tuer?

C'est une question "partager la connaissance". Je suis intéressé à apprendre de vos succès et/ou de vos échecs.

Informations qui pourraient être utiles ...

arrière-plan :

  • Contexte: langue, application, environnement, etc.
  • Comment l'insecte a-t-il été identifié?
  • Qui ou quoi a identifié le bogue?
  • Comment complexité reproduisait le bogue?

la chasse.

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

le meurtre.

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

postmortem.

  • Quelle était la cause première techniquement? dépassement tampon, etc.
  • Quelle était la cause première de 30 000 pi?
  • Combien de temps a-t-il pris le processus?
  • Y a-t-il eu des fonctionnalités défectueuses par le correctif?
  • Quelles méthodes, outils, motivations avez-vous trouvé particulièrement utiles? ... horriblement inutile?
  • Si vous pouviez tout faire à nouveau? ............

Ces exemples sont généraux, non applicables dans toutes les situations et éventuellement inutiles. S'il vous plaît assaisonner si nécessaire.

31
Rusty

Il s'agissait en fait d'un sous-composant de la visionneuse de l'image de la soirée de notre candidature.

Nous avons constaté qu'il y avait 2-3 des utilisateurs de notre application aurait souvent le composant de la visionneuse d'images lancer une exception et meurt horriblement. Cependant, nous avons eu des dizaines d'autres utilisateurs qui n'ont jamais vu la question malgré l'application de la demande de la même tâche pour la majeure partie de la journée de travail. Il y avait aussi un utilisateur en particulier qui l'a eu beaucoup plus fréquemment que le reste d'entre eux.

Nous avons essayé les étapes habituelles:

(1) Devraient-ils commuter des ordinateurs avec un autre utilisateur qui n'a jamais eu le problème d'exclure l'ordinateur/la configuration. - Le problème les a suivis.

(2) Les avaient les avaient connecté à l'application et travailler comme un utilisateur qui n'a jamais vu le problème. - Le problème les a toujours suivis.

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

(4) Avait un développeur siége avec les utilisateurs et les regarder toute la journée. Ils ont vu les erreurs, mais ne les ont pas remarquées de faire quelque chose hors de l'ordinaire pour les causer.

Nous avons lutté avec cela pendant des semaines essayant de déterminer ce que les "utilisateurs d'erreurs" avaient en commun que les autres utilisateurs ne l'ont pas fait. Je n'ai aucune idée de la façon dont, mais le développeur à l'étape (4) avait un moment d'EUREKA sur le lecteur de travailler un jour digne d'une encyclopédie brune.

Il s'est rendu compte que tous les "utilisateurs d'erreur" étaient gauchistes et ont confirmé ce fait. Seules les utilisateurs gauchers ont obtenu les erreurs, jamais les droitiers. Mais comment pourrait être gaucher provoquer un bug?

Nous l'avons eu de vous asseoir et de regarder les gauchers à nouveau particulièrement accorder une attention particulière à tout ce qu'ils pourraient faire différemment, et c'est comme ça que nous l'avons trouvée.

Il s'est avéré que le bug n'est arrivé que si vous déplacez la souris sur la colonne la plus à droite des pixels dans la visionneuse d'images alors qu'elle chargait une nouvelle image (erreur de débordement, car le fournisseur avait un calcul de 1 éteint pour l'événement Mouseover).

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

L'un utilisateur qui a eu lieu pour obtenir l'erreur la plus fréquemment était l'un de ces types d'ajout de la souris de manière compulsive autour de sa souris autour de beaucoup de manière impatiente tout en attendant la charge de la page suivante, elle déplaçait ainsi la souris sur la droite beaucoup plus rapidement et frappait le chronométrage juste à droite, alors elle l'a fait lorsque l'événement de chargement s'est passé. Jusqu'à ce que nous obtenions une solution du vendeur, nous lui avons dit simplement de lâcher la souris après avoir cliqué sur (document suivant) et ne le touchez pas avant de charger.

Il était désormais connu dans la légende sur l'équipe de Dev comme "le bug gaucher"

70
JohnFx

Cela vient d'un long il y a longtemps (la fin des années 1980).

La société que j'ai travaille pour écrit a CAD (Fortran) qui a couru sur divers postes de travail UNIX (HP, Soleil, Silcon Graphics, etc.). Nous avons utilisé notre propre format de fichier pour stocker les données et Lorsque le paquet a été démarré, l'espace disque était rare, il y avait donc beaucoup de changements de bits utilisés pour stocker plusieurs drapeaux dans les en-têtes d'entité.

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

type = record[1] MOD 4096

Sur chaque machine sauf un, cela a donné ± 1 (pour une ligne), ± 2 (pour un arc), etc. et que nous pourrions alors vérifier le signe de vue si supprimé.

Sur une machine (HP, je pense), nous avons eu un problème étrange où le traitement des articles supprimés a été foutu.

C'était dans les jours précédant l'IDE et les débuggeurs visuels, je devais donc insérer des relevés de trace et votre journalisation pour essayer de suivre le problème.

J'ai finalement découvert que c'était parce que lorsque tous les autres fabricants mis en œuvre MOD afin que -4096 MOD 4096 a abouti à -1 HP la mise en œuvre mathématiquement correctement afin que -4096 MOD 4096 a abouti à -4097.

J'ai fini par suivre la base de code entière en sauvegarde le signe de la valeur et la rendant positive avant d'effectuer le MOD puis multiplier le résultat par la valeur du signe.

Cela a pris plusieurs jours.

11
ChrisF

Wow, bonne lecture ici!

Ma plus dure était d'années de retour quand Turbo Pascal était grande, bien que cela aurait pu être l'un des meilleurs idées C++ de cette époque. En tant que développeur unique (et troisième gars à cette start-up), j'avais écrit quelque chose comme un vendeur simplifié et respectueux de la vendeuse CAD. C'était formidable à l'époque, mais développé un crash aléatoire méchant. C'était Impossible de reproduire, mais est arrivé assez souvent que je me suis parti sur une chasse à un bug.

Ma meilleure stratégie était à une seule étape du débogueur. Le bogue ne s'est produit que lorsque l'utilisateur était entré suffisamment d'un dessin et devait peut-être être dans un certain mode ou un certain état d'état, il y avait donc beaucoup de paramètres fastidieux et de raccourcissement des points d'arrêt, fonctionnant normalement pendant une minute pour entrer dans un dessin, puis passer à travers une grande partie de code. Surtout utile, étaient des points d'arrêt qui sauteraient un nombre ajustable de fois puis casser. Cet exercice a dû être répété plusieurs fois.

Finalement, je l'ai réduit à un endroit où un sous-programme était appelé, étant donné une 2 mais de l'intérieur, il a vu un nombre de gissements gibbacs. J'aurais pu attraper cela plus tôt, mais je n'avais pas entré dans ce sous-programme, en supposant que cela a obtenu ce qu'il a été donné. Aveuglé en supposant que la plus simple des choses allait bien!

Il s'est avéré de bourrer un Int de 16 bits sur la pile, mais le sous-programme attend 32 bits. Ou quelque chose comme ça. Le compilateur ne couvrait pas automatiquement toutes les valeurs à 32 bits ou faire une vérification de type suffisante. C'était trivial de fixer, juste une partie d'une ligne, pratiquement aucune pensée requise. Mais pour y arriver a pris trois jours de chasse et interrogeant l'évidence.

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

Les leçons apprises: 1) Utilisez les meilleurs compilateurs, où "Best" est défini comme incluant la vérification de nombreux problèmes que l'informatique sait comment vérifier, et 2) questionner les choses évidentes simples, ou au moins vérifier leur bon fonctionnement.

Depuis lors, tous les bugs difficiles ont été vraiment difficiles, car je sais de vérifier les choses simples plus approfondies que nécessaire.

Leçon 2 s'applique également au bug électronique le plus dur que j'ai jamais corrigé, également avec un correctif trivial, mais plusieurs Smart EES avaient été soulevés pendant des mois. Mais ce n'est pas un forum électronique, alors je n'en dirai plus de cela.

7
DarenW

la condition de race de données de réseautage de l'enfer

J'écris un client/serveur de réseau (Windows XP/C #) pour travailler avec une application similaire sur un lieu de travail vraiment ancien (Encore 32/77) écrit par un autre développeur.

Ce que l'application a essentiellement été partageant/manipuler certaines données sur l'hôte pour contrôler le processus d'hôte exécutant le système avec notre UI à écran tactile multi-surveillant basé sur PC Fancy.

Cela a fait cela avec une structure de 3 couches. Le processus de communication lu/a écrit des données à/à partir de l'hôte, a fait toutes les conversions de format nécessaires (endansnité, format de point flottant, etc.) et écrivit/lisez les valeurs vers/depuis une base de données. La base de données a agi en tant qu'intermédiaire de données entre les comms et l'écran tactile. L'application Touchscreen UI a généré des interfaces à écran tactile basées sur le nombre de moniteurs attachés au PC (il a automatiquement détecté cela).

Dans le délai imparti à un paquet de valeurs entre l'hôte et notre PC ne pouvait envoyer que 128 valeurs Max sur le fil à la fois avec une latence maximale de ~ 110 ms par tour (UDP a été utilisée avec une connexion Ethernet X-sur-dessus de les ordinateurs). Ainsi, le nombre de variables autorisées sur la base du nombre variable d'écran tactiles attachées était sous contrôle strict. En outre, l'hôte (bien qu'avoir une architecture multi-processeur assez complexe avec un bus de mémoire partagé utilisé pour l'informatique en temps réel) avait environ 1/100e la puissance de traitement de mon téléphone portable afin qu'il ait été chargé de faire aussi peu de traitement que possible et son serveur/Le client devait être écrit en montage pour assurer 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 prenaient que la nouvelle valeur saisie, mais changez 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 ont déjà exposé le symptôme. Nous avons presque manqué la question complètement jusqu'à ce que nous ayons commencé à exécuter le processus d'acceptation de la clientèle initiale.


Pour annuler la question, j'ai choisi une des valeurs oscillantes:

  • J'ai vérifié l'application tactile, c'était oscillant
  • J'ai vérifié la base de données, oscillant
  • J'ai vérifié l'application CommS, oscillant

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

  • Pas oscillant mais les paquets n'avaient pas l'air de droit, il y avait trop de données.

Je suis passé à travers chaque détail du code COMMS cent fois ne trouvant aucun défaut/erreur.

Enfin, j'ai commencé à tirer des courriels à l'autre devir Demandant en détail comment sa fin a travaillé pour voir s'il y avait quelque chose qui me manquait. Alors je l'ai trouvé.

Apparemment, lorsqu'il a envoyé des données, il n'a pas afflué la gamme de données avant la transmission, il suffisait essentiellement de remplacer le dernier tampon utilisé avec les nouvelles valeurs écrasant l'ancienne, mais les anciennes valeurs non écrasées sont toujours transmises.

Donc, si une valeur était à la position 80 de la matrice de données et que la liste des valeurs demandées a été modifiée à moins de 80 mais cette 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 en cours de lecture de la base de données dépendait de la tranche de temps de lorsque l'interface utilisateur demandait la valeur.


Le correctif était douloureusement simple. Lisez dans le nombre d'éléments entrant sur le tampon de données (il était en fait contenue dans le cadre du protocole de paquets) et ne lisez pas le tampon au-delà de ce nombre d'éléments.


Leçons apprises:

  • Ne prenez pas la puissance de calcul moderne pour acquis. Il y avait un moment où les ordinateurs ne prennent pas en charge Ethernet et lorsqu'ils rinçaient un tableau pourraient être considérés comme coûteux. Si vous voulez vraiment voir jusqu'où nous sommes venus, imaginez un système qui n'a pratiquement aucune forme d'allocation de mémoire dynamique. C'est-à-dire que le processus exécutif a dû pré-allouer toute la mémoire de tous les programmes dans l'ordre et aucun programme ne pourrait augmenter au-delà de cette frontière. IE, allouant plus de mémoire à un programme sans recompiler tout le système pourrait causer un crash massif. Je me demande si les gens vont parler des journées de collecte pré-ordures dans la même lumière un jour.

  • Lorsque vous effectuez la mise en réseau avec des protocoles personnalisés (ou la manipulation de la représentation des données binaires en général) assurez-vous de lire les spécifications jusqu'à ce que vous comprenez chaque fonction de chaque valeur envoyée sur le tuyau. Je veux dire, lisez-le jusqu'à ce que vos yeux soient blessés. Les personnes gèrent les données en manipulant des bits individuels ou des octets de manière très intelligente et efficace de faire des choses. Manquer les détails les plus petits pourrait casser le système.

Le temps général de réparer était de 2-3 jours avec la majeure partie de ce temps passé à travailler sur d'autres choses lorsque je suis arrivé à ce sujet.

Sidenote: L'ordinateur hôte en question n'a pas pris en charge Ethernet par défaut. La carte à conduire a été faite et installée sur mesure et la pile de protocole n'existait pratiquement pas. Le développeur que j'avais travaillé était un enfer d'un programmeur, il a non seulement mis en place une version dépouillée de UDP et une pile de fausse paqutes mimimal (le processeur n'était pas suffisamment puissant pour gérer une pile Ethernet complète) sur le système de ce projet. Mais il l'a fait en moins d'une semaine. Il avait également été l'un des dirigeants de l'équipe de projet d'origine qui avait conçu et programmé le système d'exploitation en premier lieu. Disons simplement que tout ce qu'il ait eu à partager sur des ordinateurs/la programmation/l'architecture, quelle que soit la longue obstacle ou combien je suis déjà nouveau, j'écouterais chaque mot. Il n'y a rien de plus utile que de travailler avec de bonnes personnes qui ont une véritable passion pour ce qu'ils font.

6
Evan Plaice

L'arrière-plan

  • Dans une application critique de la MISSIture de la WCF entraînant un site Web et fournissant une transformation trasactionnelle backend.
  • Application volumique importante (centaines d'appels par seconde)
  • Plusieurs instances de serveur multiple
  • des centaines de tests d'unité passés et d'innombrables attaques de QA

L'insecte

  • Lorsqu'il est déplacé vers la production, le serveur fonctionnerait bien pour une durée aléatoire pour une durée aléatoire, commencez à dégrader rapidement et à prendre la CPU de la boîte à 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 exploitation forestière élaborée. Les performances vérifiées sur chaque appel ont parlé à la base de données des personnes sur l'utilisation surveillée les serveurs pour des problèmes. 1 semaine

Ensuite, j'étais sûr que j'avais eu un problème de discours sur le fil. J'ai vérifié mes impacts tentés de créer la situation Créer des outils pour tenter de créer la situation de débogage. Avec la frustration de la gestion croissante, j'ai tourné vers mes pairs à quel point les choses suggéraient de redémarrer le projet à partir de zéro pour limiter le serveur à un fil. 1,5 semaines

Ensuite, j'ai regardé - Tess Ferrandez Blog a créé un fichier de vidage utilisateur et l'a annalisé avec Windebug la prochaine fois que le serveur a pris une décharge. J'ai constaté que tous mes threads étaient bloqués int La fonction Dictionary.Add.

Le long de la petite dictionnaire courte qui vient de suivre la trace dont le journal pour écrire des erreurs de threads X n'est pas synchronisé.

5
rerun

Nous avions une application qui parlait à un périphérique matériel que, dans certains cas, ne manquerait pas de fonctionner correctement si l'appareil a été physiquement débranché jusqu'à ce qu'il ait été branché et réinitialisé à deux reprises.

Le problème s'est avéré être qu'une application exécutée au démarrage était de temps en temps SEGFaulting lorsqu'il tentait de lire à partir d'un système de fichiers qui n'avait pas encore été monté (par exemple, si un utilisateur l'a configuré à lire à partir d'un volume NFS). Au démarrage, l'application enverrait des ioctls au pilote pour initialiser le périphérique, puis lire les paramètres de configuration et envoyer plus d'ioctls pour mettre le périphérique dans l'état correct.

Un bogue dans le pilote faisait une valeur non valide à écrire sur l'appareil lorsque l'appel d'initialisation a été effectué, mais la valeur a été écrasée avec des données valides une fois que les appels ont été apportés pour mettre l'appareil dans un état spécifique.

L'appareil lui-même avait une batterie et détecterait s'il perdrait la puissance de la carte mère et écrirait un drapeau dans une mémoire volatile indiquant qu'il avait perdu la puissance, il entrerait ensuite un état spécifique la prochaine fois qu'il a été alimenté et un l'instruction nécessaire pour être envoyée pour effacer le drapeau.

Le problème était que si la puissance a été supprimée une fois que les IOCTL ont été envoyés pour initialiser le périphérique (et écrivit la valeur non valide sur le périphérique) mais avant que des données valides puissent être envoyées. Lorsque l'appareil a été alimenté, le drapeau aurait été défini et tente de lire les données non valides envoyées du pilote en raison de l'initalisation incomplète. Cela mettrait le dispositif dans un état invalide où le drapeau à powered avait été nettoyé, mais l'appareil ne recevrait pas d'instructions supplémentaires tant qu'il n'a pas été réinitialisé par le conducteur. La deuxième réinitialisation signifie que l'appareil n'essayait pas de lire les données non valides qui l'avaient été stockées et recevraient des instructions de configuration correctes, ce qui lui permettrait d'être placé dans l'état correct (en supposant que l'application envoie les IOCTLS n'a pas SEGFAULT ).

En fin de compte, il a fallu environ deux semaines pour comprendre l'ensemble exact de circonstances qui causaient le problème.

3
Cercerilla

Programme principal de cadre cessé de fonctionner sans raison

Je viens de poster ceci à une autre question . voir message ici

C'est arrivé parce qu'ils ont installé une version plus récente du compilateur sur le cadre principal.

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

[.____] J'ai cruré cette application principale. Un jour, hors du bleu clair, il a cessé de fonctionner. C'est tout ... coiffé juste arrêté.

Mon travail consistait à le faire fonctionner aussi vite que possible. Le code source n'avait pas été modifié pendant deux ans, mais tout cela soudain il vient d'arrêter. J'ai essayé de compiler le code et j'ai cassé la ligne XX. J'ai regardé la ligne XX et je ne pouvais pas dire ce qui rendrait la pause de la ligne XX. J'ai demandé les spécifications détaillées pour cette application et il n'y en avait pas. La ligne XX n'était pas le coupable.

J'ai imprimé le code et j'ai commencé à y examiner du haut en bas. J'ai commencé à créer un organigramme de ce qui se passait. Le code était tellement convolué que je pouvais difficilement le donner un sens. J'ai abandonné en essayant de l'organiser. J'avais peur d'apporter des changements sans savoir comment ce changement apporterait le reste du processus, d'autant plus que je n'avais pas de détails sur ce que l'application a fait.

Ainsi, j'ai décidé de commencer en haut du code source et d'ajouter des freins blancheurs et de ligne pour rendre le code plus lisible. J'ai remarqué, dans certains cas, il y avait si des conditions combinées et ou non et que cela ne distinguait pas clairement quelles données ont été andré et quelles données ont été orédérales. J'ai donc commencé à mettre des parenthèses autour des et et OR Conditions pour les rendre plus lisibles.

Alors que je déménage lentement le nettoyant, je sauverais périodiquement mon travail. À un moment donné, j'ai essayé de compiler le code et d'une chose étrange devenue. L'erreur avait sauté a passé la ligne de code d'origine et était maintenant plus bas. Donc, j'ai continué, dépassant et OR Conditions avec des parens. Quand j'ai fini de le nettoyer, cela fonctionnait. Allez.

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

Il s'avère que l'ancien compilateur a évalué l'expression de gauche à droite, peu importe. La nouvelle version du compilateur a également évalué les expressions de gauche à droite mais ambiguïvement, ce qui signifie que la combinaison et des ours peu clairs n'a pas pu être résolue.

La leçon que j'ai apprise de cela ... Toujours, toujours, toujours, utilisez toujours des parens à séparer et à des conditions et OR conditions lorsqu'ils sont utilisés en conjonces les uns avec les autres.

Je devais résoudre des trucs de concurrence déroutant le dernier Semeur, mais le bug qui se distingue toujours le plus pour moi était dans un jeu basé sur le texte que j'écris dans l'assemblée PDP-11 pour une mission des devoirs. Il était basé sur le jeu de la vie de Conway et pour une grande raison, une grande partie des informations à côté de la grille était constamment écrasée avec des informations qui n'auraient pas dû être présentes. La logique était également assez simple, donc c'était très déroutant. Après avoir passé un tas de fois pour redécouvrir que toute la logique est correcte, j'ai soudainement remarqué quel était le problème. Cette chose: .

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

Il me distingue toujours parce que la quantité de dommages causés à une telle raffinement de 4 pixels a été causé. Alors, quelle est la conclusion? Ne codez pas dans l'assemblée PDP-11.

2
EpsilonVector

Je suis toujours sur ma chasse aux bugs la plus difficile. C'est l'un de ces parfois son là et parfois ce n'est pas des bugs. C'est pourquoi je suis ici, à 6h10 le lendemain.

arrière-plan :

  • Contexte: langue, application, environnement, etc.
    • PHP OS Commerce
  • Comment le bogue a-t-il été identifié?
    • Les ordres aléatoires qui travaillent en partie les problèmes d'échec et de redirection de manière aléatoire
  • Qui ou quoi identifié le bogue?
    • Client, et la question de redirection était évidente
  • Comment complexité reproduisait le bogue?
    • Je n'ai pas pu reproduire, mais le client a pu.

la chasse.

  • Quel était votre plan?
    • Ajouter le code de débogage, la commande de remplissage, analysez les données, répétez
  • Quelles difficultés avez-vous rencontrées?
    • Manque de problèmes répétables et de code horrible
  • Comment le code fautif a finalement été trouvé?
    • beaucoup de code fautif a été trouvé .. tout simplement pas ce dont j'avais besoin.

le meurtre.

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

postmortem.

  • Quelle était la cause première techniquement? dépassement tampon, etc.
    • mauvaise pratique de codage
  • Quelle était la cause première de 30 000 pi?
    • Je préfère ne pas dire...
  • Combien de temps le processus a-t-il finalement pris?
    • éternité et un jour
  • Y a-t-il eu des fonctions défeces négativement par le correctif?
    • caractéristique? Ou est-ce un bug?
  • Quelles méthodes, outils, motivations avez-vous trouvé particulièrement utiles? ... horriblement inutile?
  • Si vous pouviez tout faire à nouveau? ............
    • ctrl + a del
2
WalterJ89

Pour un projet universitaire, nous écrivions un système de noeuds P2P distribué qui partagent des fichiers, cette multidiffusion prise en charge pour se détecter, plusieurs anneaux de nœuds et un serveur de noms de noms de noms de noms à un client.

Écrit en C++, nous avons utilisé [~ # ~ # ~] POCO [~ # ~] Pour cela, car il permet une belle programmation IO, prise et filtration.


Il y avait deux bugs qui se produisaient qui nous ennuyaient et nous ont fait perdre beaucoup de temps, une très logique:

au hasard, un ordinateur partageait son IP localhost au lieu de sa première adresse IP.

Cela a amené les clients à se connecter au noeud sur le même PC ou les mêmes nœuds pour se connecter à eux-mêmes.

Comment avons-nous identifié cela? Lorsque nous avons amélioré la sortie dans le serveur de noms que nous avons découvert ultérieurement lorsque nous avons redémarré les ordinateurs que notre script pour déterminer l'IP à donner était faux. Au hasard, le périphérique LO a été répertorié au lieu du périphérique ETH0 ... vraiment stupide. Alors maintenant, nous avons droit à la demande de l'ETH0, car cela est partagé parmi tous les ordinateurs universitaires ...


Et maintenant un plus ennuyeux:

au hasard, le flux de paquets pause aléatoire.
[.____] Lorsque le prochain client se connecte, il continuerait ...

Cela s'est produit vraiment aléatoire et que plus d'un ordinateur est impliqué, il est plus gênant de déboguer ce problème, les ordinateurs universitaires ne nous permettent pas d'exécuter WireShark sur ceux qui ne nous laissent donc pas à deviner si le problème était à l'envoi du côté de l'envoi ou de la réception. côté.

Avec beaucoup de production dans le code, nous venons de prendre l'hypothèse que l'envoi des commandes va bien,
[.____] Cela nous a laissé se demander où le vrai problème était ... il semblait que la façon dont Poco sondages a mal et que nous devrions plutôt vérifier les caractères disponibles sur la prise entrante.

Nous avons pris l'hypothèse que cela a fonctionné comme des tests plus simples dans un prototype impliquant moins de paquets n'a pas causé cette question. Cela nous a donc amené à supposer que la déclaration du sondage fonctionnait mais ... ce n'était pas. :-(


Leçons apprises:

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

  • Les cadres ne font pas toujours leur travail (la mise en œuvre ou la documentation).

  • Fournissez suffisamment de sortie dans le code, sinon autorisé, il est certain de vous connecter à des détails étendus à un fichier.

  • Lorsque le code n'a pas été testé unitaire (car il est trop difficile) ne suppose pas que les choses fonctionnent.

2
Tamara Wijsman

C'est juste un bug très simple que je me suis transformé en un cauchemar pour moi.

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

BOGUE: Au lieu de faire deux commutateurs de threads à UserMode, il serait faute de protection générale.

La chasse au bogue: J'ai passé probablement une semaine ou deux tentatives de résolution de ce problème. Insérer des relevés de trace partout. Examiner le code de montage généré (de GCC). Impression de chaque valeur que je pourrais.

Le problème: quelque part tôt dans la chasse au bogue, j'avais placé une instruction hlt dans la CRT0. Le CRT0 est fondamentalement ce que Bootstraps un programme d'utilisateurs à utiliser dans un système d'exploitation. Cette instruction hlt provoque une GPF lorsqu'elle est exécutée à partir du mode utilisateur. Je l'ai placé là-bas et j'ai essentiellement oublié à ce sujet. (À l'origine, le problème était une erreur de débordement tampon ou d'allocation de mémoire)

Le correctif: retirez l'instruction hlt :) Après le retrait, tout a fonctionné.

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

1
Earlz

arrière-plan :

  • Contexte: Web Server (C++) qui permet aux clients de s'enregistrer eux-mêmes
  • Bug: lors de la demande de la page, cela ne répondrait tout simplement pas, toute la ferme qui est, et les processus seraient tués (et relancés) parce qu'ils ont pris trop de temps (seulement quelques secondes sont autorisés) à servir la page
  • Certains utilisateurs se sont plaints, mais il était extrêmement sporadique si inaperçu (les gens ont-ils tendance à frapper "rafraîchir" lorsqu'une page n'est pas servie). Nous avons remarqué cependant les décharges de base;)
  • Nous n'avons réellement jamais réussi à reproduire dans nos environnements locaux, le bogue est apparu à quelques reprises dans les systèmes de test mais ne s'est jamais montré lors des tests de performance?

la chasse.

  • Plan: Eh bien, puisque nous avons eu des vidanges de mémoire et des journaux, nous voulions les analyser. Étant donné que cela affectait toute la ferme et que nous avions eu des problèmes de données dans le passé, nous avons soupçonné la base de données (DB unique pour plusieurs serveurs)
  • Difficultés: Un dépotoir de serveur complet est énorme, et ils sont donc effacés assez souvent (ne pas manquer d'espace), nous devions donc être rapides à saisir un quand il se produisait ... nous avons persisté. Les décharges ont montré diverses piles (jamais de choses à dB, tant pour cela), il a échoué tout en préparant 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 prendrait parfois longtemps, même Bien que ce soit juste un moteur de base de base avec des données pré-calculées (MVC traditionnel)
  • Se rendre à d'autres échantillons et certains pensant que nous avons compris que le temps a été pris en lecture des données du disque dur (le modèle de page). Comme il s'agissait de toute la ferme, nous avons d'abord recherché des emplois programmés (crontab, lots), mais les timings ne sont jamais assortis d'un événement à un autre ... Cela m'a finalement eu pour moi que cela se soit toujours passé quelques jours Avant L'activation d'une nouvelle version du logiciel et j'ai eu un ahah ! moment ... C'était causé par la distribution du logiciel! Livrer plusieurs centaines de mégaoctets (compressé) peut mettre un peu de bosses sur les performances du disque:/bien sûr, la distribution est automatisée et l'archive poussée à tous les serveurs à la fois (multidiffusion).

le meurtre.

  • Correction de la complexité: passer aux modèles compilés
  • Code concerné: Aucun, un simple changement dans le processus de construction

postmortem.

  • Cause principale: problème opérationnel ou manque de planification directe :)
  • Timescale: Il a fallu des mois pour suivre, une question de jours pour fixer et tester, quelques semaines pour les tests d'assurance qualité et de performance et de déploiement - pas pressé là-bas, car nous savions que le déploiement du correctif déclencherait le bogue ... et rien sinon ... un peu pervers vraiment!
  • Effets secondaires indésirables: impossibilité de commuter des modèles au moment de l'exécution maintenant qu'ils sont cuits dans le code livré, nous n'avons pas beaucoup utilisé la fonctionnalité, car elles changent généralement que des modèles de commutation signifie que vous avez plus de données à verser. L'utilisation de CSS est principalement suffisante pour "petit" modification de la mise en page.
  • Méthodes, Outils: gdb + Surveillance! Il suffit de nous prendre le temps de suspecter le disque, puis d'identifier la cause des pics d'activité sur le graphique de surveillance ...
  • La prochaine fois: Traitez tout IO comme défavorable!
1
Matthieu M.

Le plus difficile que l'on ne soit jamais tué car il ne pouvait jamais être reproduit autre que dans l'environnement de production complet avec l'usine opérationnelle.

Le plus fou que j'ai tué:

Les dessins sont imprimés Gibberish!

Je regarde le code et je ne vois rien. Je tire un emploi de la file d'attente de l'imprimante et l'examine, ça a l'air bien. (C'était dans l'ère Dos, PCL5 avec HPGL/2 intégré - en fait, très bon pour les dessins de traçage et aucune maux de tête de construction d'une image raster dans une mémoire limitée.) Je le dirige vers une autre imprimante qui devrait le comprendre, elle imprime bien .

Rouler le code, le problème est toujours là.

Enfin, je fais manuellement un fichier simple et l'envoyer à l'imprimante - Gibberish. Il s'avère que ce n'était pas mon insecte mais l'imprimante elle-même. La société de maintenance l'avait flashé à la dernière version lorsqu'elle corrigeait autre chose et que la dernière version avait un bogue. Obtenez-les pour comprendre qu'ils avaient pris des fonctionnalités critiques et ont dû la classer vers une version antérieure était plus difficile que de trouver le bogue lui-même.

Celui qui était encore plus vexant mais depuis que c'était seulement sur ma boîte, je ne voudrais pas mettre en premier lieu:

Borland Pascal, code DPMI pour traiter certaines API non supportées. Exécutez-le, de temps en temps, cela a fonctionné, il est généralement allé en train d'essayer de traiter un pointeur non valide. Cela n'a jamais produit un mauvais résultat, comme vous l'attendez de piétinez sur un pointeur.

Débogage - Si je suis unique à travers le code, cela fonctionnerait toujours correctement, sinon elle é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 la bibliothèque de Borland avait un bogue majeur: les pointeurs de mode réels étaient stockés dans des variables de pointeur en mode protégé. Le problème est que la plupart des pointeurs de mode réels 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'enregistré.

2) Le débogueur ne dirait jamais rien à propos d'une telle charge non valide en mode mono-marche. Je ne sais pas ce qu'il a fait en interne, mais ce qui a été présenté à l'utilisateur avait l'air complètement correct. Je soupçonne que cela n'exécutait pas l'instruction, mais la simulant à la place.

1
Loren Pechtel