Баг

Материал из Posmotre.li
Перейти к: навигация, поиск

Баг (от англ. bug — букашка, жучок) — на сленге означает неясного происхождения ошибку, неисправность, сбой в устройстве. В этом смысле слово употреблялось еще в XIX веке телеграфистами и электротехниками. В современном сленге чаще означает ошибку в программе. Считается, что этот смысл слово приобрело благодаря обнаруженной в 1947 ошибке в работе электромеханической ЭВМ, которую вызвал попавший в реле и замкнувший контакты мотылёк. Несчастное погибшее насекомое было вклеено в рабочий журнал скотчем с иронической записью «первый подлинный случай обнаружения жучка»[1]. С той поры утекло много воды, реле в современных ЭВМ встречаются редко, но ошибки и сбои в программах так и зовутся «багами», а процесс их обнаружения и устранения называется иногда «debugging».

Что почитать об этом у нас[править]

Откуда берутся[править]

  • Ошибка. Не продумал алгоритм, сделал логическую ошибку, просто опечатался[2][3], плохо знает особенности использованного языка или инструментов. Бывает со всеми, поэтому: тесты и ещё раз тесты.
    • Кстати, современные ЯП во многом продвинулись в «защите от дурака»: наиболее опасные в этом плане приведение типов, управление памятью и работу с указателями стараются либо переложить на компилятор, либо обложить проверками в рантайме, либо сильно ограничить. Программисты старой школы часто недовольны, ибо эти меры порой снижают производительность или не дают выполнить привычные им трюки. А то, что иначе приходится попеременно бороться то с утечкой памяти, то с потерянными указателями, то с развалившимся стеком — в этом своя романтика, вам салагам не понять.
  • Плохая постановка задачи. От «мне никто не сказал, что оно должно останавливаться, если нажата левая педаль» до «вы же сами просили, чтобы по нажатии кнопки оно получало задание „убить всех людей“. В ТЗ прямо так и написано».
  • Трюкачество. Бывают очень неочевидные способы решения конкретной задачи, которые могут породить не менее неочевидные побочные эффекты. С другой стороны, без трюков порой решить задачу либо невозможно, либо слишком долго. Вот только если трюк не слишком широко распространён, когда код начнёт править другой программист (или ты же, но через полгода-год), он скорее всего неправильно поймёт, как это работает, и всё сломает. Наиболее известный трюк, использовавшийся в разработке игр в начале 00-х — Быстрый обратный квадратный корень.
  • Использование недокументированных возможностей. Во многих языках программирования, библиотеках и даже «железе» есть средства, которые еще не вошли в стандарт и не факт, что войдут. Однако некоторые программисты ими пользуются, уж очень бывает заманчиво. Если потом использованная неявная возможность будет изменена или вовсе отключена, то последствия будут печальными.
    • Хуже того, в современных языках программирования есть такая засада, как неопределённое поведение — когда в спецификации прямо пишут, что если вы сделаете так-то и так-то, то мы не знаем, что будет. Программа может отработать его так, может эдак; может под отладчиком делать одно, а в продакшне — совершенно противоположное. А может вломиться к вам в дом среди ночи и убить вашу собаку[4].
  • «Велосипедостроение». Когда вместо широко известных и многократно оттестированных инструментов используется самоделка, собранная на коленке.
  • Ошибка в использованных инструментах, созданных кем-то другим — противоположная ситуация. Все мы не идеальны и кто-то там, кто писал какую-нибудь нужную библиотеку тоже мог накосячить. Можно оправдывать все баги плохими библиотеками. Как правило, если библиотекой пользуются не 3,5 программиста, а множество людей по всему миру, и достаточно давно, большинство багов либо исправлены, либо хорошо задокументированы.
    • Бывает, баги в подобных библиотеках не исправляют не потому, что разработчики библиотеки такие ленивые, а потому, что пока они эту ошибку нашли, множество людей стали пользоваться ей как багофичей, и если её исправить сейчас, у них всё поломается.
    • Взаимодействие между сторонним софтом тоже в это умеет, когда каждая программа по отдельности работает прекрасно, а при совместной работе начинаются танцы с бубном. Взаимодействие разных версий одного и того же софта между собой — отдельная песня, исполняемая в си-бемоль-миноре.
  • Использование инструмента не по назначению. Начал забивать гвозди микроскопом — ой, стекляшки во все стороны сыплются! Использую СУБД для управления бизнес-логикой — и что-то программа стала жрать память как не в себя. Как правило, подобное начинается с того, что нужно реализовать какую-то мелочь, искать для этого специализированный инструмент лень. И потом хватаешься за голову, обнаружив, что весь проект работает на костылях, для этого не предназначенных.
    • С другой стороны, бывает, когда вроде бы инструмент используется по назначению, но авторы не подумали, что он будет использоваться так. Помните знаменитый «баг тысячелетия»? Всего лишь в 70-80 годах программисты считали, что их детище не проживёт нескольких лет, будучи заменённым на что-то более совершенное, а память надо экономить прямо сейчас: счёт порой шёл на байты. А к концу 90-х на вылавливание и исправление «коротких» дат потратили огромные деньги и всё равно 2000-го года ждали с замиранием сердца: а вдруг где-то вылезет! Или пренебрежение проверкой на граничные условия — ну не подумал кто-то, что кто-то захочет открыть вашей программой файл больше 255 гигабайт или задать отрицательную высоту какого-нибудь элемента. Часто такие баги становятся эксплойтами.
  • Плохо налаженное взаимодействие между разработчиками. Когда разработка ведётся по принципу письма Дяди Фёдора.
  • Спешка. Хороший руководитель разработки грамотно разобьёт задачу на этапы, расставит приоритеты и позаботится о средствах на случай, если что-то пойдёт не так (нанять больше разработчиков, отдать на аутсорс, отказаться от фичи или перенести её на следующую версию и т.д.) заранее. Плохой руководитель 80% времени будет заниматься механической работой: следить, чтоб не опаздывали и не занимались посторонними делами, устраивать совещания с power point-презентациями, а к концу срока узнает, что проект едва ли готов наполовину, нанимать новых разработчиков или отдавать на аутсорс поздно — просто не успеем ввести их в курс дела и перераспределить задачи, резать фичи нельзя, так как их уже анонсировали и разрекламировали, переносить срок — тоже, приходится заставлять работать сверхурочно, пренебрегать тестами и вообще делать абы как.

