2017-03-25 40 views
7

Czytam niektóre raporty o awariach z aplikacji UWP (C#, skompilowanej z .NET Native) i ciężko jest mi zrozumieć dokładną składnię/format używany w śladach stosu. Próbowałem szukać przewodników w Internecie, ale nie wymyśliłem niczego przydatnego.Jak poprawnie odczytać/zinterpretować nieprzetworzony ślad stosu C#?

Oto kilka przykładów:

1)

MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext() 
  • OnLogin to nazwa metody w SomeViewModel, więc dlaczego jest w nawiasach kątowych? Czy "ClassName".<"MethodName>..." jest zwykłym sposobem wskazania metody instancji?
  • Rozumiem, że kompilator C# zamienia każdą porcję kodu między wywołania await w anonimowe metody i planuje je za pomocą kontynuacji, więc domyślam się, że d__69 wskazuje kontynuację asynchroniczną wewnątrz bieżącej metody.
    • Co oznacza skrót "d"?
    • Czy te liczby są przypadkowe? Mam na myśli, że metoda nie ma 69 await połączeń, więc domyślam się, że te liczby nie są sekwencyjne. Czy możliwe jest znalezienie dokładnej części w oryginalnej metodzie z tej liczby w zapisie stosu?
  • Co to jest ta metoda MoveNext() na końcu? Jakiego rodzaju jest on wezwany?

2)

MyProject.UserControls.SomeControl.<.ctor>b__0_0 
  • wiem, że .ctor oznacza konstruktora obiektu, a patrząc na kod okazało się, że b__0_0 oznacza anonimowego obsługi zdarzeń dodana wewnątrz konstruktor, taki jak ten: SomeEvent += (s, e) => Foo();.
    • Co oznacza skrót "b"?
    • Dlaczego są dwa numery z podkreślnikiem? Który z nich odnosi się do anonimowego indeksu metody? Mam na myśli, że jest pierwszy (więc jego indeks wynosi 0), ale są tu dwa 0. Gdyby to był drugi, czy miałbym 0_1, 1_0 lub coś innego?

3) mam tej metody:

// 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); 
} 

I mam ten ślad stosu:

MyProject.Helpers.Extensions.TasksExtensions.<>c__3$1<System.__Canon>.<GetWatchedTask>b__3_0($Task$1<__Canon> task) 
  • Dlaczego nie robi drugi parametr (tok pl) pojawi się w podpisie?
  • Dlaczego typ $Task$1 jest zapisany za pomocą znaku "$"?Czy jest to jak jakiś wskaźnik zastępczy/grunt (jak w regex), aby można go było używać również w innych miejscach? (Mam na myśli, widzę, że $1<System.__Canon> w co myślę, jest metoda typ zwracany)
    • Jeśli to pierwsza część jest metoda typ zwracany, dlaczego nie jest tam dla wszystkich metod, które mają typ zwracany? Mam wiele śledzenia stosu z metodą zwracającą wartość, ale nie mają tego samego podpisu.
  • Co to wszystko oznacza .<>c__3$1<System.__Canon>? Od $1 i prawdopodobnie to byłby typ zwracany Task<T>, ale co to jest za część b__3_0, ponieważ nie mam wywołań asynchronicznych ani procedur obsługi zdarzeń? Czy to w tym przypadku oznacza coś innego?

4)

Windows.UI.Xaml.Media.SolidColorBrush..ctor($Color color) 
  • Dlaczego typ parametru zaczynają się od znaku '$'? Co to oznacza?

5) mam ten drugi sposób:

public static async Task<ObservableCollection<SomeCustomClass>> LoadItemGroups(String parentId) 

i ten ślad stosu:

MyProject.SQLiteDatabase.SQLiteManager.<>c__DisplayClass142_3.<LoadGroups>b__3() 
  • co to c__DisplayClass142_3? Czy jest to sposób wskazania typu zwracanego z pojedynczym typem zamiast z trzech oddzielnych klas (Task, ObservableCollection, SomeCustomClass)?
  • Ponownie, dlaczego mam tutaj b__3, gdzie w innych śladach stosu użył formatu d_xxx do wskazania fragmentu kodu asynchronicznego?

Przepraszamy za wiele pytań, mam nadzieję, że ten post pomoże także innym programistom UWP C#.

Z góry dziękujemy za pomoc!

EDIT: kwestia ta powinna nie uznać duplikat this other questions ponieważ:

  • Przedstawia różne przypadki (metody konstruktora typy generyczne składnia itp ..), a nie tylko z prośbą o znaczeniu niektórych domyślnych słów kluczowych/symboli związanych z pewnym typem zmiennych.
  • W szczególności prosi o porównanie danego śladu stosu z oryginalnym podpisem metody oraz kroki, które należy wykonać, aby osiągnąć ten cel:
  • Przedstawia różne przykłady w różnych kontekstach, zamiast po prostu zadawać ogólne pytanie:
  • A przy okazji, w jaki sposób "magiczne nazwy VS debuggera" można nawet uznać za odpowiedni tytuł pytania? W jaki sposób inny użytkownik powinien znaleźć to pytanie, szukając znaczników śladów stosu C#?

