Распространенные причины ошибок в версии выпуска отсутствуют в режиме отладки
Каковы типичные причины ошибок и ненормального поведения программы, которые проявляются только в режиме компиляции релиза, но не возникают в режиме отладки?
18 ответов
Часто в режиме отладки в C++ все переменные инициализируются нулем, тогда как в режиме выпуска это не происходит, если это не указано явно.
Проверьте наличие макросов отладки и неинициализированных переменных.
Если ваша программа использует многопоточность, то оптимизация также может вызвать некоторые проблемы в режиме выпуска.
Также проверьте все исключения, например, не связанные напрямую с режимом выпуска, но иногда мы просто игнорируем некоторые критические исключения, такие как нарушение доступа к mem в VC++, но это может быть проблемой, по крайней мере, в других ОС, таких как Linux, Solaris. В идеале ваша программа не должна перехватывать такие критические исключения, как доступ к NULL-указателю.
Типичной ошибкой является использование выражения с побочным эффектом внутри ASSERT.
В прошлом я был укушен рядом ошибок, которые исправлялись в сборках Debug, но приводили к сбоям в сборках Release. Есть много основных причин (включая, конечно, те, которые уже были кратко изложены в этой теме), и я был пойман всем следующим:
- Переменные-члены или функции-члены в
#ifdef _DEBUG
, так что класс в сборке отладки имеет другой размер. Иногда#ifndef NDEBUG
используется в сборке релиза - Точно так же есть разные
#ifdef
который присутствует только в одной из двух сборок - Отладочная версия использует отладочные версии системных библиотек, особенно функции кучи и выделения памяти.
- Встроенные функции в сборке релиза
- Порядок включения заголовочных файлов. Это не должно вызывать проблем, но если у вас есть что-то вроде
#pragma pack
это не было сброшено, то это может привести к неприятным проблемам. Подобные проблемы могут также возникать при использовании предварительно скомпилированных заголовков и принудительных включений. - Кэши: у вас может быть такой код, как кеши, которые используются только в сборках релиза, или другие ограничения размера кеша
- Конфигурации проекта: конфигурации отладки и выпуска могут иметь разные параметры сборки (это может произойти при использовании IDE)
- Условия гонки, проблемы с синхронизацией и различные побочные эффекты, возникающие в результате отладки только кода
Несколько советов, которые я накопил за эти годы, чтобы докопаться до ошибок отладки / выпуска:
- Попытайтесь воспроизвести аномальное поведение в отладочной сборке, если вы можете, и даже лучше, написать модульный тест, чтобы захватить его
- Подумайте о том, что отличается между ними: настройки компилятора, кэши, код только для отладки. Попробуйте временно минимизировать эти различия
- Создайте сборку выпуска с отключенными оптимизациями (чтобы вы с большей вероятностью получили полезные данные в отладчике) или оптимизированную сборку отладки. Минимизируя изменения между отладкой и выпуском, вы с большей вероятностью сможете определить, какая разница вызывает ошибку.
Другие различия могут быть:
- На языке сборки мусора сборщик обычно более агрессивен в режиме выпуска;
- Расположение памяти часто может быть различным;
- Память может быть инициализирована по-разному (например, может обнуляться в режиме отладки или использоваться более агрессивно в выпуске);
- Местные жители могут повышать регистрацию значений в выпуске, что может вызвать проблемы со значениями с плавающей запятой.
Да!, если у вас есть условная компиляция, могут быть ошибки синхронизации (стих с оптимизированным кодом выпуска, неоптимизированный код отладки), повторное использование памяти и куча отладки.
Это возможно, особенно если вы находитесь в мире Си.
Одной из причин может быть то, что версия DEBUG может добавить код для проверки на наличие ошибочных указателей и каким-то образом защитить ваш код от сбоев (или неправильного поведения). Если это так, вы должны тщательно проверить предупреждения и другие сообщения, которые вы получаете от вашего компилятора.
Другой причиной может быть оптимизация (которая обычно включена для версий выпуска и отключена для отладки). Возможно, код и структура данных были оптимизированы, и в то время как ваша программа отладки, например, только обращалась к неиспользуемой памяти, версия выпуска теперь пытается получить доступ к зарезервированной памяти или даже указывать на код!
РЕДАКТИРОВАТЬ: я вижу, что другие упоминали это: конечно, у вас могут быть целые разделы кода, которые условно исключены, если не компилируются в режиме DEBUG. Если это так, я надеюсь, что это действительно отладочный код, а не что-то жизненно важное для правильности самой программы!
Функции библиотеки CRT ведут себя по-разному в отладке против выпуска (/MD против /MDd).
Например, версии отладки часто предварительно заполняют буферы, которые вы передаете на указанную длину, чтобы проверить вашу заявку. Примеры включают strcpy_s
, StringCchCopy
и т. д. Даже если строки заканчиваются раньше, ваш szDest должен быть длиной n байтов!
Конечно, если вы используете такие конструкции, как
#if DEBUG
//some code
#endif
В.NET, даже если вы не используете условную компиляцию, такую как #if DEBUG
компилятор все еще более либерален с оптимизацией в режиме выпуска, чем в режиме отладки, что также может привести к выпуску только ошибок.
Существуют оптимизации компилятора, которые могут нарушить допустимый код, потому что они слишком агрессивны.
Попробуйте скомпилировать код с меньшим количеством включенной оптимизации.
Без более подробной информации, я буду предполагать, что "не в порядке" означает, что он либо не компилируется, либо выдает какую-то ошибку во время выполнения. Проверьте, есть ли у вас код, основанный на версии компиляции, либо через #if DEBUG
заявления или с помощью методов, отмеченных Conditional
приписывать.
Это возможно, если у вас есть условная компиляция, так что код отладки и код выпуска отличаются, и есть ошибка в коде, который используется только в режиме выпуска.
Кроме этого, это невозможно. Существуют различия в том, как код отладки и код выпуска компилируются, и различия в том, как выполняется код, если он запускается в отладчике или нет, но если любое из этих различий вызывает что-то кроме различия в производительности, проблема была там с самого начала.
В отладочной версии ошибка может не возникать (поскольку время или распределение памяти различаются), но это не означает, что ошибки нет. Также могут быть и другие факторы, которые не связаны с режимом отладки, который изменяет синхронизацию кода, вызывая ошибку или нет, но все сводится к тому, что если код был правильным, ошибка не возникнет в любой из ситуаций.
Итак, нет, отладочная версия не в порядке только потому, что вы можете запустить ее без получения ошибки. Если ошибка возникает при запуске в режиме выпуска, это происходит не из-за режима выпуска, а из-за того, что ошибка возникла с самого начала.
Вам нужно будет дать гораздо больше информации, но да, это возможно. Это зависит от того, что делает ваша отладочная версия. Возможно, у вас есть логирование или дополнительные проверки, которые не скомпилированы в версию выпуска. Эти только пути отладки кода могут иметь непреднамеренные побочные эффекты, которые изменяют состояние или странным образом влияют на переменные. Отладочные сборки обычно работают медленнее, поэтому это может повлиять на многопоточность и скрыть условия гонки. То же самое для прямой оптимизации от компиляции релиза, возможно (хотя в наши дни это маловероятно), что компиляция релиза может замкнуть что-то в качестве оптимизации.
В непустой функции все пути выполнения должны заканчиваться оператором возврата.
В режиме отладки, если вы забыли завершить такой путь с помощью оператора return, функция обычно возвращает 0 по умолчанию.
Однако в режиме выпуска ваша функция может возвращать значения мусора, которые могут повлиять на работу вашей программы.
Я помню некоторое время назад, когда мы собирали dll и pdb в c/ C++.
Я это помню:
- Добавление данных журнала иногда приводило к тому, что ошибка перемещалась или исчезала, или появлялась совершенно другая ошибка (поэтому это не было возможным вариантом).
- Многие из этих ошибок связаны с распределением символов в strcpy и strcat и массивами char[] и т. Д.
- Мы отсеяли некоторые из них, запустив проверку границ и просто исправив проблемы с выделением / удалением памяти.
- Много раз мы систематически просматривали код и исправляли распределение символов (как во всех файлах).
- Определенно это связано с распределением памяти и управлением, а также с ограничениями и различиями между режимом отладки и режимом выпуска.
А потом надеялся на лучшее.
Иногда я временно доставлял отладочные версии dll клиентам, чтобы не останавливать работу, работая над этими ошибками.
Я только что испытал это, когда я вызывал функцию сборки, которая не восстановила предыдущие значения регистров.
В конфигурации "Release" VS компилировал с /O2, что оптимизирует код по скорости. Таким образом, некоторые локальные переменные, которые просто отображаются в регистры ЦП (для оптимизации), которые используются совместно с вышеупомянутой функцией, приводят к серьезному повреждению памяти.
Как бы то ни было, посмотрите, не попадаете ли вы косвенно в регистры процессора где-либо в вашем коде.
Другими причинами могут быть вызовы БД. Вы сохраняете и обновляете одну и ту же запись несколько раз в одной ветке, иногда для обновления. Возможно, обновление завершилось неудачно или не сработало так, как ожидалось, поскольку предыдущая команда create все еще обрабатывала и для обновления вызов db не смог найти ни одной записи. это не произойдет при отладке, так как отладчик проверяет выполнение всех отложенных задач перед приземлением.
Возможно. Если это произойдет, и никакая условная компиляция не будет задействована, то вы можете быть уверены, что ваша программа неверна и работает в режиме отладки только из-за случайных инициализаций памяти или даже размещения в памяти!