Folclore di programmatori e ingegneri (parte 1)

Folclore di programmatori e ingegneri (parte 1)

Questa è una selezione di storie da Internet su come i bug a volte abbiano manifestazioni completamente incredibili. Forse anche tu hai qualcosa da dire.

Allergia automobilistica al gelato alla vaniglia

Una storia per ingegneri che capiscono che l'ovvio non è sempre la risposta e che, per quanto inverosimili possano sembrare i fatti, sono pur sempre i fatti. La divisione Pontiac della General Motors Corporation ha ricevuto un reclamo:

Questa è la seconda volta che ti scrivo e non ti biasimo per non aver risposto, perché sembra pazzesco. La nostra famiglia ha la tradizione di mangiare il gelato ogni sera dopo cena. Le tipologie di gelato cambiano ogni volta, e dopo cena tutta la famiglia sceglie quale gelato comprare, dopodiché vado in negozio. Recentemente ho acquistato una nuova Pontiac e da allora i miei viaggi per prendere il gelato sono diventati un problema. Vedi, ogni volta che compro il gelato alla vaniglia e torno dal negozio, la macchina non parte. Se porto qualche altro gelato la macchina parte senza problemi. Voglio porre una domanda seria, per quanto stupida possa sembrare: "Cos'ha la Pontiac che non si avvia quando porto il gelato alla vaniglia, ma si avvia facilmente quando porto un altro gusto di gelato?"

Come puoi immaginare, il presidente della divisione era scettico riguardo alla lettera. Tuttavia, per ogni evenienza, ho mandato un ingegnere a controllare. Fu sorpreso di essere stato accolto da un uomo ricco e istruito che viveva in una bellissima zona. Si accordarono per incontrarsi subito dopo cena in modo che loro due potessero andare al negozio a prendere il gelato. Quella sera era vaniglia e quando tornarono alla macchina non partiva.

L'ingegnere venne altre tre sere. La prima volta il gelato era al cioccolato. L'auto è partita. La seconda volta c'era il gelato alla fragola. L'auto è partita. La terza sera chiese di prendere la vaniglia. L'auto non è partita.

Ragionando razionalmente, l'ingegnere si rifiutò di credere che l'auto fosse allergica al gelato alla vaniglia. Pertanto ho concordato con il proprietario dell'auto che avrebbe continuato le sue visite finché non avesse trovato una soluzione al problema. E lungo la strada cominciò a prendere appunti: annotò tutte le informazioni, l'ora del giorno, il tipo di benzina, l'orario di arrivo e di ritorno dal negozio, ecc.

L'ingegnere si rese presto conto che il proprietario dell'auto dedicava meno tempo all'acquisto del gelato alla vaniglia. Il motivo era la disposizione della merce nel negozio. Il gelato alla vaniglia era il più popolare e veniva conservato in un congelatore separato nella parte anteriore del negozio per renderlo più facile da trovare. E tutte le altre varietà erano nel retro del negozio, e ci voleva molto più tempo per trovare la varietà giusta e pagare.

Ora la domanda spettava all’ingegnere: perché l’auto non partiva se era passato meno tempo dal momento in cui si era spento il motore? Dato che il problema era il tempo e non il gelato alla vaniglia, l'ingegnere trovò subito la risposta: si trattava della serratura del gas. Succedeva ogni sera, ma quando il proprietario dell'auto passava più tempo a cercare il gelato, il motore riusciva a raffreddarsi abbastanza e si avviava facilmente. E quando l'uomo comprò il gelato alla vaniglia, il motore era ancora troppo caldo e il tappo del gas non fece in tempo a sciogliersi.

Morale: anche i problemi completamente folli a volte sono reali.

Crash Bandicoot

È doloroso sperimentare questo. Come programmatore, ti abitui a incolpare il tuo codice per primo, secondo, terzo... e da qualche parte nel decimillesimo posto a incolpare il compilatore. E più in basso nell'elenco dai già la colpa all'attrezzatura.

Ecco la mia storia sul bug hardware.

Per il gioco Crash Bandicoot, ho scritto il codice per caricare e salvare su una scheda di memoria. Per uno sviluppatore di giochi così compiaciuto è stata come una passeggiata nel parco: pensavo che il lavoro avrebbe richiesto diversi giorni. Tuttavia, ho finito per eseguire il debug del codice per sei settimane. Lungo il percorso ho risolto altri problemi, ma ogni pochi giorni tornavo a questo codice per alcune ore. Era un'agonia.

Il sintomo era questo: quando salvi la partita corrente del gioco e accedi alla scheda di memoria, tutto va quasi sempre bene... Ma a volte l'operazione di lettura o scrittura va in timeout senza una ragione ovvia. Una breve registrazione spesso danneggia la scheda di memoria. Quando un giocatore tenta di salvare, non solo non riesce a salvare, ma distrugge anche la mappa. Merda.

Dopo un po’, il nostro produttore alla Sony, Connie Bus, cominciò a farsi prendere dal panico. Non potevamo spedire il gioco con questo bug e sei settimane dopo non capivo quale fosse la causa del problema. Tramite Connie abbiamo contattato altri sviluppatori PS1: qualcuno ha riscontrato qualcosa di simile? NO. Nessuno ha avuto problemi con la scheda di memoria.

Quando non si hanno idee per il debug, l’unico approccio rimasto è “divide et impera”: rimuovere sempre più codice dal programma difettoso finché non rimane un frammento relativamente piccolo che causa ancora il problema. Cioè, tagli il programma pezzo per pezzo finché non rimane la parte che contiene il bug.

Ma il fatto è che è molto difficile tagliare pezzi da un videogioco. Come eseguirlo se hai rimosso il codice che emula la gravità? O disegnare personaggi?

Dobbiamo quindi sostituire interi moduli con stub che fingono di fare qualcosa di utile, ma in realtà fanno qualcosa di molto semplice che non può contenere errori. Dobbiamo scrivere tali stampelle affinché il gioco almeno funzioni. Questo è un processo lento e doloroso.

In breve, ce l'ho fatta. Ho rimosso sempre più pezzi di codice finché non mi è rimasto il codice iniziale che configura il sistema per eseguire il gioco, inizializza l'hardware di rendering, ecc. Naturalmente in questa fase non potevo creare un menu di salvataggio e caricamento, perché avrei dovuto creare uno stub per tutto il codice grafico. Ma potrei fingere di essere un utente utilizzando la schermata di salvataggio e caricamento (invisibile) e chiedere di salvare e quindi scrivere sulla scheda di memoria.

Questo mi ha lasciato con un piccolo pezzo di codice che presentava ancora il problema di cui sopra, ma accadeva ancora in modo casuale! Molto spesso tutto funzionava bene, ma a volte si verificavano dei problemi. Ho rimosso quasi tutto il codice del gioco, ma il bug era ancora vivo. Ciò era sconcertante: il codice rimanente in realtà non faceva nulla.

Ad un certo punto, probabilmente verso le tre del mattino, mi è venuta in mente un'idea. Le operazioni di lettura e scrittura (input/output) comportano tempi di esecuzione precisi. Quando si lavora con un disco rigido, una scheda di memoria o un modulo Bluetooth, il codice di basso livello responsabile della lettura e della scrittura lo fa in base agli impulsi dell'orologio.

