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

Фон

  • У меня есть приложение с 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: приложение аварийно завершает работу, происходит "poof" и исчезает без следа.

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

Заметки

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

5 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам