Folklor programistów i inżynierów (część 1)

Folklor programistów i inżynierów (część 1)

Oto wybór historii z Internetu o tym, jak błędy czasami mają zupełnie niewiarygodne objawy. Być może ty też masz coś do powiedzenia.

Alergia samochodowa na lody waniliowe

Historia dla inżynierów, którzy rozumieją, że oczywistość nie zawsze jest odpowiedzią i że niezależnie od tego, jak naciągane mogą wydawać się fakty, nadal są one faktami. Oddział Pontiac firmy General Motors Corporation otrzymał skargę:

Piszę do Ciebie już drugi raz i nie mam Ci za złe, że nie odpowiadasz, bo to brzmi szalenie. Nasza rodzina ma tradycję jedzenia lodów każdego wieczoru po obiedzie. Za każdym razem zmieniają się rodzaje lodów, a po obiedzie cała rodzina wybiera, które lody kupić, po czym idę do sklepu. Niedawno kupiłem nowego Pontiaca i od tego czasu moje wyjazdy na lody stały się problemem. Widzisz, za każdym razem, gdy kupuję lody waniliowe i wracam ze sklepu, samochód nie odpala. Jeżeli dołożę jakieś inne lody to auto odpala bez problemu. Chcę zadać poważne pytanie, niezależnie od tego, jak głupio to zabrzmi: „Co takiego jest w Pontiacu, że nie uruchamia się, gdy przynoszę lody waniliowe, ale uruchamia się łatwo, gdy przynoszę inny smak lodów?”

Jak można sobie wyobrazić, prezes oddziału był sceptyczny wobec listu. Jednak na wszelki wypadek wysłałem inżyniera, żeby to sprawdził. Zdziwił się, że spotkał zamożnego, wykształconego mężczyznę mieszkającego w pięknej okolicy. Umówili się na spotkanie zaraz po kolacji, żeby oboje mogli pójść do sklepu na lody. Wieczorem był waniliowy i kiedy wrócili do samochodu, nie odpalił.

Inżynier przyszedł jeszcze trzy wieczory. Po raz pierwszy lody były czekoladowe. Samochód ruszył. Za drugim razem były lody truskawkowe. Samochód ruszył. Trzeciego wieczoru poprosił o wanilię. Samochód nie odpalił.

Rozumując racjonalnie, inżynier nie chciał uwierzyć, że samochód jest uczulony na lody waniliowe. Dlatego ustaliłem z właścicielem auta, że ​​będzie kontynuował wizyty do czasu znalezienia rozwiązania problemu. I po drodze zaczął robić notatki: zapisywał wszystkie informacje, porę dnia, rodzaj benzyny, godzinę przyjazdu i powrotu ze sklepu itp.

Inżynier szybko zdał sobie sprawę, że właściciel samochodu spędzał mniej czasu na kupowaniu lodów waniliowych. Powodem był układ towarów w sklepie. Największą popularnością cieszyły się lody waniliowe, które trzymano w osobnej zamrażarce z przodu sklepu, aby ułatwić ich znalezienie. Wszystkie pozostałe odmiany znajdowały się na zapleczu sklepu, a znalezienie odpowiedniej odmiany i zapłaty zajęło znacznie więcej czasu.

Teraz pytanie było do inżyniera: dlaczego samochód nie odpalił, skoro od momentu wyłączenia silnika minęło mniej czasu? Ponieważ problemem był czas, a nie lody waniliowe, inżynier szybko znalazł odpowiedź: była to blokada gazowa. Działo się to każdego wieczoru, lecz gdy właściciel samochodu spędzał więcej czasu na szukaniu lodów, silnik zdążył się dostatecznie ostudzić i bez problemu odpalił. A kiedy mężczyzna kupił lody waniliowe, silnik był nadal za gorący, a blokada gazu nie miała czasu się rozpuścić.

Morał: Nawet całkowicie szalone problemy są czasami realne.

Crash Bandicoot

Doświadczenie tego jest bolesne. Jako programista przyzwyczajasz się do obwiniania swojego kodu w pierwszej kolejności, drugiej, trzeciej... a gdzieś na dziesięciu tysięcznych miejscu obwiniasz kompilator. W dalszej części listy już obwiniasz sprzęt.

Oto moja historia o błędzie sprzętowym.

Na potrzeby gry Crash Bandicoot napisałem kod, który ładuje i zapisuje na karcie pamięci. Dla takiego zadowolonego z siebie twórcy gier było to jak spacer po parku: myślałem, że praca zajmie kilka dni. Skończyło się jednak na debugowaniu kodu przez sześć tygodni. Po drodze rozwiązałem inne problemy, ale co kilka dni wracałem do tego kodu na kilka godzin. To była agonia.

Objaw wyglądał następująco: po zapisaniu aktualnego przebiegu gry i uzyskaniu dostępu do karty pamięci wszystko prawie zawsze przebiega prawidłowo... Czasem jednak bez wyraźnego powodu przekroczenia limitu czasu operacji odczytu lub zapisu. Krótkie nagranie często uszkadza kartę pamięci. Kiedy gracz próbuje zapisać, nie tylko nie udaje mu się zapisać, ale także niszczy mapę. Gówno.

Po pewnym czasie nasza producentka w Sony, Connie Bus, wpadła w panikę. Nie mogliśmy wydać gry z tym błędem, a sześć tygodni później nie rozumiałem, co było przyczyną problemu. Za pośrednictwem Connie skontaktowaliśmy się z innymi twórcami PS1: czy ktoś spotkał się z czymś podobnym? NIE. Nikt nie miał problemów z kartą pamięci.

Kiedy nie masz pomysłów na debugowanie, pozostaje Ci jedynie podejście „dziel i zwyciężaj”: usuwaj coraz więcej kodu z wadliwego programu, aż pozostanie stosunkowo mały fragment, który nadal powoduje problem. Oznacza to, że odcinasz program kawałek po kawałku, aż pozostanie część zawierająca błąd.

Rzecz w tym, że bardzo trudno jest wyciąć kawałki z gry wideo. Jak go uruchomić, jeśli usunąłeś kod emulujący grawitację? Albo rysowanie postaci?

Dlatego musimy zastąpić całe moduły wycinkami, które udają, że robią coś przydatnego, ale w rzeczywistości robią coś bardzo prostego, co nie może zawierać błędów. Musimy napisać takie kule, żeby gra chociaż działała. Jest to proces powolny i bolesny.

Krótko mówiąc, zrobiłem to. Usuwałem coraz więcej fragmentów kodu, aż pozostał kod początkowy, który konfiguruje system do uruchomienia gry, inicjuje sprzęt renderujący itp. Oczywiście na tym etapie nie mogłem stworzyć menu zapisu i ładowania, gdyż musiałbym stworzyć kod pośredniczący dla całego kodu graficznego. Ale mógłbym udawać użytkownika korzystającego z (niewidocznego) ekranu zapisywania i ładowania i poprosić o zapisanie, a następnie zapisanie na karcie pamięci.

To pozostawiło mi mały fragment kodu, w którym nadal występował powyższy problem - ale nadal zdarzał się losowo! Najczęściej wszystko działało dobrze, ale czasami zdarzały się błędy. Usunąłem prawie cały kod gry, ale błąd nadal występował. To było zastanawiające: pozostały kod tak naprawdę nic nie zrobił.

W pewnym momencie, prawdopodobnie około trzeciej w nocy, przyszła mi do głowy pewna myśl. Operacje odczytu i zapisu (wejścia/wyjścia) wymagają precyzyjnych czasów wykonania. Kiedy pracujesz z dyskiem twardym, kartą pamięci czy modułem Bluetooth, kod niskiego poziomu odpowiedzialny za odczyt i zapis robi to zgodnie z impulsami zegara.

