Как стать автором
Обновить

Комментарии 86

НЛО прилетело и опубликовало эту надпись здесь
Напомню, что неуловимый Джо «никому не нужен».
Как поговаривают, любая аналогия хромает, в этом сравнении главное — внезапность и неуловимость.
К сожалению, некоторые баги, проявляющиеся часто и тем самым критически влияющие на характеристики продукта, тоже могут попасть в категорию «гейзенбаг». Например, мне приходилось ловить баги, связанные с перепахиванием памяти в C++. То еще удовольствие. Программа, скомпилированная в режиме «Debug», работает, а в «Release» — не работает, но в Release ее нельзя препарировать отладчиком. Попытки же вставить в программу отладочную печать тоже приводят к невоспроизводимости сбоя: память перепахивается в другом месте. Ловля каждого такого бага — это уникальный опыт.
Для тех, кто использует систему debug/release ловля бага — это удача. Сродни езде без рук и тормозов на одноколесном велосипеде. А если эту систему не использовать, то ловля багов — это технология. Основа закладывается на этапе написания кода:
— assert
— система логгирования
— try..except..finally
ну и сам собой — никакого деления на release/debug. Тяжелые проверки включаются динамически из конфига на том же коде.

Ну и технологии ловли на каждый тип бага. Например, перепахивание памяти ловится через аппаратные breakpoint на запись в данную память. В идеале — их можно настроить на логгирование, потом убрать дубликаты из списка адресов и посмотреть, откуда писали.
уважаемые, объясните за что минусуете? за одну лишь опечатку в транслитерации фамилии? ведь Шрёдингер в мысленном эксперименте с котом подчёркивает вывод аналогичный выводу из неопределённости Гейзенберга — заключение, что явления квантового мира, как и все зависимые от них последствия, бывают очень ограничены в предельно возможной точности прогнозирования. согласен, гайзенбаг лаконичнее, но и со Шрёдингером пароним улыбнул.

Дело в том, что термин "Шрёдинбаг" тоже существует — и означает нечто иное

НЛО прилетело и опубликовало эту надпись здесь
Спасибо, не знал)
Был у меня гейзенбаг, в районе 98 года.
Тогда у меня был Пентиум 200, если кто помнит, в АТ-блоках питания был не только вход, но и выход на монитор.
Естественно, для пущего удобства туда был включён сетевой фильтр, куда воткнут помимо монитора усилитель Вега с советскими 35-ваттными колонками (ну потому что это удобно — включил комп и сразу все что нужно включилось).

Баг заключался в том, что при прослушивании музыки комп иногда перезагружался.
Естественно, когда через полгода усилок был вместо АТ-выхода воткнут в обычную розетку, все прекратилось :)
У меня комп зависал (1993 год) от включения-выключения холодильника. Перезапускался процессор клавиатуры, причем первую стартовую последовательность он выдавал не полностью. BIOS от этого шизел и закрывал прерывания от клавиатуры. Пришлось писать программу для открытия прерывания и запускать её мышкой. :-)
НЛО прилетело и опубликовало эту надпись здесь
Железячной проблеме — железячное решение. Тоже холодильник на что-то влиял. Спасло найденное в хламе большое ферритовое кольцо — мотаем сетевой шнур холодильника через кольцо столько витков, сколько влезет, и — вуаля! Заодно пропадают щелчки в колонках. Впрочем, ещё лучше помогает смена совкой алюминиевой проводки на медную. Это, конечно, стены долбить, зато заодно пожаробезопачность повышается.
А у знакомых физиков такой «сетевой фильтр» был сделан из ферритового кольца диаметром сантиметров 30. Спасал компьютер от перезагрузки при включении рядом мощного генератора электронных пучков.
По миганию лампочек я решил, что это просадка напряжения, а не импульсная помеха. Но, может и неправ был.

Переключение компа на другую пробку не помогло. Так что провода, если менять — так от лестничных шин к щитку.
Да скорее всего БП по питаню провисал на той-же 12в шине…
Еще я считаю что гейзенбаги — как ни странно — растут именно с ростом скилла разработчика, которые начинают юзать все более ловкие и «умные» конструкции в программировании, что и порождает таких баг-монстров. Более «тупой» или «декларативный» стиль, пусть и выглядящий порой более скучно и уныло — на деле же редко породит такие баги.

"Декларативный" стиль и отлаживать-то невозможно — так что да, гейзенбагов (т.е. багов, которые исчезают или проявляются в отладчике) в нем допустить сложно.

Остроумие не засчитано. Стиль != язык. Декларативный стиль это например использование более многословных конструкций switch case, чем хитроумных условий, особенно многовложенные одстрочные тернарные условия, как у js.

