2017-03-25 56 views
7

我正在閱讀一些來自UWP應用程序(C#,使用.NET Native編譯)的崩潰報告,並且我很難理解堆棧跟蹤中使用的確切語法/格式。 我試過在互聯網上尋找一些指南,但我沒有拿出任何有用的東西。如何正確讀取/解釋原始C#堆棧跟蹤?

以下是幾個例子:

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();
    • 'b'代表什麼?
    • 爲什麼有兩個帶下劃線的數字?哪一個引用了匿名方法索引?我的意思是,這是第一個(所以它的索引是0),但這裏有兩個0。如果是第二,就我有0_11_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$1<System.__Canon>.<GetWatchedTask>b__3_0($Task$1<__Canon> task) 
  • 爲什麼沒有第二參數(tok en)出現在簽名中?
  • 爲什麼類型$Task$1用「$」字符寫的?它是否像某種佔位符/地面指示符(如在正則表達式中),以便它可以在其他地方使用? (我的意思是,我看到$1<System.__Canon>在我的猜測是該方法的返回類型)
    • 如果第一部分是方法的返回類型,爲什麼不是那裏所有的返回類型的方法呢?我有很多堆棧跟蹤方法,它們會返回一個值,但它們沒有相同的簽名。
  • .<>c__3$1<System.__Canon>是什麼意思?從$1和我想那將是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#程序員。

非常感謝您的幫助!

編輯:這個問題應該被視爲this other questions重複,因爲:

  • 它呈現出不同的情況下(構造方法,泛型類型語法等),而不是隻是在尋求意義與特定類型的變量相關的某些默認關鍵字/符號
  • 它具體詢問如何將給定的堆棧跟蹤與原始方法簽名進行比較,以及如何實現該步驟以實現該目的
  • 它在不同的上下文中展示了不同的例子,而不是僅僅提出一個普遍的問題
  • 順便說一下,「VS調試器魔術名稱」甚至被認爲是一個合適的問題標題?當尋找C#堆棧跟蹤符號的含義時,另一個用戶應該如何找到這個問題?

回答

5

我敢打賭埃裏克利珀以後還會來,給一個更好的答案,但是這不會發生的情況下 - 這是我拿的,因爲我也有興趣在此。 「d」,「c」和我從this得到的類似符號的含義由Eric Lippert解答。

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

這一個是相對簡單的。 OnLogin是異步方法,並且這種方法被編譯器重寫爲狀態機。這個狀態機實現IAsyncStateMachine接口,它有MoveNext方法。所以你的異步方法基本上成爲該狀態機的一個調用序列MoveNext。這就是爲什麼你在堆棧跟蹤中看到MoveNext()

MyProject.ViewModels.SomeViewModel.<OnLogin>d__69是生成的狀態機類的名稱。因爲這個狀態機與OnLogin方法有關 - 它成爲類型名稱的一部分。 d是上面鏈接的「迭代器類」。請注意,上面的鏈接信息是7歲,並在async \ await實現之前,但我想狀態機類似於迭代器(同樣的MoveNext方法,相同的原則) - 所以「迭代類」看起來很好。 69是一些獨特的號碼\計數器。我想這只是計數器,因爲如果我只用兩種異步方法編譯dll,它們的狀態機將是d__0d__1。根據這個信息推導異步方法的哪一部分是不可能的。

2)b是「匿名方法」(上面的鏈接)。我做了一些實驗,我認爲第一個索引與使用匿名方法的方法有關,第二個索引似乎與使用它們的方法中的匿名方法的索引相關。例如,假設您在同一個類中使用方法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是:

內部方法表用於實例「規範」 方法表類屬實例。 用戶永遠不會看到「__Canon」這個名稱,但它會在涉及泛型的調試器堆棧跟蹤 中出現很多,所以它被故意縮短以避免造成滋擾。

看上去很神祕,我,但我想它那種代表了運行時的T。然後,<>c__3$1<System.__Canon><>c__3$1<T>,它是編譯器生成的類的名稱,其中「c」是「匿名方法閉包類」(來自上面的鏈接)。這樣的類是由編譯器在創建閉包時生成的,因此可以在匿名方法中捕獲一些外部狀態。應該將捕獲的內容存儲在某個地方,並將其存儲在該類中。

進一步,<GetWatchedTask>b__3_0是在上面的匿名類的方法。它代表您的task => task.GetAwaiter().GetResult()方法。點2的所有內容也適用於此處。

我不知道$的含義,也許它代表了類型參數的個數。所以也許Task$1<System.__Canon>意味着Task<T>和類似Tuple$2<System.__Canon意味着Tuple<T1, T2>

4)那我不知道,也無法重現。

5)c__DisplayClass142_3又是封閉類(見第3點)。 <LoadGroups>b__3()是您在方法LoadGroups中使用的匿名方法。因此,這表明一些匿名方法是關閉(捕獲外部狀態),並在LoadGroups方法中調用。

+0

謝謝你,看起來對我來說很棒!仍然期待着Eric Lippert的另一個答案,因爲它可以增加更多的細節。 – Sergio0694

+0

@Sergio0694希望你至少知道堆棧跟蹤中提到的方法是什麼。順便說一句,你有一個代碼來重現stacktrace中的$符號嗎?特別是在你的第4點。 – Evk