Folklore von Programmierern und Ingenieuren (Teil 1)

Folklore von Programmierern und Ingenieuren (Teil 1)

Dies ist eine Auswahl von Geschichten aus dem Internet darüber, wie Insekten manchmal völlig unglaubliche Erscheinungsformen haben. Vielleicht haben Sie auch etwas zu erzählen.

Autoallergie gegen Vanilleeis

Eine Geschichte für Ingenieure, die verstehen, dass das Offensichtliche nicht immer die Antwort ist und dass die Fakten, egal wie weit hergeholt sie auch erscheinen mögen, immer noch die Fakten sind. Die Pontiac-Abteilung der General Motors Corporation erhielt eine Beschwerde:

Dies ist das zweite Mal, dass ich Ihnen schreibe, und ich kann es Ihnen nicht verübeln, dass Sie nicht geantwortet haben, denn es klingt verrückt. In unserer Familie ist es Tradition, jeden Abend nach dem Abendessen ein Eis zu essen. Die Eissorten wechseln jedes Mal und nach dem Abendessen wählt die ganze Familie aus, welches Eis sie kaufen möchte, und dann gehe ich in den Laden. Ich habe kürzlich einen neuen Pontiac gekauft und seitdem sind meine Fahrten zum Eisessen zu einem Problem geworden. Denn jedes Mal, wenn ich Vanilleeis kaufe und aus dem Laden zurückkomme, springt das Auto nicht an. Wenn ich anderes Eis mitbringe, springt das Auto problemlos an. Ich möchte eine ernsthafte Frage stellen, egal wie dumm es klingt: „Was hat es mit dem Pontiac auf sich, der dazu führt, dass er nicht anspringt, wenn ich Vanilleeis mitbringe, sondern leicht anspringt, wenn ich eine andere Eissorte mitbringe?“

Wie Sie sich vorstellen können, stand der Bereichsleiter dem Brief skeptisch gegenüber. Für alle Fälle habe ich jedoch einen Techniker zur Überprüfung geschickt. Er war überrascht, dass ihm ein wohlhabender, gebildeter Mann begegnete, der in einer wunderschönen Gegend lebte. Sie verabredeten sich gleich nach dem Abendessen, damit sie gemeinsam in den Laden gehen konnten, um Eis zu kaufen. An diesem Abend war es wie Vanille, und als sie zum Auto zurückkamen, sprang es nicht an.

Der Ingenieur kam noch drei Abende. Das erste Mal war das Eis Schokolade. Das Auto sprang an. Beim zweiten Mal gab es Erdbeereis. Das Auto sprang an. Am dritten Abend bat er um Vanille. Das Auto sprang nicht an.

Der Ingenieur weigerte sich rational zu glauben, dass das Auto allergisch auf Vanilleeis reagierte. Deshalb habe ich mit dem Besitzer des Autos vereinbart, dass er seine Besuche fortsetzen wird, bis er eine Lösung für das Problem gefunden hat. Und nebenbei begann er, sich Notizen zu machen: Er notierte alle Informationen, Tageszeit, Benzinsorte, Ankunfts- und Rückkehrzeit aus dem Laden usw.

Der Ingenieur stellte schnell fest, dass der Besitzer des Autos weniger Zeit damit verbrachte, Vanilleeis zu kaufen. Der Grund war die Anordnung der Waren im Laden. Vanilleeis war am beliebtesten und wurde in einem separaten Gefrierschrank vor dem Laden aufbewahrt, um es leichter zu finden. Und alle anderen Sorten befanden sich hinten im Laden, und es dauerte viel länger, die richtige Sorte zu finden und zu bezahlen.

Nun stellte sich für den Ingenieur die Frage: Warum sprang das Auto nicht an, wenn seit dem Abstellen des Motors weniger Zeit vergangen war? Da das Problem die Zeit und nicht das Vanilleeis war, fand der Ingenieur schnell die Antwort: Es war eine Gasschleuse. Es passierte jeden Abend, aber als der Autobesitzer mehr Zeit damit verbrachte, nach Eis zu suchen, konnte der Motor ausreichend abkühlen und sprang problemlos an. Und als der Mann Vanilleeis kaufte, war der Motor noch zu heiß und die Gassperre hatte keine Zeit, sich aufzulösen.

Moral: Selbst völlig verrückte Probleme sind manchmal real.

Crash Bandicoot

Es ist schmerzhaft, dies zu erleben. Als Programmierer gewöhnen Sie sich daran, an erster Stelle, an zweiter Stelle, an dritter Stelle Ihrem Code die Schuld zu geben ... und irgendwo an der zehntausendsten Stelle geben Sie dem Compiler die Schuld. Und weiter unten in der Liste geben Sie bereits der Ausrüstung die Schuld.

Hier ist meine Geschichte über den Hardwarefehler.

Für das Spiel Crash Bandicoot habe ich Code zum Laden und Speichern auf einer Speicherkarte geschrieben. Für einen so selbstgefälligen Spieleentwickler war es wie ein Spaziergang: Ich dachte, die Arbeit würde mehrere Tage dauern. Am Ende habe ich den Code jedoch sechs Wochen lang debuggt. Unterwegs habe ich andere Probleme gelöst, bin aber alle paar Tage für ein paar Stunden zu diesem Code zurückgekehrt. Es war eine Qual.

Das Symptom sah so aus: Wenn man den aktuellen Durchlauf des Spiels speichert und auf die Speicherkarte zugreift, läuft fast immer alles gut ... Aber manchmal kommt es beim Lese- oder Schreibvorgang ohne ersichtlichen Grund zu Zeitüberschreitungen. Eine kurze Aufnahme beschädigt oft die Speicherkarte. Wenn ein Spieler versucht zu speichern, scheitert er nicht nur, sondern zerstört auch die Karte. Mist.

Nach einer Weile geriet unsere Produzentin bei Sony, Connie Bus, in Panik. Wir konnten das Spiel mit diesem Fehler nicht ausliefern und sechs Wochen später verstand ich nicht, was das Problem verursachte. Über Connie haben wir Kontakt zu anderen PS1-Entwicklern aufgenommen: Ist jemand auf etwas Ähnliches gestoßen? Nein. Niemand hatte Probleme mit der Speicherkarte.

Wenn Sie keine Ideen zum Debuggen haben, bleibt Ihnen nur das „Teilen und Herrschen“: Entfernen Sie immer mehr Code aus dem fehlerhaften Programm, bis nur noch ein relativ kleines Fragment übrig ist, das das Problem immer noch verursacht. Das heißt, Sie schneiden das Programm Stück für Stück ab, bis der Teil übrig bleibt, der den Fehler enthält.

Aber die Sache ist, dass es sehr schwierig ist, Teile aus einem Videospiel herauszuschneiden. Wie wird es ausgeführt, wenn Sie den Code entfernt haben, der die Schwerkraft emuliert? Oder Charaktere zeichnen?

Deshalb müssen wir ganze Module durch Stubs ersetzen, die vorgeben, etwas Nützliches zu tun, in Wirklichkeit aber etwas sehr Einfaches tun, das keine Fehler enthalten darf. Wir müssen solche Krücken schreiben, damit das Spiel zumindest funktioniert. Dies ist ein langsamer und schmerzhafter Prozess.

Kurz gesagt, ich habe es geschafft. Ich habe immer mehr Codeteile entfernt, bis ich nur noch den ursprünglichen Code übrig hatte, der das System für die Ausführung des Spiels konfiguriert, die Rendering-Hardware initialisiert usw. Natürlich konnte ich zu diesem Zeitpunkt noch kein Speicher- und Lademenü erstellen, da ich einen Stub für den gesamten Grafikcode erstellen müsste. Aber ich könnte mich über den (unsichtbaren) Speicher- und Ladebildschirm als Benutzer ausgeben und darum bitten, zu speichern und dann auf die Speicherkarte zu schreiben.

Dies hinterließ bei mir einen kleinen Codeabschnitt, der immer noch das oben genannte Problem aufwies – aber es passierte immer noch zufällig! Meistens funktionierte alles gut, aber gelegentlich gab es Störungen. Ich habe fast den gesamten Spielcode entfernt, aber der Fehler bestand immer noch. Das war rätselhaft: Der verbleibende Code hat eigentlich nichts bewirkt.