Что нить типа такого:
smallestNum = (x<y && x<z)? x: (y<x && y<z)? y: (z<y && z<x)? z;

switch case — это вполне себе императивный стиль

хорошо, со switch case — пример не удачный, просто понятие «декларативности» в обиходном сленге расширилось, но вы цепляетесь за слова, вт вам пример про декларативный стиль языков программирования типа Lua, а не только языков разметки. (И да это особенно удобно когда погружаешь программный язык в какой-то язык разметки — просто в статье частный случай).

Основы декларативного программирования на Lua
«Декларативный» стиль и отлаживать-то невозможно

Да щито вы говорите?
Стыдно должно быть за попытку выглядеть умным, но не знать что такое декларативный стиль не в языках разметки.

Императивный стиль
int F(int x) { return x*x; }

int sum = 0;
for (int i=0; i<arg.Length; i++)
{
  if (arg[i] > 10) sum += F(arg[i]);
}


Декларативный стиль
int F(int x) { return x*x; }

int sum = arg.Where(x => x > 10).Sum(F);


Source: Императивный vs Декларативный стиль

То, что вы привели — это не декларативный, а императивный стиль с элементами функционального.

Упрямство юного минусатора выше желания принять факты.

Wikipedia:
К подвидам декларативного программирования также зачастую относят функциональное и логическое программирование — несмотря на то, что программы на таких языках нередко содержат алгоритмические составляющие, архитектура в императивном понимании (как нечто отдельное от кодирования) в них также отсутствует: схема программы является непосредственно частью исполнимого кода[1].

А есть «чисто декларативные» языки типа SQL/HTML.
Вы спорите с академическими фактами, а не со мной.
На повышение уровня декларативности нацелено языково-ориентированное программирование.

Декларативность это более широкое понятие, которое описывает процесс c проекции «каково нечто», а не «как его создать». Порядок выполнения и способ достижения не важен.
А есть «чисто декларативные» языки типа SQL/HTML.

Подскажите, пожалуйста, SQL-отладчик.


К подвидам декларативного программирования также зачастую относят функциональное и логическое программирование

Чистое функциональное программирование — да, декларативно. Но императивное программирование с элементами фугкционального остается императивным.

Подскажите, пожалуйста, SQL-отладчик.

Если потрудитесь отматать в начало треда, то речь шла о коде в «декларативном стиле», а не о «декларативных языках», которые мне пришлось упомянуть, чтобы вернуть вас с Луны на Землю.

Чистое функциональное программирование — да, декларативно. Но императивное программирование с элементами фугкционального остается императивным.

Попытка до упора отстоят свое, пусть и ошибочное изначально замечание — умиляет.
Ясно же, что сначала для вас было открытием «декларативный стиль» != «декларативный язык».
А теперь вы пытаетсь съехать, когда вас ткнули в академические формулировки.

Пора тредик закрывать — иначе он скатывается во флейм.

Декларативный стиль — это описание "что желаем получить", в противоположность "что делаем" императивного стиля.


На C# можно писать в декларативном стиле.


К примеру, известная ORM-библиотека Entity Framework предполагает именно такое описание для настройки отображения (да и сама концепция ORM по своей сути декларативна).


При этом процесс работы с БД через EF — императивен, в отличии от настройки отображения.


Также к декларативным библиотекам можно отнести:


  • AutoMapper;
  • IoC-контейнеры (как и саму идею IoC);
  • XML-сериализацию/десериализацию (кроме той части, что идет через интерфейс IXmlSerializable);
  • кучу мест в ASP.NET MVC, которые настраиваются через атрибуты.

Вот это все — и есть декларативный стиль. А приведенный вами кусок кода arg.Where(x => x > 10).Sum(F); — типичный императивный вызов.

По сути треда:
Так вы отказываетесь от своих слов что декларативный стиль нельзая отлаживать?

Я вам привёл ссылки на академические/мета-академические (вики) источники — и примеры скопипастил из них, дабы не порождать флейма. Можете поспорить с авторами. Или как это часто бывает — создать свою альтернативную вселенную, в которой все ваши аргументы будут логичны и правильны, а оппонента — нет.

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


После этого тред можно закрывать.

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

Глупо, потому что вы думали до этого иначе. А теперь вот подчитали на скорую руку и с пеной оправдываетесь.
Надо уметь проигрывать и признавать ошибки. Вот и всё.

P.S.:
Ваша попытка сделать удачную мину при неудачном остроумии — засчитана, но не суть вашего изначального выпада.

Где вы увидели, что я отказываюсь от своих слов?