Отдельные виды багов[править]

  • Плавающий баг — баг который проявляется не всегда, а при каких-то неизвестных условиях. Например, как описанный ниже случай с Су-24: оказалось, что чтобы его увидеть, нужно особо точно зайти на цель. Есть несколько особых разновидностей плавающих багов:
    • баг Гейзенберга — в честь одного из «отцов» квантовой механики. Суть в том, что баг исчезает, как только пытаешься его «отловить». Чаще всего это происходит когда нужно работать с многопоточностью в асинхронном режиме. Возникает состояние гонки, когда данные из разных потоков могут приходить в разное время и пошаговая отладка не поможет: пока ты поставил один поток на паузу, другой отработал и уже не мешается первому.
    • баг Шрёдингера — полная противоположность, не проявляется до тех пор, пока о ней не узнаешь. Скорее миф: чаще речь идёт о багах, выглядящих так но на самом деле они проявились не оттого, что их обнаружили, а оттого, какие действия предприняли после этого. Например, сказали пользователю «не суй лампочку в рот», а он с дуру возьмёт и засунет.
    • баг с отложенным эффектом — развалившийся стек, потерянные указатели и просто испорченные данные, которые используются далеко от места, где они испортились. Воспроизвести просто, найти причину сильно сложнее. Например, если при два потока пишут в одну общую переменную, а программист не удосужился поставить семафор (или поставил не там, где следовало)
  • Баги с переполнением числовых значений — о них есть отдельная статья.
    • Не путать с переполнением стека/буфера. Эти баги одно время широко использовались хакерами для того, чтобы через безопасные с виду файлы распространять разного рода зловредов, а также, например, для самостоятельной модификации прошивки устройств, в которых она не предусматривалась — например для снятия региональных ограничений.
  • Перекрётсные баги — когда минус на минус даёт плюс. Бывает, что две ошибки в коде полностью компенсируют друг друга и единственное следствие — что становится непонятно, как этот код вообще работает (что тоже плохо и сулит баги в будущем). Но чаще один из багов — какая-нибудь мелочь, но если его исправить всплывает другой, который не проявлялся как раз из-за исправленного. Если программист был пьян, такие баги могут порождать длинные цепочки.
    • Бывает, что баг невозможно исправить (например, если он в чужой библиотеке, кода которой у тебя нет), или просто влом — тогда ставится костыль. Когда же изначальный баг исправляют, костыль сам становится багом. Хуже когда на этот костыль опирается что-то ещё.
  • Баги, внесённые программистом намеренно. Например, если программист обиделся и перед уходом решил сделать работодателю подлость. Сегодня в век систем контроля версий, обзора кода и разработки через тестирование провернуть такой трюк тайно сильно сложнее (хотя там, где из жадности или косности продолжают работать по старинке — такое ещё может случаться), а до конца XX века случалось регулярно. К примеру, в 1982 году на АвтоВАЗе обиженный программист подправил код так, чтобы через некоторое время после его ухода в отпуск случилась рассинхронизация в работе конвейера, и как выяснилось — это был не первый случай. С другой стороны, заказчик, не имеющий доступа к исходным кодам и системе контроля версий, всё ещё уязвим к таким атакам, если это делается в сговоре или с прямой санкции руководства разработчика.