Za pomocą zegara urządzenie niepodłączone bezpośrednio do procesora jest synchronizowane z kodem wykonywanym na procesorze. Zegar określa szybkość transmisji — prędkość transmisji danych. Jeśli występuje zamieszanie z synchronizacją, oznacza to, że sprzęt lub oprogramowanie lub jedno i drugie jest również zdezorientowane. A to jest bardzo złe, ponieważ dane mogą zostać uszkodzone.

A co jeśli coś w naszym kodzie myli synchronizację? Sprawdziłem wszystko z tym związane w kodzie programu testowego i zauważyłem, że programowalny timer w PS1 ustawiliśmy na 1 kHz (1000 taktów na sekundę). To całkiem sporo, domyślnie konsola przy uruchomieniu pracuje z częstotliwością 100 Hz. Większość gier korzysta z tej częstotliwości.

Andy, twórca gry, ustawił timer na 1 kHz, aby ruchy były obliczane dokładniej. Andy ma tendencję do przesadzania, a jeśli naśladujemy grawitację, robimy to tak dokładnie, jak to możliwe!

Ale co, jeśli przyspieszenie timera w jakiś sposób wpłynęło na ogólne taktowanie programu, a tym samym na zegar regulujący szybkość transmisji karty pamięci?

Skomentowałem kod timera. Błąd nie pojawił się ponownie. Ale to nie znaczy, że to naprawiliśmy, ponieważ awaria pojawiła się losowo. A co jeśli po prostu będę miał szczęście?

Kilka dni później ponownie poeksperymentowałem z programem testowym. Błąd się nie powtórzył. Wróciłem do pełnego kodu gry i zmodyfikowałem kod zapisu i ładowania, tak aby programowalny timer resetował się do swojej pierwotnej wartości (100 Hz) przed uzyskaniem dostępu do karty pamięci, a następnie resetował się z powrotem do 1 kHz. Nie było już żadnych awarii.

Ale dlaczego tak się stało?

Wróciłem ponownie do programu testowego. Próbowałem znaleźć jakiś wzór w występowaniu błędu w przypadku timera 1 kHz. W końcu zauważyłem, że błąd występuje, gdy ktoś gra kontrolerem PS1. Ponieważ rzadko robiłbym to sam - po co miałbym potrzebować kontrolera do testowania kodu zapisywania i ładowania? - Nawet nie zauważyłem tej zależności. Ale pewnego dnia jeden z naszych artystów czekał, aż skończę testy – pewnie w tym momencie przeklinałem – i nerwowo kręcił kontrolerem w dłoniach. Wystąpił błąd. "Czekaj, co?!" Cóż, zrób to jeszcze raz!”

Kiedy zdałem sobie sprawę, że te dwa zdarzenia są ze sobą powiązane, mogłem łatwo odtworzyć błąd: zacząłem nagrywać na kartę pamięci, przeniosłem kontroler i zniszczyłem kartę pamięci. Dla mnie wyglądało to na błąd sprzętowy.

Przyszedłem do Connie i powiedziałem jej o moim odkryciu. Przekazała tę informację jednemu z inżynierów, którzy zaprojektowali PS1. „Niemożliwe” – odpowiedział. „To nie może być problem sprzętowy”. Poprosiłem Connie, żeby umówiła się z nami na rozmowę.

Inżynier zadzwonił do mnie i kłóciliśmy się w jego łamanym angielskim i moim (niezwykle) łamanym japońskim. W końcu powiedziałem: „Pozwólcie, że wyślę mój 30-liniowy program testowy, w którym poruszenie kontrolerem powoduje błąd”. Zgodził się. Powiedział, że to strata czasu i że jest strasznie zajęty pracą nad nowym projektem, ale podda się, bo jesteśmy bardzo ważnym deweloperem dla Sony. Poprawiłem program testowy i wysłałem mu go.

Następnego wieczoru (byliśmy w Los Angeles, a on w Tokio) zadzwonił do mnie i nieśmiało przeprosił. To był problem sprzętowy.

Nie wiem na czym dokładnie polegał błąd, ale z tego co słyszałem w centrali Sony, jeśli ustawisz timer na odpowiednio wysoką wartość, ingerował on w elementy na płycie głównej w pobliżu kryształu timera. Jednym z nich był kontroler szybkości transmisji karty pamięci, który jednocześnie ustalał prędkość transmisji dla kontrolerów. Nie jestem inżynierem, więc mogłem coś pomylić.

Ale najważniejsze jest to, że wystąpiła interferencja między komponentami na płycie głównej. A podczas jednoczesnego przesyłania danych przez port kontrolera i port karty pamięci z zegarem pracującym z częstotliwością 1 kHz, bity zostały utracone, dane zostały utracone, a karta uległa uszkodzeniu.

Złe krowy

W latach 1980. mój mentor Siergiej napisał oprogramowanie dla SM-1800, radzieckiego klona PDP-11. Mikrokomputer ten właśnie został zainstalowany na stacji kolejowej niedaleko Swierdłowska, ważnego węzła komunikacyjnego w ZSRR. Nowy system został zaprojektowany z myślą o kierowaniu ruchem wagonowym i towarowym. Zawierał jednak irytujący błąd, który powodował przypadkowe awarie i awarie. Do upadków dochodziło zawsze, gdy ktoś wracał wieczorem do domu. Jednak pomimo dokładnego sprawdzenia następnego dnia, komputer działał poprawnie we wszystkich testach ręcznych i automatycznych. Zwykle oznacza to sytuację wyścigową lub inny błąd związany z rywalizacją, który występuje w określonych warunkach. Zmęczony nocnymi telefonami Siergiej postanowił dotrzeć do sedna sprawy i przede wszystkim dowiedzieć się, jakie warunki na stacji rozrządowej doprowadziły do ​​awarii komputera.

Najpierw zebrał statystyki wszystkich niewyjaśnionych upadków i stworzył wykres według daty i godziny. Wzór był oczywisty. Po kilku kolejnych dniach obserwacji Siergiej zdał sobie sprawę, że z łatwością może przewidzieć czas przyszłych awarii systemu.

Wkrótce dowiedział się, że zakłócenia miały miejsce tylko wtedy, gdy na stacji sortowano pociągi z bydłem z północnej Ukrainy i zachodniej Rosji kierowanym do pobliskiej rzeźni. To samo w sobie było dziwne, bo do rzeźni zaopatrywały się gospodarstwa położone znacznie bliżej, w Kazachstanie.

Elektrownia jądrowa w Czarnobylu eksplodowała w 1986 r., a opad radioaktywny sprawił, że okoliczne tereny nie nadawały się do zamieszkania. Skażone zostały rozległe obszary północnej Ukrainy, Białorusi i zachodniej Rosji. Podejrzewając wysoki poziom promieniowania w przybywających wagonach, Siergiej opracował metodę sprawdzenia tej teorii. Ludności zakazano posiadania dozymetrów, dlatego Siergiej zarejestrował się na stacji kolejowej u kilku wojskowych. Po kilku drinkach wódki udało mu się przekonać żołnierza do zmierzenia poziomu promieniowania w jednym z podejrzanych wagonów. Okazało się, że poziom był kilkukrotnie wyższy od wartości prawidłowych.

Bydło nie tylko emitowało dużo promieniowania, ale jego poziom był tak wysoki, że powodował przypadkową utratę bitów w pamięci SM-1800, który znajdował się w budynku obok stacji.