Декларативное программирование — это, в основном, PROLOG.
Вот например случай от со-автора «the Jargon File» / «Словаря Хакера», и автора книги «Собор и Базар» Эрика Раймонда о том, как он напоролся на переполнения буфера glibc (на английском).
Изучая код разработчиков действительно высокого класса, можно заметить, что такой код не изобилует хитрыми трюками, а наоборот, выглядит очень красиво и понятно. Например, мне очень нравится изучать код ядра ReactOS (копирует структуру ядра Windows) по этой причине.

Ловкие и хитрые конструкции в коде — это удел специалистов среднего уровня, которые изучили трюки, но еще не накопили опыта и интуиции в том, когда следует их применять, а когда — нет.
Высокого — да. Это те, кто прошли последнюю ступень искушения «использую, потому что могу» и перешли в стадию просвещенных «не использую, потому, что знаю».
При открытии веб-приложения появлялось сообщение об ошибке БД, что id пользователя при вызове процедуры равно null. Сообщение было замечено пользователями при работе на тестовом сервере, потому что там все ошибки выводились через flash messages на фронтенд в виде всплывающих сообщений. Ошибка появлялась один раз на странице логина, при первом входе в систему, обычно с утра. Неавторизованного доступа в приложении нет.

Я нашел функцию, где происходит ошибка, функция вызывается из стандартного шаблона представления (layout), в котором по бокам разная информация для пользователя, в ней происходит вызов процедуры БД, куда передается user_id. Но он не может быть null, null он только на странице логина, а у нее свой шаблон.
В Firebug кроме GET-запроса страницы логина никаких других запросов к сайту нет.
Опытным путем выяснилось, что ошибка также появляется, если нажать Ctrl+F5 или Ctrl+Shift+R.

Причина
Открыл Chrome, и сразу нашел причину. На запрос favicon.ico выдается 404.
Firebug запрос фавиконки вообще не показывает.

При открытии сайта браузер запрашивает файл favicon.ico.
На тестовом сервере его почему-то нет, и на рабочем тоже.
Управление передается на index.php, приложение не находит подходящий роут и показывает ошибку 404.
Ошибка выводится со стандартным layout.
Текущего пользователя еще нет, база выдает ошибку, ошибка попадает в flash messages.
Открывается страница логина, показывает все flash messages из сессии.

А логи запросов не велись?
Велись наверно, но проблема была явно в приложении, а сообщение приложения и так отображалось в интерфейсе. А локально все работало, так как иконка была в репозитории.
Аналогично бывает на завершении приложений. Система логгирования уже завершилась, возникает баг, она реактивируется, но криво. Это такое общее место для возникновения проблем.
НЛО прилетело и опубликовало эту надпись здесь
Отлаживал девайс с гироскопом, подключенный по com-порту к самописной программе на компе, которая угол отображает. Сижу, кручу девайс, все вроде работает. И вдруг хоп — в com-порте тишина.
Перезапустил прогу и девайс — все ок. За день это повторилось раза два или три, на первый взгляд, совершенно бессистемно.

В конце концов заметил, что это происходило, только если угол наклона девайса был 19 градусов
Оказалось
Оказалось, что я зачем-то (совершенно не понимаю — зачем?!) включил программное управление потоком com-порта, в котором байт 19 (0x13, XOFF) означает «Остановить передачу». Протокол у меня был бинарный, угол передавался просто двумя байтами в градусах. Девайс выдавал данные только по запросу, а после байта 19 порт на компе больше никаких запросов отправлять не хотел.
Вот это уже классический гейзенбаг. Обнаружили проблему по наитию или путем пошаговой проверки?

Это обычный труднообнаружимый баг. Но он не меняет свои свойства при попытке обнаружения — а потому гейзенбагом считаться не может.

Если я правильно помню, то где-то с третьего-четвертого раза я заметил, что последний показанный угол всегда один и тот же.
Как пишут установку бита программеры? 1 << N, вроде без вариантов? А вот математикам роднее 2.0**N. На IA32 — вещественная арифметика 80 бит, так что все работало штатно. Лет через 5-10 после написания перенесли на ARM. И для N=31 оно начало сбоить — 2.0**N выдавало на единичку меньше, то есть все биты, кроме нужно.

N=31 это спутник G32 (GPS), в Питере он бывает виден редко, а вот на демонстрация в Москве — частенько. Так что дома все работало, а на демонстрациях — иногда был генеральский эффект.

Нашли чтением кода через 3 года после первого сбоя. И дня через 2 после начала охоты на этот баг. Воистину неуловимый Джо — это Джо, которого никто не хочет ловить.
Тут не в привычности дело, автор сего, похоже, в принципе не понимал, как числа в компьютере устроены. Попробовал 2**32 — ой, «почему-то» не работает. А что если… 2.0**32 — О! Заработало!
Математически 1<<N, 2U**N и 2.0**N идентичны. И автор решил, что оптимизатор эту математическую идентичность поймет. И может быть оптимизатор даже и понял бы… Но… поскольку это был перенос сложного кода с IA32 на ARM плюс с С++ Builder на gcc плюс с Windows на linux, оптимизатор был отключен. Ускорение оптимизатор дает процентов 5-10, а вот шансы словить хитрый баг с ним больше. Поэтому компилятор отработал по принципу «слушайте ваши Валенки и не выпендривайтесь».