Con l'aiuto di un orologio, un dispositivo che non è direttamente collegato al processore viene sincronizzato con il codice in esecuzione sul processore. L'orologio determina la velocità di trasmissione, ovvero la velocità con cui vengono trasmessi i dati. Se c'è confusione con i tempi, anche l'hardware o il software, o entrambi, sono confusi. E questo è molto negativo, perché i dati possono essere danneggiati.

Cosa succede se qualcosa nel nostro codice confonde i tempi? Ho controllato tutto ciò che riguarda questo nel codice del programma di test e ho notato che abbiamo impostato il timer programmabile in PS1 su 1 kHz (1000 tick al secondo). Questo è parecchio; per impostazione predefinita, all'avvio la console funziona a 100 Hz. E la maggior parte dei giochi utilizza questa frequenza.

Andy, lo sviluppatore del gioco, ha impostato il timer su 1 kHz in modo che i movimenti venissero calcolati in modo più accurato. Andy tende ad esagerare e, se emuliamo la gravità, lo facciamo nel modo più accurato possibile!

Ma cosa accadrebbe se l'accelerazione del timer influisse in qualche modo sulla temporizzazione complessiva del programma, e quindi sull'orologio che regola la velocità di trasmissione della scheda di memoria?

Ho commentato il codice del timer. L'errore non si è ripetuto. Ma questo non significa che abbiamo risolto il problema, perché il guasto si è verificato in modo casuale. E se fossi stato solo fortunato?

Pochi giorni dopo ho sperimentato nuovamente il programma di test. Il bug non si è ripresentato. Sono tornato al codebase completo del gioco e ho modificato il codice di salvataggio e caricamento in modo che il timer programmabile si reimpostasse al suo valore originale (100Hz) prima di accedere alla scheda di memoria, quindi reimpostato su 1kHz. Non ci sono stati più incidenti.

Ma perché è successo?

Sono tornato di nuovo al programma di test. Ho provato a trovare qualche schema nel verificarsi di un errore con un timer da 1 kHz. Alla fine ho notato che l'errore si verifica quando qualcuno gioca con un controller PS1. Dato che lo farei raramente da solo, perché dovrei aver bisogno di un controller durante il test di salvataggio e caricamento del codice? - Non ho nemmeno notato questa dipendenza. Ma un giorno uno dei nostri artisti stava aspettando che finissi i test - probabilmente stavo imprecando in quel momento - e faceva girare nervosamente il controller tra le mani. C'è stato un errore. "Aspetta cosa?!" Bene, fallo di nuovo!

Quando mi sono reso conto che questi due eventi erano interconnessi, sono riuscito a riprodurre facilmente l'errore: ho iniziato a registrare sulla scheda di memoria, ho spostato il controller e ho rovinato la scheda di memoria. A me sembrava un bug hardware.

Sono andato da Connie e le ho raccontato della mia scoperta. Ha trasmesso l'informazione a uno degli ingegneri che hanno progettato la PS1. “Impossibile”, ha risposto, “Non può essere un problema hardware”. Ho chiesto a Connie di organizzare una conversazione per noi.

L'ingegnere mi ha chiamato e abbiamo discusso nel suo inglese stentato e nel mio giapponese (estremamente) stentato. Alla fine ho detto: "Permettetemi di inviare il mio programma di test di 30 righe in cui lo spostamento del controller provoca un bug". Lui ha acconsetito. Disse che era una perdita di tempo e che era terribilmente impegnato a lavorare su un nuovo progetto, ma avrebbe ceduto perché eravamo uno sviluppatore molto importante per Sony. Ho ripulito il mio programma di test e glielo ho inviato.

La sera successiva (eravamo a Los Angeles e lui a Tokyo) mi chiamò e si scusò timidamente. È stato un problema hardware.

Non so quale fosse esattamente il bug, ma da quello che ho sentito al quartier generale di Sony, se impostavi il timer su un valore sufficientemente alto, interferiva con i componenti della scheda madre in prossimità del cristallo del timer. Uno di questi era un controller della velocità di trasmissione per la scheda di memoria, che impostava anche la velocità di trasmissione per i controller. Non sono un ingegnere, quindi potrei aver sbagliato qualcosa.

Ma il punto è che c'era un'interferenza tra i componenti della scheda madre. E durante la trasmissione simultanea dei dati attraverso la porta del controller e la porta della scheda di memoria con un timer che funziona a 1 kHz, i bit venivano persi, i dati andavano persi e la scheda veniva danneggiata.

Mucche cattive

Negli anni '1980, il mio mentore Sergei scrisse il software per l'SM-1800, un clone sovietico del PDP-11. Questo microcomputer è stato appena installato in una stazione ferroviaria vicino a Sverdlovsk, un importante snodo dei trasporti dell'URSS. Il nuovo sistema è stato progettato per instradare i vagoni e il traffico merci. Ma conteneva un fastidioso bug che portava a arresti anomali e arresti anomali casuali. Le cadute si verificavano sempre quando qualcuno tornava a casa la sera. Ma nonostante un'indagine approfondita effettuata il giorno successivo, il computer ha funzionato correttamente in tutti i test manuali e automatici. Questo di solito indica una condizione di gara o qualche altro bug competitivo che si verifica in determinate condizioni. Stanco delle chiamate a tarda notte, Sergei ha deciso di andare a fondo della questione e, prima di tutto, capire quali condizioni nello scalo di smistamento hanno portato al guasto del computer.

Per prima cosa ha raccolto le statistiche di tutte le cadute inspiegabili e ha creato un grafico per data e ora. Lo schema era ovvio. Dopo aver osservato per qualche altro giorno, Sergei si rese conto che avrebbe potuto facilmente prevedere il momento dei futuri guasti del sistema.

Ben presto apprese che i disagi si verificavano solo quando la stazione stava smistando treni carichi di bestiame provenienti dall'Ucraina settentrionale e dalla Russia occidentale diretti a un vicino macello. Già questo era strano, perché il macello veniva rifornito da allevamenti situati molto più vicini, in Kazakistan.

La centrale nucleare di Chernobyl esplose nel 1986 e le ricadute radioattive resero inabitabili le aree circostanti. Sono state contaminate vaste aree dell’Ucraina settentrionale, della Bielorussia e della Russia occidentale. Sospettando alti livelli di radiazioni nelle carrozze in arrivo, Sergei sviluppò un metodo per testare questa teoria. Alla popolazione era vietato avere dosimetri, quindi Sergei si registrò presso diversi militari alla stazione ferroviaria. Dopo diversi bicchieri di vodka, riuscì a convincere un soldato a misurare il livello di radiazioni in una delle carrozze sospette. Si è scoperto che il livello era molte volte superiore ai valori normali.

Non solo il bestiame emetteva molte radiazioni, ma il suo livello era così elevato da causare la perdita casuale di bit nella memoria dell'SM-1800, che si trovava in un edificio vicino alla stazione.