W ZSRR brakowało żywności, dlatego władze zdecydowały się na mieszanie mięsa z Czarnobyla z mięsem z innych regionów kraju. Umożliwiło to zmniejszenie ogólnego poziomu radioaktywności bez utraty cennych zasobów. Dowiedziawszy się o tym, Siergiej natychmiast wypełnił dokumenty emigracyjne. Awarie komputera ustały same, gdy poziom promieniowania z czasem się zmniejszył.

Przez rury

Dawno, dawno temu firma Movietech Solutions tworzyła oprogramowanie dla kin, przeznaczone do księgowości, sprzedaży biletów i ogólnego zarządzania. Wersja flagowej aplikacji na DOS była dość popularna wśród małych i średnich sieci kin w Ameryce Północnej. Nic więc dziwnego, że kiedy ogłoszono wersję dla systemu Windows 95, zintegrowaną z najnowszymi ekranami dotykowymi i kioskami samoobsługowymi oraz wyposażoną we wszelkiego rodzaju narzędzia do raportowania, szybko stała się ona również popularna. Najczęściej aktualizacja przebiegała bez problemów. Lokalny personel IT zainstalował nowy sprzęt, przeprowadził migrację danych i firma kontynuowała działalność. Z wyjątkiem sytuacji, gdy to nie trwało. Kiedy to się zdarzało, firma wysyłała Jamesa, zwanego „Sprzątaczem”.

Chociaż pseudonim sugeruje nikczemny typ, sprzątacz to po prostu połączenie instruktora, instalatora i specjalisty od wszystkiego. James spędzał kilka dni w siedzibie klienta, składając wszystkie komponenty, a następnie przez kolejne kilka dni uczył personel, jak korzystać z nowego systemu, rozwiązując wszelkie pojawiające się problemy sprzętowe i zasadniczo pomagając oprogramowaniu w jego powijakach.

Nic więc dziwnego, że w tych gorączkowych czasach James przybył do biura rano i zanim zdążył dotrzeć do biurka, przywitał go menadżer, napełniony ponad zwykle kofeiną.

„Obawiam się, że musisz jak najszybciej udać się do Annapolis w Nowej Szkocji”. Cały ich system uległ awarii i po nocy spędzonej z inżynierami nie możemy zrozumieć, co się stało. Wygląda na to, że sieć na serwerze uległa awarii. Ale dopiero po kilku minutach działania systemu.

— Nie wrócili do starego systemu? – odpowiedział James zupełnie poważnie, chociaż w myślach szeroko otworzył oczy ze zdziwienia.

— Dokładnie: ich specjalista IT „zmienił priorytety” i zdecydował się odejść ze starym serwerem. James, zainstalowali system w sześciu lokalizacjach i właśnie zapłacili za wsparcie premium, a ich firma działa teraz tak, jak w latach pięćdziesiątych.

James wyprostował się lekko.

- To inna sprawa. OK, zaczynajmy.

Kiedy przybył do Annapolis, pierwszą rzeczą, jaką zrobił, było znalezienie pierwszego kina klienta, w którym wystąpił problem. Na mapie zrobionej na lotnisku wszystko wyglądało przyzwoicie, jednak okolice żądanego adresu wyglądały podejrzanie. Nie getto, ale przypomina film noir. Kiedy James zaparkował przy krawężniku w centrum miasta, podeszła do niego prostytutka. Biorąc pod uwagę wielkość Annapolis, było to najprawdopodobniej jedyne w całym mieście. Jej wygląd od razu przywiódł na myśl słynną postać, która na dużym ekranie oferowała seks za pieniądze. Nie, nie o Julii Roberts, ale o Jonie Voight [nawiązanie do filmu „Nocny kowboj” – ok. uliczka].

Odesławszy prostytutkę, James poszedł do kina. Okolica poprawiła się, ale nadal sprawiała wrażenie zaniedbanej. Nie żeby James był zbytnio zmartwiony. Bywał już w okropnych miejscach. A to była Kanada, gdzie nawet rabusie są na tyle uprzejmi, by powiedzieć „dziękuję” po zabraniu portfela.

Boczne wejście do kina znajdowało się w wilgotnej uliczce. James podszedł do drzwi i zapukał. Po chwili zaskrzypiało i lekko się otworzyło.

-Jesteś sprzątaczką? - ze środka dobiegł ochrypły głos.

- Tak, to ja... Przyszedłem wszystko naprawić.

James wszedł do holu kina. Najwyraźniej nie mając innego wyjścia, pracownicy zaczęli rozdawać gościom papierowe bilety. Utrudniało to sporządzanie sprawozdań finansowych, nie mówiąc już o większej liczbie interesujących szczegółów. Jednak obsługa przywitała Jamesa z ulgą i natychmiast zabrała go do serwerowni.

Na pierwszy rzut oka wszystko było w porządku. James zalogował się na serwer i sprawdził zwykłe podejrzane miejsca. Bez problemu. Jednak ze względów ostrożności James wyłączył serwer, wymienił kartę sieciową i przywrócił system. Od razu przystąpiła do pełnej pracy. Personel ponownie zaczął sprzedawać bilety.

Jakub zadzwonił do Marka i poinformował go o sytuacji. Nietrudno sobie wyobrazić, że James mógłby chcieć zostać w pobliżu i zobaczyć, czy wydarzy się coś nieoczekiwanego. Zszedł po schodach i zaczął wypytywać pracowników, co się stało. Oczywiście system przestał działać. Wyłączyli i włączyli, wszystko działało. Ale po 10 minutach system padł.

Właśnie w tym momencie wydarzyło się coś podobnego. Nagle system sprzedaży biletów zaczął wyrzucać błędy. Personel westchnął i chwycił papierowe bilety, a James pospieszył do serwerowni. Wszystko wyglądało dobrze z serwerem.

Wtedy wszedł jeden z pracowników.

— System znów działa.

James był zdziwiony, ponieważ nic nie zrobił. A dokładniej nic, co sprawiłoby, że system zacząłby działać. Wylogował się, sięgnął po telefon i zadzwonił na linię wsparcia swojej firmy. Wkrótce ten sam pracownik wszedł do serwerowni.

- System nie działa.

James zerknął na serwer. Na ekranie tańczył ciekawy i znajomy wzór wielobarwnych kształtów - chaotycznie wijących się i splatających rur. Wszyscy widzieliśmy kiedyś ten wygaszacz ekranu. To było pięknie wydane i dosłownie hipnotyzujące.


James nacisnął przycisk i wzór zniknął. Pospieszył do kasy biletowej i po drodze spotkał wracającego do niego pracownika.

— System znów działa.

Jeśli potrafisz mentalnie zrobić facepalma, to właśnie to zrobił James. Wygaszacz ekranu. Wykorzystuje OpenGL. Dlatego podczas pracy zużywa wszystkie zasoby procesora serwera. W rezultacie każde wywołanie do serwera kończy się przekroczeniem limitu czasu.

James wrócił do serwerowni, zalogował się i zastąpił wygaszacz ekranu pięknymi rurkami z pustym ekranem. Oznacza to, że zamiast wygaszacza ekranu zużywającego 100% zasobów procesora, zainstalowałem inny, który nie zużywa zasobów. Następnie odczekałem 10 minut, aby sprawdzić moje przypuszczenia.

Kiedy James dotarł do kolejnego kina, zastanawiał się, jak wytłumaczyć swojemu menadżerowi, że właśnie przeleciał 800 km, żeby wyłączyć wygaszacz ekranu.

Katastrofa podczas określonej fazy księżyca

