Лучшие практики и инструменты для отладки различий между Отладкой и Сборками конечных версий?

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

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

Есть ли какие-либо лучшие методы разработки программного обеспечения для того, чтобы эффективно отладить, когда Отладка и Сборки конечных версий отличаются? Кроме того, что инструменты там, которые работают на более фундаментальном уровне, чем valgrind, чтобы помочь отладить эти случаи?

Править: Я замечаю много ответов, предлагающих некоторые общие хорошие методы, такие как поблочное тестирование и регрессионное тестирование, которое я согласовываю, являются большими для нахождения любой ошибки. Однако есть ли что-то конкретно адаптированное в соответствии с этим Выпуском по сравнению с проблемой Отладки? Например, есть ли такая вещь как инструмент статического анализа, который говорит "Эй, этот макрос или этот код, или эта практика программирования опасна, потому что это имеет потенциал для порождения различий между Отладкой/Сборками конечных версий?"

11
задан Eric 24 June 2010 в 02:10
поделиться

6 ответов

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

Debug и Release сборки отличаются по 3 аспектам:

  • _DEBUG define
  • оптимизации
  • разная версия стандартной библиотеки

Лучший способ, которым я часто работаю, следующий:

  • Отключите оптимизации там, где производительность не критична. Отладка важнее. Самое важное - отключить автоинлайнинг функций, сохранить стандартные оптимизации фрейма стека и повторного использования переменных. Они больше всего раздражают при отладке.
  • Контролируйте код на предмет зависимости от определения DEBUG. Никогда не используйте ассерты только для отладки, или любые другие инструменты, чувствительные к DEBUG define.
  • По умолчанию компилировать и работать /release.
3
ответ дан 3 December 2019 в 09:40
поделиться

Если отладка и выпуск различаются, это означает:

  1. ваш код зависит от _DEBUG или аналогичных макросов (определяется при компиляции отладочной версии - нет оптимизации)
  2. в вашем компиляторе есть ошибка оптимизации (я видел это несколько раз)

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

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

Вам нужно будет иметь некоторые тесты «черного ящика» для вашего приложения - valgrind - решение в этом случае. Эти решения помогут вам найти различия между выпуском и отладкой (что очень важно).

0
ответ дан 3 December 2019 в 09:40
поделиться

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

Во-вторых, я попытаюсь определить, в чем разница между отладочной и выпускной сборками. Я смотрю на настройки оптимизации компилятора, которые передаются компилятору в конфигурациях Debug и Release . Вы можете видеть, что это последняя страница свойств настроек компилятора в Visual Studio. Я начну с конфигурации выпуска и изменю аргументы командной строки, передаваемые компилятору, по одному элементу за раз, пока они не совпадут с командной строкой, которая используется для компиляции при отладке. После каждого изменения запускаю программу и воспроизводю ошибку. Это часто приводит меня к конкретным настройкам, вызывающим ошибку.

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

#pragma optimize( "", off )
void foo() {
    return 1;
}
#pragma optimize( "", on ) 

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

3
ответ дан 3 December 2019 в 09:40
поделиться

Еще одна «передовая практика», или, скорее, комбинация двух: автоматизированные модульные тесты и «Разделяй и властвуй».

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

4
ответ дан 3 December 2019 в 09:40
поделиться

Наиболее очевидной причиной является просто использование директив #ifdef и #ifndef, связанных с DEBUG или подобным символом, которые меняются между двумя сборками.

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

Одна конкретная проблема, которая приходит на ум - это макросы:

#ifdef _DEBUG_
  #define CHECK(CheckSymbol) { if (!(CheckSymbol)) throw CheckException(); }
#else
  #define CHECK(CheckSymbol)
#endif

также известные как soft-assert.

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

.
1
ответ дан 3 December 2019 в 09:40
поделиться

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

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

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

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

1) Неинициализированная память. Я использую этот термин для неинициализированных переменных, поскольку переменная может быть инициализирована, но по-прежнему указывает на память, которая не была инициализирована должным образом. В этом могут помочь инструменты диагностики памяти, такие как Valgrind.

2) Время (например, условия гонки).Это может быть кошмаром для отладки, но есть несколько многопоточных профилировщиков и диагностических инструментов, которые могут помочь. Я не могу предложить что-либо сразу, но в качестве примера можно привести Coverity Integrity Manager.

0
ответ дан 3 December 2019 в 09:40
поделиться