стек вызовов исключений усекается без повторного выброса - PullRequest
16 голосов
/ 14 марта 2011

У меня есть необычный случай, когда у меня есть очень простое Исключение, которое выдается и ловится тем же методом. Не перебрасывается (обычный тип проблем, которые есть у наивных программистов). И все же его StackFrame содержит только один текущий метод. Вот как это выглядит:

   at (my class).MyMethod() in C:\(my file path and line)

На самом деле в стеке вызовов отладчика VS2010, вероятно, есть 30 методов, ведущих через полдюжины различных сборок. Кажется невозможным для всего этого быть оптимизированным. Более того, этот код встроен в режим отладки, без оптимизации , для .NET 4. У меня даже есть (на основе http://msdn.microsoft.com/en-us/library/9dd8z24x.aspx) .ini файлов (включая один с именем [app] .vshost.ini). ) в той же папке, содержащей:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0

Кроме того, вызовы методов не находятся в конце методов, поэтому оптимизация хвостовой рекурсии представляется маловероятной.

Относительно того, как он вызывается: нет никакого отражения в стеке вызовов, нет Invoke () или BeginInvoke () любого вида. Это просто длинная цепочка звонков от нажатия кнопки. Обработчик кликов составляет около 10 вызовов на стек вызовов. Ниже у вас есть обычный WndProc, NativeWindow.Callback, собственные / управляемые переходы и цикл сообщений. В конечном итоге это происходит внутри вызова ShowDialog (), который запускается из сборки C # EXE.

Теперь я обнаружил, что могу создавать экземпляры класса StackTrace в своем обработчике перехвата, и если я передаю объект Exception, стек вызовов также становится коротким. Если вместо этого я просто вызываю новый StackTrace () без аргументов, это приводит к полному стеку вызовов.

Я использовал Reflector при попытке отладки внутренних элементов класса Exception и создания его стека вызовов, но я не смог установить точки останова в Exception или в StackTrace. Я мог бы установить их в Environment.GetStackTrace (), и этот метод (который вызывает исключение), по-видимому, не вызывается во время процесса создания и создания, но я не знаю, действительно ли отладчик работает правильно. (Этот метод запускается для некоторых других вещей, поэтому я не уверен, что с ним делать.)

Вот выдержка из метода:

private void MyMethod()
{
    ...               
    try
    {
        throw new ApplicationException("Test failure");
    }
    catch (Exception e)
    {
        StackTrace stackTrace1 = new StackTrace(e);
        StackTrace stackTrace2 = new StackTrace(e, false);
        StackTrace stackTrace3 = new StackTrace(e, true);
        StackTrace stackTrace4 = new StackTrace();
        string STs = stackTrace1.ToString() + "\n---\n"
            + stackTrace2.ToString() + "\n---\n"
            + stackTrace3.ToString() + "\n---\n"
            + stackTrace4.ToString();
        Log(EventSeverity.Debug, STs);
        ...
        }
    }

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

Я получаю те же результаты либо в отладчике, либо в автономном режиме - стек вызовов из одной строки. И я знаю, что видел эту проблему в другом месте нашей базы кода. Ранее я предполагал, что это было связано с повторным вызовом исключений, но во многих случаях мы регистрируем его прямо в начальном блоке catch. Я совершенно сбит с толку, и весь поиск в Интернете, который я сделал, ничего не дал.


Это слишком много, чтобы добавить в качестве комментария к предоставленному ответу, но вот еще немного информации:

Теперь я вижу, что это поведение обсуждается в http://dotnetthoughts.wordpress.com/2007/10/27/where-did-my-exception-occur/ и что это на самом деле описано в http://msdn.microsoft.com/en-us/library/system.exception.stacktrace.aspx (хотя я думаю, что можно легко пропустить то, что они там говорят).

Так что я думаю, что мое "решение" будет немного хитом или промахом. У нас есть центральный метод, который мы обычно вызываем для форматирования исключений. Внутри этого метода я создам новый StackTrace () как с объектом Exception, так и без него. Затем я буду искать метод, который находится в нижней части трассировки стека Исключения, и отображать все, что ниже, в новом StackTrace (), указывая, что он был вызван этой серией вызовов.

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

1 Ответ

18 голосов
/ 14 марта 2011

При возникновении исключения в свойстве Exception.StackTrace будет использоваться только частичная трассировка стека. Стек показывает только вызовы до тех пор, пока метод не перехватит исключение. Чтобы получить полный стек (как вы уже заметили), вы должны создать объект new StackTrace().

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

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

...