Где встречается[править]

Внимание! Просьба добавлять сюда только внутримировые примеры, где програмные баги или трудноуловимые ошибки аппаратуры играют заметную сюжетную роль. Для прочих багов в видеоиграх на проекте есть упомянутые выше статьи, тут им не место. В раздел реальной жизни стоит помещать наиболее знаковые и забавные примеры, так-то багов, приводивших к серьезным последствиям, было великое множество. Также просьба не помещать сюда просто примеры взбунтовавшихся или осознавших себя роботов, поскольку это другие тропы, хотя и безусловно родственные.

Литература[править]

  • Идеальный тестер — тестировщики нужны именно для этого.
  • «Бета-тестеры» — не только для этого, но в том числе.
  • О. Дивов, рассказ «На три буквы». Из-за предмета статьи, вместо того, чтобы нормально транслировать рекламный лозунг на прекрасно заметный с Земли на фоне вечернего неба экран спутника, программа принялась резать его кусками и показывать в таком виде. Зато крупно. Да еще не с начала, а с десятой буквы, а потом заново с начала. Лозунг был: «Застрахуй машину в “Бастионе”» и резался, в соответствии с названием рассказа, кусками по три.
  • С. Лукьяненко:
    • «Лабиринт отражений». Во второй книге серии упоминается, что дайверы могли видеть ошибки программ просто так, без исследования и поиска. Например, пробел в системе безопасности выглядел для них как дыра или незапертая дверь в стене. Правда в первой книге ничего подобного не описано, все суперспособности дайверов ограничиваются возможностью в произвольный момент времени выйти из виртуальной реальности и избавиться от эффектов, действующих на других её посетителей.
    • «Спектр»:
      • Человеческая версия пульта управления системой порталов между планетами позволяла выбрать не одно, а несколько мест назначения, при этом проходящий через портал размножался на все выбранные. Через сочетание «CTRL+левая кнопка мыши» — ага, как винда. Оперативно пофикшено. В тексте прямо описано, что способ управления зависит от путешественника: войдёт в ту же комнату геддар — вместо человеческой клавиатуры появятся геддарские рычаги. Так что это не у инопланетян есть клавиша Ctrl, это у людей есть клавиша Ctrl. Вдобавок, будь там натуральная винда — в ней возможность выделения нескольких пунктов по умолчанию выключена, а кто и зачем стал бы специально её включать? Скорее можно предположить, что интерфейс подстраивается под ожидания пользователя: Ирина ожидала, что в винде можно выделить несколько пунктов разом — пульт, косплеющий винду, послушно выделил.
      • Ещё один схожий баг — «размножение» Семецкого. Здесь даже не потребовалось выделения нескольких пунктов списка, достаточно было пройти Врата пьяным в дымину.
  • Павел Шумил, «Слово о драконе». Пример не программного бага: наиболее значимая техника Повелителей снабжена блоком самоуничтожения, срабатывающим после достаточно длительного бездействия, чтобы опасная аппаратура не попала в руки недостаточно развитой цивилизации. Вот только, к счастью для главных героев, в конструкции камер нуль-Т была допущена ошибка: контакты блока и прочей начинки состояли из различных металлов и создавали разность потенциалов, которая в конце концов привела с разрушению контактов[5]. В результате блок самоуничтожения не сработал и главные герои успешно получили доступ к аппаратуре.
  • А. Азимов, рассказ «Как потерялся робот» из цикла «Я — робот». Первый из трех азимовских законов роботехники гласит: «Робот не может причинить вред человеку или своим бездействием допустить, чтобы человеку был причинён вред». Для работы на одной из космических станций в обстановке секретности (земное общество и так весьма настроено против роботов) была выпущена серия машин, в которой этот закон был урезан до первой части — «не может причинить вред», ведь работа в опасных условиях подразумевает, что человек вынужден иногда сознательно идти на риск или даже вред для себя, чего обычный робот допустить не может. Однако такое изменение закона порождает возможность фактически убить человека: теперь закон не мешает роботу намеренно создать опасную ситуацию, зная, что его способностей достаточно, чтобы спасти человека, значит, его действия напрямую не угрожают человеку, а затем просто не вмешиваться, ведь закон больше этого не требует. Пример намеренно заложенного эксплойта, приведшего к неожиданным последствиям.
  • С. Лем любит такие фокусы, правда в силу времени написания, они чаще имеют техническую или смешанную, а не чисто программную, природу:
    • «Рассказы о пилоте Пирксе»
      • «Патруль». Пиркс раскрывает загадку исчезновения нескольких, таких же как он, патрульных: при определённых условиях система наблюдения корабля начинает проецировать фантом, выглядящий как неопознанный корабль, вдобавок его мерцание слегка гипнотизирует пилота. Впавший в транс пилот в погоне за электронным призраком разгоняет ракету, пока не кончаются топливо и кислород.
      • «Терминус». Пограничный случай, поскольку причины не объясняются. Пиркс обнаруживает, что старый робот на прошедшем капитальный ремонт корабле каким-то образом запомнил и периодически воспроизводит переписку запертых в своих отсеках и перестукивающихся азбукой Морзе умирающих астронавтов. Это производит жуткое впечатление, словно в роботе обитают «призраки» погибших, не осознающие своей смерти. Окончательного ответа, так это или нет, является поведение робота просто артефактом сбоящей техники или «призраки» в его памяти осознают себя, рассказ не дает.
        • Когда Пиркс пытается войти в контакт с ними, ответ таки получает, а потом «призраки» начинают спрашивать, кто он. Но тут мы подходим к проблеме китайской комнаты
      • «Условный рефлекс». Из-за сочетания нескольких факторов на отдалённой лунной станции аппаратура иногда выдает такую картину, словно вышедший для обслуживания научной аппаратуры член команды сорвался в пропасть и теперь умирает, хрипя в микрофон. Дополнительной жути на наблюдателя нагоняет тот факт, что во время постройки станции такой случай действительно произошел и человек много часов умирал под обвалом именно в этой пропасти. Кинувшийся на поиски «погибающего коллеги» космонавт сам становился жертвой, а его вернувшийся напарник кидался следом за уже реальным пострадавшим, с тем же результатом. К счастью для напарника Пиркса, пилот оказался не склонен к мистике, зато склонен к трезвому логическому мышлению.
      • «Ананке». Робопилот в процессе своего программирования-обучения воспринял от «наставника» вынесенное в заглавие психическое расстройство (кратко говоря — крайнее упрямство, педантизм и склонность к запредельному перфекционизму). Это приводит к катастрофе, когда в тривиальной ситуации, при штатной посадке на планету, корабельный компьютер самостоятельно начинает усложнять задание. Заодно и пример сбывшегося прогноза: в некоторых из модных сейчас нейросетей похожий эффект действительно может возникнуть.

Видеоигры[править]

  • Deus Ex: Human Revolution — если ближе к концу согласиться поставить «обновление программного обеспечения для имплантов» в официальной клинике «Протез», то очень быстро протагонист словит неслабые глюки (и это еще мелочи, большая часть обладателей имплантов впадёт в безумие): в обновление намеренно была внесена уязвимость (т.е. недостаточная «защита от дурака» или злоумышленника, которым в этот раз был сам автор программы).
  • Star Control 2 — агрессивность мирных зондов Слайлэндро была багом.

Реальная жизнь[править]

  • Y2K. Вопреки апокалиптическим настроениям журналистов, баг не слишком серьёзный (подумаешь, за 1999 годом вместо 2000 наступит 1900. Или 19100), зато каков масштаб — он затронул практически весь цивилизованный мир! И заодно сильно повлиял на методологии разработки ПО: например, наглядно показал, что принцип «работает — не трогай!» применим далеко не всегда.
  • Один из наиболее известных случаев программного бага с трагическими последствиями — ошибка в программе аппарата для лучевой терапии рака Therac-25. Аппарат выдавал слишком большую дозу облучения. Как минимум шесть пострадавших и двое погибших. Самое печальное, что пациенты обычно обращали внимание на необычные и болезненные ощущения, но врачи не сразу им поверили.
    • Сюда же. Из-за «ошибки 2000 года» в одной клинике произошёл сбой, и перепутались результаты анализов на риск синдрома Дауна у плода. В результате были абортированы два здоровых эмбриона, а в других семьях родились нежеланные больные дети. https://habr.com/ru/company/alfa/blog/517096/
  • В 1962 году ошибка в программе привела к отклонению от курса и взрыву космической ракеты Mariner-1.
    • Сюда же, авария при первом запуске ракеты Ариан-5 в 1996 году. Полётное ПО разрабатывалось на основе ПО для ракеты предыдущего поколения, Ариан-4, поэтому часть кода была перенесена оттуда. В том числе один маааленький модуль на три строчки который вообще не был нужен на новой ракете. И так как новая ракета была более резвой, чем старая, случившиеся в полёте переполнение переменной этого модуля вызвало зависание обоих бортовых ЭВМ, основного и резервного, что запустило аварийный механизм самоуничтожения ракеты. Считается самым дорогим багом в истории, около 500 миллионов на ракету и спутники на ней. К тому же, был нанесен ущерб репутации самой серии ракет, следующий запуск спутников производился с Байконура на «Союз-Фрегате», хотя новый «Ариан» уже был готов.
    • Тоже не самый дешевый баг. Спутник w:Mars Climate Orbiter разбился из-за того, что в программе часть величин была задана в метрической системе мер, а часть в имперской. 200 миллионов долларов в трубу.
  • Скорее анекдотический и комичный случай: при испытаниях Су-24 регулярно возникала ошибка в ПО самолета, приводившая к его перезагрузке, но только если в кресле пилота был лётчик-испытатель Ильюшин. Как выяснилось после долгих поисков, этот лётчик стабильно заходил на цель с такой точностью, что отклонение становилось меньше минимального числа, которым мог оперировать компьютер самолёта. В результате в системе коррекции прицела возникала ошибка деления на ноль.
  • Известный случай времен сирийской войны шестидесятых. Американские лётчики вынуждены были летать на низких высотах, чтобы их не замечали радары. Но при полётах над Мёртвым морем у многих из них внезапно перезагружалась вся бортовая электроника самолёта. Причину обнаружили только тогда, когда вспомнили, что уровень воды в Мёртвом море ниже официального «уровня моря», который в высотомере был установлен как ноль. В итоге система делила на ноль, с закономерными последствиями.
  • Гибель нескольких новейших Боингов, в результате того что начальство Боинга решило очень круто сэкономить уволив дорогих специалистов, и наняв индусов, которые согласились написать новые программы для Боинга… бесплатно! (надеясь на дальнейшее сотрудничество с Боингом в будущем). https://habr.com/ru/post/458224/

