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

StackOverflow https://stackoverflow.com/questions/1762088

  •  21-09-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

Часто в режиме отладки в C ++ все переменные инициализируются null, тогда как в режиме выпуска этого не происходит, если явно не указано иное.

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

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

Также проверьте все исключения, например, не связанные напрямую с режимом выпуска, но иногда мы просто игнорируем некоторые критические исключения, такие как нарушение доступа к mem в VC ++, но то же самое может быть проблемой, по крайней мере, в других ОС, таких как Linux, Solaris.В идеале ваша программа не должна перехватывать такие критические исключения, как доступ к нулевому указателю.

Другие советы

Распространенной ошибкой является использование выражения с побочным эффектом внутри ASSERT .

Другими различиями могут быть:

  • На языке, использующем сбор мусора сборщик обычно более агрессивен в режиме выпуска;
  • Расположение памяти может часто отличаться;
  • Память может быть инициализирована по-разному (например, может быть обнулена в режиме отладки или повторно использована более агрессивно в выпуске);
  • Локальные файлы могут быть повышены для регистрации значений в release, что может вызвать проблемы с плавающей запятой значения.

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

  • Переменные -члены или функции -члены в #ifdef _DEBUG, так что класс имеет другой размер в отладочной сборке.Иногда #ifndef NDEBUG используется в сборке выпуска
  • Точно так же существует другой #ifdef который, оказывается, присутствует только в одной из двух сборок
  • Отладочная версия использует отладочные версии системных библиотек, особенно функций выделения кучи и памяти
  • Встроенные функции в сборке выпуска
  • Порядок включения заголовочных файлов.Это не должно вызвать проблем, но если у вас есть что-то вроде #pragma pack если это не было сброшено, то это может привести к неприятным проблемам.Аналогичные проблемы также могут возникать при использовании предварительно скомпилированных заголовков и принудительных включений
  • Тайники:у вас может быть код, такой как кэши, который используется только в сборках релизов, или другие ограничения на размер кэша
  • Конфигурации проекта:конфигурации debug и release могут иметь разные параметры сборки (вероятно, это произойдет при использовании IDE).
  • Условия гонки, проблемы с синхронизацией и разнообразные побочные эффекты, возникающие в результате использования кода только для отладки

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

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

Да!, если у вас условная компиляция, могут быть ошибки синхронизации (оптимизированный код выпуска verse, неоптимизированный отладочный код), повторное использование памяти по сравнениюотладочная куча.

Это возможно, особенно если вы находитесь в области C.

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

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

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

Библиотечные функции CRT ведут себя по-разному в debug и release (/MD vs /MDd).

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

Конечно, например, если вы используете конструкции типа

#if DEBUG

//some code

#endif

В .NET, даже если вы не используете условную компиляцию, например #if DEBUG, компилятор по-прежнему намного более либеральен в оптимизации в режиме выпуска, чем в режиме отладки, что также может привести к выпуску только ошибок.

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

Без дополнительных подробностей я предположу, что "не в порядке" означает, что он либо не компилируется,либо выдает какую-то ошибку во время выполнения.Проверьте, есть ли у вас код, который полагается на версию компиляции, либо через #if DEBUG заявления или с помощью методов , помеченных Conditional атрибут.

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

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

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

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

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

Попробуйте скомпилировать свой код с включенной меньшей оптимизацией.

В непустотной функции все пути выполнения должны заканчиваться оператором return .

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

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

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

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

В конфигурации "Release" VS компилировался с помощью /O2, который оптимизирует код для повышения скорости.Таким образом, некоторые локальные переменные, которые просто отображались в регистры процессора (для оптимизации), которые были совместно использованы с вышеупомянутой функцией, приводили к серьезному повреждению памяти.

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

Я помню, как некоторое время назад, когда мы создавали dll и pdb на c / c ++.

Я помню это:

  • Добавление данных журнала иногда приводило к перемещению ошибки, ее исчезновению или появлению совершенно другой ошибки (так что на самом деле это был не вариант).
  • Многие из этих ошибок возникают из-за выделения символов в strcpy и strcat и массивов char[] и т.д...
  • Мы отсеяли некоторые из них, запустив bounds checker и просто исправив проблемы с выделением / освобождением памяти.
  • Много раз мы систематически просматривали код и исправляли распределение символов (например, во всех файлах).
  • Определенно, это что-то связанное с распределением памяти и управлением, а также ограничениями и различиями между режимом отладки и режимом выпуска.

А потом понадеялся на лучшее.

Иногда я временно предоставлял клиентам отладочные версии DLL-файлов, чтобы не задерживать производство во время работы над этими ошибками.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top