Irgendwann, wahrscheinlich gegen drei Uhr morgens, kam mir ein Gedanke. Lese- und Schreibvorgänge (Eingabe/Ausgabe) erfordern genaue Ausführungszeiten. Wenn Sie mit einer Festplatte, einer Speicherkarte oder einem Bluetooth-Modul arbeiten, geschieht dies der Low-Level-Code, der für das Lesen und Schreiben verantwortlich ist, entsprechend den Taktimpulsen.

Mit Hilfe einer Uhr wird ein Gerät, das nicht direkt mit dem Prozessor verbunden ist, mit dem auf dem Prozessor ausgeführten Code synchronisiert. Die Uhr bestimmt die Baudrate – die Geschwindigkeit, mit der Daten übertragen werden. Wenn es eine Verwechslung mit den Zeitvorgaben gibt, ist entweder die Hardware oder die Software oder beides ebenfalls verwechselt. Und das ist sehr schlimm, denn die Daten können beschädigt werden.

Was passiert, wenn etwas in unserem Code die Timings durcheinander bringt? Ich habe im Testprogrammcode alles dazu überprüft und festgestellt, dass wir den programmierbaren Timer in PS1 auf 1 kHz (1000 Ticks pro Sekunde) eingestellt haben. Das ist ziemlich viel; standardmäßig läuft die Konsole beim Start mit 100 Hz. Und die meisten Spiele nutzen diese Frequenz.

Andy, der Spieleentwickler, hat den Timer auf 1 kHz eingestellt, damit Bewegungen genauer berechnet werden. Andy neigt dazu, es zu übertreiben, und wenn wir die Schwerkraft nachahmen, tun wir es so genau wie möglich!

Aber was wäre, wenn sich die Beschleunigung des Timers irgendwie auf das Gesamttiming des Programms und damit auf die Uhr auswirken würde, die die Baudrate für die Speicherkarte reguliert?

Ich habe den Timer-Code auskommentiert. Der Fehler trat nicht erneut auf. Dies bedeutet jedoch nicht, dass wir das Problem behoben haben, da der Fehler zufällig aufgetreten ist. Was wäre, wenn ich einfach nur Glück hätte?

Einige Tage später experimentierte ich erneut mit dem Testprogramm. Der Fehler trat nicht erneut auf. Ich kehrte zur vollständigen Codebasis des Spiels zurück und änderte den Speicher- und Ladecode so, dass der programmierbare Timer vor dem Zugriff auf die Speicherkarte auf seinen ursprünglichen Wert (100 Hz) und dann wieder auf 1 kHz zurückgesetzt wurde. Es gab keine weiteren Abstürze.

Aber warum ist das passiert?

Ich kehrte wieder zum Testprogramm zurück. Ich habe versucht, bei einem 1-kHz-Timer ein Muster für das Auftreten eines Fehlers zu finden. Irgendwann ist mir aufgefallen, dass der Fehler auftritt, wenn jemand mit einem PS1-Controller spielt. Da ich das selbst selten tun würde – warum sollte ich beim Testen von Speicher- und Ladecode einen Controller benötigen? - Diese Abhängigkeit ist mir gar nicht aufgefallen. Aber eines Tages wartete einer unserer Künstler darauf, dass ich mit dem Testen fertig war – ich fluchte in diesem Moment wahrscheinlich – und drehte nervös den Controller in seinen Händen. Ein Fehler ist aufgetreten. "Warte was?!" Nun, mach es noch einmal!“

Als mir klar wurde, dass diese beiden Ereignisse miteinander verbunden waren, konnte ich den Fehler leicht reproduzieren: Ich begann mit der Aufnahme auf die Speicherkarte, bewegte den Controller und zerstörte die Speicherkarte. Für mich sah es nach einem Hardwarefehler aus.

Ich kam zu Connie und erzählte ihr von meiner Entdeckung. Sie gab die Informationen an einen der Ingenieure weiter, die die PS1 entworfen hatten. „Unmöglich“, antwortete er, „es kann kein Hardwareproblem sein.“ Ich bat Connie, ein Gespräch für uns zu arrangieren.

Der Ingenieur rief mich an und wir diskutierten in seinem gebrochenen Englisch und meinem (extrem) gebrochenen Japanisch. Schließlich sagte ich: „Lassen Sie mich einfach mein 30-Zeilen-Testprogramm senden, bei dem das Bewegen des Controllers einen Fehler verursacht.“ Er hat zugestimmt. Er meinte, es sei Zeitverschwendung und er sei furchtbar beschäftigt mit der Arbeit an einem neuen Projekt, würde aber nachgeben, weil wir ein sehr wichtiger Entwickler für Sony seien. Ich habe mein Testprogramm aufgeräumt und es ihm geschickt.

Am nächsten Abend (wir waren in Los Angeles und er in Tokio) rief er mich an und entschuldigte sich verlegen. Es war ein Hardwareproblem.

Ich weiß nicht genau, was der Fehler war, aber wie ich in der Sony-Zentrale gehört habe, kam es zu Störungen bei Komponenten auf der Hauptplatine in der Nähe des Timer-Quarzes, wenn man den Timer auf einen ausreichend hohen Wert einstellte. Einer davon war ein Baudraten-Controller für die Speicherkarte, der auch die Baudrate für die Controller festlegte. Ich bin kein Ingenieur, also habe ich vielleicht etwas vermasselt.

Unterm Strich kam es jedoch zu Interferenzen zwischen Komponenten auf dem Motherboard. Und bei der gleichzeitigen Datenübertragung über den Controller-Port und den Speicherkarten-Port mit einem Timer mit 1 kHz gingen Bits verloren, Daten gingen verloren und die Karte wurde beschädigt.

Schlechte Kühe

In den 1980er Jahren schrieb mein Mentor Sergei Software für den SM-1800, einen sowjetischen Klon des PDP-11. Dieser Mikrocomputer wurde gerade an einem Bahnhof in der Nähe von Swerdlowsk, einem wichtigen Verkehrsknotenpunkt in der UdSSR, installiert. Das neue System war für die Routenführung von Waggons und Güterverkehr konzipiert. Aber es enthielt einen lästigen Fehler, der zu zufälligen Abstürzen und Abstürzen führte. Zu Stürzen kam es immer, wenn jemand abends nach Hause ging. Doch trotz einer gründlichen Untersuchung am nächsten Tag funktionierte der Computer in allen manuellen und automatischen Tests korrekt. Dies weist normalerweise auf eine Race Condition oder einen anderen Wettbewerbsfehler hin, der unter bestimmten Bedingungen auftritt. Da Sergei die Anrufe spät in der Nacht satt hatte, beschloss er, der Sache auf den Grund zu gehen und zunächst herauszufinden, welche Bedingungen im Rangierbahnhof zum Ausfall des Computers führten.

Zunächst sammelte er Statistiken aller ungeklärten Stürze und erstellte eine Grafik nach Datum und Uhrzeit. Das Muster war offensichtlich. Nach einigen weiteren Beobachtungstagen erkannte Sergei, dass er den Zeitpunkt zukünftiger Systemausfälle leicht vorhersagen konnte.

Er erfuhr bald, dass es nur dann zu Störungen kam, wenn der Bahnhof Zugladungen mit Rindern aus der Nordukraine und Westrussland sortierte, die zu einem nahegelegenen Schlachthof transportiert wurden. Das war an sich schon seltsam, denn der Schlachthof wurde von Farmen beliefert, die viel näher in Kasachstan lagen.

Das Kernkraftwerk Tschernobyl explodierte 1986 und radioaktiver Niederschlag machte die umliegenden Gebiete unbewohnbar. Große Gebiete in der Nordukraine, Weißrussland und Westrussland wurden verseucht. Sergei vermutete hohe Strahlungswerte in den ankommenden Waggons und entwickelte eine Methode, um diese Theorie zu überprüfen. Da der Bevölkerung der Besitz von Dosimetern verboten war, meldete sich Sergej bei mehreren Militärs am Bahnhof. Nach mehreren Gläsern Wodka gelang es ihm, einen Soldaten davon zu überzeugen, die Strahlenbelastung in einem der verdächtigen Waggons zu messen. Es stellte sich heraus, dass der Wert um ein Vielfaches höher war als die normalen Werte.

Das Vieh strahlte nicht nur viel Strahlung aus, die Strahlung war auch so hoch, dass es zu einem zufälligen Verlust von Bits im Speicher des SM-1800 kam, der sich in einem Gebäude neben der Station befand.

