Какой была ваша самая трудная охота на жуков, и как вы ее нашли и убили?

31

Это вопрос «Поделись знаниями». Мне интересно учиться на ваших успехах и / или неудачах.

Информация, которая может быть полезна ...

Задний план:

  • Контекст: язык, приложение, среда и т. Д.
  • Как была обнаружена ошибка?
  • Кто или что выявило ошибку?
  • Насколько сложно было воспроизвести ошибку?

Охота

  • Какой у тебя был план?
  • С какими трудностями вы столкнулись?
  • Как в конце концов был найден оскорбительный код?

Убийство.

  • Насколько сложным было решение?
  • Как вы определили объем исправления?
  • Сколько кода было задействовано в исправлении?

Посмертное.

  • Что было технической причиной? переполнение буфера и т. д.
  • Какова была основная причина 30 000 футов?
  • Как долго процесс в конечном итоге занял?
  • Были ли какие-либо особенности, на которые негативно повлияло исправление?
  • Какие методы, инструменты, мотивы вы нашли особенно полезными? ... ужасно бесполезно?
  • Если бы ты мог сделать все это снова? ............

Эти примеры являются общими, не применимыми в каждой ситуации и, возможно, бесполезны. Пожалуйста, приправьте при необходимости.

ржавый
источник

Ответы:

71

Это было на самом деле в стороннем компоненте просмотра изображений нашего приложения.

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

Мы попробовали обычные шаги:

(1) Если бы они поменяли компьютеры с другим пользователем, у которого никогда не было проблем, чтобы исключить компьютер / конфигурацию. - Проблема последовала за ними.

(2) Заставили их войти в приложение и работать как пользователь, который никогда не видел проблемы. - Проблема все еще следовала за ними.

(3) Имел ли пользователь отчет о том, какое изображение он просматривал, и настроил тестовый жгут, чтобы повторять просмотр этого изображения тысячи раз подряд. Проблема не представилась в ремне безопасности.

(4) Разработчик сидел с пользователями и наблюдал за ними весь день. Они видели ошибки, но не замечали, что они делают что-то необычное, чтобы вызвать их.

Мы боролись с этим в течение нескольких недель, пытаясь выяснить, что общего было у «пользователей с ошибками», чего не было у других пользователей. Я понятия не имею, как, но у разработчика на шаге (4) был момент наступления эврики, чтобы поработать на один день, достойный энциклопедии Брауна.

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

Мы заставили его сесть и снова наблюдать за левшами, обращая особое внимание на то, что они могут делать по-другому, и вот как мы это нашли.

Оказалось, что ошибка возникала только в том случае, если вы перемещали мышь к крайнему правому столбцу пикселей в средстве просмотра изображений, когда оно загружало новое изображение (ошибка переполнения, поскольку у поставщика было вычисление 1 раз для события наведения мыши).

По-видимому, ожидая загрузки следующего изображения, все пользователи естественным образом переместили свою руку (и, следовательно, мышь) к клавиатуре.

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

Отныне он был известен в легенде в команде разработчиков как «Левша»

JohnFx
источник
14
Это самая отвратительная вещь, о которой я когда-либо слышал.
Натан Тейлор
9
Это сделало героя из парня, который решил это, все же.
JohnFx
2
Вау, теперь это чертовски ошибка!
Митчел Селлерс
3
Отличная находка! Хороший рассказ.
Toon Krijthe
11
Как будто мы с левшами не обращаемся достаточно, как с гражданами второго сорта. Теперь мы также должны быть обременены большим количеством ошибок программного обеспечения ... ну и дела, спасибо! : p
Дэн Молдинг
11

Это давным- давно (конец 1980-х годов).

Компания, в которой я работал, написала пакет САПР (на Фортране), который работал на различных рабочих станциях Unix (HP, Sun, Silcon Graphics и т. Д.). Мы использовали наш собственный формат файлов для хранения данных, и когда пакет был запущен, места на диске было мало, поэтому было много битов, используемых для хранения нескольких флагов в заголовках сущностей.

