Inverse Heisenbug - модульный тест не пройден, только если подключен отладчик - PullRequest
4 голосов
/ 25 ноября 2010

Я недавно исправил дефект в нашем продукте, симптомом которого было нарушение доступа, вызванное доступом к висячему указателю.

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

После возврата исправления дефекта я обнаружил, что мой модультест еще проходит (не хорошо).Когда я подключил к модульному тесту отладчик, чтобы увидеть, почему он проходит, тест не прошел (т. Е. Было сгенерировано исключение), и я смог сломать и заметить, что стек вызовов совпадает с тем, что было в исходном дефекте, который я исправил.1006 * Я не изменял параметры «Сбой при исключении» в Visual Studio 2005, и это действительно критическое исключение Win32, которое приводит к прекращению использования тестового набора (т. Е. Отсутствует обработчик изящных исключений).

Текст исключения:

Unhandled exception at 0x0040fc59 in _testcase.exe: 0xC0000005:
Access violation reading location 0xcdcdcdcd.

Примечание: Местоположение не всегда 0xcdcdcdcd ( выделено, но неписаная память кучи Win32 ).Иногда это 0x00000000, а иногда это другой адрес.

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

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

Есть ли какие-либо предложения относительно того, что может быть причиной этого?


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

Ответы [ 4 ]

3 голосов
/ 25 ноября 2010

Как правило, при удалении указателя на эту память отладчик VC ++ заполняет выделенную кучу память некоторым известным значением. Прошло довольно много времени с тех пор, как я использовал Visual Studio, но мне кажется разумным, что 0xcdcdcdcd может быть таким значением. Скорее всего, мне кажется, что приложение работает правильно при запуске в отладчике. При работе в режиме Release среда выполнения не тратит впустую время, перезаписывая освобожденную память, поэтому в некоторых случаях вам «везет», и данные, хранящиеся в этой памяти, остаются действительными.

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

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

1 голос
/ 25 ноября 2010

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

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

Возможно, у вас похожая ситуация.

0 голосов
/ 25 ноября 2010

Я выделил причину этой проблемы - подробности см. в этом вопросе .

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

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

0 голосов
/ 25 ноября 2010

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

...