Nell'URSS si verificò una carenza di cibo e le autorità decisero di mescolare la carne di Chernobyl con carne proveniente da altre regioni del paese. Ciò ha permesso di ridurre il livello complessivo di radioattività senza perdere risorse preziose. Dopo aver appreso questo, Sergei ha immediatamente compilato i documenti per l'emigrazione. E i crash del computer si sono interrotti da soli quando il livello di radiazioni è diminuito nel tempo.

Attraverso i tubi

Un tempo Movietech Solutions creava software per cinema, pensati per la contabilità, la biglietteria e la gestione generale. La versione DOS dell'app di punta era piuttosto popolare tra le catene di cinema di piccole e medie dimensioni del Nord America. Non sorprende quindi che quando è stata annunciata una versione di Windows 95, integrata con i più recenti touch screen e chioschi self-service e dotata di tutti i tipi di strumenti di reporting, sia diventata rapidamente anch'essa popolare. Molto spesso l'aggiornamento è avvenuto senza problemi. Il personale IT locale ha installato nuove apparecchiature, ha migrato i dati e l'attività è proseguita. Tranne quando non durava. Quando ciò accadeva, la compagnia mandava James, soprannominato "The Cleaner".

Sebbene il soprannome suggerisca un tipo nefasto, l'addetto alle pulizie è solo una combinazione di istruttore, installatore e tuttofare. James trascorreva alcuni giorni presso la sede del cliente mettendo insieme tutti i componenti, quindi trascorreva un altro paio di giorni insegnando allo staff come utilizzare il nuovo sistema, risolvendo eventuali problemi hardware che si presentavano e essenzialmente aiutando il software nella sua fase iniziale.

Pertanto, non sorprende che durante questi tempi frenetici, James sia arrivato in ufficio la mattina e, prima che potesse raggiungere la scrivania, sia stato accolto dal direttore, pieno di caffeina oltre il solito.

"Temo che tu debba andare ad Annapolis, in Nuova Scozia, il prima possibile." Il loro intero sistema è andato in tilt e, dopo una notte di lavoro con i loro ingegneri, non riusciamo a capire cosa sia successo. Sembra che la rete abbia fallito sul server. Ma solo dopo che il sistema era in funzione da diversi minuti.

— Non sono tornati al vecchio sistema? - rispose James in tutta serietà, anche se mentalmente spalancò gli occhi per la sorpresa.

— Esattamente: il loro specialista IT “ha cambiato priorità” e ha deciso di andarsene con il loro vecchio server. James, hanno installato il sistema in sei sedi e pagato solo per il supporto premium, e la loro attività ora è gestita come negli anni '1950.

James si raddrizzò leggermente.

- Questa è un'altra questione. Ok, cominciamo.

Quando arrivò ad Annapolis, la prima cosa che fece fu trovare il primo teatro del cliente che aveva un problema. Sulla mappa scattata all'aeroporto tutto sembrava a posto, ma l'area intorno all'indirizzo desiderato sembrava sospetta. Non ghetto, ma reminiscenza del film noir. Mentre James parcheggiava sul marciapiede in centro, una prostituta gli si avvicinò. Date le dimensioni di Annapolis, molto probabilmente era l'unico in tutta la città. Il suo aspetto ha subito ricordato il famoso personaggio che offriva sesso in cambio di soldi sul grande schermo. No, non su Julia Roberts, ma su Jon Voight [allusione al film "Midnight Cowboy" - ca. sentiero].

Dopo aver mandato via la prostituta, James andò al cinema. La zona circostante era migliorata, ma dava ancora l'impressione di essere degradata. Non che James fosse troppo preoccupato. È già stato in posti disgraziati. E questo era il Canada, dove anche i rapinatori sono abbastanza educati da dire "grazie" dopo aver preso il portafoglio.

L'ingresso laterale del cinema era in un vicolo umido. James andò alla porta e bussò. Ben presto scricchiolò e si aprì leggermente.

-Sei una donna delle pulizie? - dall'interno venne una voce rauca.

- Sì, sono io... sono venuto a sistemare tutto.

James entrò nell'atrio del cinema. Apparentemente non avendo altra scelta, il personale ha iniziato a distribuire biglietti cartacei ai visitatori. Ciò ha reso difficile la rendicontazione finanziaria, per non parlare dei dettagli più interessanti. Ma lo staff accolse James con sollievo e lo portò immediatamente nella sala server.

A prima vista, tutto andava bene. James è entrato nel server e ha controllato i soliti posti sospetti. Nessun problema. Tuttavia, per estrema cautela, James ha spento il server, ha sostituito la scheda di rete ed ha ripristinato il sistema. Ha subito iniziato a lavorare a pieno titolo. Lo staff ha ripreso a vendere i biglietti.

James chiamò Mark e lo informò della situazione. Non è difficile immaginare che James voglia restare e vedere se succede qualcosa di inaspettato. Scese le scale e cominciò a chiedere ai dipendenti cosa fosse successo. Ovviamente il sistema ha smesso di funzionare. L'hanno spento e riacceso, tutto ha funzionato. Ma dopo 10 minuti il ​​sistema si è interrotto.

Proprio in questo momento accadde qualcosa di simile. All'improvviso, il sistema di biglietteria ha iniziato a generare errori. Lo staff sospirò e afferrò i biglietti cartacei, e James corse nella sala server. Tutto sembrava a posto con il server.

Poi è entrato uno dei dipendenti.

— Il sistema funziona di nuovo.

James era perplesso perché non aveva fatto nulla. Più precisamente, nulla che possa far funzionare il sistema. Si è disconnesso, ha preso il telefono e ha chiamato la linea di supporto della sua azienda. Ben presto lo stesso dipendente entrò nella sala server.

- Il sistema non funziona.

James guardò il server. Uno schema interessante e familiare di forme multicolori danzava sullo schermo, contorcendosi e intrecciando caoticamente tubi. Tutti abbiamo visto questo salvaschermo prima o poi. Era reso magnificamente e letteralmente ipnotizzante.


James premette un pulsante e lo schema scomparve. Si precipitò alla biglietteria e lungo la strada incontrò un impiegato che tornava da lui.

— Il sistema funziona di nuovo.

Se riesci a fare un facepalm mentale, è esattamente quello che ha fatto James. Salvaschermo. Utilizza OpenGL. Pertanto, durante il funzionamento, consuma tutte le risorse del processore del server. Di conseguenza, ogni chiamata al server termina con un timeout.

James è tornato nella sala server, ha effettuato l'accesso e ha sostituito lo screensaver con i bellissimi tubi con uno schermo vuoto. Cioè, invece di uno screensaver che consuma il 100% delle risorse del processore, ne ho installato un altro che non consuma risorse. Poi ho aspettato 10 minuti per verificare la mia ipotesi.

Quando James arrivò al cinema successivo, si stava chiedendo come spiegare al suo manager che aveva appena volato 800 km per disattivare il salvaschermo.

Schianto durante una certa fase lunare

