Как я могу гарантировать перехват структурированного исключения EXCEPTION_STACK_OVERFLOW в C ++ в Visual Studio 2005? - PullRequest
6 голосов
/ 12 января 2009

Фон

  • У меня есть приложение с Poof-Crash [ 1 ]. Я вполне уверен, что это из-за взорванного стека.
  • Приложение является многопоточным.
  • Я компилирую с "Enable C++ Exceptions: Yes With SEH Exceptions (/EHa)".
  • Я написал функцию транслятора SE и вызвал _set_se_translator() вместе с ней.
  • Я написал функции для и настройки set_terminate() и set_unexpected().
  • Чтобы получить переполнение стека, я должен работать в режиме выпуска, под большой нагрузкой, в течение нескольких дней. Запуск под отладчиком невозможен, так как приложение не может работать достаточно быстро, чтобы достичь времени выполнения, необходимого для обнаружения проблемы.
  • Я могу смоделировать проблему, добавив бесконечную рекурсию при выполнении одной из функций, и, таким образом, протестировать перехват исключения EXCEPTION_STACK_OVERFLOW.
  • У меня есть программа установки WinDBG в качестве программы аварийного дампа, и я получаю хорошую информацию обо всех других проблемах аварийного завершения, но не эта Аварийный дамп будет содержать только один поток, который называется «Sleep ()». Все остальные темы вышли.

Вопрос

Ничто из того, что я пробовал, не привело к исключению EXCEPTION_STACK_OVERFLOW.

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

Определения

  1. Poof-Crash : приложение аварийно завершает работу, переходя в «пуф» и исчезая без следа.

(Учитывая название этого сайта, я немного удивлен, что этого вопроса уже нет!)

Примечания

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

Ответы [ 5 ]

5 голосов
/ 13 января 2009

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

Но на самом деле вы мало что можете сделать, даже если вы в состоянии заманить в ловушку такое исключение.

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

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

4 голосов
/ 14 января 2009

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

Но в любом случае, тот факт, что вы получаете пуф! , плюс то, что вы видите в WinDbg

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

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

Мое предложение состоит в том, чтобы исправить ваши исполняемые файлы, чтобы поместить, возможно, отладку INT 3 в точку входа в exit (), если она статически связана, или если она динамически связана, исправьте импорт, а также исправьте любой импорт kernel32: : TerminateProcess вместо этого выбрасывает DebugBreak ().

Конечно, exit () и / или TerminateProcess () также могут вызываться при обычном завершении работы, поэтому вам придется отфильтровывать ложные тревоги, но если вы сможете получить стек вызовов для случая, когда он вот-вот доказательство, у вас должно быть то, что вам нужно.

РЕДАКТИРОВАТЬ ДОБАВИТЬ: просто написать свою собственную версию exit () и связать ее вместо CRTL-версии, что может помочь.

1 голос
/ 13 января 2009

Рассматривали ли вы ADPlus из средств отладки для Windows?

ADPlus присоединяет отладчик CDB к процессу в режиме «сбоя» и создает дампы для большинства исключений, которые генерирует процесс. По сути, вы запускаете «ADPlus -crash -p yourPIDhere», он выполняет инвазивное присоединение и начинает регистрацию.

Учитывая ваш комментарий выше о работе под отладчиком, я просто хотел добавить, что CDB добавляет практически нулевые издержки в режиме -crash на приличной (двухъядерной, 2 ГБ ОЗУ) машине, поэтому не позволяйте этому сдерживать от попытки этого.

1 голос
/ 12 января 2009

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

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

0 голосов
/ 12 января 2009

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

И документация для _set_se_translator гласит, что у каждого потока есть свой собственный транслятор SE. Вы устанавливаете один для каждой темы?

set_unexpected, вероятно, запрещен, по крайней мере, согласно документации VS 2005. И каждый поток также имеет свой собственный обработчик terminate, поэтому вы должны также установить его для каждого потока.

Я бы также настоятельно рекомендовал НЕ , используя перевод SE. Он принимает аппаратные исключения, которые вы не должны игнорировать (т. Е. Вы действительно должны регистрировать ошибки и завершать работу) и превращать их в то, что вы можете игнорировать (исключения C ++). Если вы хотите перехватить этот тип ошибки, используйте обработчик __try/__except.

...