In der UdSSR herrschte Nahrungsmittelknappheit, und die Behörden beschlossen, Tschernobyl-Fleisch mit Fleisch aus anderen Regionen des Landes zu mischen. Dadurch war es möglich, die Gesamtradioaktivität zu reduzieren, ohne wertvolle Ressourcen zu verlieren. Als Sergei davon erfuhr, füllte er sofort die Auswanderungsdokumente aus. Und die Computerabstürze hörten von selbst auf, als die Strahlenbelastung mit der Zeit abnahm.

Durch die Rohre

Es war einmal, dass Movietech Solutions Software für Kinos entwickelte, die für Buchhaltung, Ticketverkauf und allgemeine Verwaltung konzipiert war. Die DOS-Version der Flaggschiff-App erfreute sich bei kleinen und mittelgroßen Kinoketten in Nordamerika großer Beliebtheit. Es ist daher nicht verwunderlich, dass die Ankündigung einer Windows 95-Version, die mit den neuesten Touchscreens und Selbstbedienungskiosken integriert und mit allen möglichen Berichterstellungstools ausgestattet ist, schnell auch Popularität erlangte. Meistens verlief das Update ohne Probleme. Das örtliche IT-Personal installierte neue Geräte, migrierte Daten und der Betrieb ging weiter. Außer wenn es nicht von Dauer war. Wenn dies geschah, schickte das Unternehmen James mit dem Spitznamen „The Cleaner“ los.

Obwohl der Spitzname auf einen schändlichen Typ hindeutet, ist die Reinigungskraft nur eine Kombination aus Ausbilder, Installateur und Alleskönner. James verbrachte ein paar Tage beim Kunden vor Ort, um alle Komponenten zusammenzubauen, und verbrachte dann noch ein paar Tage damit, den Mitarbeitern beizubringen, wie sie das neue System verwenden, alle auftretenden Hardwareprobleme lösten und der Software im Wesentlichen dabei halfen, ihre Kinderschuhe zu überstehen.

Daher ist es nicht verwunderlich, dass James in diesen hektischen Zeiten morgens im Büro ankam und bevor er seinen Schreibtisch erreichen konnte, vom Manager begrüßt wurde, der über das übliche Maß hinaus mit Koffein gefüllt war.

„Ich fürchte, Sie müssen so schnell wie möglich nach Annapolis, Nova Scotia.“ Ihr gesamtes System ist ausgefallen, und nach einer Nacht voller Arbeit mit ihren Ingenieuren können wir nicht herausfinden, was passiert ist. Es sieht so aus, als ob das Netzwerk auf dem Server ausgefallen ist. Allerdings erst, nachdem das System mehrere Minuten lang gelaufen war.

— Sie sind nicht zum alten System zurückgekehrt? - James antwortete völlig ernst, obwohl er im Geiste vor Überraschung die Augen weitete.

— Genau: Ihr IT-Spezialist hat „die Prioritäten geändert“ und beschlossen, mit dem alten Server zu gehen. James, sie haben das System an sechs Standorten installiert und nur den Premium-Support bezahlt, und ihr Unternehmen wird jetzt wie in den 1950er Jahren geführt.

James richtete sich leicht auf.

- Das ist eine andere Sache. Okay, fangen wir an.

Als er in Annapolis ankam, suchte er als Erstes nach dem ersten Kino des Kunden, das ein Problem hatte. Auf der am Flughafen aufgenommenen Karte sah alles ordentlich aus, die Gegend um die gewünschte Adresse wirkte jedoch verdächtig. Kein Ghetto, aber eine Reminiszenz an Film Noir. Als James am Straßenrand in der Innenstadt parkte, kam eine Prostituierte auf ihn zu. Angesichts der Größe von Annapolis war es höchstwahrscheinlich das einzige in der ganzen Stadt. Ihr Auftritt erinnerte sofort an die berühmte Figur, die auf der großen Leinwand Sex gegen Geld anbot. Nein, nicht über Julia Roberts, sondern über Jon Voight [Anspielung auf den Film „Midnight Cowboy“ – ca. Fahrbahn].

Nachdem er die Prostituierte weggeschickt hatte, ging James ins Kino. Die Umgebung war zwar besser geworden, machte aber immer noch einen heruntergekommenen Eindruck. Nicht, dass James sich allzu viele Sorgen gemacht hätte. Er war schon einmal an elenden Orten. Und das war Kanada, wo selbst Straßenräuber höflich genug sind, „Danke“ zu sagen, nachdem sie Ihre Brieftasche gestohlen haben.

Der Seiteneingang zum Kino befand sich in einer feuchten Gasse. James ging zur Tür und klopfte. Bald knarrte es und öffnete sich leicht.

-Sind Sie eine Reinigungskraft? - Eine heisere Stimme kam von drinnen.

- Ja, ich bin es... Ich bin gekommen, um alles in Ordnung zu bringen.

James ging in die Kinolobby. Offenbar hatten die Mitarbeiter keine andere Wahl und begannen, den Besuchern Papiertickets zu verteilen. Dies erschwerte die Finanzberichterstattung, geschweige denn interessantere Details. Doch das Personal begrüßte James erleichtert und brachte ihn sofort in den Serverraum.

Auf den ersten Blick war alles in Ordnung. James meldete sich am Server an und überprüfte die üblichen verdächtigen Stellen. Kein Problem. Aus großer Vorsicht fuhr James jedoch den Server herunter, tauschte die Netzwerkkarte aus und setzte das System zurück. Sie begann sofort voll zu arbeiten. Das Personal begann wieder mit dem Ticketverkauf.

James rief Mark an und informierte ihn über die Situation. Es ist nicht schwer, sich vorzustellen, dass James hier bleiben und abwarten möchte, ob etwas Unerwartetes passiert. Er ging die Treppe hinunter und begann, die Angestellten zu fragen, was passiert sei. Offensichtlich funktioniert das System nicht mehr. Sie schalteten es aus und wieder ein, alles funktionierte. Aber nach 10 Minuten fiel das System aus.

Gerade in diesem Moment geschah etwas Ähnliches. Plötzlich fing das Ticketsystem an, Fehler zu melden. Das Personal seufzte und schnappte sich die Papiertickets, und James eilte in den Serverraum. Mit dem Server sah alles gut aus.

Dann kam einer der Angestellten herein.

— Das System funktioniert wieder.

James war verwirrt, weil er nichts getan hatte. Genauer gesagt, nichts, was das System zum Laufen bringen würde. Er loggte sich ab, griff zum Telefon und rief die Support-Hotline seines Unternehmens an. Bald darauf betrat derselbe Mitarbeiter den Serverraum.

- Das System ist ausgefallen.

James warf einen Blick auf den Server. Ein interessantes und vertrautes Muster aus mehrfarbigen Formen tanzte auf dem Bildschirm – chaotisch sich windende und ineinander verschlungene Rohre. Wir alle haben diesen Bildschirmschoner schon einmal gesehen. Es war wunderschön wiedergegeben und im wahrsten Sinne des Wortes hypnotisierend.


James drückte einen Knopf und das Muster verschwand. Er eilte zum Fahrkartenschalter und traf unterwegs einen Mitarbeiter, der zu ihm zurückkehrte.

— Das System funktioniert wieder.

Wenn Sie einen mentalen Facepalm machen können, ist das genau das, was James getan hat. Bildschirmschoner. Es verwendet OpenGL. Und verbraucht daher im Betrieb alle Ressourcen des Serverprozessors. Dies hat zur Folge, dass jeder Aufruf an den Server mit einem Timeout endet.

James kehrte in den Serverraum zurück, loggte sich ein und ersetzte den Bildschirmschoner mit den schönen Pfeifen durch einen leeren Bildschirm. Das heißt, anstelle eines Bildschirmschoners, der 100 % der Prozessorressourcen verbraucht, habe ich einen anderen installiert, der keine Ressourcen verbraucht. Dann wartete ich 10 Minuten, um meine Vermutung zu überprüfen.

Als James im nächsten Kino ankam, überlegte er, wie er seinem Manager erklären sollte, dass er gerade 800 km geflogen war, um den Bildschirmschoner auszuschalten.

Absturz während einer bestimmten Mondphase