Интереснее, что человек может быть отличным алгоритмистом и отвратительным кодировщиком одновременно.

Что касается понимания чисел — меня всегда удивляло, как она может, глядя на значение чисел в матрице сказать, какой баг был допущен в обработке. Причем в процентов 95 — подтверждается.

>Математически 1<<N, 2U**N и 2.0**N идентичны. И автор решил, что оптимизатор эту математическую идентичность поймет.

«не думаю» (ц) :)
Сознательно написать число с точкой в надежде, что оптимизатор превратит его в целое… Как-то неправдоподобно. Но, даже, если ход мысли был именно такой, это всё-равно принципиальное непонимание как числа в компьютере устроены. Даже если оптимизатор превратил бы 2.0 в 2, ошибка бы была и на IA32. Там ведь у сибилдера целые тоже 32-х битовые. Поэтому, видимо, точку добавили именно потому, что без точки не работало, а понять почему не работало, не осилили.
Впрочем, не важно, история сама по себе интересная.
Отличие в том, что вы ДУМАЕТЕ, а я ЗНАЮ. Под 100 тысяч строк этого автора поддерживаю.

Да, для программиста оптимизация pow(2,N) в целое — маловероятна. И у программеров есть понимание, что pow(2,N) и pow(2,0,N) — одно и тоже, ибо аргумент у pow вещественный. И есть понимание отличий внешних от внутренних функций. Внутренние — это функции вроде sqrt, которые могут быть оптимизированы до машинной команды. А pow — внешняя, даже pow(x,2) не оптимизируется до x*x.

> Даже если оптимизатор превратил бы 2.0 в 2, ошибка бы была и на IA32
АРГУМЕНТИРУЙТЕ. Желательно тестовым примером. Или доказательством, что 56 бит на IA32 мантиссы мало.

> точку добавили именно потому, что без точки не работало, а понять почему не работало, не осилили.
Значит тоже не понимаете, что pow(2,N) и pow(2,0,N) дает одинаковый код.
А, так у вас нормального возведения в степень нету, только pow есть. Что же голову с 2**N морочили? :)

>> Даже если оптимизатор превратил бы 2.0 в 2, ошибка бы была и на IA32
>АРГУМЕНТИРУЙТЕ. Желательно тестовым примером. Или доказательством, что 56 бит на IA32 мантиссы мало.

Это вы меня смутили операцией **, я си давно забыл, думал, что там есть такая. С pow-то понятно, фактические аргументы будут приведены к типу формальных.

>Отличие в том, что вы ДУМАЕТЕ, а я ЗНАЮ. Под 100 тысяч строк этого автора поддерживаю.

Вы продолжаете утверждать, что автор знал устройство чисел в компьютере? Вроде, довольно очевидно, что не знал. Тем, кто знает и в голову не приходит использовать вещественную арифметику в виде целочисленной, надеясь на оптимизатор. Тем более, в таком простом случае. Про оптимизатор я теперь даже затрудняюсь понять что вы про него имели в виду. Довольно очевидно, что оптимизатор вряд ли станет оптимизировать вещественную функцию в целочисленную. За токое канделябром бьют.
> Это вы меня смутили операцией **, я си давно забыл, думал, что там есть такая.
я тоже забыл. :-)

> Довольно очевидно, что оптимизатор вряд ли станет оптимизировать вещественную функцию в целочисленную.
ДА НУ?
const int four = 2.*2.;
Получаем mov [ebp-0x04], 0x00000004

Если компилятор сумеет просчитать функцию с константными аргументами при компиляции — он вполне оптимизирует вещественную функцию в целый результат.

> За токое канделябром бьют.
Ну бейте. Компилятор Borland C++ Builder

> Тем, кто знает и в голову не приходит использовать вещественную арифметику в виде целочисленной, надеясь на оптимизатор.
Как видите — оно даже работает.

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

> Вы продолжаете утверждать, что автор знал устройство чисел в компьютере?
ДА, разумеется. Думаю, что о накоплении ошибок в вычислительных алгоритмах она знает побольше вас. Просто возможности компилятора не понимает.
>ДА НУ?
>const int four = 2.*2.;
>Получаем mov [ebp-0x04], 0x00000004

А где здесь функция? Вы уровня сложности pow напишите. Она же степени через экспоненты считает?

>Ну бейте. Компилятор Borland C++ Builder