Prawdziwa historia. Pewnego dnia w oprogramowaniu pojawił się błąd zależny od fazy księżyca. W różnych programach MIT powszechnie stosowano pewną procedurę do obliczania przybliżenia prawdziwej fazy Księżyca. GLS wbudował tę procedurę w program LISP, który podczas zapisywania pliku wyświetlał linię ze znacznikiem czasu o długości prawie 80 znaków. Bardzo rzadko zdarzało się, że pierwsza linia wiadomości była zbyt długa i prowadziła do następnej. A kiedy program później odczytał ten plik, przeklął. Długość pierwszej linii zależała od dokładnej daty i godziny, a także od długości specyfikacji fazy w momencie drukowania znacznika czasu. Oznacza to, że błąd dosłownie zależał od fazy księżyca!

Pierwsze wydanie papierowe Plik żargonu (Steele-1983) zawierał przykład takiej linijki, która doprowadziła do opisanego błędu, ale zecer go „naprawił”. Od tego czasu zostało to opisane jako „błąd fazy księżyca”.

Należy jednak zachować ostrożność w domysłach. Kilka lat temu inżynierowie z CERN (Europejskiego Centrum Badań Jądrowych) natknęli się na błędy w eksperymentach prowadzonych w Wielkim Zderzaczu Elektron-Pozyton. Ponieważ komputery aktywnie przetwarzają ogromną ilość danych generowanych przez to urządzenie przed udostępnieniem wyników naukowcom, wielu spekulowało, że oprogramowanie jest w jakiś sposób wrażliwe na fazę księżyca. Kilku zdesperowanych inżynierów dotarło do sedna prawdy. Błąd powstał w wyniku niewielkiej zmiany geometrii pierścienia o długości 27 km w wyniku deformacji Ziemi podczas przejścia Księżyca! Ta historia weszła do folkloru fizyki jako „Zemsta Newtona na fizyce cząstek” i przykład związku najprostszych i najstarszych praw fizyki z najbardziej zaawansowanymi koncepcjami naukowymi.

Spłukiwanie toalety zatrzymuje pociąg

Największy błąd sprzętowy, o jakim kiedykolwiek słyszałem, wystąpił w pociągu dużych prędkości we Francji. Błąd doprowadził do awaryjnego hamowania pociągu, ale tylko wtedy, gdy na pokładzie byli pasażerowie. W każdym takim przypadku pociąg był wycofywany z eksploatacji, sprawdzany, ale nic nie stwierdzono. Następnie odesłano go z powrotem na linię i natychmiast się zatrzymał.

Podczas jednej z kontroli podróżujący pociągiem inżynier poszedł do toalety. Wkrótce się zmył, BOOM! Awaryjny postój.

Inżynier skontaktował się z kierowcą i zapytał:

— Co robiłeś tuż przed hamowaniem?

- No cóż, zwolniłem na zjeździe...

Było to dziwne, bo podczas normalnej jazdy pociąg na zjazdach zwalnia dziesiątki razy. Pociąg ruszył dalej, a na kolejnym zjeździe maszynista ostrzegł:

- Zamierzam zwolnić.

Nic się nie stało.

— Co zrobiłeś podczas ostatniego hamowania? – zapytał kierowca.

- No cóż... byłem w toalecie...

- No to idź do toalety i rób to samo, kiedy znowu zeszliśmy na dół!

Inżynier poszedł do toalety, a gdy kierowca ostrzegł: „Zwalniam”, spuścił wodę. Oczywiście pociąg natychmiast się zatrzymał.

Teraz mogli odtworzyć problem i musieli znaleźć przyczynę.

Po dwóch minutach zauważyli, że kabel zdalnego sterowania hamulcem silnikowym (pociąg miał po jednym silniku na każdym końcu) został odłączony od ścianki szafki elektrycznej i leżał na przekaźniku sterującym elektromagnesem wtyczki WC... Gdy przekaźnik się zapalił został włączony, spowodowało to ingerencję w linkę hamulcową, a zabezpieczenie systemu przed awarią polegało po prostu na hamowaniu awaryjnym.

Brama, która nienawidziła FORTRANU

Kilka miesięcy temu zauważyliśmy, że połączenia sieciowe na kontynencie [to było na Hawajach] stawały się bardzo, bardzo powolne. Może to trwać 10–15 minut, a następnie nagle wystąpić ponownie. Po pewnym czasie kolega poskarżył mi się, że połączenia sieciowe na kontynencie ogólnie nie działa. Miał kod w FORTRAN, który musiał zostać skopiowany na maszynę na kontynencie, ale nie mógł tego zrobić, ponieważ „sieć nie wytrzymała wystarczająco długo, aby zakończyć przesyłanie FTP”.

Tak, okazało się, że awarie sieci wystąpiły, gdy kolega próbował przesłać FTP plik z kodem źródłowym w FORTRAN na maszynę na kontynencie. Próbowaliśmy zarchiwizować plik: następnie został on bezproblemowo skopiowany (jednak maszyna docelowa nie posiadała rozpakowywacza, więc problem nie został rozwiązany). Na koniec „podzieliliśmy” kod FORTRAN na bardzo małe części i wysłaliśmy je pojedynczo. Większość fragmentów została skopiowana bez problemów, ale kilka fragmentów nie przeszło lub przeszło dalej liczny próbowanie.

Kiedy sprawdziliśmy problematyczne fragmenty, odkryliśmy, że mają ze sobą coś wspólnego: wszystkie zawierały bloki komentarzy rozpoczynające się i kończące liniami składającymi się z dużej litery C (tak jak kolega wolał komentować w FORTRANIE). Wysłaliśmy e-maile do ekspertów sieciowych na kontynencie i poprosiliśmy o pomoc. Oczywiście chcieli zobaczyć próbki naszych plików, których nie można było przesłać przez FTP... ale nasze listy do nich nie dotarły. W końcu wpadliśmy na prosty pomysł opisaćjak wyglądają pliki, których nie można przenieść. Zadziałało :) [Czy mogę dodać tutaj przykład jednego z problematycznych komentarzy FORTRAN? Prawdopodobnie nie warto!]

W końcu udało nam się to ustalić. Niedawno zainstalowano nową bramkę pomiędzy naszą częścią kampusu a siecią na kontynencie. Miał OGROMNE trudności z przesyłaniem pakietów zawierających powtarzające się fragmenty wielkich liter C! Zaledwie kilka takich pakietów może zająć wszystkie zasoby bramy i uniemożliwić przedostanie się większości innych pakietów. Złożyliśmy skargę do producenta bramy... a on odpowiedział: „Och, tak, masz do czynienia z błędem w postaci powtarzającego się C! Już o nim wiemy.” Ostatecznie rozwiązaliśmy problem kupując nową bramkę od innego producenta (w obronie tego pierwszego brak możliwości przeniesienia programów FORTRAN może dla niektórych być zaletą!).

Ciężkie czasy

Kilka lat temu pracując nad stworzeniem systemu ETL w Perlu, który miał obniżyć koszty badań klinicznych III fazy, musiałem przetworzyć około 40 000 dat. Dwóch z nich nie zdało egzaminu. Nie przeszkadzało mi to zbytnio, gdyż daty te zostały wzięte z danych dostarczonych przez klientów, co często, powiedzmy, było zaskakujące. Ale kiedy sprawdziłem oryginalne dane, okazało się, że były to daty 1 stycznia 2011 i 1 stycznia 2007. Myślałem, że błąd tkwi w programie, który właśnie napisałem, ale okazało się, że to już 30 lat stary. Może się to wydawać tajemnicze dla osób niezaznajomionych z ekosystemem oprogramowania. Ze względu na wieloletnią decyzję innej firmy o zarabianiu pieniędzy, mój klient zapłacił mi za naprawienie błędu, który jedna firma wprowadziła przez przypadek, a druga celowo. Abyście zrozumieli o czym mówię, muszę omówić firmę, która dodała funkcję, która okazała się błędem, a także kilka innych ciekawych wydarzeń, które przyczyniły się do naprawienia tajemniczego błędu, który naprawiłem.

