如何正确读取/解释原始C#堆栈跟踪? [英] How to read/interpret a raw C# stack trace correctly?

查看:112
本文介绍了如何正确读取/解释原始C#堆栈跟踪?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从UWP应用程序(C#,使用.NET Native编译)中读取一些崩溃报告,并且我很难理解堆栈跟踪中使用的确切语法/格式. 我尝试在互联网上寻找一些指南,但没有提出任何有用的建议.

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 traces. I tried looking for some guides on the internet but I didn't come up with anything useful.

以下是一些示例:

1)

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

  • OnLoginSomeViewModel中方法的名称,那么为什么它放在尖括号中? "ClassName".<"MethodName>..."是指示实例方法的常用方法吗?
  • 我知道C#编译器将await调用之间的所有代码块都转换为匿名方法,并使用延续来计划它们,因此我想d__69指示当前方法内部的异步延续.
    • "d"代表什么?
    • 这些数字是随机的吗?我的意思是,该方法没有69个await调用,所以我猜这些数字不是连续的.是否可以从堆栈跟踪中的该编号中找出原始方法中的确切部分?
      • OnLogin is the name of a method in SomeViewModel, so why is it inside angular brackets? Is the "ClassName".<"MethodName>..." the usual way to indicate an instance method?
      • I understand that the C# compiler turns every chunk of code between await calls into anonymous methods and schedules them using continuations, so I guess that d__69 indicates an async continuation inside the current method.
        • What does the 'd' stand for?
        • Are those numbers random? I mean, the method doesn't have 69 await calls, so I guess those numbers aren't sequential. Is it possible to find out the exact part in the original method from that number in the stack trace?
        • 2)

          MyProject.UserControls.SomeControl.<.ctor>b__0_0
          

          • 我知道.ctor代表对象构造函数,然后查看代码,我发现b__0_0代表在构造函数内部添加的匿名事件处理程序,如下所示:SomeEvent += (s, e) => Foo();.
            • "b"代表什么?
            • 为什么有两个带下划线的数字?其中哪一个引用了匿名方法索引?我的意思是,它是第一个(因此其索引为0),但是这里有两个0.如果是第二个,我会使用0_11_0还是其他?
              • I know that .ctor stands for the object constructor, and looking at the code I found out that b__0_0 stands for an anonymous event handler added inside the constructor, like this: SomeEvent += (s, e) => Foo();.
                • What does the 'b' stand for?
                • Why are there two numbers with an underscore? Which one of them refers to the anonymous method index? I mean, it's the first one (so its index is 0) but there are two 0s here. If it was the second, would I have had 0_1, 1_0 or something else?
                • 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$1<System.__Canon>.<GetWatchedTask>b__3_0($Task$1<__Canon> task)
                  

                  • 为什么第二个参数(令牌)没有显示在签名中?
                  • 为什么类型$Task$1用'$'字符书写?就像某种占位符/地面指示器(例如在正则表达式中)一样,以便它也可以在其他地方使用吗? (我的意思是,我认为$1<System.__Canon>是我的方法返回类型)
                    • 如果第一部分是方法返回类型,为什么没有所有具有返回类型的方法呢?我有很多带有方法的堆栈跟踪,它们确实返回一个值,但是它们没有相同的签名.
                      • Why doesn't the second parameter (the token) show up in the signature?
                      • Why is the type $Task$1 written with the '$' character? Is it like some sort of placeholder/ground indicator (like in a regex) so that it can be used in other places too? (I mean, I see that $1<System.__Canon> in what I guess is the method return type)
                        • If that first part is the method return type, why isn't it there for all the methods that have a return type? I have lots of stack traces with method that do return a value, but they don't have that same signature.
                        • 4)

                          Windows.UI.Xaml.Media.SolidColorBrush..ctor($Color color)
                          

                          • 为什么参数类型以"$"字符开头?它代表什么?
                          • 5),我还有另一种方法:

                            5) I have this other method:

                            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格式表示异步代码块?
                              • What's that c__DisplayClass142_3? Is that a way to indicate the return type with a single type rather than the three separate classes (Task, ObservableCollection, SomeCustomClass)?
                              • Again, why do I have b__3 here, where in other stack traces it used the format d_xxx to indicate an async code chunk?
                              • 很抱歉有很多问题,我希望这篇文章也能对其他UWP C#程序员有所帮助.

                                Sorry for the many questions, I hope this post will help other UWP C# programmers too.

                                预先感谢您的帮助!

                                编辑:不应将此问题视为

                                EDIT: this question should not be considered a duplicate of this other questions because:

                                • 它呈现不同的情况(构造方法,泛型语法等),而不仅仅是询问与某种类型的变量相关的一些默认关键字/符号的含义
                                • 它专门询问如何将给定的堆栈跟踪与原始方法签名进行比较,以及实现该签名所需执行的步骤
                                • 它在不同的上下文中提供了不同的示例,而不仅仅是提出一个普遍的问题
                                • 顺便说一句,"VS调试器的魔术名称"如何甚至被认为是适当的问题标题?在寻找C#堆栈跟踪符号含义时,另一个用户应该如何找到这个问题?

                                推荐答案

                                我敢打赌,埃里克·利珀特(Eric Lippert)会稍后再提出更好的答案,但万一不会发生-这是我的看法,因为我也对此感兴趣这.我从埃里克·利珀特(Eric Lippert)的答案中获得的"d","c"和类似符号的含义.

                                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()

                                这是相对简单的. OnLogin是异步方法,并且编译器将这些方法重写为状态机.此状态机实现具有MoveNext方法的IAsyncStateMachine接口.因此,您的异步方法基本上成为该状态机的MoveNext调用序列.这就是为什么您在堆栈跟踪中看到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是生成的状态机类的名称.因为此状态机与OnLogin方法相关-它成为类型名称的一部分.通过上面的链接,d是迭代器类".请注意,来自上面链接的信息已有7年之久,并且早于async \ await实现,但是我想状态机类似于迭代器(相同的MoveNext方法,相同的原理)-因此迭代器类"看起来不错. 69是一些唯一的数字\计数器.我想这只是反驳,因为如果我只用两种异步方法编译dll,它们的状态机将是d__0d__1.无法根据此信息推断出异步方法的哪一部分.

                                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是匿名方法"(上面的链接).我做了一些实验,我认为第一个索引与使用匿名方法的方法有关,而第二个索引似乎与使用匿名方法的内部方法中的匿名方法的索引有关.例如,假设您在同一类中的构造函数中使用了2个匿名方法,在方法Foo中使用了2个匿名方法.然后:

                                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)这看起来很复杂.首先,您问为什么第二个参数(令牌)未显示在签名中".这很简单-因为所讨论的方法表示匿名方法task => task.GetAwaiter().GetResult(),而不是您的GetWatchedTask方法.现在,我无法使用此代码来重现您的堆栈跟踪,但仍然可以获取一些信息.首先,System.__Canon是:

                                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:

                                用于实例化规范"的内部方法表 通用实例化的方法表. 用户永远不会看到名称"__Canon",但在调试器堆栈跟踪中会出现很多 涉及泛型,因此请尽量避免使用泛型.

                                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.

                                为我看似神秘,但我想它在运行时代表了您的T.然后,<>c__3$1<System.__Canon><>c__3$1<T>,并且是编译器生成的类的名称,其中"c"是匿名方法闭包类"(来自上面的链接).当您创建闭包时,此类由编译器生成,因此请在您的匿名方法中捕获某些外部状态.已捕获的内容应存储在某个位置,并存储在此类中.

                                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.

                                更进一步,<GetWatchedTask>b__3_0是上述匿名类中的一种方法.它代表您的task => task.GetAwaiter().GetResult()方法.从第2点开始的所有内容也都适用于此.

                                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.

                                我不知道$的含义,也许它表示类型参数的数量.因此,也许Task$1<System.__Canon>表示Task<T>,而类似Tuple$2<System.__Canon的意思就是Tuple<T1, T2>.

                                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)很遗憾,我不知道并且无法复制.

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

                                5)c__DisplayClass142_3还是闭包类(请参见第3点). <LoadGroups>b__3()是您在方法LoadGroups中使用的匿名方法.因此,这表明了一个匿名方法,它是闭包(捕获的外部状态),在LoadGroups方法中被调用.

                                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.

                                这篇关于如何正确读取/解释原始C#堆栈跟踪?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