Программа только сработает как релиз сборки - как отлаживать?

У меня здесь проблема типа "Schroedinger Cat" - моя программа (на самом деле тестовый набор для моей программы, но программа тем не менее) сбой, но только при построении в режиме выпуска и только при запуске из командной строки. Через отладку пещерного человека (т.е. Неприятные сообщения printf() повсюду) я определил метод тестирования, где происходит сбой кода, но, к сожалению, фактический сбой, похоже, происходит в некотором деструкторе, поскольку последние сообщения трассировки, которые я вижу, находятся в другие деструкторы, которые выполняются чисто.

Когда я пытаюсь запустить эту программу внутри Visual Studio, она не сбой. То же самое происходит при запуске из WinDbg.exe. Сбой возникает только при запуске из командной строки. Это происходит под Windows Vista, кстати, к сожалению, у меня нет доступа к машине XP прямо сейчас, чтобы протестировать.

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

Изменить: проблема действительно была вызвана массивом out-of-bounds, который я больше описываю в этом сообщении. Спасибо всем за помощь в поиске этой проблемы!

Ответ 1

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

Ответ 2

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

int* p;
....
if (p == 0) { // do stuff }

В режиме отладки код в if не выполняется, но в режиме выпуска p содержит значение undefined, которое вряд ли будет 0, поэтому код выполняется часто, вызывая сбой.

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

Ответ 3

Что нужно искать:

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

Условия гонки - есть ли у вас несколько потоков, если это состояние гонки много, только когда приложение выполняется непосредственно.

Связывание - ваша сборка релизов вытягивает нужные библиотеки.

Что нужно попробовать:

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

Ответ 4

До сих пор ни один ответ не пытался дать серьезный обзор доступных методов для отладки приложений выпуска:

  • Сборка релизов и отладок ведет себя по-разному по многим причинам. Вот отличный обзор. Каждое из этих различий может привести к ошибка в сборке Release, которая не существует в сборке Debug.

  • Наличие отладчика может также изменить поведение программы, как для релизов, так и для отладки. См. этот ответ. Короче говоря, по крайней мере, отладчик Visual Studio автоматически использует отладочную кучу при подключении к программе. Вы можете отключить кучу отладки, используя переменную среды _NO_DEBUG_HEAP. Вы можете указать это либо в свойствах вашего компьютера, либо в настройках проекта в Visual Studio. Это может привести к тому, что аварийный сигнал будет воспроизводиться при подключенном отладчике.

    Подробнее об отладке кучи здесь.

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

  • Вы можете улучшить код обработки исключений, и если это производственное приложение, вы должны:

    а. Установите собственный обработчик завершения, используя std::set_terminate

    Если вы хотите отладить эту проблему локально, вы можете запустить бесконечный цикл внутри обработчика завершения и вывести на консоль текст, чтобы уведомить вас о том, что был вызван std::terminate. Затем присоедините отладчик и проверьте стек вызовов. Или вы печатаете трассировку стека, как описано в этом ответе.

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

    б. Использовать механизм структурированной обработки исключений Microsoft, позволяющий обнаруживать как аппаратные, так и программные исключения. См. MSDN. Вы можете защищать части своего кода с помощью SEH и использовать тот же подход, что и для a) для отладки проблемы. SEH дает больше информации об исключении, которое произошло, которое вы могли бы использовать при отправке отчета об ошибке из производственного приложения.

Ответ 5

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

windbg /I

Подробнее здесь.

Что касается причины, это, скорее всего, унифицированная переменная, как указывают другие ответы.

Ответ 6

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

char *end = static_cast<char*>(attr->data) + attr->dataSize;

Это ошибка fencepost (ошибка "один за другим" ) и была исправлена:

char *end = static_cast<char*>(attr->data) + attr->dataSize - 1;

Странно, я поместил несколько вызовов _CrtCheckMemory() вокруг различных частей моего кода, и они всегда возвращались 1. Я смог найти источник проблемы, поставив "return false"; вызовы в тестовом примере, а затем, в конечном итоге, определить путем проб и ошибок, в которых произошла ошибка.

Спасибо всем за ваши комментарии - я много узнал о windbg.exe сегодня!:)

Ответ 7

Несмотря на то, что вы создали свой exe в качестве выпуска, вы все равно можете создавать файлы PDB (Program database), которые позволят вам трассировать трассировку и выполнять ограниченную проверку переменных. В ваших настройках сборки есть возможность создавать файлы PDB. Включите это и повторно подключите. Затем попробуйте запустить из среды IDE, чтобы убедиться, что вы получили сбой. Если так, то здорово - вы все готовы смотреть на вещи. Если нет, то при запуске из командной строки вы можете сделать одну из двух вещей:

  • Запустите EXE и перед сбоем выполните команду Attach To Process (меню Tools в Visual Studio).
  • После сбоя выберите вариант запуска отладчика.

Когда вас попросят указать файлы PDB, найдите их. Если PDB были помещены в ту же папку вывода, что и EXE или DLL, они, вероятно, будут автоматически загружены.

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

NB: Я предполагаю среду Windows/Visual Studio здесь.

Ответ 8

Чтобы иметь аварийный дамп, который вы можете проанализировать:

  • Создайте файлы pdb для вашего кода.
  • Вы обновляете, чтобы ваши exe и dll загружались по одному и тому же адресу.
  • Включить отладчик post mortem, например Dr. Уотсон
  • Проверьте адрес сбоя при сбое с помощью инструмента, такого как поиск сбоев.

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