W starych, dobrych czasach komputery Apple czasami spontanicznie ustawiały datę na 1 stycznia 1904 roku. Powód był prosty: do śledzenia daty i godziny wykorzystywał zasilany bateryjnie „zegar systemowy”. Co się stało, gdy bateria się wyczerpała? Komputery zaczęły śledzić datę na podstawie liczby sekund od początku epoki. Przez epokę rozumieliśmy pierwotną datę referencyjną, a dla komputerów Macintosh był to 1 stycznia 1904. A po wyczerpaniu się baterii aktualna data została zresetowana do określonej. Ale dlaczego tak się stało?

Wcześniej firma Apple używała 32 bitów do przechowywania liczby sekund od pierwotnej daty. Jeden bit może przechowywać jedną z dwóch wartości - 1 lub 0. Dwa bity mogą przechowywać jedną z czterech wartości: 00, 01, 10, 11. Trzy bity - jedną wartość z ośmiu: 000, 001, 010, 011, 100 , 101, 110, 111 itd. A 32 może przechowywać jedną z 232 wartości, czyli 4 294 967 296 sekund. W przypadku dat Apple oznaczało to około 136 lat, więc starsze komputery Mac nie obsługują dat po 2040 roku. A jeśli bateria systemowa wyczerpie się, data zostanie zresetowana do 0 sekund od początku epoki i trzeba ręcznie ustawiać datę za każdym razem, gdy włączasz komputer (lub do czasu zakupu nowej baterii).

Jednak decyzja Apple o przechowywaniu dat jako sekund od epoki oznaczała, że ​​nie mogliśmy sobie poradzić z datami poprzedzającymi epokę, co, jak zobaczymy, miało dalekosiężne konsekwencje. Firma Apple wprowadziła funkcję, a nie błąd. Oznaczało to między innymi, że system operacyjny Macintosha był odporny na „błąd milenijny” (czego nie można było powiedzieć o wielu aplikacjach Mac, które miały własne systemy daty w celu obejścia ograniczeń).

Zacząć robić. Użyliśmy Lotus 1-2-3, „zabójczej aplikacji” IBM, która pomogła zapoczątkować rewolucję w komputerach PC, chociaż komputery Apple miały VisiCalc, dzięki któremu komputer osobisty odniósł sukces. Szczerze mówiąc, gdyby nie pojawiło się 1-2-3, komputery PC raczej nie zyskałyby na popularności, a historia komputerów osobistych mogłaby potoczyć się zupełnie inaczej. Lotus 1-2-3 błędnie potraktował rok 1900 jako rok przestępny. Kiedy Microsoft wypuścił swój pierwszy arkusz kalkulacyjny, Multiplan, zdobył niewielki udział w rynku. A kiedy uruchomili projekt Excel, postanowili nie tylko skopiować schemat nazewnictwa wierszy i kolumn z Lotusa 1-2-3, ale także zapewnić zgodność z błędami, celowo traktując rok 1900 jako rok przestępny. Problem ten istnieje do dziś. Oznacza to, że w 1-2-3 był to błąd, ale w Excelu była to świadoma decyzja, która zapewniła wszystkim użytkownikom 1-2-3 możliwość importowania swoich tabel do Excela bez zmiany danych, nawet jeśli były one nieprawidłowe.

Ale był inny problem. Najpierw Microsoft wypuścił program Excel dla komputerów Macintosh, który nie rozpoznawał dat wcześniejszych niż 1 stycznia 1904 r. A w programie Excel 1 stycznia 1900 r. uznawano za początek ery. Dlatego programiści dokonali zmiany, aby ich program rozpoznał typ ery i zapisał w sobie dane zgodnie z żądaną erą. Microsoft napisał nawet artykuł wyjaśniający na ten temat. I ta decyzja doprowadziła do mojego błędu.

Mój system ETL otrzymał od klientów arkusze kalkulacyjne Excel, które zostały utworzone w systemie Windows, ale można je było również utworzyć na komputerze Mac. Dlatego początek ery w tabeli może przypadać na 1 stycznia 1900 r. lub 1 stycznia 1904 r. Jak się dowiedzieć? Format pliku Excel pokazuje niezbędne informacje, ale parser, którego używałem, tego nie pokazał (teraz tak) i założył, że znasz epokę dla konkretnej tabeli. Prawdopodobnie mógłbym poświęcić więcej czasu na zrozumienie formatu binarnego Excela i wysłanie łatki do autora parsera, ale miałem dużo więcej do zrobienia dla klienta, więc szybko napisałem heurystykę, aby określić epokę. Była prosta.

W Excelu datę 5 lipca 1998 można przedstawić w formacie „07-05-98” (bezużyteczny system amerykański), „5 lipca 98”, „5 lipca 1998”, „5-lip-98” lub jakiś inny format, kolejny bezużyteczny format (jak na ironię, jednym z formatów, którego moja wersja Excela nie oferowała, był ISO 8601). Jednak w tabeli niesformatowana data została zapisana jako „35981” dla epoki 1900 lub „34519” dla epoki 1904 (liczby reprezentują liczbę dni od epoki). Po prostu użyłem prostego parsera, aby wyodrębnić rok ze sformatowanej daty, a następnie użyłem parsera Excela, aby wyodrębnić rok z niesformatowanej daty. Jeśli obie wartości różniły się o 4 lata, to wiedziałem, że korzystam z systemu z epoką 1904.

Dlaczego po prostu nie użyłem sformatowanych dat? Ponieważ 5 lipca 1998 r. można sformatować jako „lipiec 98” z utraconym dniem miesiąca. Otrzymaliśmy tabele od tak wielu firm, które stworzyły je na tak wiele różnych sposobów, że do nas (w tym przypadku mnie) należało ustalenie dat. Poza tym, jeśli Excel zrobi to dobrze, to my też powinniśmy!

W tym samym czasie natknąłem się na liczbę 39082. Przypomnę, że Lotus 1-2-3 uważał rok 1900 za rok przestępny i zostało to wiernie powtórzone w Excelu. A ponieważ dodało to jeden dzień do roku 1900, wiele funkcji obliczania daty może być błędnych dla tego właśnie dnia. Oznacza to, że 39082 mogło mieć datę 1 stycznia 2011 r. (na komputerach Mac) lub 31 grudnia 2006 r. (w systemie Windows). Jeśli mój „parser roku” wyodrębnił ze sformatowanej wartości rok 2011, wszystko jest w porządku. Ponieważ jednak parser programu Excel nie wie, jaka epoka jest używana, domyślnie przyjmuje epokę 1900 i zwraca rok 2006. Moja aplikacja zauważyła, że ​​różnica wynosiła 5 lat, uznała to za błąd, zarejestrowała ją i zwróciła niesformatowaną wartość.

Aby obejść ten problem, napisałem to (pseudokod):

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

A następnie wszystkie 40 000 dat zostało poprawnie przeanalizowanych.

W środku dużych zadań drukowania

Na początku lat 1980. mój ojciec pracował w Storage Technology, nieistniejącym już dziale, który tworzył napędy taśmowe i systemy pneumatyczne do szybkiego podawania taśm.

Przeprojektowali dyski tak, aby jeden centralny dysk „A” był podłączony do siedmiu dysków „B”, a mały system operacyjny w pamięci RAM sterujący dyskiem „A” mógł delegować operacje odczytu i zapisu do wszystkich dysków „B”.