Этого даже бить не надо. Само помрэ.

>Как видите — оно даже работает

Вы убедительно доказали, что оно НЕ работает.

>Но чтобы отличить, где оптимизация сработает, а где нет — нужно понимать хотя бы отличие внутренних функций от внешних. А вот это математик не знала.

Нет. Для решения этой проблемы надо знать лишь устройство чисел в компьютере и всё. Основы. А ваше деление на внутренние и внешние — это какое-то местечковое сибилдерное шаманство? Вот его точно знать не надо. Его даже гуголь не знает. А код, зависящий от знания способа оптимизации компилятора == говнокод. Тем более, в случае такой банальщины.

>Думаю, что о накоплении ошибок в вычислительных алгоритмах она знает побольше вас. Просто возможности компилятора не понимает.

Ну, продолжайте пребывать в этой ложной уверенности. А лучше дайте «ей» что-нибудь уже почитать. Да и сами не пренебрегайте. В конце концов, это вам, не мне, разгребать 100к+ говнокода, написанного без какого бы то ни было понимания. «я могу писать со скоростью 400 знаков в минуту, правда такая фигня получается...».
Расскажу вам об эпическом баге, который поразил нашу команду 5 лет назад. Ввиду специфики темы придётся некоторые подробности опустить.
Разрабатывали комп для военных. Разработали. Всё работает. Весит, правда, как новорожденный буйволёнок, но это не наша вина. (Зачем-то наши вояки для всех устройств требуют работоспособность при -60С. И действительно её проверяют. За счёт одного этого устройство весит в 3 раза больше, чем могло бы при -20С, и главное никому нафиг не нужно это).
В качестве сдачи заказчику — участие взвода с нашей системой в больших учениях в полях. Взвод с нашей системой и 10 со старой. Средство связи у всех взводов, включая наш, одинаковое — такая рация с функцией передачи цифровых данных. Давным давно у этой рации мы обнаружили забавный глюк — она не передаёт сообщения длинной 31 байт. Больше или меньше — да. Ну да ладно, учли это в коде и вроде работает. Прямо за полчаса до начала учений всё проверили, всё работает.
Раздаётся приказ к началу учений, и тут у нас пропадает связь. Вся, разом, как накрыли медным тазом. И в течении 5 часов связи не было. Раздался приказ об окончании учений. И сразу хренак! Всё снова работает. Потом были долгие разборки-штрафы-санкции, но по сути это событие закрыло компанию, где я работал.
И только через месяц мы как-то по наитию догадались, в чём дело. В API на передачу пакета данных в рацию есть поле «приоритет». По документации значение от 1 до 10, 1 — самый главный. Ну мы и поставили 1 для сообщений писец-класса, 2 для класса срочных и так далее. Наивные. В старой системе же стоял приоритет 1 для всего вообще, включая телеметрию. Поэтому когда вокруг было 10 раций со старой системой, всё работало. А вот когда их стало 500 — на нас просто не хватило эфирного времени.
То есть косяк был даже не наш, но в армии, сами знаете, это ничего не значит. Отака фигня.
Итог: я просидел 5 часов на ящике с гранатами при -15, и с тех пор сыплю фразами типа «если этот сервер поддерживает одновременно 400 абонентов, это ещё не значит, что он поддержит и 350»
Баг в давнишней игре под старые телефоны (brew). В игре можно было менять цвет одежды персонажа. Тестеры обнаружили, что если выбрать русский язык, то цвет одежды менялся слабо и всегда был какой-то серый и тусклый. Баг долго отпинывали, потом решили разобраться.
Вообщем после исследования обнаружилось, что слово «продолжить» несколько длиннее слова «continue» и своим хвостом залезает в кусок памяти с цветом одежды.
Была такая проблема много лет назад. В программе получался косяк, но многократная тщательная проверка кода ничем не помогала. Оказалось, что надо было отключить оптимизиацию в компиляторе. Очень помучался тогда с этим.

Скорее, не надо было допускать в коде UB. Отключение оптимизации — это костыль, приемлемый для старого кода, но говорить "надо отключить оптимизацию" нельзя. "Надо" только исправить код.

Не знаю что такое UB. Там было очень много условных ветвлений и вложенных циклов. Если бы я знал в чем именно проблема, то попытался бы упростить. Но насколько я помню, особо там упрощать было нечего.

UB — это undefined behavior. Ситуация, в которой, согласно стандарту языка, компилятору разрешено делать что угодно. Современные оптимизаторы считают такую ситуацию невозможной и оптимизируют код исходя из этого


Обычно это означает выкидывание условий, которые оптимизатор счет невозможными.


К UB относятся ситуации целочисленного знакового переполнения, разыменования нулевого указателя, бесконечный цикл.


