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


У меня есть программа Visual Studio 2005 C++, которая работает иначе в режиме выпуска, чем в режиме отладки. В режиме выпуска происходит (очевидный) прерывистый сбой. В режиме отладки он не падает. Каковы некоторые причины, по которым сборка выпуска будет работать иначе, чем сборка отладки?

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

спасибо вперед!

10 54

10 ответов:

выживание в версии дает хороший обзор.

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

инициализация переменной безусловно, самый распространенный. В Visual Studio, в отладочных построениях явно инициализировать выделенную память до заданных значений, см., например,Значения Памяти здесь. Эти значения, как правило, легко обнаружить, вызывают ошибку out of bounds при использовании в качестве индекса или нарушения прав доступа при использовании в качестве указателя. Однако неинициализированное логическое значение истинно и может привести к тому, что неинициализированные ошибки памяти останутся незамеченными в течение многих лет.

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

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

Действующий Оптимизации второе место в мой стаж. Стандарт C++ позволяет выполнять множество оптимизаций, которые могут быть удивительными, но полностью действительны, например, когда два указателя псевдонима одного и того же места памяти, порядок инициализации не рассматривается, или несколько потоков изменяют одни и те же места памяти, и вы ожидаете определенного порядка, в котором поток B видит изменения, сделанные потоком A. часто компилятор обвиняется в них. Не так быстро, молодой йеди! - см. ниже

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

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

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

#ifdef DEBUG
#define Log(x) cout << #x << x << "\n";
#else 
#define Log(x)
#endif

if (foo)
  Log(x)
if (bar)
  Run();

, который, в сборке выпуска, оценивает в if (foo & & bar) Этот тип ошибок очень редко встречается с обычным кодом C/C++ и макросами, которые правильно написаны.

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

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

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

Release build (надеюсь) будет работать быстрее, чем ваша отладочная сборка. Если вы используете более одного потока, вы можете увидеть больше чередования или просто один поток работает быстрее, чем другие, которые вы, возможно, не заметили в debug build.

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

  • выделение памяти обрабатывается по-разному с отладочными сборками в компиляторе VS (т. е. запись 0xcc поверх очищенной памяти и т. д.)
  • развертывание цикла и другие оптимизации компилятора
  • зажигание указателей

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

в частности, переменные могут быть инициализированы только в режиме отладки и оставлены неинициализированными в режиме выпуска. STL в компиляторах Visual Studio различаются в режимах отладки и выпуска. Идея заключается в том, что итераторы полностью проверяются в DEBUG для обнаружения возможных ошибок (используя invalidated итераторы, например итератор в вектор недействителен, если вставка происходит после извлечения итератора).

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

со всеми изменениями, код и объем памяти будет отличаться в обоих режимах. Указатель (чтение позиция один проход конец массива) проблема может пройти незамеченным, если эта позиция читается.

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

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

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

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

Я знаю, что это относится к компилятору gcc C++, но я не уверен в компиляторе Microsoft.

http://www.debuginfo.com/tips/userbpntdll.html

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

используйте PageHeap (или, если у вас есть Установленные инструменты отладки вы можете использовать gflags) для обнаружения ошибок, связанных с поврежденными кучами.

http://support.microsoft.com/?id=286470

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

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

этот пост вместе с предоставленными ссылками очень полезен для исправления связанной ошибки. добавление к приведенному выше списку, разница в соглашениях о вызовах также может привести к такому поведению - это не удалось в сборке выпуска только с оптимизацией для меня. я объявил как _ _ stdcall и определил как __cdecl ( по умолчанию). [странно, что это предупреждение Не выбрано даже в предупреждении уровня 4 MSVC?]