Подтвердить что ты не робот

Как отладить stackoverflowexception в .NET.

Сценарий:

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

Все, что я получаю, это: Необработанное исключение типа "System.StackOverflowException" произошло в tag-you're-it.dll

Параметры:

  • Просканируйте все изменения и попытайтесь указать проблему. (может быть медленным)
  • Используйте отладчик и пройдите, пока не найдете проблему. (вероятно, лучше, чем 1.)
  • Используйте профиль и найдите наиболее известные методы.

PS:

Это гипотетическая ситуация (хотя и не слишком необычная), поэтому код не доступен.

4b9b3361

Ответ 1

WinDbg может выполнить свою работу, в том числе даже получить разумную трассировку стека (clr). Вам понадобится WinDbg, если вы не установили его вместе с Visual Studio или Windows SDK. Примечание: "Предварительный просмотр WinDbg" с новым графическим интерфейсом работал хорошо для меня.

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

Примечание. Сразу после запуска процесса CLR не загружается, и .loadby SOS.dll clr завершится ошибкой ("не удается найти модуль" clr "). Необходимо дождаться загрузки CLR. Чтобы остановить выполнение, как только бывает выполнить:

  • sxe ld clr

После загрузки CLR вам нужно будет выполнить следующие шаги, чтобы сломать SackOverflowException (введите в командном окне/строке):

  • .loadby SOS.dll clr (не .loadby sos clr - это может привести к двойной загрузке расширения)
  • !stoponexception -create System.StackOverflowException
  • g (продолжает отладку)

вызвать StackOverflowException/ждать, пока это произойдет

  • !clrstack (напечатает трассировку стека)

Известные источники:

Ответ 2

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

Чтобы найти его:

  • ОБНОВЛЕНО: я не понимал, но, видимо, вы не можете получить трассировку стека для StackOverflowException (я думаю, что-то связано с тем, что не удалось ее поймать). Однако есть способы получить дамп как указано здесь.
  • ReSharper покажет методы, которые называют себя (он помещает маленький зеленый круг в боковую панель для рекурсивных вызовов), хотя он не будет ловить рекурсию, где задействованы два или более метода.
  • Используйте инструмент, например ANTS Profiler, чтобы узнать, какие методы вызывают в большинстве случаев.
  • Следите за событиями, которые могут вызывать код, который означает, что одно и то же событие снова срабатывает, вызывая цикл.

Иногда вы также получаете опечатки:

private string name;

public string Name
{
    get { return Name; } // Ooops! This is recursive, all because of a typo...
}

Это одна из причин, почему я лично предпочитаю использовать автоматические свойства.

Ответ 3

Перейдите в раздел "Отладка", "Исключения" и установите флажок "Исправлены исключения". Теперь, когда вы вызываете исключение stackoverflow, отладчик остановится (в конце концов) и покажет вам стек вызовов.

Ответ 4

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

Ответ 5

Утилита ProcDump помогла нам отладить проблему, как подробно описано здесь. шаги:

  1. Скачать инструмент
  2. Запустите процесс, запишите его идентификатор
  3. Подключите отладчик, выполнив procdump -accepteula -e 1 -f C00000FD.STACK_OVERFLOW -g -ma <process ID> d:\home\DebugTools\Dumps (каталог должен существовать)
  4. Сделайте исключение, чтобы произойти, и procdump сделает вас дампом.
  5. Откройте файл дампа в Visual Studio. Для моего примера приложения сразу после открытия файла дампа VS выделил строку, на которой произошла SO.

Мы можем использовать ту же технику в Azure, включив расширение CrashDiagnoser, как описано здесь. В основном он выполняет те же действия, что и выше. Сгенерированный им дамп файл можно загрузить и открыть в Visual Studio.

Ответ 6

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

Как только вы заметите это, поместите точку останова в текущее местоположение, где бы это ни было. Продолжить выполнение (F5 в Visual Studio) - если вы на правильном пути, отладчик остановится очень быстро в одном месте, а стек вызовов будет еще глубже.

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

Ответ 7

Просто хотел добавить к ответу об использовании WinDbg то, что я нашел для отладки основного приложения dotnet

  1. убедитесь, что у вас установлен windbg
  2. запустите ваше приложение через командную строку с помощью dotnet run
  3. присоединить к процессу бега с помощью windbg
  4. из командной строки введите .loadby sos coreclr (это должно определить версию используемого вами ядра .net, но если нет, вы можете использовать .load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.5\sos где 2.05 - это версия .netcore, которую вы используете.
  5. Команды теперь доступны при вводе !help
  6. Используйте !dso чтобы получить дамп стека

В моем случае это точно указывало, где происходило исключение stackoverflow

Ответ 8

Я подозреваю, что если размер стека потока, вызвавшего переполнение стека, превышает некоторый порог, который может отслеживать отладчик Visual Studio, то стек вызовов недоступен.

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

        (new Thread(delegate ()
        {
            ProduceAStackOverFlowHere() ;
        }, 256 * 1024)).Start();//Default size for 32 bit process is 1MB, 64 bit process is 4MB. So I'll set the size at 256KB.

Ответ 9

Если у вас есть код и вы можете запустить свою программу из Visual Studio, он должен сломаться в отладчике (если включены исключения с первым шансом), когда он встречает исключение System.StackOverflowException. Оттуда вы можете проверить стек вызовов и посмотреть, какие вызовы выдувают стек. enter image description here

Я подтвердил, что это работает для Visual Studio 2010 и Visual С# 2010 Express.

Ответ 10

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

У меня было два потока, работающих параллельно, поэтому я остановил один из них (или вы могли его непараллелизировать).

Затем я просмотрел свои функции и добавил как функции распечатки, такие как: Как только начинается функция:

Console.WriteLine("<Enter method: {0}", DebuggingHelper.GetCurrentMethod());

Перед возвратом функции:

Console.WriteLine(">Exit method: {0}", DebuggingHelper.GetCurrentMethod());

Где GetCurrentMethod определяется как:

[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCurrentMethod()
{
    StackTrace st = new StackTrace();
    StackFrame sf = st.GetFrame(1);
    return sf.GetMethod().Name;
}

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

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

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

Довольно просто и быстро найти, где это происходит.

Ответ 11

Найти методы, которые вызывают себя (или один метод вызывает другой и наоборот) и проверить их. Рекурсия обычно является главным подозреваемым, когда вы получаете исключения SO.