Примечания[править]

  1. Хотя, если подумать, сама эта запись подразумевает, что были и более ранние случаи, в которых просто не было «реально обнаруженного жучка». То есть слово стало означать ошибку/сбой ещё до того. Кстати, по одной из легенд, первое применение слова bug по отношению к технике приписывают еще Томасу Эдисону. Во время работы над фонографом он якобы долго не мог понять, почему же собранный прототип отказывается работать. Перебрав в уме все возможные варианты и так и не найдя решения, он предположил, что во время сборки между деталями устройства мог попасть жук. И хотя на самом деле никаких насекомых в фонографе не оказалось, в будущем он продолжил использовать слово bug для обозначения досадных неисправностей.
  2. Например, если в программе на некоторых языках напечатать «=» вместо «==», то вместо сравнения двух значений получится запись значения справа от выражениия в переменную слева. Если такое действие в данном случае возможно, сама программа ни о какой ошибке не сообщит — откуда ей знать, хотел программист поменять значения или только сравнить? А потом у кого-то годовая премия станет равной нулю.
  3. В еще более некоторых языках (точно в JavaScript, но, возможно, он не уникален) из-за крайне нетривиальной типизации есть нежно любимая разработчиками разница между сравнением по значению и по типу, в которой последствия ошибки еще более неочевидны.
  4. Шутки шутками, а порой UB может приводить к совершенно непредвиденным ситуациям, которые оказываются вполне логичными после изучения работы конкретного компилятора.
  5. Подобное бывает в реальности. Например, если срастить кабель чувствительной аппаратуры куском кабеля из другого металла, будут помехи. Постарайтесь воздержаться от этого, ибо обнаружившие баг ремонтники непременно пожелают нанести автору тяжкие телесные повреждения.