Eine glaubwürdige Geschichte. Eines Tages trat ein Softwarefehler auf, der von der Mondphase abhing. Es gab eine kleine Routine, die in verschiedenen MIT-Programmen häufig verwendet wurde, um die Annäherung an die wahre Mondphase zu berechnen. GLS baute diese Routine in ein LISP-Programm ein, das beim Schreiben einer Datei eine Zeile mit einem fast 80 Zeichen langen Zeitstempel ausgab. Es kam sehr selten vor, dass die erste Zeile einer Nachricht zu lang wurde und zur nächsten Zeile führte. Und als das Programm später diese Datei las, fluchte es. Die Länge der ersten Zeile hing vom genauen Datum und der genauen Uhrzeit sowie von der Länge der Phasenangabe zum Zeitpunkt des Druckens des Zeitstempels ab. Das heißt, der Käfer hing buchstäblich von der Mondphase ab!

Erste Papierausgabe Jargon-Datei (Steele-1983) enthielt ein Beispiel für eine solche Zeile, die zu dem beschriebenen Fehler führte, der jedoch vom Schriftsetzer „behoben“ wurde. Dies wurde seitdem als „Mondphasenfehler“ beschrieben.

Seien Sie jedoch vorsichtig mit Annahmen. Vor einigen Jahren stießen Ingenieure des CERN (Europäisches Zentrum für Kernforschung) auf Fehler bei Experimenten, die am Large Electron-Positron Collider durchgeführt wurden. Da Computer die enormen Datenmengen, die dieses Gerät erzeugt, aktiv verarbeiten, bevor sie den Wissenschaftlern das Ergebnis zeigen, spekulierten viele, dass die Software irgendwie empfindlich auf die Mondphase reagierte. Mehrere verzweifelte Ingenieure sind der Wahrheit auf den Grund gegangen. Der Fehler entstand durch eine leichte Änderung der Geometrie des 27 km langen Rings aufgrund der Verformung der Erde während des Monddurchgangs! Diese Geschichte ist als „Newtons Rache an der Teilchenphysik“ in die Folklore der Physik eingegangen und ein Beispiel für die Verbindung zwischen den einfachsten und ältesten Gesetzen der Physik und den fortschrittlichsten wissenschaftlichen Konzepten.

Die Toilettenspülung stoppt den Zug

Der größte Hardwarefehler, von dem ich je gehört habe, trat in einem Hochgeschwindigkeitszug in Frankreich auf. Der Fehler führte zu einer Notbremsung des Zuges, allerdings nur, wenn Fahrgäste an Bord waren. In jedem dieser Fälle wurde der Zug außer Betrieb genommen und überprüft, es wurde jedoch nichts gefunden. Dann wurde er zurück an die Linie geschickt und kam sofort krachend zum Stehen.

Bei einer der Kontrollen ging ein im Zug reisender Lokführer auf die Toilette. Er wurde bald weggespült, BOOM! Not-Halt.

Der Ingenieur kontaktierte den Fahrer und fragte:

— Was hast du kurz vor dem Bremsen gemacht?

- Nun, ich bin beim Abstieg langsamer geworden ...

Das war seltsam, denn im Normalbetrieb bremst der Zug bei Bergabfahrten Dutzende Male ab. Der Zug fuhr weiter und bei der nächsten Talfahrt warnte der Lokführer:

- Ich werde langsamer.

Nichts ist passiert.

— Was haben Sie beim letzten Bremsen gemacht? - fragte den Fahrer.

- Na ja... ich war auf der Toilette...

- Na dann geh auf die Toilette und mach das, was du getan hast, wenn wir wieder runtergehen!

Der Lokführer ging zur Toilette und als der Fahrer warnte: „Ich werde langsamer“, ließ er die Wasserspülung laufen. Natürlich hielt der Zug sofort an.

Jetzt konnten sie das Problem reproduzieren und mussten die Ursache finden.

Nach zwei Minuten bemerkten sie, dass das Fernbedienungskabel der Motorbremse (der Zug hatte an jedem Ende einen Motor) von der Wand des Schaltschranks getrennt war und auf dem Relais lag, das das Magnetventil des Toilettensteckers steuerte ... Als das Relais Beim Einschalten verursachte es Störungen im Bremskabel und der Systemschutz vor Ausfällen umfasste einfach eine Notbremsung.

Das Tor, das FORTRAN hasste

Vor ein paar Monaten bemerkten wir, dass die Netzwerkverbindungen auf dem Festland [das war in Hawaii] sehr, sehr langsam wurden. Dies kann 10–15 Minuten dauern und dann plötzlich wieder auftreten. Nach einiger Zeit beschwerte sich mein Kollege bei mir über die Netzwerkverbindungen auf dem Festland im Allgemeinen arbeiten nicht. Er hatte einen FORTRAN-Code, der auf einen Computer auf dem Festland kopiert werden musste, was aber nicht möglich war, weil „das Netzwerk nicht lange genug durchhielt, um den FTP-Upload abzuschließen“.

Ja, es stellte sich heraus, dass es zu Netzwerkausfällen kam, als ein Kollege versuchte, eine Datei mit Quellcode in FORTRAN per FTP auf einen Rechner auf dem Festland zu übertragen. Wir haben versucht, die Datei zu archivieren: Dann wurde sie reibungslos kopiert (aber der Zielcomputer hatte keinen Entpacker, sodass das Problem nicht gelöst wurde). Schließlich „spalteten“ wir den FORTRAN-Code in sehr kleine Stücke auf und schickten sie einzeln. Die meisten Fragmente wurden ohne Probleme kopiert, aber einige Stücke wurden nicht bestanden oder gingen später weiter zahlreich Versuche.

Als wir die problematischen Passagen untersuchten, stellten wir fest, dass sie etwas gemeinsam hatten: Sie enthielten alle Kommentarblöcke, die mit Zeilen begannen und endeten, die aus einem großen C bestanden (wie ein Kollege es vorzog, in FORTRAN zu kommentieren). Wir haben Netzwerkexperten auf dem Festland eine E-Mail geschickt und um Hilfe gebeten. Natürlich wollten sie Muster unserer Dateien sehen, die nicht per FTP übertragen werden konnten... aber unsere Briefe erreichten sie nicht. Schließlich haben wir uns etwas Einfaches ausgedacht beschreibenwie nicht übertragbare Dateien aussehen. Es hat funktioniert :) [Darf ich hier ein Beispiel für einen der problematischen FORTRAN-Kommentare hinzufügen? Wahrscheinlich lohnt es sich nicht!]

Am Ende haben wir es geschafft, es herauszufinden. Vor kurzem wurde ein neues Gateway zwischen unserem Teil des Campus und dem Festlandnetz installiert. Es hatte RIESIGE Schwierigkeiten, Pakete zu übertragen, die wiederholte Bits von Großbuchstaben C enthielten! Nur wenige dieser Pakete könnten alle Gateway-Ressourcen beanspruchen und die meisten anderen Pakete daran hindern, durchzukommen. Wir haben uns beim Gateway-Hersteller beschwert... und er hat geantwortet: „Oh ja, Sie haben es mit einem Fehler durch wiederholtes C zu tun!“ Wir wissen bereits von ihm.“ Wir haben das Problem schließlich gelöst, indem wir ein neues Gateway von einem anderen Hersteller gekauft haben (zur Verteidigung des ersteren: Die Unfähigkeit, FORTRAN-Programme zu übertragen, könnte für einige von Vorteil sein!).

Harte Zeiten

Als ich vor einigen Jahren an der Erstellung eines ETL-Systems in Perl arbeitete, um die Kosten für klinische Studien der Phase 40 zu senken, musste ich etwa 000 Daten verarbeiten. Zwei von ihnen haben die Prüfung nicht bestanden. Das störte mich nicht allzu sehr, da diese Daten aus von Kunden bereitgestellten Daten stammten, was, sagen wir mal, oft überraschend war. Aber als ich die Originaldaten überprüfte, stellte sich heraus, dass diese Daten der 1. Januar 2011 und der 1. Januar 2007 waren. Ich dachte, dass der Fehler in dem Programm enthalten war, das ich gerade geschrieben hatte, aber es stellte sich heraus, dass es bereits 30 Jahre her war alt. Für diejenigen, die mit dem Software-Ökosystem nicht vertraut sind, mag dies mysteriös klingen. Aufgrund der langjährigen Entscheidung eines anderen Unternehmens, Geld zu verdienen, bezahlte mich mein Kunde dafür, einen Fehler zu beheben, den das eine Unternehmen versehentlich und das andere absichtlich eingeführt hatte. Damit Sie verstehen, wovon ich spreche, muss ich über das Unternehmen sprechen, das die Funktion hinzugefügt hat, die letztendlich zu einem Fehler wurde, sowie über einige andere interessante Ereignisse, die zu dem mysteriösen Fehler beigetragen haben, den ich behoben habe.