Тип объекта (линия, дуга, текст и т. Д.) Был умножен на 4096 (я думаю) при сохранении. Кроме того, это значение было отменено для обозначения удаленного элемента. Итак, чтобы получить тип, у нас был код, который сделал:

type = record[1] MOD 4096

На каждой машине, кроме одной, это дало ± 1 (для линии), ± 2 (для дуги) и т. Д., И мы могли тогда проверить знак, чтобы видеть, был ли удален.

На одной машине (я думаю, HP) у нас была странная проблема, когда обработка удаленных элементов была испорчена.

Это было в дни до IDE и визуальных отладчиков, поэтому мне пришлось вставлять трассировочные операторы и журналы, чтобы попытаться отследить проблему.

В конце концов я обнаружил, что это произошло потому, что в то время как каждый другой производитель реализовал это MODтак, в -4096 MOD 4096результате -1HP применила его математически правильно, что -4096 MOD 4096привело к -4097.

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

Это заняло несколько дней.

ChrisF
источник
3
Вероятно, за эти годы были более трудные поиски жуков, но этот запомнился мне уже более 20 лет!
ChrisF
7

Ух ты, хорошее чтение здесь!

Мои самые тяжелые были годы назад, когда Turbo Pascal был большим, хотя, возможно, это была одна из ранних C ++ IDE того времени. Как единственный разработчик (и третий парень в этом стартапе) я написал что-то вроде упрощенной программы САПР, удобной для продавцов. В то время это было здорово, но развился неприятный случайный сбой. Воспроизвести было невозможно, но случалось достаточно часто, и я отправлялся на охоту за ошибками.

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

В конце концов я сузил его до места, где вызывали подпрограмму, получив 2, но изнутри увидел какое-то бессмысленное число. Я мог бы поймать это раньше, но не вошел в эту подпрограмму, предполагая, что она получила то, что ей дали. Ослепленный, предполагая, что самые простые вещи были в порядке!

Оказалось, что в стек вставляется 16-битное int, но подпрограмма ожидает 32-битного. Или что-то типа того. Компилятор не автоматически дополняет все значения до 32 бит или не выполняет достаточную проверку типов. Это было тривиально исправить, просто часть одной строки, вряд ли нужно было думать. Но чтобы добраться туда, понадобилось три дня охоты и допроса очевидного.

Таким образом, у меня есть личный опыт с этим анекдотом о дорогостоящем консультанте, который через некоторое время делает одно прикосновение куда-то и берет 2000 долларов. Руководители требуют разбивки, и это 1 доллар за кран, 1999 долларов за знание, где нажать. За исключением моего случая, это было время, а не деньги.

Извлеченные уроки: 1) использовать лучшие компиляторы, где «лучший» определяется как включающий проверку на столько проблем, сколько компьютерная наука знает, как проверять, и 2) ставить под сомнение простые очевидные вещи или, по крайней мере, проверять их правильное функционирование.

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

Урок 2 также относится к самой сложной ошибке электроники, которую я когда-либо исправлял, также с тривиальным исправлением, но несколько умных EE были поставлены в тупик на месяцы. Но это не форум по электронике, так что я не буду больше говорить об этом.

DarenW
источник
Пожалуйста, опубликуйте ошибку электроники в другом месте и ссылку здесь!
tgkprog
6

Состояние гонки сетевых данных из ада