Odpowiedz

5

Założę się, że Eric Lippert przyjdzie później i da lepszą odpowiedź, ale na wypadek, gdyby tak się nie stało - oto moje zdanie, bo też się tym zainteresowałem. Znaczenie "d", "c" i podobnych symboli otrzymałem od Eric Lipperta.

1) MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext()

Ta stosunkowo prosta. OnLogin jest metodą asynchroniczną i takie metody są przepisywane przez kompilator na maszynę stanów. Ten automat stanów implementuje interfejs IAsyncStateMachine, który ma metodę MoveNext. Tak więc twoja metoda asynchroniczna staje się w zasadzie ciągiem inwokacji tego automatu stanów. Dlatego widzisz MoveNext() w śladzie stosu.

MyProject.ViewModels.SomeViewModel.<OnLogin>d__69 to nazwa wygenerowanej klasy maszyn stanów. Ponieważ ten automat stanów jest powiązany z metodą OnLogin - staje się częścią nazwy typu. d jest "klasą iteratora" przez powyższy link. Zauważ, że informacje z powyższego łącza mają 7 lat i są przed implementacją async \ await, ale domyślam się, że maszyna stanu jest podobna do iteratora (ta sama metoda MoveNext) - więc "klasa iteratora" wygląda dobrze. 69 to jakiś unikalny numer \ licznik. Sądzę, że jest to po prostu sprzeczne, ponieważ jeśli skompiluję bibliotekę dll tylko z dwiema asynchronicznymi metodami, ich maszyny stanowe będą d__0 i d__1. Nie można wywnioskować, która część metody asynchronicznej została rzucona w oparciu o te informacje.

2) b to "metoda anonimowa" (powyższy link). Zrobiłem kilka eksperymentów i myślę, że pierwszy indeks jest związany z metodą, w której użyto anonimowej metody, a drugi indeks wydaje się być powiązany z indeksem anonimowej metody wewnątrz tej metody, w której są używane. Załóżmy na przykład, że używasz 2 metod anonimowych w konstruktorze i 2 metod anonimowych w metodzie Foo w tej samej klasie. Następnie:

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) To wygląda dość skomplikowane. Najpierw pytasz "Dlaczego drugi parametr (token) nie pojawia się w podpisie". To proste - ponieważ omawiana metoda reprezentuje anonimową metodę task => task.GetAwaiter().GetResult(), a nie metodę GetWatchedTask. Teraz nie byłem w stanie odtworzyć twojego śladu stosu tym, ale wciąż trochę informacji. Po pierwsze, System.__Canon jest:

methodtable wewnętrzne stosowane do instancji "canonical" methodtable dla dawałaby generycznych. Nazwa "__Canon" nigdy nie będzie widoczna dla użytkowników, ale w śladach stosu debugera pojawi się dużo, ponieważ jest ona celowo krótka, aby uniknąć uciążliwości.

Wygląda dla mnie tajemniczo, ale myślę, że to trochę reprezentuje twój T w środowisku wykonawczym. Następnie, <>c__3$1<System.__Canon> jest <>c__3$1<T> i jest nazwą klasy generowanej przez kompilator, gdzie "c" jest "anonimową klasą zamknięcia metody" (z powyższego linku). Klasa ta jest generowana przez kompilator podczas tworzenia zamknięcia, więc przechwyć jakiś stan zewnętrzny w swojej anonimowej metodzie. To, co zostało schwytane, powinno być gdzieś przechowywane i przechowywane w takiej klasie.

Going futher, <GetWatchedTask>b__3_0 to metoda w powyższej anonimowej klasie. Reprezentuje on Twoją metodę task => task.GetAwaiter().GetResult(). Obowiązuje tu także wszystko od punktu 2.

Nie rozumiem znaczenia $, może to oznacza liczbę parametrów typu.Więc może Task$1<System.__Canon> oznacza Task<T> i coś w rodzaju Tuple$2<System.__Canon oznaczałoby Tuple<T1, T2>.

4) Że niestety nie wiem i nie mogłem się rozmnażać.

5) c__DisplayClass142_3 jest ponownie klasą zamknięcia (patrz punkt 3). <LoadGroups>b__3() to anonimowa metoda użyta w metodzie LoadGroups. To oznacza anonimową metodę, która jest zamknięta (przechwycony stan zewnętrzny) i która została wywołana w metodzie LoadGroups.

+0

Dzięki człowieku, wygląda mi to na wspaniałą odpowiedź! Z niecierpliwością czekam na kolejną odpowiedź Ericka Lipperta, ponieważ na pewno może dodać więcej szczegółów. – Sergio0694

+0

@ Sergio0694 Mam nadzieję, że wiesz co najmniej może dowiedzieć się, co dokładnie jest wspomniana metoda w ślad stosu. Przy okazji, czy masz kod do odtworzenia tych znaków $ w stosie stacktrace? Zwłaszcza w twoim czwartym punkcie. – Evk