In der guten alten Zeit stellten Apple-Computer ihr Datum manchmal spontan auf den 1. Januar 1904 zurück. Der Grund war einfach: Es wurde eine batteriebetriebene „Systemuhr“ verwendet, um Datum und Uhrzeit im Auge zu behalten. Was ist passiert, als die Batterie leer war? Computer begannen, das Datum anhand der Anzahl der Sekunden seit Beginn einer Epoche zu verfolgen. Mit Epoche meinten wir das ursprüngliche Referenzdatum, und für Macintosh-Computer war es der 1. Januar 1904. Und nachdem die Batterie leer war, wurde das aktuelle Datum auf das angegebene Datum zurückgesetzt. Aber warum ist das passiert?

Zuvor verwendete Apple 32 Bit, um die Anzahl der Sekunden seit dem ursprünglichen Datum zu speichern. Ein Bit kann einen von zwei Werten speichern – 1 oder 0. Zwei Bits können einen von vier Werten speichern: 00, 01, 10, 11. Drei Bits – ein Wert von acht: 000, 001, 010, 011, 100 , 101, 110, 111 usw. Und 32 könnte einen von 232 Werten speichern, also 4 Sekunden. Bei Apple-Daten entsprach dies etwa 294 Jahren, sodass ältere Macs keine Daten nach 967 verarbeiten können. Und wenn die Systembatterie leer ist, wird das Datum auf 296 Sekunden seit Beginn der Epoche zurückgesetzt, und Sie müssen das Datum jedes Mal manuell einstellen, wenn Sie den Computer einschalten (oder bis Sie eine neue Batterie kaufen).

Die Entscheidung von Apple, Daten als Sekunden seit der Epoche zu speichern, bedeutete jedoch, dass wir Daten vor der Epoche nicht verarbeiten konnten, was weitreichende Konsequenzen hatte, wie wir sehen werden. Apple hat eine Funktion eingeführt, keinen Fehler. Dies bedeutete unter anderem, dass das Macintosh-Betriebssystem immun gegen den „Millennium-Bug“ war (was man von vielen Mac-Anwendungen, die über eigene Datumssysteme zur Umgehung von Beschränkungen verfügten, nicht behaupten konnte).

Fortfahren. Wir nutzten Lotus 1-2-3, die „Killeranwendung“ von IBM, die zum Start der PC-Revolution beitrug, obwohl Apple-Computer über VisiCalc verfügten, was den Personal Computer zum Erfolg machte. Fairerweise muss man sagen: Wenn 1-2-3 nicht erschienen wäre, hätten sich PCs kaum durchgesetzt, und die Geschichte der Personalcomputer hätte sich ganz anders entwickeln können. Lotus 1-2-3 behandelte 1900 fälschlicherweise als Schaltjahr. Als Microsoft seine erste Tabellenkalkulation Multiplan herausbrachte, eroberte es einen kleinen Marktanteil. Und als sie das Excel-Projekt starteten, beschlossen sie, nicht nur das Zeilen- und Spaltenbenennungsschema von Lotus 1-2-3 zu kopieren, sondern auch die Fehlerkompatibilität sicherzustellen, indem sie 1900 bewusst als Schaltjahr behandelten. Dieses Problem besteht auch heute noch. Das heißt, in 1-2-3 war dies ein Fehler, aber in Excel war es eine bewusste Entscheidung, die sicherstellte, dass alle 1-2-3-Benutzer ihre Tabellen in Excel importieren konnten, ohne die Daten zu ändern, selbst wenn diese falsch waren.

Aber es gab noch ein anderes Problem. Zunächst veröffentlichte Microsoft Excel für den Macintosh, das Datumsangaben vor dem 1. Januar 1904 nicht erkannte. Und in Excel galt der 1. Januar 1900 als Beginn der Ära. Deshalb haben die Entwickler eine Änderung vorgenommen, sodass ihr Programm die Art der Epoche erkennt und Daten entsprechend der gewünschten Epoche in sich selbst speichert. Microsoft hat dazu sogar einen erklärenden Artikel geschrieben. Und diese Entscheidung führte zu meinem Fehler.

Mein ETL-System erhielt Excel-Tabellen von Kunden, die unter Windows erstellt wurden, aber auch auf einem Mac erstellt werden konnten. Daher könnte der Beginn der Ära in der Tabelle entweder der 1. Januar 1900 oder der 1. Januar 1904 sein. Wie finde ich das heraus? Das Excel-Dateiformat zeigt die notwendigen Informationen an, aber der Parser, den ich verwendet habe, zeigte sie nicht an (jetzt zeigt er sie an) und ging davon aus, dass Sie die Epoche für eine bestimmte Tabelle kennen. Ich hätte wahrscheinlich mehr Zeit damit verbringen können, das Excel-Binärformat zu verstehen und einen Patch an den Parser-Autor zu senden, aber ich hatte viel mehr für den Client zu tun, also habe ich schnell eine Heuristik geschrieben, um die Epoche zu bestimmen. Sie war einfach.

In Excel kann das Datum 5. Juli 1998 im Format „07-05-98“ (nutzloses amerikanisches System), „Jul 5, 98“, „July 5, 1998“, „5-Jul-98“ oder dargestellt werden ein anderes Format. ein anderes nutzloses Format (ironischerweise war eines der Formate, die meine Excel-Version nicht anbot, ISO 8601). In der Tabelle wurde das unformatierte Datum jedoch entweder als „35981“ für Epoche 1900 oder als „34519“ für Epoche 1904 gespeichert (die Zahlen geben die Anzahl der Tage seit der Epoche an). Ich habe einfach einen einfachen Parser verwendet, um das Jahr aus dem formatierten Datum zu extrahieren, und dann den Excel-Parser verwendet, um das Jahr aus dem unformatierten Datum zu extrahieren. Wenn sich beide Werte um 4 Jahre unterschieden, wusste ich, dass ich ein System mit der Epoche 1904 verwende.

Warum habe ich nicht einfach formatierte Datumsangaben verwendet? Denn der 5. Juli 1998 kann als „Juli 98“ formatiert werden, wobei der Tag des Monats verloren geht. Wir haben Tabellen von so vielen Unternehmen erhalten, die sie auf so unterschiedliche Weise erstellt haben, dass es an uns (in diesem Fall an mir) lag, die Daten herauszufinden. Außerdem: Wenn Excel alles richtig macht, sollten wir das auch tun!

Zur gleichen Zeit stieß ich auf 39082. Ich möchte Sie daran erinnern, dass Lotus 1-2-3 das Jahr 1900 als Schaltjahr betrachtete und dies in Excel originalgetreu wiederholt wurde. Und da dadurch das Jahr 1900 um einen Tag verlängert wurde, könnten viele Datumsberechnungsfunktionen für genau diesen Tag falsch sein. Das heißt, 39082 könnte der 1. Januar 2011 (auf Macs) oder der 31. Dezember 2006 (auf Windows) gewesen sein. Wenn mein „Jahresparser“ das Jahr 2011 aus dem formatierten Wert extrahiert hat, ist alles in Ordnung. Da der Excel-Parser jedoch nicht weiß, welche Epoche verwendet wird, verwendet er standardmäßig die Epoche 1900 und gibt das Jahr 2006 zurück. Meine Anwendung stellte fest, dass die Differenz 5 Jahre betrug, betrachtete dies als Fehler, protokollierte ihn und gab einen unformatierten Wert zurück.

Um dies zu umgehen, habe ich Folgendes geschrieben (Pseudocode):

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

Und dann wurden alle 40 Daten korrekt analysiert.

Mitten in großen Druckaufträgen

In den frühen 1980er Jahren arbeitete mein Vater bei Storage Technology, einer inzwischen aufgelösten Abteilung, die Bandlaufwerke und pneumatische Systeme für den Hochgeschwindigkeits-Bandvorschub entwickelte.

Sie haben die Laufwerke so umgestaltet, dass ein zentrales „A“-Laufwerk mit sieben „B“-Laufwerken verbunden werden konnte und das kleine Betriebssystem im RAM, das das „A“-Laufwerk steuerte, Lese- und Schreibvorgänge an alle „B“-Laufwerke delegieren konnte.

