Как я могу гарантировать перехват структурированного исключения 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
исключение.
Кто-нибудь знает, как гарантировать получение шанса на это исключение во время выполнения в режиме выпуска?
Определения
- Poof-Crash: приложение аварийно завершает работу, происходит "poof" и исчезает без следа.
(Учитывая название этого сайта, я немного удивлен, что этот вопрос уже не здесь!)
Заметки
- Вкратце был опубликован ответ о корректировке размера стека, чтобы потенциально вызвать проблему раньше и позволить отловить ее с помощью отладчика. Это умная мысль, но, к сожалению, я не верю, что это поможет. Проблема, вероятно, вызвана тем, что приводит к бесконечной рекурсии. Сокращение стека не приведет к более быстрому раскрытию проблемы и, скорее всего, вызовет несвязанный сбой в действительно глубоком коде. Хорошая идея, хотя, и спасибо за размещение, даже если вы удалили его.
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
обработчик.