How to read/interpret a raw C# stack trace correctly?

前端 未结 1 539
北海茫月
北海茫月 2020-12-23 23:24

I\'m reading some crash reports from a UWP application (C#, compiled with .NET Native) and I\'m having a hard time understanding the exact syntax/format used in the stack tr

相关标签:
1条回答
  • 2020-12-23 23:38

    I bet Eric Lippert will come later and give a better answer, but in case that won't happen - here is my take, because I also got interested in this. The meaning of "d", "c" and similar symbols I got from this answer by Eric Lippert.

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

    This one is relatively simple. OnLogin is async method, and such methods are rewritten by compiler into a state machine. This state machine implements IAsyncStateMachine interface which has MoveNext method. So your async method basically becomes a sequence of MoveNext invocations of that state machine. That is why you see MoveNext() in stack trace.

    MyProject.ViewModels.SomeViewModel.<OnLogin>d__69 is the name of generated state machine class. Because this state machine is related to OnLogin method - it becomes part of type name. d is "iterator class" by the link above. Note that information from link above is 7 years old and is before async\await implementation, but I guess that state machine is similar to iterator (the same MoveNext method, same principle) - so "iterator class" looks fine. 69 is some unique number \ counter. I guess it's just counter, because if I compile dll with just two async methods - their state machines would be d__0 and d__1. It's not possible to deduce which part of async method has thrown based on this info.

    2) b is "anonymous method" (link above). I made some experiments and I think first index is related to the method in which anonymous method was used, and second index seems to be related to index of anonymous method inside that method in which they are used. For example suppose you use 2 anonymous methods in constructor and 2 anonymous methods in method Foo in the same class. Then:

    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) This looks quite complicated. First you ask "Why doesn't the second parameter (the token) show up in the signature". That's simple - because method in question represents anonymous method task => task.GetAwaiter().GetResult(), not your GetWatchedTask method. Now I was not able to reproduce your stack trace with this one, but still some info. First, System.__Canon is:

    Internal methodtable used to instantiate the "canonical" methodtable for generic instantiations. The name "__Canon" will never been seen by users but it will appear a lot in debugger stack traces involving generics so it is kept deliberately short as to avoid being a nuisance.

    Looks cryptic for me, but I guess it kind of represents your T in runtime. Then, <>c__3$1<System.__Canon> is <>c__3$1<T> and is a name of compiler generated class, where "c" is "anonymous method closure class" (from the link above). Such class is generated by compiler when you create a closure, so capture some external state in your anonymous method. What has been captured should be stored somewhere, and it is stored in such class.

    Going futher, <GetWatchedTask>b__3_0 is a method in that anonymous class above. It represents your task => task.GetAwaiter().GetResult() method. Everything from point 2 applies here as well.

    I don't know the meaning of $, maybe it represents number of type parameters. So maybe Task$1<System.__Canon> means Task<T> and something like Tuple$2<System.__Canon would mean Tuple<T1, T2>.

    4) That I unfortunately don't know and was not able to reproduce.

    5) c__DisplayClass142_3 is again closure class (see point 3). <LoadGroups>b__3() is anonymous method you used in method LoadGroups. So that indicates some anonymous method which is closure (captured external state) and which was called in LoadGroups method.

    0 讨论(0)
提交回复
热议问题