Bei jedem Start von Laufwerk „A“ war es notwendig, eine Diskette in das an „A“ angeschlossene Peripherielaufwerk einzulegen, um das Betriebssystem in dessen Speicher zu laden. Es war äußerst primitiv: Die Rechenleistung wurde von einem 8-Bit-Mikrocontroller bereitgestellt.

Die Zielgruppe dieser Geräte waren Unternehmen mit sehr großen Datenlagern – Banken, Einzelhandelsketten usw. –, die viele Adressetiketten oder Kontoauszüge drucken mussten.

Ein Kunde hatte ein Problem. Während eines Druckauftrags kann es vorkommen, dass ein bestimmtes Laufwerk „A“ nicht mehr funktioniert und der gesamte Auftrag zum Stillstand kommt. Um den Betrieb des Laufwerks wiederherzustellen, mussten die Mitarbeiter alles neu starten. Und wenn dies mitten in einer sechsstündigen Aufgabe passierte, ging eine Menge teurer Computerzeit verloren und der Zeitplan des gesamten Vorgangs wurde gestört.

Techniker wurden von Storage Technologies entsandt. Doch trotz aller Bemühungen gelang es ihnen nicht, den Fehler unter Testbedingungen zu reproduzieren: Er schien mitten in großen Druckaufträgen aufzutreten. Das Problem war nicht die Hardware, sie ersetzten alles, was sie konnten: RAM, Mikrocontroller, Diskettenlaufwerk, jeden erdenklichen Teil des Bandlaufwerks – das Problem blieb bestehen.

Dann riefen die Techniker die Zentrale an und riefen den Experten an.

Der Experte schnappte sich einen Stuhl und eine Tasse Kaffee, setzte sich in den Computerraum – damals gab es Räume, die nur für Computer reserviert waren – und sah zu, wie die Mitarbeiter einen großen Druckauftrag in die Warteschlange stellten. Der Experte wartete darauf, dass ein Fehler eintritt – und das geschah auch. Alle schauten den Experten an, aber er hatte keine Ahnung, warum das passierte. Also ordnete er an, den Auftrag erneut in die Warteschlange zu stellen, und alle Mitarbeiter und Techniker gingen wieder an die Arbeit.

Der Experte setzte sich wieder auf den Stuhl und begann auf einen Misserfolg zu warten. Es vergingen etwa sechs Stunden und der Fehler trat auf. Der Experte hatte wiederum keine Ahnung, außer dass sich alles in einem Raum voller Menschen abspielte. Er befahl, die Mission neu zu starten, setzte sich wieder hin und wartete.

Beim dritten Fehler bemerkte der Experte etwas. Der Fehler trat auf, als das Personal die Bänder in einem fremden Laufwerk wechselte. Darüber hinaus trat der Fehler auf, sobald einer der Mitarbeiter durch eine bestimmte Bodenfliese ging.

Der Doppelboden bestand aus Aluminiumfliesen, die in einer Höhe von 6 bis 8 Zoll verlegt wurden. Unter dem Doppelboden verliefen zahlreiche Kabel von Computern, um zu verhindern, dass jemand versehentlich auf ein wichtiges Kabel tritt. Die Fliesen wurden sehr dicht verlegt, um zu verhindern, dass Schmutz unter den Doppelboden gelangt.

Der Sachverständige stellte fest, dass eine der Fliesen deformiert war. Als ein Mitarbeiter auf die Ecke trat, rieben die Kanten der Fliese an den angrenzenden Fliesen. Auch die Kunststoffteile, mit denen die Fliesen verbunden waren, rieben an ihnen, was zu statischen Mikroentladungen führte, die Funkfrequenzstörungen verursachten.

Heutzutage ist RAM viel besser vor Funkfrequenzstörungen geschützt. Aber das war in jenen Jahren nicht der Fall. Der Experte erkannte, dass dieser Eingriff den Speicher und damit den Betrieb des Betriebssystems störte. Er rief den Support an, bestellte neue Fliesen, verlegte sie selbst und das Problem verschwand.

Es ist Flut!

Die Geschichte spielte sich in einem Serverraum im vierten oder fünften Stock eines Büros in Portsmouth (glaube ich) im Hafenbereich ab.

Eines Tages stürzte der Unix-Server mit der Hauptdatenbank ab. Sie haben ihn neu gestartet, aber er fiel immer wieder glücklich hin. Wir beschlossen, jemanden vom Support anzurufen.

Der Support-Typ... Ich glaube, sein Name war Mark, aber das spielt keine Rolle... Ich glaube nicht, dass ich ihn kenne. Es spielt eigentlich keine Rolle. Bleiben wir bei Mark, okay? Großartig.

Ein paar Stunden später kam Mark an (von Leeds nach Portsmouth ist es ja nicht weit), schaltete den Server ein und alles funktionierte ohne Probleme. Typischer verdammter Support, der Kunde ist darüber sehr verärgert. Mark durchsucht die Protokolldateien und findet nichts Ungewöhnliches. Also steigt Mark wieder in den Zug (oder mit welchem ​​Transportmittel auch immer er gekommen ist, es hätte eine lahme Kuh sein können, soweit ich weiß ... egal, egal, okay?) und macht sich verschwendet auf den Rückweg nach Leeds der Tag.

Am selben Abend stürzt der Server erneut ab. Die Geschichte ist die gleiche... der Server steigt nicht. Mark versucht aus der Ferne zu helfen, aber der Client kann den Server nicht starten.

Noch ein Zug, Bus, Zitronenbaiser oder so ein Mist, und schon ist Mark zurück in Portsmouth. Schauen Sie, der Server bootet ohne Probleme! Wunder. Mark verbringt mehrere Stunden damit, zu überprüfen, ob mit dem Betriebssystem oder der Software alles in Ordnung ist, und macht sich auf den Weg nach Leeds.

Ungefähr zur Mittagszeit stürzt der Server ab (lassen Sie es ruhig an!). Dieses Mal erscheint es sinnvoll, die Hardware-Support-Mitarbeiter hinzuzuziehen, um den Server auszutauschen. Aber nein, nach ca. 10 Stunden fällt es auch.

Die Situation wiederholte sich mehrere Tage lang. Der Server funktioniert, stürzt nach ca. 10 Stunden ab und startet die nächsten 2 Stunden nicht. Sie überprüften die Kühlung, Speicherlecks, sie überprüften alles, fanden aber nichts. Dann hörten die Abstürze auf.

Die Woche verlief unbeschwert... alle waren glücklich. Glücklich, bis alles wieder von vorne beginnt. Das Bild ist das gleiche. 10 Stunden Arbeit, 2-3 Stunden Ausfallzeit...

Und dann sagte jemand (ich glaube, er hat mir gesagt, dass diese Person nichts mit IT zu tun hat):

„Es ist die Flut!“

Der Ausruf wurde mit ausdruckslosen Blicken beantwortet, und wahrscheinlich zögerte jemand beim Betätigen des Sicherheitsrufknopfs.

„Es funktioniert nicht mehr mit der Flut.“

Dies scheint ein völlig fremdes Konzept für IT-Supportmitarbeiter zu sein, die wahrscheinlich nicht das Tide Yearbook lesen, während sie sich zum Kaffeetrinken hinsetzen. Sie erklärten, dass dies in keiner Weise mit der Flut zusammenhängen könne, da der Server seit einer Woche ohne Ausfälle funktioniere.

„Letzte Woche war die Flut niedrig, aber diese Woche ist sie hoch.“

Eine kleine Terminologie für diejenigen, die keinen Yachtführerschein haben. Die Gezeiten hängen vom Mondzyklus ab. Und während sich die Erde dreht, erzeugt die Anziehungskraft von Sonne und Mond alle 12,5 Stunden eine Flutwelle. Zu Beginn des 12,5-Stunden-Zyklus herrscht Hochwasser, in der Mitte des Zyklus Ebbe und am Ende erneut Hochwasser. Aber wenn sich die Umlaufbahn des Mondes ändert, ändert sich auch der Unterschied zwischen Ebbe und Flut. Wenn sich der Mond zwischen der Sonne und der Erde oder auf der gegenüberliegenden Seite der Erde befindet (Vollmond oder kein Mond), gibt es Syzygyn-Gezeiten – das höchste Hochwasser und das niedrigste Niedrigwasser. Bei Halbmond gibt es Quadraturfluten – die niedrigsten Gezeiten. Der Unterschied zwischen den beiden Extremen nimmt stark ab. Der Mondzyklus dauert 28 Tage: Syzygian – Quadratur – Syzygian – Quadratur.

