Почему появляются гейзенбаги и как с ними работать

Почему появляются гейзенбаги и как с ними работать

Что такое плавающий баг.

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

Рассказываем, что нужно знать о гейзенбаге и как его исправить. 

Что такое гейзенбаг
 

Гейзенбаг (плавающий баг) — это возникающие спонтанно нежелательные действия программы. Такие баги ищут QA-специалисты. Алгоритмов, которые автоматически определяют плавающие баги, нет. 

Термин назван в честь физика Вернера Гейзенберга. Он сформулировал принцип неопределенности в квантовой физике.

Причины появления плавающего бага

  • Время

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

  • Память 

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

  • Assert

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

  • Переполнение

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

  • Рандомизация

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

Как решить проблему с плавающим багом
 

Универсального решения нет, но есть техники, которые способны помочь: 

  • Мониторинг системных процессов

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

  • Запуск на CPU вместо GPU. Графический процессор дает прирост производительности, но программное обеспечение по умолчанию может его не поддерживать. Выключите графический процессор в своей системе и попробуйте запустить его на CPU.
     
  • Проверка кода на наличие генераторов случайных чисел. Нужно протестировать его, находя генераторы случайных чисел, и отследить случайный вывод для удачных и ошибочных прогонов.
     
  • Запуск на одном узле NUMA гарантирует, что плавающий баг возник не из-за проблем с архитектурой системы. Между узлами NUMA часто наблюдается нестабильность межсоединений.
     
  • Изменение зависимости ПО. Важно, чтобы гейзенбаг не исходил от внешнего программного компонента. Например, вместо использования TensorFlow, оптимизированного для Intel с DNNLv1.1, можно попробовать Vanilla TensorFlow (созданный из исходных кодов или установленный с помощью pip).
     
  • Удаление лишних функций по очереди позволит выяснить, какая из них — источник плавающего бага.
     
  • Запуск стресс-тестов поможет выявить потенциальные ошибки в экстремальных условиях. Но тестирование не доказывает отсутствие ошибок. Стресс-тесты только позволяют снизить риск незамеченных ошибок в рабочей среде.
     
  • Использование специальных алгоритмов для обнаружения потенциального состояния гонки (Race condition). Алгоритм набора блокировок сообщает о потенциальном состоянии гонки, когда к общей памяти обращаются два или более потоков, удерживающих общую блокировку. Он может сообщать о ложных срабатываниях. Алгоритм «происходит до» основан на частичном упорядочении событий (любой инструкции, включая чтение/запись и блокировки) в распределенных системах внутри и между потоками. Этот алгоритм генерирует очень мало ложных срабатываний, но он чувствителен к порядку выполнения, поэтому может потребоваться запустить его несколько раз, прежде чем удастся обнаружить состояние гонки, вызывающее плавающий баг.

  • Обратный дебаггинг — способность дебаггера останавливаться после сбоя в программе и возвращаться к истории выполнения, чтобы раскрыть причину сбоя.
     
  • Использование специальных инструментов дебаггинга для состояния гонки. Вот несколько примеров:

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

#2. The Intel Inspector помогает находить и отлаживать ошибки потоковой передачи, памяти и постоянной памяти на ранних этапах цикла проектирования.

#3. Kiuwan — инструмент статического анализа кода. Он обнаруживает угрозы безопасности до того, как они попадут в рабочую среду. 

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

  • Использование инструментов дебаггинга:

C ++: UDB. Находит и исправляет ошибки. 

C #: RevDeBug. Исправляет ошибки при диагностике сбоев программного обеспечения. 

Python: RevPDB. Позволяет перемещаться вперед и назад.

Ещё статьи
Инструкция от Product Analyst Lead в SQUAD.
История, архитектура и основы обучения.