Стек вызовов исключений усечен без какого-либо повторного броска
У меня необычный случай, когда у меня очень простое исключение, которое бросает и поймает тот же метод. Это не re-thrown (обычный тип проблем у наивных программистов). И все же его StackFrame содержит только один текущий метод. Вот как выглядит:
at (my class).MyMethod() in C:\(my file path and line)
В действительности существует, вероятно, 30 методов, ведущих к этому в стеке вызовов отладчика VS2010, проходя через полдюжины различных сборок. Кажется невозможным, чтобы все это было оптимизировано. Кроме того, этот код построен в режиме отладки без оптимизации, для .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(), который запускается из сборки С# EXE.
Теперь я обнаружил, что могу построить экземпляры класса StackTrace в моем обработчике catch, и если я передаю объект Exception, стек вызовов также будет коротким. Если вместо этого я просто вызываю новый StackTrace() без аргументов, он дает полный стек вызовов.
Ive использовал 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);
...
}
}
Это действительно очень просто: исключить Throw, уловить и зарегистрировать его.
Я получаю те же результаты либо в отладчике, либо при автономном выполнении - однострочный стек вызовов. И я знаю, что видел эту проблему в другом месте нашей базы кода. Раньше я предполагал, что это произошло из-за повторных бросков исключений, но во многих случаях он регистрируется прямо в исходном блоке catch. Im довольно сбит с толку, и все веб-поиски, которые я выполнил, ничего не производят.
Это слишком много, чтобы добавить комментарий к предоставленному ответу, но вот еще несколько сведений:
Теперь я вижу, что это поведение обсуждается на
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, так и без него. Затем я буду искать метод, который находится в нижней части трассировки стека Exception, и отобразить все ниже этого в новом StackTrace(), указав, что он был вызван этой серией вызовов.
С другой стороны, конечно, если этот метод не используется, информации там не будет. Но я должен был ожидать какого-то изменения кода где-то.
Ответы
Ответ 1
Когда генерируется исключение, в свойстве Exception.StackTrace
будет использоваться только частичная трассировка стека. Стек показывает только вызовы до тех пор, пока метод, который перехватывает исключение. Чтобы получить полный стек (как вы отметили), вы должны создать объект new StackTrace()
.
В настоящий момент я не могу найти ссылки на него, но я считаю, что трассировка стека строится, идя вверх по стеку, бросая исключение. Когда исключение достигает блока catch, стек перестает компилироваться. Поэтому вы получаете только частичный стек.
Как правило, блок catch не связан с тем, кто его вызвал, но откуда происходит исключение.