Storia vera. Un giorno si è verificato un bug nel software che dipendeva dalla fase lunare. C'era una piccola routine comunemente usata in vari programmi del MIT per calcolare l'approssimazione alla vera fase lunare. GLS ha integrato questa routine in un programma LISP che, durante la scrittura di un file, produceva una riga con un timestamp lungo quasi 80 caratteri. Era molto raro che la prima riga di un messaggio risultasse troppo lunga e portasse alla riga successiva. E quando il programma in seguito ha letto questo file, ha imprecato. La lunghezza della prima riga dipendeva dalla data e dall'ora esatte, nonché dalla lunghezza della specificazione della fase al momento della stampa del timestamp. Cioè, l'insetto dipendeva letteralmente dalla fase lunare!

Prima edizione cartacea Archivio gergo (Steele-1983) conteneva un esempio di una riga che portava al bug descritto, ma il tipografo lo "corresse". Da allora questo è stato descritto come un "bug delle fasi lunari".

Attenzione però alle supposizioni. Alcuni anni fa, gli ingegneri del CERN (Centro europeo per la ricerca nucleare) hanno riscontrato errori negli esperimenti condotti al Large Electron-Positron Collider. Poiché i computer elaborano attivamente l’enorme quantità di dati generati da questo dispositivo prima di mostrare il risultato agli scienziati, molti hanno ipotizzato che il software fosse in qualche modo sensibile alla fase lunare. Diversi ingegneri disperati sono arrivati ​​​​al fondo della verità. L'errore è nato a causa di un leggero cambiamento nella geometria dell'anello lungo 27 km dovuto alla deformazione della Terra durante il passaggio della Luna! Questa storia è entrata nel folklore della fisica come “La vendetta di Newton sulla fisica delle particelle” e un esempio della connessione tra le leggi più semplici e antiche della fisica e i concetti scientifici più avanzati.

Lo sciacquone ferma il treno

Il miglior bug hardware di cui abbia mai sentito parlare si è verificato su un treno ad alta velocità in Francia. La cimice comportava la frenata d'emergenza del treno, ma solo se a bordo c'erano passeggeri. In ciascuno di questi casi, il treno è stato messo fuori servizio, controllato, ma non è stato trovato nulla. Poi è stato rimandato in linea e si è immediatamente fermato.

Durante uno dei controlli, un macchinista che viaggiava sul treno si è recato in bagno. Presto venne spazzato via, BOOM! Arresto di emergenza.

L'ingegnere contattò l'autista e chiese:

— Cosa stavi facendo prima di frenare?

- Beh, ho rallentato nella discesa...

Questo era strano, perché durante il normale funzionamento il treno rallenta in discesa decine di volte. Il treno proseguì e alla discesa successiva il macchinista avvertì:

- Rallenterò.

Non è successo niente.

— Cosa hai fatto durante l'ultima frenata? - chiese l'autista.

- Beh... ero in bagno...

- Bene, allora vai in bagno e fai quello che hai fatto quando scendiamo di nuovo!

L'ingegnere è andato in bagno e quando l'autista ha avvertito: "Sto rallentando", ha tirato l'acqua. Naturalmente il treno si fermò immediatamente.

Ora potevano riprodurre il problema e dovevano trovarne la causa.

Dopo due minuti, notarono che il cavo del telecomando del freno motore (il treno aveva un motore a ciascuna estremità) era scollegato dalla parete dell'armadio elettrico e giaceva sul relè che controllava il solenoide della presa della toilette... Quando il relè era acceso, creava interferenze nel cavo del freno e la protezione del sistema contro i guasti includeva semplicemente la frenata di emergenza.

Il gateway che odiava FORTRAN

Alcuni mesi fa abbiamo notato che le connessioni di rete sulla terraferma [questo era alle Hawaii] stavano diventando molto, molto lente. Ciò potrebbe durare 10-15 minuti e poi verificarsi di nuovo all'improvviso. Dopo qualche tempo, il mio collega si è lamentato con me che le connessioni di rete sulla terraferma affatto non funziona. Aveva del codice FORTRAN che doveva essere copiato su una macchina sulla terraferma, ma non poteva perché "la rete non ha resistito abbastanza a lungo per completare il caricamento FTP".

Sì, si è scoperto che si sono verificati guasti alla rete quando un collega ha tentato di inviare tramite FTP un file con codice sorgente in FORTRAN su una macchina sulla terraferma. Abbiamo provato ad archiviare il file: poi è stato copiato senza problemi (ma la macchina di destinazione non aveva un programma di decompressione, quindi il problema non è stato risolto). Infine abbiamo "diviso" il codice FORTRAN in pezzetti molto piccoli e li abbiamo inviati uno alla volta. La maggior parte dei frammenti furono copiati senza problemi, ma alcuni pezzi non passarono, o passarono dopo numeroso tentativi.

Quando abbiamo esaminato i passaggi problematici, abbiamo scoperto che avevano qualcosa in comune: contenevano tutti blocchi di commento che iniziavano e finivano con righe composte da C maiuscola (come preferiva commentare un collega in FORTRAN). Abbiamo inviato un'e-mail agli esperti di rete sulla terraferma e abbiamo chiesto aiuto. Naturalmente volevano vedere campioni dei nostri file che non potevano essere trasferiti tramite FTP... ma le nostre lettere non sono arrivate. Alla fine abbiamo pensato a un semplice descriverecome appaiono i file non trasferibili. Ha funzionato :) [Oserei aggiungere qui un esempio di uno dei commenti problematici FORTRAN? Probabilmente non ne vale la pena!]

Alla fine siamo riusciti a capirlo. Recentemente è stato installato un nuovo gateway tra la nostra parte del campus e la rete continentale. Aveva ENORMI difficoltà nel trasmettere pacchetti che contenevano ripetuti bit di C maiuscola! Solo alcuni di questi pacchetti potrebbero occupare tutte le risorse del gateway e impedire il passaggio della maggior parte degli altri pacchetti. Ci siamo lamentati con il produttore del gateway... e loro hanno risposto: “Oh, sì, ti trovi di fronte a un bug di C ripetuto! Sappiamo già di lui." Alla fine abbiamo risolto il problema acquistando un nuovo gateway da un altro produttore (a difesa del primo, l'impossibilità di trasferire programmi FORTRAN potrebbe essere un vantaggio per alcuni!).

Tempi difficili

Qualche anno fa, mentre lavoravo alla creazione di un sistema ETL in Perl per ridurre i costi degli studi clinici di fase 40, avevo bisogno di elaborare circa 000 dati. Due di loro non hanno superato il test. La cosa non mi ha disturbato più di tanto perché queste date sono state prese dai dati forniti dai clienti che spesso erano, per così dire, sorprendenti. Ma quando ho controllato i dati originali, ho scoperto che queste date erano 1 gennaio 2011 e 1 gennaio 2007. Pensavo che il bug fosse contenuto nel programma che avevo appena scritto, ma si è scoperto che erano già 30 anni vecchio. Questo può sembrare misterioso a chi non ha familiarità con l’ecosistema software. A causa della decisione di lunga data di un'altra azienda di fare soldi, il mio cliente mi ha pagato per correggere un bug che un'azienda aveva introdotto per sbaglio e l'altra di proposito. Per farti capire di cosa sto parlando, devo parlare dell'azienda che ha aggiunto la funzionalità che alla fine è diventata un bug, nonché di alcuni altri eventi interessanti che hanno contribuito al misterioso bug che ho risolto.