Я писал сетевой клиент / сервер (Windows XP / C #) для работы с аналогичным приложением на очень старой (Encore 32/77) рабочей станции, написанной другим разработчиком.

По сути, приложение делило / манипулировало определенными данными на хосте, чтобы контролировать процесс хоста, на котором запущена система, с помощью нашего модного интерфейса монитора с сенсорным экраном на базе нескольких ПК.

Он сделал это с 3-х уровневой структурой. Процесс связи считывал / записывал данные на / с хоста, делал все необходимые преобразования формата (порядковый номер, формат с плавающей запятой и т. Д.) И записывал / считывал значения в / из базы данных. База данных действовала как посредник данных между интерфейсами связи и сенсорного экрана. Приложение сенсорного интерфейса генерировало интерфейсы сенсорного экрана в зависимости от того, сколько мониторов было подключено к ПК (это автоматически обнаруживалось).

В заданном временном интервале пакет значений между хостом и нашим компьютером мог передавать только 128 значений максимум по каналу за раз с максимальной задержкой ~ 110 мс на передачу в оба конца (UDP использовался с прямым соединением Ethernet через x-over между Компьютеры). Таким образом, количество разрешенных переменных на основе переменного количества подключенных сенсорных экранов находилось под строгим контролем. Кроме того, хост (хотя и имеющий довольно сложную многопроцессорную архитектуру с шиной совместно используемой памяти, используемой для вычислений в реальном времени) имел вычислительную мощность моего сотового телефона примерно в 1/100, поэтому ему было поручено выполнять как можно меньше обработки, и его сервер / client должен был быть написан на ассемблере, чтобы убедиться в этом (на хосте выполнялась полная симуляция в реальном времени, на которую не могла повлиять наша программа).

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


Чтобы определить проблему, я выбрал одно из колеблющихся значений:

  • Я проверил приложение Touchscreen, оно качалось
  • Я проверил базу данных, колеблюсь
  • Я проверил приложение связи, колеблюсь

Затем я запустил wireshark и начал вручную декодировать захват пакетов. Результат:

  • Не колеблется, но пакеты выглядят неправильно, слишком много данных.

Я сто раз перебирал каждую деталь кода связи, не обнаружив ни ошибки, ни ошибки.

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

Очевидно, что когда он отправлял данные, он не сбрасывал массив данных перед передачей, поэтому, по сути, он просто перезаписывал последний использованный буфер новыми значениями, перезаписывая старые, но старые данные, не перезаписанные, все еще передавались.

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

Значение, считываемое из базы данных, зависело от временного интервала, когда пользовательский интерфейс запрашивал значение.


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


Уроки выучены:

  • Не принимайте современные вычислительные мощности как должное. Было время, когда компьютеры не поддерживали Ethernet, а очистка массива считалась дорогой. Если вы действительно хотите увидеть, как далеко мы продвинулись, представьте систему, которая практически не имеет формы динамического распределения памяти. То есть исполнительный процесс должен был предварительно выделить всю память для всех программ по порядку, и ни одна программа не могла вырасти за эту границу. То есть выделение большего объема памяти программе без перекомпиляции всей системы может привести к серьезному сбою. Интересно, будут ли когда-нибудь люди говорить о днях перед сборкой мусора в одном свете?

  • При работе в сети с пользовательскими протоколами (или обработке двоичного представления данных в целом) обязательно читайте спецификацию, пока не поймете каждую функцию каждого значения, передаваемого по каналу. Я имею ввиду, читай, пока твои глаза не болят. Люди обрабатывают данные, манипулируя отдельными битами или байтами, имеют очень умные и эффективные способы ведения дел. Отсутствие мельчайших деталей может сломать систему.

Общее время исправления составило 2-3 дня, и большую часть времени я потратил на работу над другими вещами, когда я разочаровался в этом.

SideNote: рассматриваемый хост-компьютер не поддерживает Ethernet по умолчанию. Карта для ее вождения была изготовлена ​​на заказ и модифицирована, а стек протоколов практически не существовал. Разработчик, с которым я работал, был чертовским программистом, он не только реализовал урезанную версию UDP и минимальный поддельный стек Ethernet (процессор не был достаточно мощным для обработки полного стека Ethernet) в системе для этого проекта но он сделал это менее чем за неделю. Он также был одним из руководителей проектных команд, которые изначально разрабатывали и программировали ОС. Скажем так: все, что ему когда-либо приходилось рассказывать о компьютерах / программировании / архитектуре, независимо от того, сколько времени я уже выучил или сколько я уже новичок, я слушал каждое слово.

Эван Плейс
источник
5

Фон

  • В критически важном приложении WCF, ведущем веб-сайт и обеспечивающем обработку транзакций бэкенда.
  • Приложение большого объема (сотни звонков в секунду)
  • Несколько серверов несколько экземпляров
  • сотни пройденных юнит-тестов и бесчисленное количество QA-атак

Ошибка

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

Как я это нашел

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

Тогда я был уверен, что у меня возникла проблема с конфликтом потоков. Я проверил мои тупики, попытался создать ситуацию, создать инструменты, чтобы попытаться создать ситуацию в отладке. С растущим разочарованием в управлении я обратился к своим коллегам с советами: от перезапуска проекта с нуля до ограничения сервера одним потоком. 1,5 недели

Затем я посмотрел на блог Тесс Феррандез, создал файл дампа пользователя и аннулировал его с помощью windebug в следующий раз, когда сервер сделал дамп. Обнаружил, что все мои темы застряли в функции dictionary.add.

Длинный короткий один небольшой словарь, который просто отслеживал, в какой журнал записывать ошибки x потоков, не был синхронизирован.

повторный показ
источник
3

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

Проблема оказалась в том, что приложение, запущенное при запуске, иногда происходило с ошибками при попытке чтения из файловой системы, которая еще не была смонтирована (например, если пользователь настроил его для чтения из тома NFS). При запуске приложение отправляет драйверу несколько ioctl для инициализации устройства, затем считывает параметры конфигурации и отправляет дополнительные ioctl, чтобы привести устройство в правильное состояние.

Ошибка в драйвере приводила к тому, что недопустимое значение записывалось на устройство при выполнении вызова инициализации, но значение перезаписывалось допустимыми данными после выполнения вызовов для перевода устройства в определенное состояние.

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

Проблема заключалась в том, что если питание было отключено после отправки ioctl для инициализации устройства (и записи недопустимого значения на устройство), но до того, как могли быть отправлены действительные данные. Когда устройство снова включится, оно увидит, что флаг установлен, и попытается прочитать недействительные данные, которые были отправлены из драйвера из-за неполной инициализации. Это переведет устройство в недопустимое состояние, когда флаг отключения питания был сброшен, но устройство не получит дальнейшие инструкции, пока драйвер не выполнит его повторную инициализацию. Второй сброс будет означать, что устройство не пытается прочитать недействительные данные, которые были сохранены на нем, и получит правильные инструкции по настройке, позволяющие перевести его в правильное состояние (при условии, что приложение, отправляющее ioctls, не переставало работать) ).

