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

StackOverflow https://stackoverflow.com/questions/436671

Вопрос

Предыстория

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

Этот Вопрос

Ни одна из вещей, которые я пробовал, не привела к получению EXCEPTION_STACK_OVERFLOW исключение.

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

Определения

  1. Пуф-Грохот:Приложение выходит из строя, переходя в режим "пуф" и исчезая без следа.

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

Примечания

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

Решение

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

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

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

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

Другие советы

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

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

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

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

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

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

РЕДАКТИРОВАТЬ ДОБАВЛЯТЬ:Простое написание вашей собственной версии exit() и связывание ее с версией CRTL может сделать свое дело.

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

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

Вы подумывали об этом ADPlus из средств отладки для Windows?

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top