Ai vecchi tempi, i computer Apple a volte reimpostavano spontaneamente la data al 1 gennaio 1904. Il motivo era semplice: utilizzava un “orologio di sistema” alimentato a batteria per tenere traccia della data e dell’ora. Cosa è successo quando la batteria è morta? I computer iniziarono a tenere traccia della data in base al numero di secondi trascorsi dall'inizio di un'epoca. Per epoca intendevamo la data originale di riferimento, e per i Macintosh era il 1 gennaio 1904. E dopo che la batteria si è scaricata, la data attuale è stata ripristinata a quella specificata. Ma perché è successo questo?

In precedenza, Apple utilizzava 32 bit per memorizzare il numero di secondi trascorsi dalla data originale. Un bit può memorizzare uno dei due valori: 1 o 0. Due bit possono memorizzare uno dei quattro valori: 00, 01, 10, 11. Tre bit: un valore su otto: 000, 001, 010, 011, 100 , 101, 110, 111, ecc. E 32 potrebbe memorizzare uno dei 232 valori, ovvero 4 secondi. Per le date Apple, ciò equivale a circa 294 anni, quindi i Mac più vecchi non possono gestire date successive al 967. E se la batteria del sistema si scarica, la data viene ripristinata a 296 secondi dall'inizio dell'epoca e dovrai impostare manualmente la data ogni volta che accendi il computer (o finché non acquisti una nuova batteria).

Tuttavia, la decisione di Apple di memorizzare le date come secondi dall'epoca significava che non potevamo gestire le date precedenti all'epoca, il che ha avuto conseguenze di vasta portata, come vedremo. Apple ha introdotto una funzionalità, non un bug. Ciò significava tra l’altro che il sistema operativo Macintosh era immune al “millennium bug” (cosa che non si poteva dire di molte applicazioni Mac che disponevano di propri sistemi di dati per aggirare le restrizioni).

Andare avanti. Abbiamo usato Lotus 1-2-3, la "killer application" di IBM che ha contribuito a lanciare la rivoluzione dei PC, anche se i computer Apple avevano VisiCalc, che ha reso il personal computer un successo. In tutta onestà, se non fosse apparso 1-2-3, i PC difficilmente sarebbero decollati e la storia dei personal computer avrebbe potuto svilupparsi in modo molto diverso. Lotus 1-2-3 trattava erroneamente il 1900 come un anno bisestile. Quando Microsoft pubblicò il suo primo foglio di calcolo, Multiplan, conquistò una piccola quota di mercato. E quando lanciarono il progetto Excel, decisero non solo di copiare lo schema di denominazione di righe e colonne da Lotus 1-2-3, ma anche di garantire la compatibilità dei bug trattando deliberatamente il 1900 come un anno bisestile. Questo problema esiste ancora oggi. Cioè, in 1-2-3 si trattava di un bug, ma in Excel è stata una decisione consapevole che assicurava che tutti gli utenti 1-2-3 potessero importare le proprie tabelle in Excel senza modificare i dati, anche se erano errati.

Ma c'era un altro problema. Innanzitutto, Microsoft ha rilasciato Excel per Macintosh, che non riconosceva le date prima del 1 gennaio 1904. E in Excel, il 1 gennaio 1900 era considerato l'inizio dell'era. Pertanto, gli sviluppatori hanno apportato una modifica in modo che il loro programma riconoscesse il tipo di epoca e memorizzasse i dati al suo interno in conformità con l'era desiderata. Microsoft ha persino scritto un articolo esplicativo al riguardo. E questa decisione ha portato al mio bug.

Il mio sistema ETL ha ricevuto fogli di calcolo Excel dai clienti creati su Windows, ma che potevano essere creati anche su Mac. Pertanto, l'inizio dell'era nella tabella potrebbe essere il 1 gennaio 1900 o il 1 gennaio 1904. Come scoprirlo? Il formato del file Excel mostra le informazioni necessarie, ma il parser che ho usato non le ha mostrate (ora lo fa) e presuppone che tu conosca l'epoca per una tabella specifica. Probabilmente avrei potuto dedicare più tempo alla comprensione del formato binario di Excel e all'invio di una patch all'autore del parser, ma avevo molto altro da fare per il client, quindi ho scritto rapidamente un'euristica per determinare l'epoca. Era semplice.

In Excel, la data 5 luglio 1998 può essere rappresentata nel formato "07-05-98" (sistema americano inutile), "5 luglio 98", "5 luglio 1998", "5-lug-98" o qualche altro formato un altro formato inutile (ironicamente, uno dei formati che la mia versione di Excel non offriva era ISO 8601). Tuttavia, all'interno della tabella, la data non formattata è stata memorizzata come "35981" per l'epoca-1900 o "34519" per l'epoca-1904 (i numeri rappresentano il numero di giorni trascorsi dall'epoca). Ho semplicemente utilizzato un semplice parser per estrarre l'anno dalla data formattata, quindi ho utilizzato il parser di Excel per estrarre l'anno dalla data non formattata. Se entrambi i valori differivano di 4 anni, allora sapevo che stavo usando un sistema con l'epoca 1904.

Perché non ho semplicemente utilizzato le date formattate? Perché il 5 luglio 1998 può essere formattato come "Luglio 98" perdendo il giorno del mese. Abbiamo ricevuto tabelle da così tante aziende che le hanno create in così tanti modi diversi che spettava a noi (in questo caso, a me) stabilire le date. Inoltre, se Excel lo fa bene, allora dovremmo farlo anche noi!

Allo stesso tempo ho incontrato 39082. Lascia che ti ricordi che Lotus 1-2-3 considerava il 1900 un anno bisestile, e questo veniva ripetuto fedelmente in Excel. E poiché questo aggiunge un giorno all'anno 1900, molte funzioni di calcolo della data potrebbero essere errate proprio per quel giorno. Cioè, 39082 avrebbe potuto essere il 1 gennaio 2011 (su Mac) o il 31 dicembre 2006 (su Windows). Se il mio "parser anno" ha estratto l'anno 2011 dal valore formattato, allora va tutto bene. Ma poiché il parser di Excel non sa quale epoca viene utilizzata, per impostazione predefinita utilizza l'epoca 1900, restituendo l'anno 2006. La mia applicazione ha rilevato che la differenza era di 5 anni, lo ha considerato un errore, lo ha registrato e ha restituito un valore non formattato.

Per aggirare questo problema, ho scritto questo (pseudocodice):

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

E poi tutte le 40 date sono state analizzate correttamente.

Nel mezzo di grandi lavori di stampa

All'inizio degli anni '1980, mio ​​padre lavorava presso Storage Technology, una divisione ormai defunta che creava unità a nastro e sistemi pneumatici per l'alimentazione del nastro ad alta velocità.

Hanno ridisegnato le unità in modo che potessero avere un'unità centrale “A” collegata a sette unità “B” e il piccolo sistema operativo nella RAM che controllava l'unità “A” potesse delegare le operazioni di lettura e scrittura a tutte le unità “B”.