Przy każdym uruchomieniu napędu „A” konieczne było włożenie dyskietki do napędu peryferyjnego podłączonego do „A” w celu załadowania systemu operacyjnego do jego pamięci. Był niezwykle prymitywny: moc obliczeniową zapewniał 8-bitowy mikrokontroler.

Grupą docelową tego sprzętu były firmy posiadające bardzo duże hurtownie danych – banki, sieci handlowe itp. – które musiały drukować wiele etykiet adresowych czy wyciągów bankowych.

Jeden klient miał problem. W trakcie zadania drukowania jeden konkretny dysk „A” może przestać działać, powodując zatrzymanie całego zadania. Aby przywrócić działanie dysku, personel musiał wszystko zrestartować. A jeśli zdarzyło się to w trakcie sześciogodzinnego zadania, wówczas stracono ogromną ilość kosztownego czasu komputerowego i zakłócono harmonogram całej operacji.

Wysłano techników z firmy Storage Technologies. Jednak pomimo największych wysiłków nie udało im się odtworzyć błędu w warunkach testowych: wydawało się, że pojawia się on w trakcie dużych zadań drukowania. Problemem nie był sprzęt, wymienili wszystko, co się dało: pamięć RAM, mikrokontroler, stację dyskietek, każdą możliwą część napędu taśmowego – problem nadal występował.

Następnie technicy zadzwonili do centrali i wezwali Eksperta.

Ekspert chwycił krzesło i filiżankę kawy, usiadł w sali komputerowej – w tamtych czasach były to pomieszczenia przeznaczone wyłącznie dla komputerów – i patrzył, jak pracownicy ustawiają się w kolejce z dużym zadaniem do druku. Ekspert czekał, aż nastąpi awaria - i tak się stało. Wszyscy spojrzeli na Eksperta, ale on nie miał pojęcia, dlaczego tak się stało. Nakazał więc ponownie ustawić zadanie w kolejce, a cały personel i technicy wrócili do pracy.

Ekspert ponownie usiadł na krześle i zaczął czekać na porażkę. Minęło około sześciu godzin i nastąpiła awaria. Ekspert znów nie miał żadnych pomysłów poza tym, że wszystko działo się w pomieszczeniu wypełnionym ludźmi. Rozkazał wznowić misję, usiadł i czekał.

Przy trzeciej porażce Ekspert coś zauważył. Awaria wystąpiła, gdy personel zmienił taśmy w zagranicznym napędzie. Co więcej, awaria nastąpiła w momencie, gdy jeden z pracowników przeszedł przez określoną płytkę na podłodze.

Podłogę podniesioną wykonano z płytek aluminiowych ułożonych na wysokości od 6 do 8 cali. Pod podniesioną podłogą poprowadzono liczne przewody od komputerów, aby zapobiec przypadkowemu nadepnięciu na ważny kabel. Płytki ułożono bardzo ciasno, aby zapobiec przedostawaniu się zanieczyszczeń pod podłogę podniesioną.

Biegły stwierdził, że jedna z płytek jest zdeformowana. Kiedy pracownik nadepnął na jej róg, krawędzie płytki ocierały się o sąsiednie płytki. Plastikowe części łączące płytki również ocierały się o nie, co powodowało mikrowyładowania statyczne, które powodowały zakłócenia o częstotliwości radiowej.

Obecnie pamięć RAM jest znacznie lepiej chroniona przed zakłóceniami o częstotliwości radiowej. Ale w tamtych latach tak nie było. Ekspert zdał sobie sprawę, że ingerencja ta zakłóciła pamięć, a wraz z nią działanie systemu operacyjnego. Zadzwonił do serwisu, zamówił nowe płytki, sam je zamontował i problem zniknął.

To najwyższy przypływ!

Historia miała miejsce w serwerowni, na czwartym lub piątym piętrze biura w Portsmouth (tak mi się wydaje), w rejonie doków.

Pewnego dnia uległ awarii serwer uniksowy z główną bazą danych. Uruchomili go ponownie, ale szczęśliwie nadal upadał. Postanowiliśmy zadzwonić do kogoś z działu wsparcia.

Facet ze wsparcia... Chyba miał na imię Mark, ale to nie ma znaczenia... Chyba go nie znam. To nie ma znaczenia, naprawdę. Trzymajmy się Marka, dobrze? Świetnie.

Tak więc kilka godzin później przyjechał Mark (wiesz, z Leeds do Portsmouth nie jest daleko), włączył serwer i wszystko działało bez problemów. Typowe cholerne wsparcie, klient bardzo się tym denerwuje. Mark przegląda pliki dziennika i nie znajduje niczego niepokojącego. Więc Mark wraca do pociągu (lub innego środka transportu, którym przyjechał, z tego, co wiem, mogła to być kulawa krowa… w każdym razie to nie ma znaczenia, ok?) i wraca do Leeds, wyczerpany dzień.

Tego samego wieczoru serwer ponownie się zawiesza. Historia jest taka sama... serwer nie podnosi się. Marek próbuje pomóc zdalnie, ale klient nie może uruchomić serwera.

Kolejny pociąg, autobus, beza cytrynowa czy jakieś inne badziewie i Marek wraca do Portsmouth. Spójrz, serwer uruchamia się bez żadnych problemów! Cud. Mark spędza kilka godzin sprawdzając, czy wszystko jest w porządku z systemem operacyjnym lub oprogramowaniem i wyrusza do Leeds.

Mniej więcej w połowie dnia serwer ulega awarii (nie martw się!). Tym razem rozsądne wydaje się zatrudnienie osób zajmujących się wsparciem sprzętowym w celu wymiany serwera. Ale nie, po około 10 godzinach też spada.

Sytuacja powtarzała się przez kilka dni. Serwer działa, zawiesza się po około 10 godzinach i nie uruchamia się przez kolejne 2 godziny. Sprawdzili chłodzenie, wycieki pamięci, sprawdzili wszystko, ale nic nie znaleźli. Potem awarie ustały.

Tydzień minął beztrosko... wszyscy byli zadowoleni. Szczęśliwy, dopóki wszystko nie zacznie się od nowa. Obraz jest taki sam. 10 godzin pracy, 2-3 godziny przestoju...

I wtedy ktoś (chyba mi powiedział, że ta osoba nie ma nic wspólnego z IT) powiedział:

„To jest przypływ!”

Okrzyk spotkał się z pustymi spojrzeniami, a czyjaś ręka prawdopodobnie zawahała się na przycisku wezwania ochrony.

„Przestaje działać z przypływem”.

Pracownikom wsparcia IT, którzy raczej nie czytają Rocznika Tide przy kawie, wydawałoby się to zupełnie obce. Wyjaśnili, że nie można tego w żaden sposób powiązać z przypływem, gdyż serwer od tygodnia pracuje bezawaryjnie.

„W zeszłym tygodniu przypływ był niski, ale w tym tygodniu jest wysoki”.

Trochę terminologii dla tych, którzy nie mają licencji na jacht. Pływy zależą od cyklu księżycowego. A gdy Ziemia się obraca, co 12,5 godziny przyciąganie grawitacyjne Słońca i Księżyca tworzy falę pływową. Na początku 12,5-godzinnego cyklu następuje przypływ, w środku cyklu następuje odpływ, a na końcu ponownie następuje przypływ. Ale wraz ze zmianą orbity Księżyca zmienia się również różnica między odpływem i przypływem. Kiedy Księżyc znajduje się pomiędzy Słońcem a Ziemią lub po przeciwnej stronie Ziemi (księżyc w pełni lub bez księżyca), mamy pływy Syzygyn – najwyższe przypływy i najniższe odpływy. W czasie połowy księżyca mamy przypływy kwadraturowe – najniższe przypływy. Różnica między tymi dwoma skrajnościami znacznie się zmniejsza. Cykl księżycowy trwa 28 dni: syzygian - kwadratura - syzygian - kwadratura.

