пятница, 10 сентября 2010 г.

Почему бAARDак не убрали в релизе?

В бета-версиях Windows 3.1 был скрытый и зашифрованный код, который при запуске на DR-DOS выдавал непонятное сообщение о вымышленной ошибке.

В релизе решили подобными фокусами не заниматься, но код проверок и само сообщение не убрали: они так и остались внутри WIN.COM, и достаточно изменить один байт, чтобы AARD-код снова выполнялся при каждом запуске.

Зачем его оставили? Неужели Microsoft рассчитывала однажды в будущем разблокировать эти сомнительные проверки?
Конечно же, нет. Даже сообщение в релизе осталось неизменённое: «Please contact Windows 3.1 beta support.» Если бы сообщение действительно предназначалось для показа, после окончания бета-тестирования его бы обновили.

Так зачем оставлять в релизе бессмысленный код, который никогда не выполняется?

Ларри Остерман объясняет:
Обойти выполнение кода, вставив команду JMP, довольно безопасно; а если удалить его, в оставшемся коде изменятся смещения функций — т.е. это будет уже новый, непротестированный код. Бета-тестирование было уже окончено, поэтому разработчики старались не заменять протестированный код непротестированным.

Но с какой стати такое незначительное изменение кода может на что-то влиять? Крис Прэтли приводит пример:
Даже перелинковка кода, не то что перекомпиляция, может внести неожиданные баги. Несколько лет назад, когда мы работали над азиатским выпуском Word97 и уже считали весь код готовым, мы занялись его окончательной оптимизацией. У нас есть инструмент, который собирает статистику по выполнению отдельных функций, и переупорядочивает их в файле оптимальным образом; код функций при этом не изменяется. После оптимизации мы отдали код на окончательное тестирование, и — оба-на! — нашёлся баг. Когда тестировщики пользовались определённой функцией программы, на некоторых машинах оптимизированный код рушился. На тех же самых машинах та же самая функция отлично работала до оптимизации.

Мы занялись отладкой; но если мы добавляли в оптимизированную версию информацию для отладки, она больше не рушилась. Мы пробовали отлаживать её без дополнительной информации; но даже если мы просто запускали её под отладчиком, она больше не рушилась. Каким способом бы мы ни пытались узнать причину падений, программа не падала; но она падала абсолютно всегда, когда мы оставляли её в покое.

Мы уже собирались применить ICE (аппаратный отладчик), когда заметили закономерность: программа рушилась только на процессорах Pentium с частотой 150МГц и ниже, хотя не на всех. Это уже была зацепка. Мы пошли на сайт Intel, и заглянули в «список неточностей» (так они называют свои баги). Бинго! В процессорах Pentium была «неточность», в определённых условиях приводящая к сбою. В очень определённых условиях: если через 33 байта после JMP стоит условный переход, а сам JMP располагается на границе страницы памяти. Эта «неточность» была исправлена, начиная с выпуска Pentium 150МГц.

На самом деле, в чипах довольно часто бывают баги, хотя лишь немногие из них становятся общеизвестными. В конце концов, микрокод чипа пишут люди, а не боги. Обычно, как только производители чипа обнаруживают в нём баг, они сообщают производителям компиляторов; так что компиляторы генерируют код в обход обнаруженных багов, и обычные программисты с этими багами уже не сталвиваются. Оказалось, у нас был немного устаревший компилятор, который об этом конкретном баге ещё не знал.

Когда представитель Intel подтвердил, что таинственные падения программы могут быть вызваны этим самым багом, мы прошлись по оптимизированному коду, нашли виновную последовательность команд, и вручную переставили три байта так, чтобы расстояние между двумя прыжками составило 34 байта. Падения прекратились.

Теперь, когда кто-то меня уверяет, что его исправление «абсолютно безопасно», я всегда рассказываю эту историю. Ни одно изменение кода не может быть абсолютно безопасным.

Я и сам сталкивался с похожим багом: когда мы работали над Exchange 5.5, очередной билд рушился на одной тестовой машине — всегда на одной и той же. Мы несколько дней пытались выяснить причину, но безуспешно: баг исчезал от малейшего изменения кода. Зато он абсолютно всегда проявлялся, когда мы прекращали отладку. В конце концов мы, как и Крис, нашли «список неточностей»; и действительно, наш код страдал от одной из них. Не успокоившись исправлением конкретного билда, мы нашли набор опций компиляции, при которых баг был невозможен.

Поэтому неудивительно, что разработчики Windows оставили AARD-код в релизе: они уже много недель, если не месяцев, тестировали Windows с этим кодом, и знали наверняка, что когда этот код на месте, Windows работает. Работает ли Windows без него, они — перед самым релизом — не рискнули выяснять.



БAARDак был не единственной интересной особенностью бета-версии Windows 3.1.
Впервые Windows перехватывала нажатие Ctrl-Alt-Del, и показывала собственный экран «давайте я вам помогу закрыть зависшую программу». Пользовавшиеся Windows 3.x помнят, что этот экран был синим; в бета-версии же он был скучно чёрный.

Если пользователь подтверждал закрытие зависшего приложения, но Windows не удавалось его завершить (например, если зависших приложений в системе просто не было), то единственное, что Windows могла предложить, — перезагрузка.

В релизе Windows 3.1 эту странность исправили: теперь, если зависших приложений нет, Windows предлагает оставить всё как есть.



Другую похожую историю я читал, кажется, у Рэймонда Чена; мне не удалось сейчас найти первоисточник. Там Чен рассказывал, что в одном из древних билдов Windows обнаружили неиспользуемую переменную. Удалили её — в совершенно другом месте кода перестала работать функция. Вернули переменную — баг пропал.

В этот раз чипы были в порядке: проблема действительно оказалась у программистов. В сломавшейся функции была переменная, которая не во всех случаях инициализировалась перед использованием. Значением неинициализированной переменной был мусор, случайно оказавшийся в выделенной под неё ячейке стека; и так сложилось, что в этом месте всегда оказывался нуль. Нуль был подходящим значением для той переменной, и программа продолжала работать.

Когда удалили неиспользуемую переменную, все остальные переменные в программе «съехали» по стеку, и теперь в месте, отведённом под неинициализированную переменную, оказывалось какое-то другое значение. Теперь программа рушилась.

На удивление коллег Чена, бажная функция была написана за много месяцев до этого, и даже попала в предыдущий релиз Windows! Из-за удачного стечения обстоятельств, она всегда работала правильно, несмотря на баг, — именно поэтому баг так долго оставался незамеченным.

Чен приводил эту историю как объяснение того, почему в Windows XP есть фрагменты кода, которые никто не трогал со времён Windows 3.0. Никто не знает, сколько там есть невидимых багов; зато все знают наверняка: этот код работает.




Источник: Хабрахабр - Windows
Оригинальная страница: Почему бAARDак не убрали в релизе?

Комментариев нет:

Отправить комментарий