Как правильно читать / интерпретировать необработанную трассировку стека C#?
я читаю некоторые отчеты о сбоях из приложения UWP (C#, скомпилированного с .NET Native), и мне трудно понять точный синтаксис/формат, используемый в трассировках стека. Я попытался найти некоторые руководства в интернете, но я не придумал ничего полезного.
вот несколько примеров:
1)
MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext()
-
OnLogin
- это имя метода вSomeViewModel
, так почему же он внутри угловых скобок? Это"ClassName".<"MethodName>..."
обычный способ указать метод экземпляра? - я понимаю, что компилятор C# поворачивает каждый кусок кода между
await
вызывает анонимные методы и планирует их с помощью продолжения, поэтому я думаю, чтоd__69
указывает на асинхронное продолжение внутри текущего метода.- что означает "d"?
- эти числа случайны? Я имею в виду, что метод не имеет 69
await
звонки, поэтому я думаю, что эти номера не являются последовательными. Есть можно ли узнать точную часть исходного метода из этого числа в трассировке стека?
- что это
MoveNext()
метод в конце? Что это за тип?
2)
MyProject.UserControls.SomeControl.<.ctor>b__0_0
- я знаю, что
.ctor
означает конструктор объектов, и, глядя на код, я узнал, чтоb__0_0
означает анонимный обработчик событий, добавленный внутри конструктора, например это:SomeEvent += (s, e) => Foo();
.- что означает буква "Б"?
- почему есть два числа с подчеркиванием? Какой из них относится к индексу анонимного метода? Я имею в виду, что это первый (поэтому его индекс равен 0), но здесь есть два 0. Если бы это было второе,я бы ... --17-->,
1_0
или что-то еще?
3) у меня есть этот метод:
// Returns a Task that immediately throws when awaited, as soon as the token is cancelled
public static Task<T> GetWatchedTask<T>(this Task<T> awaitableTask, CancellationToken token)
{
return awaitableTask.ContinueWith(task => task.GetAwaiter().GetResult(), token);
}
и у меня есть этот стек Трейс:
MyProject.Helpers.Extensions.TasksExtensions.<>c__3<System.__Canon>.<GetWatchedTask>b__3_0($Task<__Canon> task)
- почему второй параметр (токен) не отображается в подписи?
- почему типа
$Task
написано с символом"$"? Это как какой-то индикатор заполнителя/земли (например, в регулярном выражении), чтобы его можно было использовать и в других местах? (Я имею в виду, я вижу это<System.__Canon>
в том, что я думаю, это тип возврата метода)- если эта первая часть является типом возврата метода, почему она не существует для всех методов, которые имеют тип возврата? У меня есть много трассировок стека с методом, которые возвращают значение, но у них нет той же подписи.
- что все это
.<>c__3<System.__Canon>
в смысле? Оти я думаю, что он будет
Task<T>
тип возврата, но что этоb__3_0
часть, так как у меня нет асинхронных вызовов или обработчиков событий? Означает ли это что-то другое в данном случае?
4)
Windows.UI.Xaml.Media.SolidColorBrush..ctor($Color color)
- почему начинается ли тип параметра с символа"$"? Что это значит?
5) у меня есть другой способ:
public static async Task<ObservableCollection<SomeCustomClass>> LoadItemGroups(String parentId)
и эта трассировка стека:
MyProject.SQLiteDatabase.SQLiteManager.<>c__DisplayClass142_3.<LoadGroups>b__3()
- что это
c__DisplayClass142_3
? Это способ указать возвращаемый тип с одним типом, а не с тремя отдельными классами (Task, ObservableCollection, SomeCustomClass)? - опять же, почему у меня
b__3
здесь, где в другой стек трассировки он использовал форматd_xxx
указать фрагмент асинхронного кода?
извините за многие вопросы, я надеюсь, что этот пост поможет другим программистам UWP c#.
заранее спасибо за вашу помощь!
редактировать: этот вопрос надо не считается дубликатом этот и другие вопросы потому что:
- он представляет различные случаи (методы конструктора, общие типы синтаксис и т. д..) вместо того, чтобы просто спрашивать о значении некоторых ключевых слов/символов по умолчанию, связанных с определенным типом переменных
- он конкретно спрашивает, как сравнить данную трассировку стека с оригинальной сигнатурой метода и шаги, которые нужно сделать для достижения этого
- он представляет различные примеры в разных контекстах, а не просто задает общий вопрос
- и, кстати, как "магические имена VS debugger" даже можно считать правильным вопросом титул? Как другой пользователь должен был найти этот вопрос при поиске символов трассировки стека C#?
1 ответов
держу пари, Эрик Липперт придет позже и даст лучший ответ, но в случае, если этого не произойдет - вот мой дубль, потому что я тоже заинтересовался этим. Значение "d", " c " и подобных символов я получил от этой ответ Эрика Липперта.
1) MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext()
это относительно просто. OnLogin
является асинхронным методом, и такие методы перезаписываются компилятором в государственную машину. Эта государственная машина реализует IAsyncStateMachine
интерфейс, который имеет MoveNext
метод. Таким образом, ваш асинхронный метод в основном становится последовательностью MoveNext
вызовы этой государственной машины. Вот почему вы видите MoveNext()
в стек трассировки.
MyProject.ViewModels.SomeViewModel.<OnLogin>d__69
- имя сгенерированного класса машины состояния. Потому что эта машина состояния связана с OnLogin
method-он становится частью имени типа. d
это "итератор" по ссылке выше. Обратите внимание, что информация из ссылки выше 7 лет и находится до реализации async\await, но я думаю, что государственная машина похожие на iterator (то же самое MoveNext
метод, тот же принцип) - так что "класс итератора" выглядит нормально. 69-это некоторое уникальное число \ счетчик. Я думаю, это просто счетчик, потому что если я скомпилирую dll только с двумя асинхронными методами - их государственные машины будут d__0
и d__1
. Невозможно вывести, какая часть асинхронного метода была брошена на основе этой информации.
2) b
является "анонимным методом" (ссылка выше). Я провел несколько экспериментов, и я думаю, что первый индекс связан с методом в котором использовался анонимный метод, а второй индекс, по-видимому, связан с индексом анонимного метода внутри того метода, в котором они используются. Например, предположим, что вы используете 2 анонимных метода в конструкторе и 2 анонимных метода в методе Foo
в том же классе. Затем:
public Test() {
Handler += (s, e) => Foo(); // this will be `b__0_0` because it's first in this method
Handler += (s, e) => Bar(); // this will be `b__0_1` because it's second
}
static void Foo() {
Action a = () => Console.WriteLine("test"); // this is `b__1_0`, 1 refers to it being in another method, not in constructor.
// if we use anonymous method in `Bar()` - it will have this index 2
a();
Action b = () => Console.WriteLine("test2"); // this is `b__1_1`
b();
}
3) это выглядит довольно сложным. Сначала вы спрашиваете: "почему второй параметр (токен) не отображается в подписи". Это просто - потому что метод, о котором идет речь, представляет анонимный метод task => task.GetAwaiter().GetResult()
, а не ваши GetWatchedTask
метод. Теперь я не смог воспроизвести трассировку стека с помощью этого, но все же некоторую информацию. Во-первых, System.__Canon
- это:
внутренняя methodtable используется для создания экземпляра " canonical" methodtable для общих экземпляров. Имя "_ _ Canon" никогда не будет замечено пользователями, но оно будет много отображаться в трассировках стека отладчика с участием дженериков, поэтому он намеренно короткий, чтобы избежать неприятностей.
выглядит загадочно для меня, но я думаю, что это своего рода представляет ваш T
в runtime. Затем:<>c__3<System.__Canon>
is <>c__3<T>
и является именем класса, сгенерированного компилятором, где " c "- это" класс закрытия анонимного метода " (по ссылке выше). Такой класс генерируется компилятором при создании закрытия, поэтому захватите некоторое внешнее состояние в анонимном методе. То, что было захвачено, должно храниться где-то, и оно хранится в таком классе.
Идем дальше, <GetWatchedTask>b__3_0
является методом в этом анонимном класс выше. Он представляет ваш task => task.GetAwaiter().GetResult()
метод. Все, что относится к пункту 2, применимо и здесь.
я не знаю смысла $
, возможно, он представляет количество параметров типа. Так что, может быть Task<System.__Canon>
означает Task<T>
и что-то вроде Tuple<System.__Canon
означает Tuple<T1, T2>
.
4) что я, к сожалению, не знаю и не смог воспроизвести.
5) c__DisplayClass142_3
снова класс закрытия (см. пункт 3). <LoadGroups>b__3()
анонимный метод используется в методе LoadGroups
. Так что это указывает на какой-то анонимный метод, который является закрытием (захваченным внешним состоянием) и который был вызван в LoadGroups
метод.