Als den Technikern das Wesen der Gezeitenkräfte erklärt wurde, dachten sie sofort, dass sie die Polizei rufen müssten. Und ganz logisch. Aber es stellte sich heraus, dass der Typ Recht hatte. Zwei Wochen zuvor machte ein Zerstörer unweit des Büros fest. Jedes Mal, wenn die Flut eine bestimmte Höhe erreichte, landete der Radarposten des Schiffes auf der Höhe des Bodens des Serverraums. Und das Radar (oder die elektronische Kriegsausrüstung oder ein anderes militärisches Spielzeug) verursachte Chaos in den Computern.

Flugmission für die Rakete

Ich wurde damit beauftragt, ein großes (ungefähr 400 Zeilen) Raketenstartkontroll- und -überwachungssystem auf neue Versionen des Betriebssystems, des Compilers und der Sprache zu portieren. Genauer gesagt, von Solaris 2.5.1 auf Solaris 7 und vom Verdix Ada Development System (VADS), geschrieben in Ada 83, zum Rational Apex Ada-System, geschrieben in Ada 95. VADS wurde von Rational gekauft, und sein Produkt war es auch veraltet, obwohl Rational versucht hat, kompatible Versionen von VADS-spezifischen Paketen zu implementieren, um den Übergang zum Apex-Compiler zu erleichtern.

Drei Leute haben mir geholfen, den Code sauber zu kompilieren. Es dauerte zwei Wochen. Und dann habe ich alleine daran gearbeitet, dass das System funktioniert. Kurz gesagt, es war die schlechteste Architektur und Implementierung eines Softwaresystems, die ich je erlebt hatte, sodass die Fertigstellung der Portierung weitere zwei Monate dauerte. Anschließend wurde das System zum Testen eingereicht, was noch mehrere Monate dauerte. Ich habe die beim Testen gefundenen Fehler sofort behoben, ihre Zahl nahm jedoch schnell ab (der Quellcode war ein Produktionssystem, daher funktionierte seine Funktionalität recht zuverlässig, ich musste nur die Fehler beseitigen, die bei der Anpassung an den neuen Compiler aufgetreten sind). Als schließlich alles so funktionierte, wie es sollte, wurde ich zu einem anderen Projekt versetzt.

Und am Freitag vor Thanksgiving klingelte das Telefon.

Der Raketenstart sollte in etwa drei Wochen getestet werden, und während der Labortests des Countdowns war die Befehlsfolge blockiert. Im wirklichen Leben würde dies zum Abbruch des Tests führen, und wenn die Blockade innerhalb weniger Sekunden nach dem Starten des Triebwerks auftritt, würden mehrere irreversible Aktionen in den Hilfssystemen auftreten, die eine lange – und teure – Bereitschaft der Rakete erfordern würden. Es hätte nicht angefangen, aber viele Leute wären sehr verärgert über den Verlust von Zeit und viel, viel Geld. Lassen Sie sich von niemandem sagen, dass das Verteidigungsministerium rücksichtslos Geld ausgibt – ich habe noch nie einen Vertragsmanager getroffen, bei dem das Budget nicht an erster oder zweiter Stelle stand und dann der Zeitplan.

In den vergangenen Monaten wurde diese Countdown-Challenge in vielen Variationen hunderte Male durchgeführt, mit nur wenigen kleineren Problemen. Die Wahrscheinlichkeit dafür war also sehr gering, aber die Folgen waren sehr erheblich. Wenn Sie diese beiden Faktoren multiplizieren, werden Sie verstehen, dass die Nachrichten eine ruinierte Urlaubswoche für mich und Dutzende von Ingenieuren und Managern vorhersagten.

Und mir als der Person, die das System portiert hat, wurde Aufmerksamkeit geschenkt.

Wie bei den meisten sicherheitskritischen Systemen wurden viele Parameter protokolliert, sodass es ziemlich einfach war, die wenigen Codezeilen zu identifizieren, die vor dem Systemabsturz ausgeführt wurden. Und natürlich war an ihnen absolut nichts Ungewöhnliches; dieselben Ausdrücke wurden im selben Lauf buchstäblich tausende Male erfolgreich ausgeführt.

Wir haben die Leute von Apex zu Rational gerufen, weil sie diejenigen waren, die den Compiler entwickelt haben und einige der von ihnen entwickelten Routinen im verdächtigen Code aufgerufen wurden. Sie (und alle anderen) waren beeindruckt, dass es notwendig war, einem Problem von buchstäblich nationaler Bedeutung auf den Grund zu gehen.

Da es in den Fachzeitschriften nichts Interessantes gab, beschlossen wir, das Problem in einem örtlichen Labor zu reproduzieren. Dies war keine leichte Aufgabe, da das Ereignis etwa einmal pro 1000 Läufe auftrat. Ein vermuteter Grund war, dass ein Aufruf einer vom Hersteller entwickelten Mutex-Funktion (Teil des VADS-Migrationspakets) Unlock führte nicht zur Entsperrung. Der Verarbeitungsthread, der die Funktion aufgerufen hat, verarbeitete Heartbeat-Nachrichten, die nominell jede Sekunde eintrafen. Wir erhöhten die Frequenz auf 10 Hz, also 10 Mal pro Sekunde, und begannen zu laufen. Etwa eine Stunde später sperrte sich das System. Im Protokoll sahen wir, dass die Reihenfolge der aufgezeichneten Nachrichten dieselbe war wie beim fehlgeschlagenen Test. Wir führten mehrere weitere Durchläufe durch, das System war 45–90 Minuten nach dem Start ständig blockiert und jedes Mal enthielt das Protokoll die gleiche Route. Obwohl wir technisch gesehen unterschiedlichen Code ausführten – die Nachrichtenhäufigkeit war unterschiedlich –, war das Verhalten des Systems dasselbe, sodass wir zuversichtlich waren, dass dieses Lastszenario das gleiche Problem verursachte.

Jetzt mussten wir herausfinden, wo genau die Blockierung in der Ausdrucksfolge auftrat.

Diese Implementierung des Systems verwendete das Ada-Aufgabensystem, nutzte es jedoch unglaublich schlecht. Aufgaben sind ein gleichzeitig ausführbares Konstrukt auf hoher Ebene in Ada, so etwas wie Ausführungsthreads, die nur in die Sprache selbst integriert sind. Wenn zwei Aufgaben kommunizieren müssen, „vereinbaren sie ein Rendezvous“, tauschen die erforderlichen Daten aus, stoppen dann das Rendezvous und kehren zu ihren unabhängigen Ausführungen zurück. Allerdings wurde das System anders umgesetzt. Nachdem eine Zielaufgabe ein Rendezvous hatte, traf sich diese Zielaufgabe mit einer anderen Aufgabe, die sich dann mit einer dritten Aufgabe traf, und so weiter, bis eine Verarbeitung abgeschlossen war. Danach waren alle diese Rendezvous abgeschlossen und jede Aufgabe musste zu ihrer Ausführung zurückkehren. Das heißt, wir hatten es mit dem teuersten Funktionsaufrufsystem der Welt zu tun, das den gesamten „Multitasking“-Prozess stoppte, während es einen Teil der Eingabedaten verarbeitete. Und das führte vorher nicht nur deshalb zu Problemen, weil der Durchsatz sehr gering war.

Ich habe diesen Aufgabenmechanismus beschrieben, weil es zu einem „Aufgabenwechsel“ kommen konnte, wenn ein Rendezvous angefordert wurde oder dessen Abschluss erwartet wurde. Das heißt, der Prozessor könnte mit der Verarbeitung einer anderen Aufgabe beginnen, die zur Ausführung bereit ist. Es stellt sich heraus, dass, wenn eine Aufgabe bereit ist, sich mit einer anderen Aufgabe zu treffen, eine völlig andere Aufgabe mit der Ausführung beginnen kann und die Kontrolle schließlich zum ersten Rendezvous zurückkehrt. Es können auch andere Ereignisse eintreten, die dazu führen, dass die Aufgabe wechselt. Ein solches Ereignis ist ein Aufruf einer Systemfunktion, beispielsweise das Drucken oder Ausführen eines Mutex.

