Qual è stata la tua caccia ai bug più difficile e come l'hai trovata e uccisa?


31

Questa è una domanda "Condividi la conoscenza". Sono interessato a imparare dai tuoi successi e / o fallimenti.

Informazioni che potrebbero essere utili ...

Sfondo:

  • Contesto: lingua, applicazione, ambiente, ecc.
  • Come è stato identificato il bug?
  • Chi o cosa ha identificato il bug?
  • Quanto è stata complessa la riproduzione del bug?

La caccia

  • Qual era il tuo piano?
  • Quali difficoltà hai incontrato?
  • Come è stato finalmente trovato il codice offensivo?

L'uccisione.

  • Quanto è stata complessa la correzione?
  • Come hai determinato l'ambito della correzione?
  • Quanto codice è stato coinvolto nella correzione?

Postmortem.

  • Qual è stata la causa principale tecnicamente? sovraccarico del buffer, ecc.
  • Qual è stata la causa principale da 30.000 piedi?
  • Quanto tempo ha richiesto il processo?
  • Ci sono state delle funzionalità influenzate negativamente dalla correzione?
  • Quali metodi, strumenti, motivazioni hai trovato particolarmente utili? ... orribilmente inutile?
  • Se potessi rifarlo di nuovo? ............

Questi esempi sono generali, non applicabili in ogni situazione e forse inutili. Si prega di condire secondo necessità.

Risposte:


71

In realtà era in un sottocomponente visualizzatore di immagini di terze parti della nostra applicazione.

Abbiamo scoperto che c'erano 2-3 degli utenti della nostra applicazione che avrebbero spesso il componente visualizzatore di immagini generato un'eccezione e sarebbero morti orribilmente. Tuttavia, abbiamo avuto dozzine di altri utenti che non hanno mai visto il problema nonostante avessero utilizzato l'applicazione per lo stesso compito per gran parte della giornata lavorativa. Inoltre c'era un utente in particolare che lo riceveva molto più frequentemente rispetto agli altri.

Abbiamo provato i soliti passaggi:

(1) Gli hanno fatto cambiare computer con un altro utente che non ha mai avuto il problema di escludere il computer / la configurazione. - Il problema li ha seguiti.

(2) Li hanno fatti accedere all'applicazione e funzionare come un utente che non ha mai visto il problema. - Il problema ANCORA li ha seguiti.

(3) L'utente ha segnalato quale immagine stava visualizzando e ha impostato un cablaggio di prova per ripetere la visualizzazione di quell'immagine migliaia di volte in rapida successione. Il problema non si presentava nell'imbracatura.

(4) Uno sviluppatore si è seduto con gli utenti e li ha guardati tutto il giorno. Hanno visto gli errori, ma non si sono accorti che facevano qualcosa di straordinario per causarli.

Abbiamo lottato con questo per settimane cercando di capire cosa gli "Utenti errore" avevano in comune con gli altri utenti. Non ho idea di come, ma lo sviluppatore nel passaggio (4) ha avuto un momento eureka sul disco per lavorare un giorno degno dell'Enciclopedia Brown.

Si rese conto che tutti gli "Utenti dell'errore" erano mancini e lo confermò. Solo gli utenti mancini hanno ricevuto gli errori, mai i Giusti. Ma come può essere lasciato mancino a causare un bug?

Lo abbiamo fatto sedere e guardare di nuovo i mancini, prestando particolare attenzione a tutto ciò che avrebbero potuto fare diversamente, ed è così che l'abbiamo trovato.