Ad ogni avvio del drive “A”, era necessario inserire un floppy disk nel drive periferico collegato ad “A” per caricare nella sua memoria il sistema operativo. Era estremamente primitivo: la potenza di calcolo era fornita da un microcontrollore a 8 bit.

Il pubblico target di tali apparecchiature erano le aziende con magazzini dati molto grandi - banche, catene di vendita al dettaglio, ecc. - che avevano bisogno di stampare molte etichette di indirizzi o estratti conto bancari.

Un cliente ha avuto un problema. Nel bel mezzo di un lavoro di stampa, una particolare unità “A” potrebbe smettere di funzionare, causando lo stallo dell'intero lavoro. Per ripristinare il funzionamento dell'unità, il personale ha dovuto riavviare tutto. E se ciò accadeva nel mezzo di un'attività di sei ore, veniva persa un'enorme quantità di tempo prezioso per il computer e il programma dell'intera operazione veniva interrotto.

I tecnici sono stati inviati da Storage Technologies. Ma nonostante i loro migliori sforzi, non sono riusciti a riprodurre il bug nelle condizioni di prova: sembrava che si verificasse nel bel mezzo di grandi lavori di stampa. Il problema non era l'hardware, hanno sostituito tutto quello che potevano: RAM, microcontrollore, unità floppy, ogni parte immaginabile dell'unità nastro: il problema persisteva.

Poi i tecnici hanno chiamato la sede e hanno chiamato l'Esperto.

L'esperto prese una sedia e una tazza di caffè, si sedette nella sala computer (a quei tempi c'erano stanze dedicate ai computer) e guardò il personale mettere in coda un grosso lavoro di stampa. L'esperto aspettava che si verificasse un guasto, e così è stato. Tutti guardarono l'Esperto, ma non aveva idea del perché ciò fosse accaduto. Allora ordinò di rimettere in coda il lavoro, e tutto il personale e i tecnici tornarono al lavoro.

L'esperto si sedette di nuovo sulla sedia e iniziò ad aspettare un fallimento. Passarono circa sei ore e si verificò il guasto. Anche in questo caso l'Esperto non aveva idee, tranne che tutto accadde in una stanza piena di gente. Ordinò di riavviare la missione, si sedette di nuovo e attese.

Al terzo fallimento, l'Esperto notò qualcosa. L'errore si è verificato quando il personale ha sostituito i nastri in un'unità esterna. Inoltre, il guasto si è verificato non appena uno dei dipendenti ha attraversato una determinata piastrella sul pavimento.

Il pavimento sopraelevato è stato realizzato con piastrelle di alluminio posate ad un'altezza compresa tra 6 e 8 pollici. Numerosi fili dei computer correvano sotto il pavimento sopraelevato per impedire a chiunque di calpestare accidentalmente un cavo importante. Le piastrelle sono state posate molto strette per evitare che i detriti finissero sotto il pavimento sopraelevato.

L'esperto si accorse che una delle piastrelle era deformata. Quando un dipendente metteva piede sul suo angolo, i bordi della piastrella sfregavano contro le piastrelle adiacenti. Con esse si sfregavano anche le parti in plastica che collegavano le piastrelle, provocando microscariche statiche che creavano interferenze in radiofrequenza.

Oggi la RAM è molto meglio protetta dalle interferenze in radiofrequenza. Ma in quegli anni non era così. L'esperto si è reso conto che questa interferenza disturbava la memoria e con essa il funzionamento del sistema operativo. Ha chiamato il servizio di supporto, ha ordinato nuove piastrelle, le ha installate lui stesso e il problema è scomparso.

C'è l'alta marea!

La storia si svolgeva in una sala server, al quarto o quinto piano di un ufficio a Portsmouth (credo), nella zona del porto.

Un giorno il server Unix con il database principale si è bloccato. Lo hanno riavviato, ma ha continuato felicemente a cadere ancora e ancora. Abbiamo deciso di chiamare qualcuno del servizio di supporto.

Il ragazzo dell'assistenza... penso che si chiamasse Mark, ma non importa... non credo di conoscerlo. Non importa, davvero. Restiamo con Mark, ok? Grande.

Così, qualche ora dopo è arrivato Mark (non è molta strada da Leeds a Portsmouth, lo sai), ha acceso il server e tutto ha funzionato senza problemi. Tipico dannato supporto, il cliente si arrabbia molto per questo. Mark esamina i file di registro e non trova nulla di spiacevole. Quindi Mark risale sul treno (o qualunque sia il mezzo di trasporto con cui è arrivato, per quanto ne so avrebbe potuto essere una mucca zoppa... comunque, non importa, ok?) e torna a Leeds, dopo aver sprecato il giorno.

Quella stessa sera il server si blocca di nuovo. La storia è la stessa... il server non si alza. Mark tenta di aiutare da remoto, ma il client non riesce ad avviare il server.

Un altro treno, autobus, meringa al limone o qualche altra schifezza, e Mark è di nuovo a Portsmouth. Guarda, il server si avvia senza problemi! Miracolo. Mark passa diverse ore a verificare che tutto sia in ordine con il sistema operativo o il software e parte per Leeds.

Verso metà giornata il server si blocca (prenditela con calma!). Questa volta sembra ragionevole coinvolgere il personale del supporto hardware per sostituire il server. Ma no, dopo circa 10 ore cade anche lui.

La situazione si è ripetuta per diversi giorni. Il server funziona, si blocca dopo circa 10 ore e non si avvia per le successive 2 ore. Hanno controllato il raffreddamento, le perdite di memoria, hanno controllato tutto, ma non hanno trovato nulla. Poi gli incidenti cessarono.

La settimana è trascorsa spensierata... tutti erano felici. Felice finché tutto non ricomincerà. L'immagine è la stessa. 10 ore di lavoro, 2-3 ore di inattività...