Вот одна из статей на эту тему: https://habrahabr.ru/company/pvs-studio/blog/276657/

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

Значит, было что-то другое. В статье рассказано только про один вид UB.

Вам же явно описали — при попытке перестановки условий (а всего вариантов — порядка N факториал) оптимизатор запутался. Возможно — переполнение внутренних таблиц, возможно — srict aliaing сработал.

Вообще-то этот пример раньше на начальных курсах института проходили. Компилятор FORTRAN OE, IBM 360/370. Псевдокод основной идеи на Си.

if (div != 0)
Res = sum /div;
else {
div = 1;
Res = sum /div;
};

Оптимизируя общие подвыражения, оптимизатор сделал так
Res = sum /div;
if (div == 0)
div = 1;

Угу, общие подвыражения он вынес. Но с водой потерял и ребенка. Пример классический, многократно описан в книгах. Стыдно такое не знать.

Нарушение strict aliasing — это UB.

ОК. А теперь со ссылкой на стандарт докажите, что в С++ запрещено реализовывать двухсвязные списки (на каждый элемент есть две ссылки — у предыдущего и следующего члена в списке), деревья (много листьев ссылается на предыдущий уровень дерева), графы и так далее.

Ну или признайтесь, что несколько поторопились.
Лучше вы откройте стандарт и прочитайте что такое strict aliasing.
Вера в безошибочность компилятора обычно крепка только до написания своего первого компилятора. :-) А потом начинаешь понимать. Компиляция компилятором самого себя — это тестирование примерно 30% языковых конструкций. Компиляция ядра линукс — процентов 50. А все остальное — в простых примерах работает, а вот сложные комбинации — не тестированы

Для недоверчивых — https://gcc.gnu.org/bugzilla/buglist.cgi?quicksearch=optimization 2231 баг, связанный с оптимизацией, некоторым багам — уже больше 10 лет.

Так что UB иногда бывает, но баги оптимизации встречаются чаще. Вот ещё статья с примерами https://habrahabr.ru/post/114117/ к чему приводит strict aliasing.

Что касается пользы от оптимизатора — она процентов 5-10. Если больше — значит программист надеется на оптимизатор и пишет неэффективно. С другой стороны, переделка алгоритмов дает выигрыш в разы, в моей практике — и в тысячи раз.

UB править стоит — но не фанатично. Ибо в виндосовских хеадерах UB более, чем хватает. Как пример — преобразование беззнакового в знаковое того же размера. Иногда даже компилятор ругается (warning или hint) на особо наглый код. Суть в том, что если вы попадете на машину, где используется прямой или обратный код — вашей программе будет плохо. Но с вызовами виндовскими хеадерами — будет так же плохо. Иными словами — на такой машине windows все равно работать не будет и ваш код там бесполезен.

По моим оценкам отказы оптимизации
30% ошибка программиста
5% тонкие эффекты изменения точности
65% баги оптимизатора

А если не верить компилятору — программирование не имеет смысла.

Знаете — программирование вообще занятие для не верящих. Потому что даже в процессор верить на 100% не стоит.

Как пример 2.0 было равно 16.0 в одной из ранних ЕС-1036. В микрокоде вещественные числа на равенство сравнивали по мантиссе. И забыли сделать сравнение ординаты.

Ещё пример. комп (М6000) иногда портил файловую структуру диска. Длительное расследование показало, что во время длиной (на пару страниц) процедуры поиска дорожки и сектора в бите С (бит целочисленного переполнения) держится тип операции: чтение или запись. Но что-то в процессоре иногда сбоило и вместо бит С менялся той командой, которая его менять не должна была.

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

Чтобы не думали, что баги были только на древних процах -Pentium-III, производства Малазии имел обыкновение зависать при уменьшении рабочего набора. Починили заменой процессора. скорее всего причиной — трещина в кристалле. Но все тесты он проходил на ура.

Можете для повышения кругозора посмотреть errata на современные SOC-чипы. Там тоже немало.

А уж ошибок в компиляторах — ВАГОНЫ. Их в сотни раз больше, чем ошибок в процессорах.

Это как со слоном — то, что вы никогда не видели слона — не означает, что слон не существует. Это означат, что вы ходите по одним местам, а слоны — по другим.

Так что пока вы копируете стили из учебника — все будет более-менее хорошо. Как только начнете думать сами, а не повторять чужие образцы — сразу вероятность нахождения бага в компиляторе увеличится.

Но верить в программировании — нельзя НИКОМУ — ни процессору, ни компилятору, ни ядру linux, ни господу богу. Нужно просто писать НАДЕЖНО. Так, чтобы ваши ошибки в коде не мешали работать остальным частям вашей программы.