Kiedy technikom wyjaśniono istotę sił pływowych, od razu pomyśleli, że trzeba wezwać policję. I całkiem logiczne. Okazuje się jednak, że koleś miał rację. Dwa tygodnie wcześniej niedaleko biura zacumował niszczyciel. Za każdym razem, gdy przypływ podnosił go do określonej wysokości, słupek radaru statku kończył się na poziomie podłogi serwerowni. A radar (lub elektroniczny sprzęt bojowy lub inna zabawka wojskowa) spowodował chaos w komputerach.

Misja lotnicza rakiety

Dostałem zadanie przeniesienia dużego (ok. 400 tys. linii) systemu kontroli i monitorowania startu rakiety na nowe wersje systemu operacyjnego, kompilatora i języka. Dokładniej, od Solaris 2.5.1 do Solaris 7 i od Verdix Ada Development System (VADS), napisanego w Ada 83, do systemu Rational Apex Ada, napisanego w Ada 95. VADS został zakupiony przez Rational, a jego produkt został przestarzałe, chociaż Rational próbował zaimplementować kompatybilne wersje pakietów specyficznych dla VADS, aby ułatwić przejście do kompilatora Apex.

Trzy osoby pomogły mi w czystej skompilowaniu kodu. Zajęło to dwa tygodnie. A potem sam pracowałem nad tym, żeby system działał. Krótko mówiąc, była to najgorsza architektura i wdrożenie systemu oprogramowania, z jakim się spotkałem, więc ukończenie przeniesienia zajęło kolejne dwa miesiące. Następnie system został przekazany do testów, które trwały jeszcze kilka miesięcy. Błędy wykryte podczas testów natychmiast poprawiłem, jednak ich ilość szybko się zmniejszyła (kod źródłowy był systemem produkcyjnym, więc jego funkcjonalność działała w miarę niezawodnie, pozostało mi jedynie usunąć błędy, które powstały podczas adaptacji do nowego kompilatora). Ostatecznie, gdy wszystko działało jak należy, zostałem przeniesiony do innego projektu.

A w piątek przed Świętem Dziękczynienia zadzwonił telefon.

Wystrzelenie rakiety miało zostać przetestowane za około trzy tygodnie, a podczas laboratoryjnych testów odliczania sekwencja poleceń została zablokowana. W praktyce oznaczałoby to przerwanie testu, a gdyby do blokady doszło w ciągu kilku sekund od uruchomienia silnika, w układach pomocniczych doszłoby do kilku nieodwracalnych zmian, co wymagałoby długiej i kosztownej gotowości rakiety. Nie zaczęłoby się to, ale wiele osób byłoby bardzo zmartwionych stratą czasu i mnóstwa pieniędzy. Niech nikt nie mówi, że Departament Obrony wydaje pieniądze lekkomyślnie — nigdy nie spotkałem kierownika kontraktu, który nie stawiałby budżetu na pierwszym miejscu lub na drugim miejscu, a potem harmonogramu.

W poprzednich miesiącach wyzwanie polegające na odliczaniu było przeprowadzane setki razy w wielu odmianach, z kilkoma drobnymi czkawkami. Zatem prawdopodobieństwo wystąpienia tego zdarzenia było bardzo niskie, ale jego konsekwencje były bardzo znaczące. Pomnóż oba te czynniki, a zrozumiesz, że wiadomości wróżyły mi i dziesiątkom inżynierów i menadżerów zrujnowany wakacyjny tydzień.

I zwrócono uwagę na mnie jako osobę, która przeportowała system.

Podobnie jak w przypadku większości systemów o krytycznym znaczeniu dla bezpieczeństwa, rejestrowano wiele parametrów, więc dość łatwo było zidentyfikować kilka wierszy kodu, które zostały wykonane przed awarią systemu. I oczywiście nie było w nich absolutnie nic niezwykłego; te same wyrażenia zostały pomyślnie wykonane dosłownie tysiące razy podczas tego samego przebiegu.

Wezwaliśmy ludzi z Apex do Rational, ponieważ to oni opracowali kompilator i niektóre z opracowanych przez nich procedur zostały wywołane w podejrzanym kodzie. Oni (i wszyscy inni) byli pod wrażeniem konieczności dotarcia do sedna problemu o dosłownym znaczeniu krajowym.

Ponieważ w czasopismach nie było nic interesującego, postanowiliśmy spróbować odtworzyć problem w lokalnym laboratorium. Nie było to łatwe zadanie, gdyż zdarzenie to zdarzało się średnio raz na 1000 przebiegów. Jednym z podejrzanych powodów było wywołanie opracowanej przez dostawcę funkcji mutex (część pakietu migracyjnego VADS) Unlock nie doprowadziło do odblokowania. Wątek przetwarzający wywołujący funkcję przetworzył komunikaty pulsu, które nominalnie docierały co sekundę. Podnieśliśmy częstotliwość do 10 Hz, czyli 10 razy na sekundę, i zaczęliśmy biec. Po około godzinie system sam się zablokował. W logu widzieliśmy, że kolejność zarejestrowanych wiadomości była taka sama jak podczas nieudanego testu. Wykonaliśmy jeszcze kilka przejazdów, system konsekwentnie blokował się po 45-90 minutach od startu i za każdym razem w logu była ta sama trasa. Mimo że technicznie uruchamialiśmy inny kod – częstotliwość komunikatów była inna – zachowanie systemu było takie samo, więc mieliśmy pewność, że ten scenariusz obciążenia powoduje ten sam problem.

Teraz musieliśmy dowiedzieć się, gdzie dokładnie wystąpiło blokowanie w sekwencji wyrażeń.

Ta implementacja systemu wykorzystywała system zadań Ada i wykorzystywała go niesamowicie słabo. Zadania to współbieżnie wykonywalna konstrukcja wysokiego poziomu w Adzie, coś w rodzaju wątków wykonawczych, wbudowanych tylko w sam język. Kiedy dwa zadania muszą się porozumieć, „ustalają spotkanie”, wymieniają niezbędne dane, a następnie przerywają spotkanie i wracają do swoich niezależnych realizacji. Jednak system został zaimplementowany inaczej. Po spotkaniu z docelowym zadaniem, to docelowe zadanie spotkało się z innym zadaniem, które następnie spotkało się z trzecim zadaniem i tak dalej, aż do zakończenia pewnego przetwarzania. Potem wszystkie te spotkania zostały zakończone i każde zadanie musiało wrócić do realizacji. Czyli mieliśmy do czynienia z najdroższym na świecie systemem wywoływania funkcji, który wstrzymywał cały proces „wielozadaniowości” na czas przetwarzania części danych wejściowych. A wcześniej nie powodowało to problemów tylko dlatego, że przepustowość była bardzo niska.

Opisałem ten mechanizm zadań, ponieważ gdy zażądano spotkania lub spodziewano się jego zakończenia, może nastąpić „przełączenie zadań”. Oznacza to, że procesor może rozpocząć przetwarzanie innego zadania, które jest gotowe do wykonania. Okazuje się, że gdy jedno zadanie jest gotowe do spotkania z innym zadaniem, może rozpocząć się wykonywanie zupełnie innego zadania i ostatecznie kontrola wraca do pierwszego spotkania. Mogą również wystąpić inne zdarzenia, które powodują zmianę zadania; jednym z takich zdarzeń jest wywołanie funkcji systemowej, takiej jak drukowanie lub wykonanie muteksu.

