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

查看:32
本文介绍了如何正确读取/解释原始 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 在我猜是方法返回类型中)
                    • 如果第一部分是方法返回类型,为什么所有具有返回类型的方法都没有它?我有很多带有返回值的方法的堆栈跟踪,但它们没有相同的签名.
                      • 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.d__69.MoveNext()

                                这个比较简单.OnLogin 是异步方法,此类方法由编译器重写为状态机.该状态机实现了 IAsyncStateMachine 接口,该接口具有 MoveNext 方法.因此,您的异步方法基本上变成了该状态机的一系列 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.d__69 是生成的状态机类的名称.因为此状态机与 OnLogin 方法相关 - 它成为类型名称的一部分.d 是上面链接中的迭代器类".请注意,上面链接中的信息已有 7 年历史,并且在 asyncawait 实现之前,但我想状态机类似于迭代器(相同的 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 asyncawait 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.

                                更进一步,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 的意思是 Task 而类似 Tuple$2 的意思是 >元组.

                                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 点).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天全站免登陆