E poi qualcuno (penso mi abbiano detto che questa persona non aveva nulla a che fare con l'IT) ha detto:

"È la marea!"

L'esclamazione fu accolta da sguardi vuoti e probabilmente la mano di qualcuno esitò sul pulsante di chiamata della sicurezza.

"Smette di funzionare con la marea."

Questo sembrerebbe un concetto completamente estraneo agli addetti al supporto IT, che difficilmente leggeranno il Tide Yearbook mentre sono seduti a prendere un caffè. Hanno spiegato che ciò non poteva essere correlato in alcun modo alla marea, perché il server funzionava senza guasti da una settimana.

"La settimana scorsa la marea era bassa, ma questa settimana è alta."

Un po' di terminologia per chi non ha la patente nautica. Le maree dipendono dal ciclo lunare. E mentre la Terra ruota, ogni 12,5 ore l'attrazione gravitazionale del Sole e della Luna crea un'onda di marea. All'inizio del ciclo di 12,5 ore c'è l'alta marea, a metà del ciclo c'è il riflusso e alla fine c'è di nuovo l'alta marea. Ma quando cambia l’orbita della Luna, cambia anche la differenza tra bassa e alta marea. Quando la Luna si trova tra il Sole e la Terra o sul lato opposto della Terra (luna piena o assenza di luna), si ottengono le maree Syzygyn: le alte maree più alte e le basse maree più basse. A mezza luna otteniamo maree in quadratura: le maree più basse. La differenza tra i due estremi diminuisce notevolmente. Il ciclo lunare dura 28 giorni: sizigio - quadratura - sizigio - quadratura.

Quando ai tecnici è stata spiegata l'essenza delle forze delle maree, hanno subito pensato che fosse necessario chiamare la polizia. E abbastanza logico. Ma si scopre che il tizio aveva ragione. Due settimane prima, un cacciatorpediniere aveva ormeggiato non lontano dall'ufficio. Ogni volta che la marea la portava ad una certa altezza, la postazione radar della nave finiva al livello del pavimento della sala server. E il radar (o l’equipaggiamento per la guerra elettronica, o qualche altro giocattolo militare) creava il caos nei computer.

Missione di volo per il razzo

Mi è stato assegnato il compito di portare un grande sistema di controllo e monitoraggio del lancio di razzi (circa 400mila righe) su nuove versioni del sistema operativo, del compilatore e del linguaggio. Più precisamente, da Solaris 2.5.1 a Solaris 7, e dal Verdix Ada Development System (VADS), scritto in Ada 83, al sistema Rational Apex Ada, scritto in Ada 95. VADS è stato acquistato da Rational, e il suo prodotto è stato obsoleto, sebbene Rational abbia tentato di implementare versioni compatibili di pacchetti specifici VADS per facilitare la transizione al compilatore Apex.

Tre persone mi hanno aiutato a compilare il codice in modo pulito. Ci sono volute due settimane. E poi ho lavorato da solo per far funzionare il sistema. In breve, si trattava della peggiore architettura e implementazione di un sistema software che avessi mai incontrato, quindi ci sono voluti altri due mesi per completare il port. Il sistema è stato quindi sottoposto a test, che hanno richiesto diversi mesi. Ho immediatamente corretto i bug rilevati durante i test, ma il loro numero è diminuito rapidamente (il codice sorgente era un sistema di produzione, quindi la sua funzionalità funzionava in modo abbastanza affidabile, dovevo solo rimuovere i bug che si sono verificati durante l'adattamento al nuovo compilatore). Alla fine, quando tutto funzionava come doveva, sono stato trasferito a un altro progetto.

E il venerdì prima del Ringraziamento squillò il telefono.

Il lancio del razzo avrebbe dovuto essere testato in circa tre settimane e durante i test di laboratorio del conto alla rovescia la sequenza dei comandi è stata bloccata. Nella vita reale, ciò interromperebbe il test e, se il blocco si verificasse entro pochi secondi dall'avvio del motore, si verificherebbero diverse azioni irreversibili nei sistemi ausiliari, che richiederebbero una lunga e costosa preparazione del razzo. Non sarebbe iniziato, ma molte persone sarebbero rimaste molto dispiaciute per la perdita di tempo e di molti, molti soldi. Non permettere a nessuno di dirti che il Dipartimento della Difesa spende soldi in modo sconsiderato: non ho mai incontrato un responsabile appaltatori che non abbia messo il budget al primo posto o al secondo posto, seguito dal programma.

Nei mesi precedenti, questa sfida con il conto alla rovescia era stata lanciata centinaia di volte in molte varianti, con solo qualche piccolo intoppo. Quindi la probabilità che ciò accadesse era molto bassa, ma le sue conseguenze erano molto significative. Moltiplicate entrambi questi fattori e capirete che la notizia prevedeva una settimana di vacanze rovinata per me e per decine di ingegneri e manager.

E l'attenzione è stata prestata a me come persona che ha effettuato il porting del sistema.

Come con la maggior parte dei sistemi critici per la sicurezza, venivano registrati molti parametri, quindi era abbastanza facile identificare le poche righe di codice eseguite prima che il sistema si bloccasse. E, naturalmente, non c'era assolutamente nulla di insolito in loro: le stesse espressioni erano state eseguite con successo letteralmente migliaia di volte durante la stessa corsa.

Abbiamo chiamato il personale di Apex in Rational perché sono stati loro a sviluppare il compilatore e alcune delle routine da loro sviluppate erano richiamate nel codice sospetto. Loro (e tutti gli altri) furono colpiti dalla necessità di andare alla radice di un problema di importanza letteralmente nazionale.

Poiché nei diari non c'era nulla di interessante, abbiamo deciso di provare a riprodurre il problema in un laboratorio locale. Non è stato un compito facile poiché l'evento si è verificato circa una volta ogni 1000 esecuzioni. Una ragione sospetta era che una chiamata a una funzione mutex sviluppata dal fornitore (parte del pacchetto di migrazione VADS) Unlock non ha portato allo sblocco. Il thread di elaborazione che ha chiamato la funzione ha elaborato i messaggi heartbeat, che nominalmente arrivavano ogni secondo. Abbiamo alzato la frequenza a 10 Hz, cioè 10 volte al secondo, e abbiamo iniziato a correre. Circa un'ora dopo il sistema si è bloccato. Nel registro abbiamo visto che la sequenza dei messaggi registrati era la stessa del test fallito. Abbiamo effettuato diverse altre corse, il sistema si bloccava costantemente 45-90 minuti dopo la partenza e ogni volta il registro conteneva lo stesso percorso. Anche se tecnicamente eseguivamo codice diverso (la frequenza dei messaggi era diversa) il comportamento del sistema era lo stesso, quindi eravamo sicuri che questo scenario di carico causasse lo stesso problema.

Ora dovevamo capire dove si è verificato esattamente il blocco nella sequenza di espressioni.

Questa implementazione del sistema utilizzava il sistema di attività Ada e lo utilizzava incredibilmente male. Le attività sono un costrutto di alto livello eseguibile contemporaneamente in Ada, qualcosa come thread di esecuzione, integrati solo nel linguaggio stesso. Quando due attività devono comunicare, "fissano un appuntamento", si scambiano i dati necessari, quindi interrompono l'appuntamento e tornano alle loro esecuzioni indipendenti. Tuttavia, il sistema è stato implementato in modo diverso. Dopo che un'attività di destinazione si è incontrata, l'attività di destinazione si è incontrata con un'altra attività, che si è poi incontrata con una terza attività e così via fino al completamento di alcune elaborazioni. Successivamente, tutti questi appuntamenti venivano completati e ogni compito doveva tornare alla sua esecuzione. Si trattava cioè del sistema di chiamata di funzioni più costoso al mondo, che bloccava l'intero processo di “multitasking” mentre elaborava parte dei dati in ingresso. E prima questo non causava problemi solo perché il rendimento era molto basso.

Ho descritto questo meccanismo di attività perché quando veniva richiesto o previsto il completamento di un appuntamento, poteva verificarsi un "cambio di attività". Cioè, il processore potrebbe iniziare a elaborare un'altra attività pronta per essere eseguita. Si scopre che quando un'attività è pronta per incontrarsi con un'altra attività, un'attività completamente diversa può iniziare l'esecuzione e alla fine il controllo ritorna al primo appuntamento. E potrebbero verificarsi altri eventi che causano il cambio dell'attività; uno di questi eventi è una chiamata a una funzione di sistema, come la stampa o l'esecuzione di un mutex.

Per capire quale riga di codice causava il problema, dovevo trovare un modo per registrare i progressi attraverso una sequenza di istruzioni senza attivare un cambio di attività, il che avrebbe impedito il verificarsi di un arresto anomalo. Quindi non potevo approfittarne Put_Line()per evitare di eseguire operazioni di I/O. Potrei impostare una variabile contatore o qualcosa di simile, ma come posso vederne il valore se non riesco a visualizzarlo sullo schermo?

Inoltre, esaminando il registro, si è scoperto che, nonostante il blocco dell'elaborazione dei messaggi heartbeat, che bloccava tutte le operazioni di I/O del processo e impediva l'esecuzione di altre elaborazioni, altre attività indipendenti continuavano ad essere eseguite. Cioè, il lavoro non è stato bloccato del tutto, ma solo una catena (critica) di compiti.

Questo era l'indizio necessario per valutare l'espressione bloccante.

Ho creato un pacchetto Ada che conteneva un'attività, un tipo enumerato e una variabile globale di quel tipo. I letterali enumerabili erano legati a espressioni specifiche della sequenza problematica (ad es. Incrementing_Buffer_Index, Locking_Mutex, Mutex_Unlocked), quindi vi ho inserito le espressioni di assegnazione che assegnavano l'enumerazione corrispondente a una variabile globale. Poiché il codice oggetto di tutto questo memorizzava semplicemente una costante in memoria, il cambio di attività come risultato della sua esecuzione era estremamente improbabile. Eravamo principalmente diffidenti nei confronti delle espressioni che potevano cambiare l'attività, poiché il blocco si verificava durante l'esecuzione anziché il ritorno quando si ripristinava l'attività (per diversi motivi).

L'attività di tracciamento veniva semplicemente eseguita in loop e verificava periodicamente se il valore della variabile globale fosse cambiato. Ad ogni modifica, il valore veniva salvato in un file. Poi una breve attesa e un nuovo assegno. Ho scritto la variabile nel file perché l'attività è stata eseguita solo quando il sistema l'ha selezionata per l'esecuzione quando si cambia attività nell'area problematica. Qualunque cosa accada in questa attività non influenzerà le altre attività bloccate non correlate.

Ci si aspettava che quando il sistema raggiungesse il punto di esecuzione del codice problematico, la variabile globale venisse reimpostata quando si passa a ciascuna espressione successiva. Quindi accadrà qualcosa che causerà il cambio dell'attività e poiché la sua frequenza di esecuzione (10 Hz) è inferiore a quella dell'attività di monitoraggio, il monitor potrebbe acquisire il valore della variabile globale e scriverlo. In una situazione normale, potrei ottenere una sequenza ripetuta di un sottoinsieme di enumerazioni: gli ultimi valori della variabile al momento del cambio di attività. In caso di sospensione, la variabile globale non dovrebbe più cambiare e l'ultimo valore scritto indicherà quale espressione non è stata completata.

Ho eseguito il codice con il monitoraggio. Si immobilizzò. E il monitoraggio ha funzionato come un orologio.

Il log conteneva la sequenza prevista, interrotta da un valore che indicava che era stato chiamato un mutex Unlocke l'attività non è stata completata, come nel caso di migliaia di chiamate precedenti.

In quel momento gli ingegneri di Apex stavano analizzando febbrilmente il loro codice e trovarono un punto nel mutex dove, teoricamente, potrebbe verificarsi un blocco. Ma la sua probabilità era molto bassa, poiché solo una determinata sequenza di eventi verificatisi in un determinato momento poteva portare al blocco. La legge di Murphy, ragazzi, è la legge di Murphy.

Per proteggere la parte di codice di cui avevo bisogno, ho sostituito le chiamate alla funzione mutex (costruite sulla funzionalità mutex del sistema operativo) con un piccolo pacchetto Ada mutex nativo per controllare l'accesso mutex a quella parte.

L'ho inserito nel codice ed ho eseguito il test. Sette ore dopo il codice funzionava ancora.

Il mio codice è stato inviato a Rational, dove lo hanno compilato, disassemblato e verificato che non utilizzasse lo stesso approccio utilizzato nelle funzioni mutex problematiche.

Questa è stata la revisione del codice più affollata della mia carriera 🙂 C'erano una decina di ingegneri e manager nella stanza con me, altre dieci persone erano in teleconferenza e tutti hanno esaminato circa 20 righe di codice.

Il codice è stato rivisto, nuovi file eseguibili sono stati assemblati e sottoposti a test di regressione formale. Un paio di settimane dopo, il test del conto alla rovescia ha avuto successo e il razzo è decollato.

Ok, va tutto bene, ma qual è il punto della storia?

Era un problema assolutamente disgustoso. Centinaia di migliaia di righe di codice, esecuzione parallela, oltre una dozzina di processi interagenti, architettura e implementazione scadenti, interfacce per sistemi embedded e milioni di dollari spesi. Nessuna pressione, giusto.

Non sono stato l'unico a lavorare su questo problema, anche se ero sotto i riflettori mentre stavo realizzando il porting. Ma anche se l'ho fatto, ciò non significa che ho capito tutte le centinaia di migliaia di righe di codice, o addirittura le ho scremate. Il codice e i log sono stati analizzati dagli ingegneri di tutto il Paese, ma quando mi hanno spiegato le loro ipotesi sulle cause del guasto, mi ci è voluto solo mezzo minuto per confutarle. E quando mi chiedevano di analizzare le teorie, le passavo a qualcun altro, perché per me era ovvio che questi ingegneri stavano andando nella direzione sbagliata. Sembra presuntuoso? Sì, è vero, ma ho respinto le ipotesi e le richieste per un altro motivo.

Ho capito la natura del problema. Non sapevo esattamente dove stesse succedendo o perché, ma sapevo cosa stava succedendo.

Nel corso degli anni ho accumulato molta conoscenza ed esperienza. Sono stato uno dei pionieri nell'utilizzo di Ada e ne ho compreso i vantaggi e gli svantaggi. So come le librerie runtime Ada gestiscono le attività e gestiscono l'esecuzione parallela. E capisco la programmazione di basso livello a livello di memoria, registri e assembler. In altre parole, ho una profonda conoscenza nel mio campo. E li ho usati per trovare la causa del problema. Non ho semplicemente aggirato il bug, ho capito come trovarlo in un ambiente di runtime molto sensibile.

Tali storie di lotta con il codice non sono molto interessanti per coloro che non hanno familiarità con le caratteristiche e le condizioni di tale lotta. Ma queste storie ci aiutano a capire cosa serve per risolvere problemi davvero difficili.

Per risolvere problemi davvero difficili, devi essere più di un semplice programmatore. È necessario comprendere il "destino" del codice, come interagisce con l'ambiente e come funziona l'ambiente stesso.

E poi avrai la tua settimana di vacanza rovinata.

Per essere continuato.

Fonte: habr.com

Aggiungi un commento