Aby zrozumieć, który wiersz kodu powoduje problem, musiałem znaleźć sposób na rejestrowanie postępu poprzez sekwencję instrukcji bez konieczności przełączania zadań, co zapobiegłoby wystąpieniu awarii. Więc nie mogłem skorzystać Put_Line()aby uniknąć wykonywania operacji we/wy. Mógłbym ustawić zmienną licznika lub coś podobnego, ale jak mogę zobaczyć jej wartość, jeśli nie mogę wyświetlić jej na ekranie?

Ponadto podczas analizy logu okazało się, że pomimo zamrożenia przetwarzania komunikatów pulsu, które blokowało wszystkie operacje we/wy procesu i uniemożliwiało wykonanie innego przetwarzania, inne niezależne zadania były nadal wykonywane. Oznacza to, że praca nie została całkowicie zablokowana, a jedynie (krytyczny) łańcuch zadań.

To była wskazówka potrzebna do oceny wyrażenia blokującego.

Zrobiłem pakiet Ada, który zawierał zadanie, typ wyliczeniowy i zmienną globalną tego typu. Liczne literały zostały powiązane z konkretnymi wyrażeniami problematycznej sekwencji (np. Incrementing_Buffer_Index, Locking_Mutex, Mutex_Unlocked), a następnie wstawiłem do niego wyrażenia przypisania, które przypisały odpowiednie wyliczenie do zmiennej globalnej. Ponieważ kod obiektowy tego wszystkiego po prostu zapisywał stałą w pamięci, przełączenie zadań w wyniku jego wykonania było niezwykle mało prawdopodobne. Przede wszystkim podejrzliwie podchodziliśmy do wyrażeń, które mogłyby przełączyć zadanie, ponieważ blokowanie następowało podczas wykonywania, a nie powracało po przełączeniu zadania z powrotem (z kilku powodów).

Zadanie śledzenia po prostu działało w pętli i okresowo sprawdzało, czy wartość zmiennej globalnej uległa zmianie. Przy każdej zmianie wartość była zapisywana w pliku. Następnie krótkie oczekiwanie i nowy czek. Zapisałem zmienną do pliku, ponieważ zadanie zostało wykonane dopiero wtedy, gdy system wybrał je do wykonania przy przełączaniu zadania w obszarze problemowym. Cokolwiek stanie się w tym zadaniu, nie będzie miało wpływu na inne, niepowiązane, zablokowane zadania.

Oczekiwano, że gdy system osiągnie punkt wykonania problematycznego kodu, zmienna globalna zostanie zresetowana przy przejściu do każdego kolejnego wyrażenia. Wtedy stanie się coś, co spowoduje przełączenie zadania, a ponieważ jego częstotliwość wykonywania (10 Hz) jest niższa niż w przypadku zadania monitorowania, monitor może przechwycić wartość zmiennej globalnej i ją zapisać. W normalnej sytuacji mógłbym otrzymać powtarzającą się sekwencję podzbioru wyliczeń: ostatnie wartości zmiennej w momencie przełączenia zadania. Po zawieszeniu zmienna globalna nie powinna już się zmieniać, a ostatnia zapisana wartość wskaże, które wyrażenie nie zostało zakończone.

Uruchomiłem kod ze śledzeniem. Zamarł. A monitoring działał jak w zegarku.

Log zawierał oczekiwaną sekwencję, która została przerwana wartością wskazującą, że został wywołany muteks Unlock, a zadanie nie zostaje wykonane - jak to ma miejsce w przypadku tysięcy poprzednich wywołań.

Inżynierowie Apexa gorączkowo analizowali w tym czasie swój kod i znaleźli w muteksie miejsce, w którym teoretycznie mogła wystąpić blokada. Ale jego prawdopodobieństwo było bardzo niskie, ponieważ tylko pewna sekwencja zdarzeń występujących w określonym czasie mogła doprowadzić do zablokowania. Prawo Murphy'ego, chłopaki, to prawo Murphy'ego.

Aby chronić potrzebny fragment kodu, zastąpiłem wywołania funkcji mutex (zbudowane na bazie funkcjonalności mutex systemu operacyjnego) małym natywnym pakietem mutex Ada, aby kontrolować dostęp mutex do tego fragmentu.

Wstawiłem go do kodu i uruchomiłem test. Siedem godzin później kod nadal działał.

Mój kod został przesłany do firmy Rational, gdzie go skompilował, zdeasemblował i sprawdził, czy nie korzysta z tego samego podejścia, które zastosowano w problematycznych funkcjach mutex.

To była najbardziej zatłoczona recenzja kodu w mojej karierze 🙂 W pokoju było ze mną około dziesięciu inżynierów i menedżerów, kolejne dziesięć osób uczestniczyło w telekonferencji i wszyscy sprawdzili około 20 linii kodu.

Kod został poddany przeglądowi, złożono nowe pliki wykonywalne i przesłano je do formalnych testów regresyjnych. Kilka tygodni później test odliczania przebiegł pomyślnie i rakieta wystartowała.

OK, wszystko fajnie, ale jaki jest sens tej historii?

To był absolutnie obrzydliwy problem. Setki tysięcy linii kodu, równoległe wykonanie, kilkanaście oddziałujących na siebie procesów, słaba architektura i słaba implementacja, interfejsy dla systemów wbudowanych i wydane miliony dolarów. Żadnego ciśnienia, prawda.

Nie byłem jedyną osobą, która pracowała nad tym problemem, chociaż podczas przenoszenia byłem w centrum uwagi. Ale nawet jeśli to zrobiłem, nie oznacza to, że zrozumiałem wszystkie setki tysięcy linii kodu lub nawet je przejrzałem. Kod i logi były analizowane przez inżynierów w całym kraju, ale kiedy przedstawili mi swoje hipotezy na temat przyczyn awarii, obalenie ich zajęło mi tylko pół minuty. A kiedy poproszono mnie o analizę teorii, przekazałem to komuś innemu, bo było dla mnie oczywiste, że ci inżynierowie idą w złym kierunku. Brzmi pretensjonalnie? Tak, to prawda, ale odrzuciłem te hipotezy i wnioski z innego powodu.

Zrozumiałem naturę problemu. Nie wiedziałem dokładnie, gdzie to się działo i dlaczego, ale wiedziałem, co się dzieje.

Przez te wszystkie lata zgromadziłem ogromną wiedzę i doświadczenie. Byłem jednym z pionierów stosowania Ady i rozumiałem jej zalety i wady. Wiem, jak biblioteki wykonawcze Ady radzą sobie z zadaniami i wykonaniem równoległym. Rozumiem także programowanie niskopoziomowe na poziomie pamięci, rejestrów i asemblera. Innymi słowy, mam głęboką wiedzę w swojej dziedzinie. I wykorzystałem je, aby znaleźć przyczynę problemu. Nie tylko obszedłem błąd, ale zrozumiałem, jak go znaleźć w bardzo wrażliwym środowisku wykonawczym.

Takie historie zmagań z kodem nie są zbyt interesujące dla tych, którzy nie są zaznajomieni ze specyfiką i warunkami takiej walki. Ale te historie pomagają nam zrozumieć, czego potrzeba, aby rozwiązać naprawdę trudne problemy.

Aby rozwiązywać naprawdę trudne problemy, trzeba być kimś więcej niż tylko programistą. Musisz zrozumieć „los” kodu, jego interakcję z otoczeniem i działanie samego środowiska.

A potem będziesz miał swój własny zrujnowany tydzień wakacji.

Aby być kontynuowane.

Źródło: www.habr.com

Dodaj komentarz