Надеюсь, что это поможет...

Ответ 9

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

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

Ответ 10

Как только у меня возникла проблема, когда приложение было похоже на ваше. Это оказалось неприятным переполнением буфера в sprintf. Естественно, он работал при запуске с подключенным отладчиком. Я установил необработанный фильтр исключений (SetUnhandledExceptionFilter), в котором я просто блокировал бесконечно (используя WaitForSingleObject на фиктивном дескрипторе с таймаутом значение INFINITE).

Итак, вы могли бы что-то вроде:

long __stdcall MyFilter(EXCEPTION_POINTERS *)
{
    HANDLE hEvt=::CreateEventW(0,1,0,0);
    if(hEvt)
    {
        if(WAIT_FAILED==::WaitForSingleObject(hEvt, INFINITE))
        {
            //log failure
        }
    }

}
// somewhere in your wmain/WinMain:
SetUnhandledExceptionFilter(MyFilter);

Затем я приложил отладчик после того, как ошибка проявилась (программа gui перестала отвечать).

Затем вы можете взять дамп и работать с ним позже:

.dump/ma path_to_dump_file

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

s-d esp Диапазон 1003f

Команда будет искать адресное пространство стека для записей (ов) CONTEXT при длительности поиска. Обычно я использую что-то вроде 'l? 10000'. Обратите внимание: не используйте нестандартно большие числа в качестве записи, которую вы обычно находите рядом с необработанным фреймом фильтра исключений. 1003f - это комбинация флагов (я считаю, что она соответствует CONTEXT_FULL), используемая для захвата состояния процессора. Ваш поиск будет выглядеть примерно так:

0: 000 > s-d esp l1000 1003f
0012c160 0001003f 00000000 00000000 00000000?................

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

.cxr 0012c160

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

.exr -1

чтобы узнать, какое именно исключение произошло.

Надеюсь, что это поможет.

Ответ 11

Что касается проблем с получением диагностической информации, попробовали ли вы использовать adplus.vbs в качестве альтернативы WinDbg.exe? Чтобы подключиться к выполняемому процессу, используйте

adplus.vbs -crash -p <process_id>

Или запустить приложение в случае, если авария произойдет быстро:

adplus.vbs -crash -sc your_app.exe

Полную информацию о adplus.vbs можно найти по адресу: http://support.microsoft.com/kb/286350

Ответ 12

Ntdll.dll с прикрепленным отладчиком

Небольшая разница между запуском программы из среды IDE или WinDbg, а не ее запуском из командной строки/рабочего стола, заключается в том, что при запуске с подключенным отладчиком (т.е. IDE или WinDbg) ntdll.dll использует другую реализацию кучи, которая выполняет некоторая небольшая проверка на распределение/освобождение памяти.

Вы можете прочитать некоторую релевантную информацию в неожиданной точке прерывания пользователя в файле ntdll.dll. Одним из инструментов, который может помочь вам идентифицировать проблему, является PageHeap.exe.

Анализ сбоев

Вы не писали, что такое "авария", которую вы испытываете. Как только программа выйдет из строя и предложит вам отправить информацию об ошибках в Microsoft, вы должны будете щелкнуть по технической информации и, по крайней мере, проверить код исключения, и с некоторыми усилиями вы можете даже выполнить посмертный анализ (см. Heisenbug: программа WinApi сбой на некоторых компьютерах) для инструкций)

Ответ 13

Vista SP1 действительно имеет действительно красивый генератор аварийных дампов, встроенный в систему. К сожалению, он не включен по умолчанию!

См. статью: http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx

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

Ответ 14

Иногда это происходит, потому что вы завернули важную операцию внутри макроса "assert". Как вы знаете, "assert" оценивает выражения только в режиме отладки.

Ответ 15

Как мой опыт, это больше всего проблема с повреждением памяти.

Например:

char a[8];
memset(&a[0], 0, 16);

: /*use array a doing some thing */

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

Но в релизе это может быть/может быть сбой.

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

Используйте некоторые инструменты, такие как Visual Leak Detector (windows) или valgrind (linux) - более мудрый выбор.

Ответ 16

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

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

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

Ответ 17

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

Ответ 18

Это подозрительно, что это произойдет вне отладчика, а не внутри; работа в отладчике обычно не изменяет поведение приложения. Я бы проверил различия в среде между консолью и IDE. Также, очевидно, скомпилируйте выпуск без оптимизации и с информацией об отладке и посмотрите, влияет ли это на поведение. Наконец, ознакомьтесь с инструментами отладки после вскрытия, предложенными другими людьми, как правило, вы можете получить от них какую-то подсказку.

Ответ 19

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

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

Ответ 20

Попробуйте использовать _CrtCheckMemory(), чтобы узнать, в каком состоянии находится выделенная память. Если все идет хорошо, _CrtCheckMemory возвращает TRUE, else FALSE.

Ответ 21

Вы можете запустить свое программное обеспечение с включенными глобальными флагами (см. "Инструменты отладки для Windows" ). Это очень часто поможет устранить проблему.

Ответ 22

Сделайте вашу программу сгенерированной мини-дампом при возникновении исключения, затем откройте ее в отладчике (например, в WinDbg). Основные функции: MiniDumpWriteDump, SetUnhandledExceptionFilter

Ответ 23

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

Ответ 24

У меня была эта ошибка, и я потерпел крушение даже при попытке! мой проект. Поэтому я удалил файлы obj вручную из каталога Release, и после этого он построил просто отлично.

Ответ 25

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