В итоге потребовалось около двух недель, чтобы выяснить точный набор обстоятельств, вызвавших проблему.

Cercerilla
источник
2

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

Написанный на C ++, мы использовали для этого POCO, поскольку он позволяет хорошо программировать IO, Socket и Thread.


Возникли две ошибки, которые раздражали нас и заставляли нас терять много времени, действительно логичный:

Случайно, компьютер разделял свой локальный IP-адрес вместо своего удаленного IP-адреса.

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

Как мы это определили? Когда мы улучшили вывод на сервере имен, мы позже обнаружили, когда перезагружали компьютеры, что наш скрипт для определения IP-адреса был неправильным. Случайно, сначала было указано устройство lo вместо устройства eth0 ... Действительно глупо. Так что теперь мы жестко запрограммировали запросить его из eth0, так как он распределяется между всеми университетскими компьютерами ...


А теперь еще более раздражающий:

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

Это произошло действительно случайно, и поскольку задействовано более одного компьютера, это стало более раздражающим для устранения этой проблемы, университетские компьютеры не позволяют нам запускать Wireshark на них, поэтому нам остается только догадываться, была ли проблема на стороне отправителя или получателя боковая сторона.

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

Мы предположили, что это работало, поскольку более простые тесты в прототипе с меньшим количеством пакетов не вызывали этой проблемы, поэтому мы просто предположили, что оператор poll работает, но ... это не так. :-(


Уроки выучены:

  • Не делайте глупых предположений, таких как порядок сетевых устройств.

  • Фреймворки не всегда делают свою работу (как реализацию, так и документацию) правильно.

  • Обеспечьте достаточно вывода в коде, если не разрешено, обязательно сохраняйте расширенные детали в файл.

  • Когда код не был модульным тестом (потому что это слишком сложно), не думайте, что все работает.

Тамара Вийсман
источник
1
Решение проблем с сетью без Wireshark (или аналогичного инструмента) является героическим в самом деле.
Эван Плейс
2

Я все еще нахожусь на своей самой трудной охоте на жука. Это один из тех, иногда его там, а иногда и не ошибки. Вот почему я здесь, в 6:10 утра следующего дня.

Задний план:

  • Контекст: язык, приложение, среда и т. Д.
    • PHP OS Commerce
  • Как была обнаружена ошибка?
    • Случайный порядок, который частично работает, случайный сбой и проблемы с перенаправлением
  • Кто или что выявило ошибку?
    • Клиент, и проблема перенаправления была очевидна
  • Насколько сложно было воспроизвести ошибку?
    • Я не смог воспроизвести, но клиент смог.

Охота

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

Убийство.

  • Насколько сложным было решение?
    • очень
  • Как вы определили объем исправления?
    • не было никакой возможности ... это было везде
  • Сколько кода было задействовано в исправлении?
    • Все это? Я не думаю, что был файл нетронутым

Посмертное.

  • Что было технической причиной? переполнение буфера и т. д.
    • плохая практика кодирования
  • Какова была основная причина 30 000 футов?
    • Я бы предпочел не говорить ...
  • Как долго процесс в конечном итоге занял?
    • Вечность и один день
  • Были ли какие-либо особенности, на которые негативно повлияло исправление?
    • характерная черта? или это баг?
  • Какие методы, инструменты, мотивы вы нашли особенно полезными? ... ужасно бесполезно?
  • Если бы ты мог сделать все это снова? ............
    • Ctrl + Del
WalterJ89
источник
Если причиной была «плохая практика кодирования», возможно, вы захотите обсудить с вашим боссом, подходит ли это время для пересмотра практики кодирования в вашей команде и, возможно, для ознакомления с рецензированием?
2

В прошлом семестре мне пришлось исправлять некоторые запутанные вещи, связанные с параллелизмом, но ошибка, которая по-прежнему наиболее важна для меня, была в текстовой игре, которую я писал в сборке PDP-11 для домашнего задания. Он был основан на игре жизни Конвея, и по какой-то странной причине большая часть информации рядом с сеткой постоянно перезаписывалась информацией, которой не должно было быть. Логика также была довольно простой, поэтому она была очень запутанной. Пройдя несколько раз, чтобы обнаружить, что вся логика верна, я вдруг заметил, в чем проблема. Эта вещь:.

В PDP-11 эта маленькая точка рядом с числом делает ее основанием 10 вместо 8. Это было рядом с числом, которое ограничивало цикл, который должен был быть ограничен сеткой, размер которой был определен с теми же числами, но в основе 8.

Это все еще выделяется для меня, потому что из суммы ущерба, который вызвало такое крошечное 4-пиксельное дополнение. Так какой вывод? Не кодируйте в сборке PDP-11.

EpsilonVector
источник
2

Программа основного кадра перестала работать без причины

Я только что отправил это на другой вопрос. Смотрите пост здесь

Это произошло потому, что они установили более новую версию компилятора на основной фрейм.

Обновление 06/11/13: (Исходный ответ был удален OP)

Я унаследовал это основное приложение. Однажды, из чистого синего это перестало работать. Вот и все ... Боже, он просто остановился.

Моя работа состояла в том, чтобы заставить его работать как можно быстрее. Исходный код не изменялся в течение двух лет, но внезапно он просто прекратился. Я попытался скомпилировать код, и он разбился на строке XX. Я посмотрел на строку XX, и я не мог сказать, что заставит линию XX разорваться. Я попросил подробные спецификации для этого приложения, и не было ни одного. Линия XX не была виновником.

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

Итак, я решил начать с верхней части исходного кода и добавить whitespce и линейные тормоза, чтобы сделать код более читабельным. Я заметил, что в некоторых случаях были условия, которые объединяли AND и OR, и не было четкого различия между тем, какие данные AND и какие данные OR. Поэтому я начал ставить скобки вокруг условий И и ИЛИ, чтобы сделать их более читабельными.

Когда я медленно спускался, убирая это, я периодически сохранял свою работу. Однажды я попытался скомпилировать код, и случилось нечто странное. Ошибка перепрыгнула через исходную строку кода и теперь была еще ниже. Поэтому я продолжил, разграничивая условия «И» и «ИЛИ» паренами. Когда я закончил убирать это, это сработало. Пойди разберись.

Затем я решил посетить операционную мастерскую и спросить их, устанавливали ли они недавно какие-либо новые компоненты на основной раме. Они сказали да, мы недавно обновили компилятор. Хммм.

Оказывается, старый компилятор вычислял выражение слева направо независимо. Новая версия компилятора также оценивает выражения слева направо, но неоднозначный код, означающий, что неясная комбинация AND и OR не может быть решена.

Урок, который я извлек из этого ... ВСЕГДА, ВСЕГДА, ВСЕГДА используйте парены в разделенных И, а также ИЛИ условиях, когда они используются в сочетании друг с другом.

Майкл Райли - AKA Gunny
источник
сообщение, на которое указывает ваша ссылка, было удалено - не могли бы вы обновить ответ?
комнат
1
@gnat - нашел его на archive.org :)
Майкл Райли - AKA Gunny
1

