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

С# реконструировать исключение: как получить стек исключений в среде IDE?

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

Рассмотрим этот код:

   static void foo() {
        throw new Exception("boo!");
    }

    static void Main(string[] args) {
        try {
            foo();
        } catch (Exception x) {
            // do some stuff
            throw;
        }
    }

Исключительное исключение имеет правильную трассировку стека, отображающую foo() как источник исключения. Тем не менее, окно "Стек вызовов GUI" показывает только Main, тогда как я ожидал, что он покажет стек вызовов исключений, вплоть до foo.

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

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

Кстати, я получаю такое же поведение, если добавляю [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)], или если я изменю catch на просто catch (Exception).

Мой вопрос: учитывая, что в коде, который я использую rethrow, может кто-нибудь предложить удобный способ навигации по стеку вызовов, связанному с исключением? Я использую Visual Studio 2010.

4b9b3361

Ответ 1

Отладчик разбивается на throw в Main, потому что это исключение необработанно. По умолчанию отладчик будет прерывать только необработанные исключения. Когда вы остановились на Main, в исключении присутствует стек вызовов для исходного исключения из foo, но весь другой контекст был потерян (например, locals, состояние стека/памяти).

Похоже, вы хотите, чтобы отладчик разбился на throw в foo, поэтому вы должны сообщить отладчику о разрыве исключений с первым шансом:

  • Отладка "Исключения... (Ctrl + Alt + E)
  • Отметьте "Брошенный" для типов исключений, о которых вы заботитесь (в данном случае, исключения времени выполнения Commange Language).
  • Нажмите "ОК"
  • Начать отладку

В этом случае отладчик немедленно разрывается, когда foo выдает исключение. Теперь вы можете проверить стек, locals и т.д. В контексте исходного исключения. Если вы продолжите выполнение (F5), отладчик снова сломается при ретроне в Main.

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

Ответ 2

Если вы используете ReSharper, вы можете скопировать трассировку стека исключений в буфер обмена, а затем выберите в меню: ReSharper> Инструменты> Просмотр трассировки стека (Ctrl + E, T). Он покажет трассировку стека с кликабельными местоположениями, поэтому вы сможете быстро перемещаться.


(источник: jetbrains.com)

Эта функция также очень полезна при копании журналов пользователей (если в журнале регистрируются трассировки исключений).

Ответ 3

Не нужно перебрасывать, но здесь в блоге о том, как сохранить трассировку стека, по сути, сводится к следующему:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

...
catch (Exception ex)
{
  // do something
  // ...
  PreserveStackTrace(ex);
  throw;
}

Ответ 4

Майк Столл дал большое и простое решение для вашей проблемы:

Отметьте методы, в которых вы воссоздаете исключение с атрибутом [DebuggerNonUserCode]

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

(если следующий повтор также раздражает, отметьте его как [DebuggerNonUserCode], и т.д.)