https://gcc.gnu.org/bugzilla/buglist.cgi?bug_status=RESOLVED&resolution=FIXED&target_milestone=6.2 — список ошибок, исправленных в gcc 6.2. как писали в readme к одной популярной библиотеке «Исправлены обнаруженные ошибки, оставлены необнаруженные, добавлены новые.»

P.S. Могу ещё и про ошибки в ядрах linux и FreeBSD рассказать.
> Для недоверчивых — https://gcc.gnu.org/bugzilla/buglist.cgi?quicksearch=optimization 2231 баг, связанный с оптимизацией, некоторым багам — уже больше 10 лет.

Первые 10 багов по этой ссылке:

1. нет предупреждения в случае, когда функция, объявленная как pure или const, не является таковой;
2. вылет с OOM;
3. FP EMULATION медленнее чем в clang;
4. std::pow(std::complex(0),1) возвращает (Nan, Nan) в режиме -funsafe-math-optimizations;
5. генерируется неверная отладочная информация для конструктора и деструктора;
6. что-то с маркером для оператора return, использующимся при отладке;
7. неоптимальность преобразования fold_binary_op_with_conditional_arg;
8. отсутствие векторизации в определенном случае;
9. отсутствие оптимизации в некотором случае;
10. какое-то лишнее предупреждение.

Из этих багов только 1 может привести к генерации неверного кода — но только в режиме оптимизации, который еще надо специально включить.

И остальные незакрытые баги — такие же.
2231 баг. Если верить вашей статистике (1 к 10), то это 223 бага при оптимизации. И намного больше разных проявлений одного и того же бага (дубли сводятся в один багрепорт). И огромное количество случаев, когда людям просто проще написать обход ошибки, чем багрепорт.

>только в режиме оптимизации, который еще надо специально включить.
Любуйтесь. https://gcc.gnu.org/onlinedocs/gcc-6.2.0/gcc/Optimize-Options.html#Optimize-Options
-Ofast включает -ffast-math, -ffast-math включает -funsafe-math-optimizations

gcc — хороший компилятор, но как в каждой большой программе — там хватает ошибок.
Когда-то я читал историю про выключатель, к которому подходил только один провод, но если его выключить — компьютер (из эпохи шкафов) начинал глючить. А потом потерял ссылку. Кто-нибудь может напомнить?
Вот ссылка (на английском).
Мой любимый, у коиентов работающих под VMWare временами вместо меню были красные кресты, обычное дело если ты упал гденибудь в процессе рисования. Но элемент стандартный, а крест есть, и не у всех клиентов, и имеет свойство пропадать/появляться без обновления приложения. Долгие раскопки привели к тому, что в многоэкранных конфигурациях, VMWare объединяет видео пространство в одно целое.
С одним интересным эффектом, если первый логический монитор в левом нижнем углу то проблем нет, а если в любом другом положении, то на вопрос о мониторе по умолчанию, приходит ответ, что нет монитора по умолчанию.
Баг чинился у клиентов очень просто перестановкой мониторов и почти всегда вызывал полнейшее недоумение :)
В копилку:

Это было моё первое задание, когда году в 1998м я пришел устраиваться на подработку эникейщиком. Было там пять совершенно одинаковых компьютеров отвратительной конфигурации – процессор AMD K5 100 непонятно на какой материнской плате, 16 мегабайт памяти, видеокарта AliMagic и гигабайтный винчестер Samsung.

Так вот, на один из этих компьютеров программа 1С: Предприятие 7.5 прекрасно устанавливалась, а на второй нет. Инсталлятор вылетал с ошибкой на скольких-то там процентах и всё. Не помогала даже переустановка Windows на чистый диск. Я просто поменял жесткие диски местами. Вот и попробуйте угадать, как куда программа после этого устанавливалась, а куда нет.

Ответ
Она установилась на оба компьютера!

Почему? А кто её знает… Я ж говорю – проще решить проблему, чем найти объяснение феномену.