Задний план:

  • Контекст: веб-сервер (C ++), который позволяет клиентам регистрироваться самостоятельно
  • Ошибка: при запросе страницы она просто не отвечала, вся ферма, которая есть, и процессы были бы уничтожены (и перезапущены), потому что они обслуживали страницу слишком долго (разрешено всего несколько секунд)
  • Некоторые пользователи жаловались, но это было крайне спорадическим, поэтому в основном незаметным (люди просто нажимают «Обновить», когда страница не обслуживается). Хотя мы заметили дампы ядра;)
  • На самом деле нам никогда не удавалось воспроизвести в наших локальных средах, ошибка появлялась несколько раз в тестовых системах, но никогда не обнаруживалась во время тестов производительности.

Охота

  • План: Ну, поскольку у нас были дампы памяти и журналы, мы хотели их проанализировать. Поскольку это затрагивало всю ферму, и в прошлом у нас были проблемы с базами данных, мы подозревали, что база данных (одна БД для нескольких серверов)
  • Трудности: полный дамп сервера огромен, и поэтому он очищается довольно часто (чтобы не хватило места), поэтому нам нужно было быстро получить его, когда это произошло ... Мы упорствовали. Дампы показывали различные стеки (никогда не было каких-либо вещей из БД, особенно для этого), он завершился неудачно при подготовке самой страницы (не в предыдущих вычислениях) и подтвердил то, что показывали журналы, подготовка страницы иногда занимала много времени, даже хотя это просто базовый шаблонизатор с предварительно вычисленными данными (традиционный MVC)
  • Приступая к этому: После еще нескольких примеров и некоторых размышлений мы поняли, что было потрачено время на чтение данных с жесткого диска (шаблон страницы). Поскольку это касалось всей фермы, мы сначала искали запланированные задания (crontab, партии), но сроки никогда не совпадали от одного вхождения к другому ... Мне наконец пришло в голову, что это всегда происходило за несколько дней до активации новой версии. софта и у меня был ахах! момент ... это было вызвано распространением программного обеспечения! Доставка нескольких сотен мегабайт (сжатых) может немного повлиять на производительность диска: / Конечно, распределение автоматизировано, и архив передается на все серверы одновременно (многоадресная передача).