Um zu verstehen, welche Codezeile das Problem verursachte, musste ich eine Möglichkeit finden, den Fortschritt durch eine Folge von Anweisungen aufzuzeichnen, ohne einen Taskwechsel auszulösen, der einen Absturz verhindern würde. Ich konnte es also nicht ausnutzen Put_Line()um die Durchführung von E/A-Vorgängen zu vermeiden. Ich könnte eine Zählervariable oder etwas Ähnliches setzen, aber wie kann ich ihren Wert sehen, wenn ich ihn nicht auf dem Bildschirm anzeigen kann?

Bei der Untersuchung des Protokolls stellte sich außerdem heraus, dass trotz der Verarbeitung hängender Heartbeat-Nachrichten, die alle E/A-Vorgänge des Prozesses blockierten und die Ausführung anderer Verarbeitungen verhinderten, weiterhin andere unabhängige Aufgaben ausgeführt wurden. Das heißt, die Arbeit wurde nicht vollständig blockiert, sondern nur eine (kritische) Aufgabenkette.

Dies war der Hinweis, der zur Bewertung des Blockierungsausdrucks erforderlich war.

Ich habe ein Ada-Paket erstellt, das eine Aufgabe, einen Aufzählungstyp und eine globale Variable dieses Typs enthielt. Aufzählbare Literale wurden an bestimmte Ausdrücke der problematischen Sequenz gebunden (z. B. Incrementing_Buffer_Index, Locking_Mutex, Mutex_Unlocked) und dann darin Zuweisungsausdrücke eingefügt, die die entsprechende Aufzählung einer globalen Variablen zuwiesen. Da der Objektcode von all dem lediglich eine Konstante im Speicher speicherte, war ein Aufgabenwechsel infolge seiner Ausführung äußerst unwahrscheinlich. Wir waren in erster Linie misstrauisch gegenüber Ausdrücken, die die Aufgabe wechseln könnten, da die Blockierung bei der Ausführung auftrat und nicht beim Zurückschalten der Aufgabe zurückkehrte (aus mehreren Gründen).

Die Tracking-Aufgabe lief einfach in einer Schleife und überprüfte regelmäßig, ob sich der Wert der globalen Variablen geändert hatte. Bei jeder Änderung wurde der Wert in einer Datei gespeichert. Dann kurzes Warten und ein neuer Scheck. Ich habe die Variable in die Datei geschrieben, weil die Aufgabe nur dann ausgeführt wurde, wenn das System sie beim Wechseln der Aufgabe im Problembereich zur Ausführung ausgewählt hat. Was auch immer in dieser Aufgabe passiert ist, hat keine Auswirkungen auf andere, nicht damit zusammenhängende blockierte Aufgaben.

Es wurde erwartet, dass, wenn das System den Punkt erreicht, an dem der problematische Code ausgeführt wird, die globale Variable beim Übergang zu jedem nächsten Ausdruck zurückgesetzt wird. Dann passiert etwas, das dazu führt, dass die Aufgabe umschaltet, und da ihre Ausführungsfrequenz (10 Hz) niedriger ist als die der Überwachungsaufgabe, könnte der Monitor den Wert der globalen Variablen erfassen und schreiben. In einer normalen Situation könnte ich eine sich wiederholende Folge einer Teilmenge von Aufzählungen erhalten: die letzten Werte der Variablen zum Zeitpunkt des Aufgabenwechsels. Beim Hängen sollte sich die globale Variable nicht mehr ändern und der zuletzt geschriebene Wert zeigt an, welcher Ausdruck nicht abgeschlossen wurde.

Ich habe den Code mit Tracking ausgeführt. Er erstarrte. Und die Überwachung funktionierte wie am Schnürchen.

Das Protokoll enthielt die erwartete Sequenz, die durch einen Wert unterbrochen wurde, der darauf hinwies, dass ein Mutex aufgerufen wurde Unlock, und die Aufgabe ist nicht abgeschlossen – wie es bei Tausenden vorherigen Anrufen der Fall war.

Apex-Ingenieure analysierten zu diesem Zeitpunkt fieberhaft ihren Code und fanden eine Stelle im Mutex, an der theoretisch eine Sperre auftreten könnte. Die Wahrscheinlichkeit war jedoch sehr gering, da nur eine bestimmte Abfolge von Ereignissen zu einem bestimmten Zeitpunkt zu einer Blockierung führen konnte. Murphys Gesetz, Leute, es ist Murphys Gesetz.

Um den von mir benötigten Codeteil zu schützen, habe ich die Mutex-Funktionsaufrufe (die auf der Mutex-Funktionalität des Betriebssystems aufbauen) durch ein kleines natives Ada-Mutex-Paket ersetzt, um den Mutex-Zugriff auf diesen Teil zu steuern.

Ich habe es in den Code eingefügt und den Test ausgeführt. Sieben Stunden später funktionierte der Code immer noch.

Mein Code wurde an Rational übermittelt, wo sie ihn kompilierten, disassemblierten und überprüften, dass er nicht denselben Ansatz verwendete, der in den problematischen Mutex-Funktionen verwendet wurde.

Das war die am meisten überfüllte Codeüberprüfung meiner Karriere 🙂 Es waren ungefähr zehn Ingenieure und Manager mit mir im Raum, weitere zehn Leute waren in einer Telefonkonferenz – und alle haben ungefähr 20 Codezeilen untersucht.

Der Code wurde überprüft, neue ausführbare Dateien wurden zusammengestellt und einem formellen Regressionstest unterzogen. Ein paar Wochen später war der Countdown-Test erfolgreich und die Rakete startete.

Okay, das ist alles schön und gut, aber was ist der Sinn der Geschichte?

Es war ein absolut ekliges Problem. Hunderttausende Codezeilen, parallele Ausführung, über ein Dutzend interagierende Prozesse, schlechte Architektur und schlechte Implementierung, Schnittstellen für eingebettete Systeme und Millionen ausgegebener Dollar. Kein Druck, richtig.

Ich war nicht der Einzige, der an diesem Problem arbeitete, obwohl ich bei der Portierung im Rampenlicht stand. Aber selbst wenn ich es getan habe, heißt das nicht, dass ich alle Hunderttausend Zeilen Code verstanden oder sie auch nur überflogen habe. Der Code und die Protokolle wurden von Ingenieuren im ganzen Land analysiert, aber als sie mir ihre Hypothesen über die Ursachen des Fehlers mitteilten, brauchte ich nur eine halbe Minute, um sie zu widerlegen. Und wenn ich gebeten wurde, Theorien zu analysieren, habe ich es an jemand anderen weitergegeben, weil mir klar war, dass diese Ingenieure den falschen Weg gingen. Klingt anmaßend? Ja, das stimmt, aber ich habe die Hypothesen und Forderungen aus einem anderen Grund abgelehnt.

Ich habe die Natur des Problems verstanden. Ich wusste nicht genau, wo es geschah oder warum, aber ich wusste, was geschah.

Im Laufe der Jahre habe ich viel Wissen und Erfahrung gesammelt. Ich war einer der Pioniere bei der Nutzung von Ada und verstand seine Vor- und Nachteile. Ich weiß, wie die Ada-Laufzeitbibliotheken Aufgaben handhaben und mit der parallelen Ausführung umgehen. Und ich verstehe Low-Level-Programmierung auf der Ebene von Speicher, Registern und Assembler. Mit anderen Worten: Ich verfüge über umfassende Kenntnisse in meinem Fachgebiet. Und ich habe sie genutzt, um die Ursache des Problems zu finden. Ich habe den Fehler nicht nur umgangen, sondern auch verstanden, wie ich ihn in einer sehr sensiblen Laufzeitumgebung finden kann.

Solche Geschichten über den Kampf mit Code sind für diejenigen, die mit den Merkmalen und Bedingungen eines solchen Kampfes nicht vertraut sind, nicht sehr interessant. Aber diese Geschichten helfen uns zu verstehen, was nötig ist, um wirklich schwierige Probleme zu lösen.

Um wirklich schwierige Probleme zu lösen, müssen Sie mehr als nur ein Programmierer sein. Sie müssen das „Schicksal“ des Codes verstehen, wie er mit seiner Umgebung interagiert und wie die Umgebung selbst funktioniert.

Und dann haben Sie Ihre eigene ruinierte Urlaubswoche.

To be continued.

Source: habr.com

Kommentar hinzufügen