Этому должно быть какое-то теоретическое объяснение, но мне ничего не приходит в голову. А что Вас надоумило поменять местами жесткие диски?
Пытался локализовать проблему. Но результат эксперимента поставил меня в тупик :) Заказчика устроило, дальше экспериментировать не было возможности, железо пошло в бой. Я вообще удивляюсь, как там что-то работало — компьютеры пережили потом в помещении (их не залило, но следы коррозии от повышенной влажности были), потом ремонт с пылью.
2 года назад пытался помочь своему однокласснику со школьным заданием по информатике. Суть в том, что при прогоне через отладчик узнали, что в приложениях на PascalABC.NET именно этот комп в качестве результата операции 7 + 1 возвращал 9. Баг больше не воспроизводился никак — исключительно на конкретном компьютере, под конкретным аккаунтом и только в PascalABC.NET, но при этом не зависел от Release/Debug-конфигурации
Стандартный налет в разных навигаторах — в 3 часа ночи с субботы на воскресенье координаты улетает на несколько сотен метров и машина едет прямо через дома. Продолжается от 15 минут до часа. Причина — смена номера недели GPS (эфемериды на старую неделю, а время от новой). но для непосвященных — очень странный глюк.
Так рождаются суеверия — не ездить на машине в ночь с субботы на воскресенье :)
Скорее быть готовым к тому, что навигатор поедет через дом или потребует свернуть на мост, снесенный паводком лет 20 назад. Мы так чуть в аварию не попали — водитель очень хотела по навигатору ехать, а он вел прямо в реку.
Насчет номера недели, он переполняется примерно раз в 19 лет. Последний раз было в начале этого года.
Если оборудование брало время оттуда, то происходил сбой.
Существую ли способы найти баг, который воспроизводится раз в N лет?
> Существую ли способы найти баг, который воспроизводится раз в N лет?
ДА. Внимательным чтением кода находится и не такое.

> Если оборудование брало время оттуда, то происходил сбой.
Нет, причина не в этом. TOW (время он начала недели) сбрасывается в 0 при смене недели. А эфемериды в момент смены недели не меняются, действуют те, что приняты на старой неделе. Если специально не учесть этот момент и не добавить 604800 к текущему времени, расчет эфемерид происходит на начало предыдущей недели.

> Насчет номера недели, он переполняется примерно раз в 19 лет.
Для расчета координат это не важно. Для теплого старта — хватит 3-4 бит номера недели (альманахи старше месяца лучше не использовать). Реально в альманахах — 8 бит.

Важно это для показа даты, но там или опираемся на ГЛОНАСС-М (там есть номер четырехлетия) или на ГЛОНАСС (день внутри четырехлетия и его совпадение с датой по GPS) или используем L2 (там 13 бит номера недели, то есть переполнение раз в 157 лет) или на SRAM (часто вместе с RTC).

Иными словами в приемниках GPS/ГЛОНАСС ошибка раз в 19 лет крайне маловероятна, а вот в приемниках чистого GPS — она может быть (особенно при сдохшей батарейке).

Это я ещё не глянул стандарт на SBAS…
Чудесная сказка Bormor'а в тему
— Послушай, — обратился демиург Шамбамбукли к пророку. — У меня для тебя есть важное поручение.
— Какое?
— Пойди в землю, которую я тебе укажу, собери там всех рыжих и вели им никогда не есть крокодилов.
— Чего!?
— Не важно. Просто передай это, и всё.
Пророк задумчиво почесал в бороде.
— У этой заповеди есть какой-то высший смысл? — спросил он. — Доступный человеческому пониманию?
— Конечно, есть! — воскликнул Шамбамбукли. — Сейчас объясню. Посмотри на небо. Видишь звёзды?
— Вижу. А что с ними не так?
— Хотел бы я это знать, — вздохнул Шамбамбукли. — Понимаешь, звезды управляют всевозможными процессами на земле, у каждой из них своя важная функция в этом мире. Ну так вот, обрати внимание на те три звезды; я заметил, что когда кто-нибудь рыжий ест крокодила, пусть даже маленький кусочек, их функции выдают неверные значения. А это приводит к ошибочной индексации памяти, в результате которой… впрочем, тебе незачем знать технические подробности. А мне, сам понимаешь, проще дать лишнюю заповедь, чем переделывать всё мироздание. Эти космические взаимодействия так перепутаны…
— Но почему запрет касается только рыжих?! Почему только им нельзя питаться крокодилами?
— А вот это, — сказал Шамбамбукли, — меня самого очень интересует.
По поводу последнего бага, вашего личного. Если проблема проявляется на одной системе и не проявляется на другой (live), нужно понять, чем системы отличаются. Сделайте, скажем, diff между списками установленных пакетов (с версиями) или между системами целиком. Вообще, смотрите. В live бага нет. Значит, скорее всего, если сделать чистую установку с теми же пакетами (с версиями) (продолжение в следующем посте)
, что и в live, то бага не будет. Затем начните приближать друг к другу две эти системы (в которой есть баг и в которой нет), пока не поймёте в чём дело. Я так успешно находил баги. Да, по идее авторы софта должны это делать за вас. Но, к сожалению, в мире свободного ПО баги вообще редко фиксятся, а тем более баги с непонятной причиной. Так что пока вы не найдёте источник вот таким вот образом, никто ваш баг чинить не будет
Большое спасибо, я думал никогда не найду больше эту историю!
Только вот скорость распространения сигнала в меди меньше, чем в вакууме. Так что не 559 миль, а всего 375. Несостыковочка-с.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории