Стек вызовов исключений усекается без повторного выброса
У меня есть необычный случай, когда у меня есть очень простое Исключение, которое выдается и ловится тем же методом. Это не перебрасывается (обычная проблема наивных программистов). И все же его 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 в своем обработчике catch, и если я передаю объект 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 ответ
Когда генерируется исключение, только частичная трассировка стека будет использоваться в Exception.StackTrace
имущество. Стек показывает только вызовы до тех пор, пока метод не перехватит исключение. Чтобы получить полный стек (как вы уже заметили), вы должны создать new StackTrace()
объект.
На данный момент я не могу найти никаких ссылок на него, но я полагаю, что трассировка стека создается путем обхода стека при выдаче исключения. Как только исключение достигает блока catch, стек перестает компилироваться. Следовательно, вы получаете только частичный стек.
Как правило, блок catch касается не того, кто его вызвал, а того, откуда возникло исключение.