Si è scoperto che il bug si verificava solo se si spostava il mouse sulla colonna di pixel più a destra nel visualizzatore di immagini durante il caricamento di una nuova immagine (errore di overflow perché il fornitore aveva un calcolo una tantum per l'evento del passaggio del mouse).

Apparentemente, in attesa del caricamento dell'immagine successiva, tutti gli utenti hanno naturalmente mosso la mano (e quindi il mouse) verso la tastiera.

L'unico utente che ha riscontrato l'errore più frequentemente è stato uno di quei tipi ADD che hanno spostato compulsivamente il mouse molto impazientemente mentre aspettava il caricamento della pagina successiva, quindi spostava il mouse a destra molto più rapidamente e colpiva il il tempismo giusto, quindi l'ha fatto quando si è verificato l'evento load. Fino a quando non abbiamo ricevuto una correzione dal fornitore, le abbiamo detto di lasciar andare il mouse dopo aver fatto clic (documento successivo) e di non toccarlo fino a quando non è stato caricato.

Da allora era noto nella leggenda del team di sviluppo come "The Left Handed Bug"


14
Questa è la cosa più cattiva di cui abbia mai sentito parlare.
Nathan Taylor,

9
Ha però reso un eroe il ragazzo che l'ha risolto.
JohnFx,

2
Caspita, ora che diamine è un insetto!
Mitchel Sellers,

3
Grande scoperta! Bella storia.
Toon Krijthe,

11
Come se noi mancini non fossimo già trattati abbastanza come cittadini di seconda classe. Ora dobbiamo anche essere sellati da qualcosa di più della nostra giusta dose di bug del software ... cavolo, grazie! : p
Dan Molding,

11

Questo è molto tempo fa (fine anni '80).

La società per cui ho lavorato ha scritto un pacchetto CAD (in FORTRAN) che girava su varie workstation Unix (HP, Sun, Silcon Graphics ecc.). Abbiamo usato il nostro formato di file per archiviare i dati e quando il pacchetto è stato avviato lo spazio su disco era scarso, quindi è stato utilizzato molto spostamento dei bit per memorizzare più flag nelle intestazioni delle entità.

Il tipo di entità (linea, arco, testo ecc.) È stato moltiplicato per 4096 (credo) quando memorizzato. Inoltre, questo valore è stato negato per indicare un elemento eliminato. Quindi per ottenere il tipo avevamo il codice che faceva:

type = record[1] MOD 4096

Su ogni macchina tranne una, questo ha dato ± 1 (per una linea), ± 2 (per un arco) ecc. E potremmo quindi controllare il segno per vedere se è stato cancellato.

Su una macchina (credo HP) abbiamo avuto uno strano problema in cui la gestione degli elementi eliminati è stata rovinata.

Questo avveniva nei giorni precedenti agli IDE e ai debugger visivi, quindi ho dovuto inserire istruzioni di tracciamento e registrazione per provare a rintracciare il problema.

Alla fine ho scoperto che era perché mentre ogni altro produttore implementata MODin modo tale che -4096 MOD 4096ha portato -1HP implementato matematicamente correttamente in modo che -4096 MOD 4096ha provocato -4097.

Ho finito per dover passare attraverso l'intera base di codice salvando il segno del valore e rendendolo positivo prima di eseguire il MODe quindi moltiplicare il risultato per il valore del segno.

Ci sono voluti diversi giorni.


3
Probabilmente ci sono state più cacce agli insetti più difficili nel corso degli anni, ma questa è rimasta impressa nella mia mente per oltre 20 anni!
ChrisF

7

Wow, buona lettura qui!

Il mio anno più difficile è stato quando Turbo Pascal era grande, anche se avrebbe potuto essere uno dei primi IDE C ++ di quel tempo. Come unico sviluppatore (e terzo ragazzo in questa startup) avevo scritto qualcosa come un programma CAD semplificato per i venditori. All'epoca era eccezionale, ma sviluppò un brutto incidente casuale. È stato impossibile riprodurlo, ma è accaduto abbastanza frequentemente da iniziare una caccia agli insetti.

La mia migliore strategia è stata quella di eseguire un solo passaggio nel debugger. Il bug si verificava solo quando l'utente aveva inserito abbastanza di un disegno e forse doveva essere in una certa modalità o stato di zoom, quindi c'erano molte impostazioni noiose e la cancellazione dei punti di interruzione, che funzionavano normalmente per un minuto per entrare in un disegno, e poi passare attraverso un grosso pezzo di codice. Particolarmente utili erano i punti di interruzione che saltavano un numero regolabile di volte e poi si rompevano. L'intero esercizio ha dovuto essere ripetuto più volte.

Alla fine l'ho ridotto a un punto in cui veniva chiamata una subroutine, ricevendo un 2 ma dall'interno vedevo un numero incomprensibile. Avrei potuto prenderlo in precedenza, ma non ero entrato in questa subroutine, supponendo che avesse ottenuto ciò che gli era stato dato. Accecati dal presupposto che le cose più semplici fossero a posto!

Si è rivelato essere uno stacking a 16 bit nello stack, ma la subroutine si aspettava a 32 bit. O qualcosa di simile. Il compilatore non ha automaticamente pad tutto il valore a 32 bit, né ha eseguito un controllo del tipo sufficiente. Era banale da risolvere, solo una parte di una riga, quasi nessun pensiero richiesto. Ma per arrivarci ci sono voluti tre giorni di caccia e di domande sull'ovvio.

Quindi ho esperienza personale con quell'aneddoto sul costoso consulente che arriva, dopo un po 'fa un tocco da qualche parte e fa pagare $ 2000. I dirigenti richiedono una ripartizione, ed è $ 1 per il rubinetto, $ 1999 per sapere dove toccare. Tranne nel mio caso, non è stato tempo, non denaro.

Lezioni apprese: 1) utilizzare i migliori compilatori, in cui si definisce "migliore" includendo il controllo di tutti i problemi che l'informatica sa come verificare e 2) mettere in discussione le semplici cose ovvie o almeno verificarne il corretto funzionamento.

Da allora tutti i bug difficili sono stati davvero difficili, poiché so controllare le cose semplici più accuratamente di quanto sembri necessario.

La lezione 2 si applica anche al bug dell'elettronica più difficile che abbia mai risolto, anche con una correzione banale, ma diversi EE intelligenti erano stati bloccati per mesi. Ma questo non è un forum di elettronica, quindi non dirò altro.


Si prega di inviare il bug dell'elettronica altrove e un link qui!
tgkprog,

6

Le condizioni di gara dei dati di rete dall'inferno

Stavo scrivendo un client / server di rete (Windows XP / C #) per lavorare con un'applicazione simile su una workstation davvero vecchia (Encore 32/77) scritta da un altro sviluppatore.

Ciò che l'applicazione ha fatto essenzialmente è stato condividere / manipolare determinati dati sull'host per controllare il processo dell'host che esegue il sistema con la nostra interfaccia utente touchscreen multi-monitor basata su PC.

Lo ha fatto con una struttura a 3 strati. Il processo di comunicazione ha letto / scritto i dati da / verso l'host, ha effettuato tutte le conversioni di formato necessarie (endianness, formato a virgola mobile, ecc.) E ha scritto / letto i valori su / da un database. Il database ha funzionato da intermediario dei dati tra le interfacce di comunicazione e touchscreen. L'app UI del touchscreen ha generato interfacce touchscreen in base al numero di monitor collegati al PC (lo ha rilevato automaticamente).

Nell'intervallo di tempo dato un pacchetto di valori tra l'host e il nostro PC è stato in grado di inviare solo 128 valori max attraverso il cavo alla volta con una latenza massima di ~ 110 ms per round trip (UDP è stato utilizzato con una connessione ethernet diretta x-over tra i computer). Pertanto, il numero di variabili consentite in base al numero variabile di touchscreen collegati era sotto stretto controllo. Inoltre, l'host (sebbene abbia un'architettura multiprocessore piuttosto complessa con bus di memoria condiviso utilizzato per il calcolo in tempo reale) aveva circa 1/100 della potenza di elaborazione del mio telefono cellulare, quindi aveva il compito di eseguire il minor numero di elaborazioni possibili e il suo server / client ha dovuto essere scritto in assembly per garantire ciò (l'host stava eseguendo una simulazione in tempo reale completa che non poteva essere influenzata dal nostro programma).

Il problema era. Alcuni valori, se modificati sul touchscreen, non assumono solo il valore appena immesso, ma passano in modo casuale tra quel valore e il valore precedente. Questo e solo su alcuni valori specifici su alcune pagine specifiche con una certa combinazione di pagine ha mai mostrato il sintomo. Abbiamo quasi perso completamente il problema fino a quando non abbiamo iniziato a eseguirlo attraverso il processo di accettazione iniziale del cliente


Per fissare il problema ho scelto uno dei valori oscillanti:

  • Ho controllato l'app Touchscreen, stava oscillando
  • Ho controllato il database, oscillando
  • Ho controllato l'app delle comunicazioni, oscillando

Poi ho fatto scattare il wirehark e ho iniziato a decodificare manualmente le acquisizioni di pacchetti. Risultato:

  • Non oscillante ma i pacchetti non sembravano corretti, c'erano troppi dati.

Ho esaminato centinaia di volte ogni dettaglio del codice delle comunicazioni senza trovare difetti / errori.

Alla fine ho iniziato a inviare email agli altri sviluppatori chiedendo in dettaglio come funzionava la sua fine per vedere se mancava qualcosa. Poi l'ho trovato.

Apparentemente, quando ha inviato i dati non ha svuotato l'array di dati prima della trasmissione, quindi, in sostanza, stava semplicemente sovrascrivendo l'ultimo buffer utilizzato con i nuovi valori sovrascrivendo il vecchio, ma i vecchi valori non sovrascritti venivano ancora trasmessi.

Quindi, se un valore fosse nella posizione 80 dell'array di dati e l'elenco dei valori richiesti fosse cambiato in meno di 80 ma lo stesso valore fosse contenuto nel nuovo elenco, entrambi i valori esisterebbero nel buffer di dati per quel buffer specifico in qualsiasi tempo a disposizione.

Il valore letto dal database dipendeva dalla fascia oraria in cui l'interfaccia utente richiedeva il valore.


La correzione era dolorosamente semplice. Leggere il numero di elementi in entrata nel buffer dei dati (in realtà era contenuto come parte del protocollo del pacchetto) e non leggere il buffer oltre quel numero di elementi.


Lezioni imparate:

  • Non dare per scontata la moderna potenza di calcolo. C'è stato un tempo in cui i computer non supportavano Ethernet e quando si scaricava un array poteva essere considerato costoso. Se vuoi davvero vedere fino a che punto siamo arrivati, immagina un sistema che non ha praticamente alcuna forma di allocazione dinamica della memoria. IE, il processo esecutivo ha dovuto pre-allocare tutta la memoria per tutti i programmi in ordine e nessun programma potrebbe crescere oltre quel limite. Ad esempio, l'allocazione di più memoria a un programma senza ricompilare l'intero sistema potrebbe causare un arresto anomalo. Mi chiedo se un giorno le persone parleranno dei giorni della raccolta dei rifiuti nella stessa luce.

  • Quando si esegue il collegamento in rete con protocolli personalizzati (o si gestisce la rappresentazione di dati binari in generale), assicurarsi di leggere le specifiche fino a quando non si comprende ogni funzione di ogni valore inviato attraverso la pipe. Voglio dire, leggilo fino a quando i tuoi occhi fanno male. Le persone gestiscono i dati manipolando singoli bit o byte hanno modi molto intelligenti ed efficienti di fare le cose. Manca il più piccolo dettaglio potrebbe rompere il sistema.

Il tempo complessivo di riparazione è stato di 2-3 giorni, con la maggior parte del tempo trascorso a lavorare su altre cose quando sono stato frustrato da questo.

Nota a margine: il computer host in questione non supportava Ethernet per impostazione predefinita. La scheda per guidarla è stata realizzata su misura e adattata e lo stack di protocollo praticamente non esisteva. Lo sviluppatore con cui stavo lavorando era un programmatore infernale, non solo implementava una versione ridotta di UDP e uno stack ethernet falso (il processore non era abbastanza potente da gestire uno stack ethernet completo) sul sistema per questo progetto ma lo ha fatto in meno di una settimana. Era stato anche uno dei leader del team di progetto originale che aveva progettato e programmato il sistema operativo in primo luogo. Diciamo solo, qualsiasi cosa abbia mai avuto da condividere su computer / programmazione / architettura, non importa quanto a lungo o quanto già nuovo, ascolterei ogni parola.


5

Lo sfondo

  • In un'applicazione WCF mission-critical che guida un sito Web e fornisce l'elaborazione transazionale del back-end.
  • Applicazione per grandi volumi (centinaia di chiamate al secondo)
  • Istanze multiple su più server
  • centinaia di test unitari superati e innumerevoli attacchi QA

Il bug

  • Una volta spostato in produzione, il server funzionava correttamente per un periodo di tempo casuale, quindi iniziava a degradarsi rapidamente e portava la CPU della scatola al 100%.

Come l'ho trovato

All'inizio ero sicuro che si trattasse di un normale problema di prestazioni, quindi ho creato una registrazione elaborata. Le prestazioni verificate su ogni chiamata parlata agli utenti del database sull'utilizzo hanno osservato i problemi dei server. 1 settimana

Quindi ero sicuro di avere un problema con la discussione. Ho verificato che i miei deadlock hanno tentato di creare la situazione, creare strumenti per tentare di creare la situazione in debug. Con crescente frustrazione gestionale, mi sono rivolto ai miei colleghi su come suggerire le cose dal riavvio del progetto da zero alla limitazione del server a un thread. 1,5 settimane

Poi ho guardato il blog di Tess Ferrandez che ha creato un file di dump utente e l'ho annullato con windebug la volta successiva che il server ha eseguito il dump. Ho scoperto che tutti i miei thread erano bloccati nella funzione Dictionary.add.

Il lungo piccolo dizionario che teneva traccia di quale log per scrivere gli errori di thread x non era sincronizzato.


3

Avevamo un'applicazione che stava parlando con un dispositivo hardware che, in alcuni casi, non avrebbe funzionato correttamente se il dispositivo fosse stato scollegato fisicamente fino a quando non fosse stato ricollegato e ripristinato due volte.

Il problema si è rivelato essere che un'applicazione in esecuzione all'avvio occasionalmente segfaulting quando cercava di leggere da un filesystem che non era ancora stato montato (ad esempio, se un utente lo configurava per leggere da un volume NFS). All'avvio, l'applicazione inviava alcuni ioctls al driver per inizializzare il dispositivo, quindi leggeva le impostazioni di configurazione e inviava più ioctls per riportare il dispositivo nello stato corretto.

Un errore nel driver causava la scrittura di un valore non valido sul dispositivo quando veniva effettuata la chiamata di inizializzazione, ma il valore veniva sovrascritto con dati validi una volta effettuate le chiamate per mettere il dispositivo in uno stato specifico.

Il dispositivo stesso aveva una batteria e avrebbe rilevato se avesse perso energia dalla scheda madre, e avrebbe scritto una bandiera nella memoria volatile indicando che aveva perso energia, sarebbe quindi entrato in uno stato specifico alla successiva accensione e uno specifico le istruzioni dovevano essere inviate per cancellare la bandiera.

Il problema era che se l'alimentazione veniva rimossa una volta che lo ioctls era stato inviato per inizializzare il dispositivo (e aveva scritto il valore non valido sul dispositivo) ma prima che i dati validi potevano essere inviati. Quando il dispositivo veniva riacceso, vedeva che il flag era stato impostato e tentava di leggere i dati non validi che erano stati inviati dal driver a causa dell'inizializzazione incompleta. Ciò metterebbe il dispositivo in uno stato non valido in cui il flag di spegnimento era stato cancellato ma il dispositivo non avrebbe ricevuto ulteriori istruzioni fino a quando non fosse stato reinizializzato dal driver. Il secondo ripristino significherebbe che il dispositivo non stava cercando di leggere i dati non validi che erano stati memorizzati su di esso e avrebbe ricevuto le istruzioni di configurazione corrette, consentendo di metterlo nello stato corretto (supponendo che l'applicazione che inviava gli ioctls non fosse segfault ).

Alla fine ci sono volute circa due settimane per capire l'esatta serie di circostanze che stavano causando il problema.


2

Per un progetto universitario stavamo scrivendo un sistema di nodi P2P distribuiti che condividono file, questo supportava il multicasting per rilevare l'un l'altro, più anelli di nodi e un nameserver in modo che un nodo fosse assegnato a un client.

Scritto in C ++ abbiamo usato POCO per questo in quanto consente una buona programmazione IO, Socket e Thread.


Ci sono stati due bug che ci hanno infastidito e ci hanno fatto perdere molto tempo, uno davvero logico:

Casualmente, un computer stava condividendo il suo IP localhost invece del suo IP remoto.

Ciò ha fatto sì che i client si connettessero al nodo sullo stesso PC o nodi per connettersi con se stessi.

Come l'abbiamo identificato? Quando abbiamo migliorato l'output nel nameserver abbiamo scoperto in un momento successivo quando abbiamo riavviato i computer che il nostro script per determinare l'IP da dare era sbagliato. Casualmente, il dispositivo lo è stato elencato per primo invece del dispositivo eth0 ... Davvero stupido. Quindi ora abbiamo hardcoded per riqualificarlo da eth0 in quanto questo è condiviso tra tutti i computer universitari ...


E ora più fastidioso:

Casualmente, il flusso di pacchetti si interrompe casualmente.
Quando il client successivo si connette continuerebbe ...

Ciò è accaduto in modo assolutamente casuale e poiché è coinvolto più di un computer, è diventato più fastidioso eseguire il debug di questo problema, i computer dell'università non ci consentono di eseguire Wireshark su quelli, quindi non ci resta che indovinare se il problema era sul lato mittente o sulla ricezione lato.

Con un sacco di output nel codice abbiamo appena assunto il presupposto che l'invio dei comandi vada bene,
questo ci ha lasciato a chiederci dove fosse il vero problema ... Sembrava che il modo in cui i sondaggi POCO fossero sbagliati e che invece dovremmo controllare i caratteri disponibili sulla presa in entrata.

Abbiamo assunto il presupposto che questo ha funzionato come test più semplici in un prototipo che coinvolgono meno pacchetti non hanno causato questo problema, quindi questo ci ha fatto supporre che la dichiarazione del sondaggio funzionasse ma ... Non lo era. :-(


Lezioni imparate:

  • Non fare stupide ipotesi come l'ordine dei dispositivi di rete.

  • I frame non fanno sempre il loro lavoro (implementazione o documentazione) nel modo giusto.

  • Fornire un output sufficiente nel codice, se non consentito, assicurarsi di registrare i dettagli estesi in un file.

  • Quando il codice non è stato testato in unità (perché è troppo difficile) non dare per scontato che le cose funzionino.


1
Affrontare i problemi di rete senza WireShark (o strumento simile) è eroico in / di iteslf.
Evan Plaice,

2

Sono ancora nella mia caccia agli insetti più difficile. È uno di quelli a volte è lì e a volte non è bug. Ecco perché sono qui, alle 6:10 del giorno successivo.

Sfondo:

  • Contesto: lingua, applicazione, ambiente, ecc.
    • PHP OS Commerce
  • Come è stato identificato il bug?
    • L'ordine casuale funziona in modo parziale se i problemi falliscono e reindirizzano in modo casuale
  • Chi o cosa ha identificato il bug?
    • Client e il problema di reindirizzamento era ovvio
  • Quanto è stata complessa la riproduzione del bug?
    • Non sono stato in grado di riprodurre, ma il cliente è stato in grado di farlo.

La caccia

  • Qual era il tuo piano?
    • Aggiungi il codice di debug, compila l'ordine, analizza i dati, ripeti
  • Quali difficoltà hai incontrato?
    • Mancanza di problemi ripetibili e codice orribile
  • Come è stato finalmente trovato il codice offensivo?
    • è stato trovato un sacco di codice offensivo ... non esattamente quello di cui avevo bisogno.

L'uccisione.

  • Quanto è stata complessa la correzione?
    • molto
  • Come hai determinato l'ambito della correzione?
    • non c'era spazio ... era dappertutto
  • Quanto codice è stato coinvolto nella correzione?
    • Tutto? Non credo che ci fosse un file intatto

Postmortem.

  • Qual è stata la causa principale tecnicamente? sovraccarico del buffer, ecc.
    • cattiva pratica di codifica
  • Qual è stata la causa principale da 30.000 piedi?
    • Preferirei non dire ...
  • Quanto tempo ha richiesto il processo?
    • per sempre e un giorno
  • Ci sono state delle funzionalità influenzate negativamente dalla correzione?
    • caratteristica? o è un bug?
  • Quali metodi, strumenti, motivazioni hai trovato particolarmente utili? ... orribilmente inutile?
  • Se potessi rifarlo di nuovo? ............
    • ctrl + a Canc

Se la ragione fosse "cattiva pratica di codifica", potresti voler discutere con il tuo capo se questo è un buon momento per rivedere le pratiche di codifica del tuo team e forse introdurre una revisione tra pari?

2

Ho dovuto risolvere alcune cose confuse sulla concorrenza lo scorso semestre, ma il bug che spicca ancora di più per me era in un gioco basato sul testo che stavo scrivendo nell'assemblea PDP-11 per un compito a casa. Si basava sul gioco della vita di Conway e per qualche strana ragione una gran parte delle informazioni accanto alla griglia veniva costantemente sovrascritta con informazioni che non avrebbero dovuto essere lì. Anche la logica era piuttosto semplice, quindi era molto confusa. Dopo averlo esaminato diverse volte per scoprire che tutta la logica è corretta, improvvisamente ho notato quale fosse il problema. Questa cosa:.

In PDP-11 questo puntino accanto a un numero lo rende base 10 anziché 8. Era accanto a un numero che delimitava un loop che doveva essere limitato alla griglia, la cui dimensione era definita con gli stessi numeri ma in base 8.

Si distingue ancora per me a causa della quantità di danno causata da un'aggiunta così piccola di 4 pixel. Quindi qual è la conclusione? Non codificare nell'assieme PDP-11.


2

Il programma Main Frame ha smesso di funzionare senza motivo

Ho appena pubblicato questo per un'altra domanda. Vedi messaggio qui

È successo perché hanno installato una versione più recente del compilatore sul Main-Frame.

Aggiornamento del 06/11/13: (la risposta originale è stata cancellata dall'OP)

Ho ereditato questa applicazione main frame. Un giorno, di punto in bianco, ha smesso di funzionare. È tutto ... ma è appena finito.

Il mio compito era farlo funzionare il più velocemente possibile. Il codice sorgente non era stato modificato per due anni, ma all'improvviso si è fermato. Ho provato a compilare il codice e si è rotto sulla linea XX. Ho guardato la linea XX e non riuscivo a capire cosa avrebbe interrotto la linea XX. Ho chiesto le specifiche dettagliate per questa applicazione e non ce ne sono state. La linea XX non era il colpevole.

Ho stampato il codice e ho iniziato a esaminarlo dall'alto verso il basso. Ho iniziato a creare un diagramma di flusso di ciò che stava succedendo. Il codice era così contorto che non riuscivo nemmeno a capirlo. Ho rinunciato a provare a disegnarlo. Avevo paura di apportare modifiche senza sapere come avrebbe influito sul resto del processo, soprattutto perché non avevo dettagli su ciò che l'applicazione faceva.

Così, ho deciso di iniziare nella parte superiore del codice sorgente e aggiungere whitespce e freni di linea per rendere il codice più leggibile. Ho notato, in alcuni casi, che esistevano condizioni che combinavano AND e OR e che non era chiaramente distinguibile tra quali dati venivano AND e quali dati venivano OR. Così ho iniziato a mettere tra parentesi le condizioni AND e OR per renderle più leggibili.

Mentre procedevo lentamente pulendola, salvavo periodicamente il mio lavoro. A un certo punto ho provato a compilare il codice e una cosa strana è accaduta. L'errore era passato oltre la riga di codice originale e ora era più in basso. Quindi ho continuato, divaricando le condizioni AND e OR con le parentesi. Quando ho finito di pulirlo ha funzionato. Vai a capire.

Ho quindi deciso di visitare il negozio operativo e chiedere loro se avevano recentemente installato nuovi componenti sul telaio principale. Hanno detto di sì, abbiamo recentemente aggiornato il compilatore. Hmmmm.

Si scopre che il vecchio compilatore ha valutato l'espressione da sinistra a destra, indipendentemente. La nuova versione del compilatore ha anche valutato le espressioni da sinistra a destra ma non è stato possibile risolvere il codice ambiguo che significa una combinazione poco chiara di AND e OR.

Lezione che ho imparato da questo ... SEMPRE, SEMPRE, SEMPRE usare i genitori per separare le condizioni AND e le condizioni OR quando vengono utilizzate in congiunzione tra loro.


il post a cui punta il tuo link è stato rimosso - ti dispiacerebbe aggiornare la risposta?
moscerino

1
@gnat - Trovato su archive.org :)
Michael Riley - AKA Gunny l'

1

Sfondo:

  • Contesto: Web Server (C ++) che consente ai clienti di effettuare il check-in autonomamente
  • Bug: quando si richiedeva la pagina, semplicemente non rispondeva, l'intera farm che era e i processi venivano eliminati (e riavviati) perché impiegavano troppo tempo (sono consentiti solo pochi secondi) per servire la pagina
  • Alcuni utenti si sono lamentati, ma era estremamente sporadico, quindi per lo più inosservato (le persone tendono a premere "Aggiorna" quando una pagina non viene pubblicata). Tuttavia abbiamo notato le discariche principali;)
  • In realtà non siamo mai riusciti a riprodurre nei nostri ambienti locali, il bug è apparso alcune volte nei sistemi di test ma non è mai apparso durante i test delle prestazioni ??

La caccia

  • Piano: beh, dato che avevamo dei dump e dei log di memoria, volevamo analizzarli. Dal momento che stava interessando l'intera farm e in passato abbiamo avuto alcuni problemi con i database, sospettavamo che il database (DB singolo per più server)
  • Difficoltà: il dump di un server completo è enorme e quindi vengono cancellati abbastanza frequentemente (per non esaurire lo spazio), quindi dovevamo essere veloci per afferrarne uno quando si verificava ... Abbiamo persistito. I dump hanno mostrato vari stack (mai roba DB, così tanto per quello), non sono riusciti durante la preparazione della pagina stessa (non nei calcoli precedenti) e hanno confermato ciò che i log mostravano, preparare la pagina a volte richiederebbe molto tempo, anche sebbene sia solo un motore di template di base con dati pre-calcolati (MVC tradizionale)
  • Come arrivare: dopo alcuni altri esempi e alcune riflessioni ci siamo resi conto che è stato impiegato del tempo per leggere i dati dall'HDD (il modello di pagina). Dato che riguardava l'intera fattoria, per prima cosa cercavamo lavori programmati (crontab, batch) ma i tempi non coincidevano mai da un'occorrenza all'altra ... Alla fine mi venne in mente che ciò avveniva sempre pochi giorni prima dell'attivazione di una nuova versione del software e ho avuto un AhAh! momento ... è stato causato dalla distribuzione del software! Fornire diverse centinaia di megabyte (compressi) può influire negativamente sulle prestazioni del disco: / Naturalmente la distribuzione è automatizzata e l'archivio è passato a tutti i server contemporaneamente (multicast).

L'uccisione.

  • Complessità fissa: passaggio ai modelli compilati
  • Codice interessato: nessuno, una semplice modifica nel processo di generazione

Postmortem.

  • Causa principale: problema operativo o mancanza di pianificazione futura :)
  • Tempistica: ci sono voluti mesi per rintracciare, una questione di giorni per risolvere e testare, alcune settimane per test e implementazione di QA e Performance - non c'è fretta lì, poiché sapevamo che la distribuzione della correzione avrebbe innescato il bug ... e niente altrimenti ... un po 'pervertito davvero!
  • Effetti collaterali avversi: impossibilità di cambiare i modelli in fase di esecuzione ora che sono cotti nel codice consegnato, tuttavia non abbiamo usato la funzione molto, poiché generalmente cambiare modello significa che hai più dati da versare. L'uso di css è principalmente sufficiente per "piccoli" cambiamenti di layout.
  • Metodi, strumenti: gdb+ monitoraggio! Ci è bastato del tempo per sospettare il disco e quindi identificare la causa dei picchi di attività sul grafico di monitoraggio ...
  • La prossima volta: considera tutti gli IO come avversi!

1

Il più difficile non è mai stato ucciso perché non poteva mai essere riprodotto se non nel pieno ambiente di produzione con la fabbrica in funzione.

Il più pazzo che ho ucciso:

I disegni sono inesorabili!

Guardo il codice e non vedo nulla. Estraggo un lavoro dalla coda della stampante e lo controllo, sembra a posto. (Questo era nell'era del DOS, PCL5 con HPGl / 2 incorporato - in realtà, molto buono per la stampa di disegni e nessun mal di testa nella creazione di un'immagine raster in memoria limitata.) Lo dirigo verso un'altra stampante che dovrebbe capirlo, stampa bene .

Ripristina il codice, il problema è ancora presente.

Finalmente faccio manualmente un semplice file e lo invio alla stampante - senza senso. Si scopre che non era affatto il mio bug ma la stampante stessa. La società di manutenzione lo aveva aggiornato all'ultima versione quando stavano riparando qualcos'altro e quest'ultima versione aveva un bug. Far capire loro che avevano eliminato le funzionalità critiche e che avevano dovuto riportarle a una versione precedente era più difficile che trovare il bug stesso.

Uno che era ancora più fastidioso ma dato che era solo sulla mia scatola non avrei messo al primo posto:

Borland Pascal, codice DPMI per gestire alcune API non supportate. Eseguilo, a volte ha funzionato, di solito è andato boom cercando di gestire un puntatore non valido. Non ha mai prodotto un risultato sbagliato, tuttavia, come ci si aspetterebbe da calpestare un puntatore.

Debug: se avessi eseguito il passaggio singolo attraverso il codice avrebbe sempre funzionato correttamente, altrimenti sarebbe stato instabile come prima. L'ispezione ha sempre mostrato i valori giusti.

Il colpevole: c'erano due.

1) Il codice della libreria di Borland presentava un grosso bug: i puntatori in modalità reale venivano memorizzati in variabili puntatore in modalità protetta. Il problema è che la maggior parte dei puntatori in modalità reale hanno indirizzi di segmento non validi in modalità protetta e quando si tenta di copiare il puntatore, questo viene caricato in una coppia di registri e quindi salvato.

2) Il debugger non direbbe mai nulla di un tale carico non valido in modalità a passaggio singolo. Non so cosa abbia fatto internamente, ma ciò che è stato presentato all'utente sembrava completamente corretto. Ho il sospetto che in realtà non stesse eseguendo l'istruzione ma la simulasse invece.


