A terribly depressing bug hunt can feel sort of worth it if there is a teachable lesson or structural solution at the end of it.
John Carmack ( https://twitter.com/id_aa_carmack/status/284830336737742848 )
В этой теме каждый желающий может централизованно выкладывать свои побеждённые баги.
Для того, чтобы выплеснуть эмоции, "чтобы не забылось", чтобы дать другим источник для вдохновения в поисках, чтобы набрать статистику, потому что баг весёлый был, да мало ли зачем.
Я не знаю, взлетит ли идея, ну вот и посмотрим.
f(x) не равно f(x)
Я почти начинаю, но Тарас меня опередил.
Два тупых, невдохновляющих, житейских бага. Однако пару часов они мне стоили.
1-й баг.
Есть строчка текста длиной в len символов, собираюсь её нарисовать на экране. Собираю VB (в системной памяти) из 2 треугольников на букву, и говорю:
render_vb(vb.data( ), len*6, GL_TRIANGLES, 2,4,0,3);
(внутре там glDrawArrays). Рисуется то, что надо, плюс фигня.
Решение: Пробел не генерирует квада (2-х треугольников), в буфере мало данных.
Исправление:
render_vb(vb.data( ), vb.size( )/9, GL_TRIANGLES, 2,4,0,3);
2-й баг.
Звуковой движок (типа OpenAL, только тупее). Удаление буфера. Вызывает:
static void clean_buffer(si32 id) { if( valid_buf_id( id)) { buffer &buf=buffers[id]; buf.free=true; buf.fmt=0; buf.size=0; if( buf.data) delete[] buf.data; } }
Падение.
Решение: функция вызывается несколько раз, соответственно - multiple-free.
Исправление:
static void clean_buffer(si32 id) { if( valid_buf_id( id)) { buffer &buf=buffers[id]; buf.free=true; buf.fmt=0; buf.size=0; if( buf.data) delete[] buf.data; buf.data=nullptr; } }
Интересно то, что я некоторое время думал, что баги связаны между собой (случайные изменения в 1-м влияли на падения).
FordPerfect
> delete[]
Сколько-то лет назад был забавный баг, когда удаление одного элемента из std::set удаляло чуть ли не половину контейнера. Оказалось, у этого элемента часть ключа - float получался nan (в силу другого бага). А в реализации сета даже для одного элемента делается equal_range, и удаляется весь рейнж.
TarasB
Отличные картинки с водоплавающими питухами
TarasB
> делит
Нашёл ещё один баг?))) Очень распространённый?
krian
А вот интересно, поидеи в трии сет можно класть флоат, но с осторожностью. Поэтому никогда так не рисковал. Насчёт нана - я считаю можно было исправить, подправив компаратор
А, ещё вспомнил, чужой баг. Человек делал как-то так:
int field_ = -1; f << (int8)field_; field_ = 0; f >> ( int8&)field_;
Правка: неправильно баг рассказал.
laMer007
Да так-то можно, почему нет, до тех пор, пока сравнения остаются транзитивными и антисимметричными.
krian
f - файловый поток? Я голову долго ломал, почему сдвигаем, но не сохраняем результат.
TarasB
Да, конечно. Только я напутал сначала, там не про то было.
TarasB
>f(x) не равно f(x)
Ты подробнее, подробнее давай...
>delete[]
Ты про то, чтобы все аллокации заворачивать с умные указатели или векторы?
Встроенные векторы там, откуда унаследован код вроде не получались по Объективным Законам ЖизниTM, а остальное... возможно и стоило бы. Инерция мышления, всё такое.
krian
>Сколько-то лет назад был забавный баг, когда удаление одного элемента из std::set удаляло чуть ли не половину контейнера.
Ядрёно! Напомнило https://web.archive.org/web/20151002151248/http://home.comcast.ne… %20bugs%5D%5D (даю такую ссылку, потому что блог, вроде, сейчас лежит).
Ну и раз уж разговор зашёл об этом - вспомню свой.
Считал как-то какую-то математику, которая была по смыслу целочисленная, в плавучке.
Вывожу, соответственно, через printf("%.0f");
Вывожу разность двух величин, которая математически должна быть равна 0.
Выводит 1. Сидел, долго думал.
FordPerfect
> Ты подробнее, подробнее давай...
Да всё то же, 3д-вектор, не удовлетворяющий условию dot(v,n)<c, запихивают в процедуру, где его как-то правят в цикле. Условие выхода из цикла - чтобы вектор удовлетворял условию dot(v,n)<c. После процедуры вектор (который внутри проходил поправку) имеет то же самое значение, что и до входа в неё. Прога повисала от такого.
Ещё вспомнился баг, банальный на самом деле, но хочется интересную тему поддержать.
Итак, гуи, иерархия виджетов на shared_ptr, слабые ссылки вверх на родителя.
И всё было хорошо, пока в какой-то момент не захотелся комплексный контрол, который в себе же строил сразу иерархию с собой в корне. В конструкторе.
Ну дальше, думаю понятно, он в конструкторе добавлял в себя дочерний виджет, этим дёргал свой shared_from_this(), чтобы дать его ребёнку запомнить.
На возврате из Element::_set_parent() этот shared_ptr умирал, зануляя счётчик, вызывая родителю деструктор и освобождая память.
Мораль - two-stage construction рулит.
auto PaletteControl::Create(const Ruleset& rules) -> shared_ptr<PaletteControl> { shared_ptr<PaletteControl> thiz{new PaletteControl{rules}}; if ( !thiz->_init( )) thiz.reset( ); return thiz; }
Свежий баг
tblib::StringRef lpCmdLine; tblib::StrBuf hardCmdLine; ... tblib::StringRef cl = hardCmdLine ? hardCmdLine : lpCmdLine; ... работаем с cl
стрингреф и стрбуф имеют операторы приведение друг к другу, при приведение рефа к буфу выделяется память, при выделении буфа к рефу просто возвращается указатель