Убийство.

  • Исправить сложность: переход на скомпилированные шаблоны
  • Затрагиваемый код: нет, простое изменение в процессе сборки

Посмертное.

  • Основная причина: операционная проблема или отсутствие перспективного планирования :)
  • Сроки: потребовались месяцы, чтобы отследить, несколько дней, чтобы исправить и протестировать, несколько недель на QA и тестирование производительности и развертывание - не спешите, так как мы знали, что развертывание исправления вызовет ошибку ... и ничего еще ... вроде извращенец действительно!
  • Неблагоприятные побочные эффекты: невозможность переключения шаблонов во время выполнения теперь, когда они запекаются в поставляемом коде, однако мы не очень часто использовали эту функцию, так как обычно переключение шаблонов означает, что у вас есть больше данных для заливки. Использование css в основном достаточно для «небольших» изменений макета.
  • Методы, инструменты: gdb+ мониторинг! Просто нам потребовалось время, чтобы заподозрить диск, а затем определить причину всплесков активности на графике мониторинга ...
  • В следующий раз: относитесь ко всем IO как к неблагоприятным!
Матье М.
источник
1

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

Самый безумный, которого я убил:

Чертежи печатают бред!

Я смотрю на код и ничего не вижу. Я вытаскиваю задание из очереди принтера и проверяю, все ли хорошо. (Это было в эпоху дос, PCL5 со встроенным HPGl / 2 - на самом деле, очень хорошо для построения чертежей и без головной боли при создании растрового изображения в ограниченном объеме памяти.) Я направляю его на другой принтер, который должен это понимать, он печатает нормально ,

Откат кода, проблема все еще там.

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

Тот, который был еще более неприятным, но так как он был только на моей коробке, я бы не поставил на первое место:

Borland Pascal, код DPMI для работы с некоторыми неподдерживаемыми API. Запустите его, иногда это работало, обычно он шел, пытаясь разобраться с недопустимым указателем. Тем не менее, он никогда не приводил к неверному результату, как вы ожидаете от нажатия на указатель.

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

Виновник: их было двое.

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

2) Отладчик никогда не скажет ничего о такой некорректной загрузке в одношаговом режиме. Я не знаю, что он делал внутри, но то, что было представлено пользователю, выглядело совершенно правильно. Я подозреваю, что на самом деле он не выполнял инструкцию, а имитировал ее.

Лорен Печтель
источник
1

Это просто очень простая ошибка, которую я почему-то превратил в кошмар для меня.

Фон: я работал над созданием собственной операционной системы. Отладка очень сложна (все, что вы можете иметь в трассировке, а иногда даже нет)

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

Поиск ошибок: я провел, вероятно, неделю или две, пытаясь решить эту проблему. Вставка трассировочных операторов везде. Изучение сгенерированного кода сборки (из GCC). Распечатка каждого значения, которое я мог.

Проблема: где-то в начале поиска ошибок я поместил hltинструкцию в crt0. Crt0 - это в основном то, что загружает пользовательскую программу для использования в операционной системе. Эта hltинструкция вызывает GPF при выполнении из пользовательского режима. Я поместил это там и в основном забыл об этом. (изначально проблема заключалась в переполнении буфера или ошибке выделения памяти)

Исправление: hltудалите инструкцию :) После удаления все заработало гладко.

Что я узнал: пытаясь отладить проблему, не забывайте исправления, которые вы пытаетесь исправить. Делайте регулярные сравнения с последней стабильной версией контроля версий и посмотрите, что вы изменили за последнее время, когда больше ничего не работает

Earlz
источник