1

Questo è solo un bug molto semplice che in qualche modo mi sono trasformato in un incubo per me.

Background: stavo lavorando per creare il mio sistema operativo. Il debug è molto difficile (le istruzioni trace sono tutto ciò che puoi avere, e talvolta nemmeno quello)

Bug: Invece di fare due switch di thread in modalità utente, avrebbe invece un errore di protezione generale.

La caccia ai bug: ho trascorso probabilmente una settimana o due a cercare di risolvere questo problema. Inserimento di istruzioni trace ovunque. Esame del codice assembly generato (da GCC). Stampa ogni singolo valore che potrei.

Il problema: da qualche parte all'inizio della caccia ai bug, avevo inserito hltun'istruzione in crt0. Crt0 è fondamentalmente ciò che avvia un programma utente per l'utilizzo in un sistema operativo. Questohlt istruzione provoca un GPF quando eseguito dalla modalità utente. L'ho messo lì e praticamente me ne sono dimenticato. (originariamente il problema riguardava un overflow del buffer o un errore di allocazione della memoria)

La correzione: rimuovere le hltistruzioni :) Dopo averlo rimosso, tutto ha funzionato senza problemi.

Cosa ho imparato: quando provi a eseguire il debug di un problema, non perdere traccia delle correzioni che provi. Differenze regolari rispetto all'ultima versione stabile del controllo del codice sorgente e vedi cosa hai cambiato di recente quando nient'altro funziona

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.