web-dev-qa-db-ja.com

生のC#スタックトレースを正しく読み取って解釈するにはどうすればよいですか?

UWPアプリケーション(.NET NativeでコンパイルされたC#)からいくつかのクラッシュレポートを読んでいますが、スタックトレースで使用されている正確な構文/形式を理解するのに苦労しています。インターネットでいくつかのガイドを探してみましたが、便利なものは思いつきませんでした。

以下にいくつかの例を示します。

1)

_MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext()
_
  • OnLoginSomeViewModel内のメソッドの名前なので、なぜangular括弧内にあるのですか?_"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」は何の略ですか?
    • 下線付きの2つの数値があるのはなぜですか?匿名メソッドインデックスを参照するのはどれですか。つまり、それは最初のものです(つまり、インデックスは0です)が、ここには2つの0があります。それが2番目の場合、_0_1_、_1_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)
_
  • 2番目のパラメーター(トークン)が署名に表示されないのはなぜですか?
  • タイプ_$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_は何ですか?これは、3つの個別のクラス(Task、ObservableCollection、SomeCustomClass)ではなく、単一の型で戻り値の型を示す方法ですか?
  • 繰り返しますが、ここに_b__3_があるのはなぜですか。他のスタックトレースでは、非同期コードチャンクを示すために_d_xxx_という形式を使用していますか?

多くの質問で申し訳ありませんが、この投稿が他のUWP C#プログラマーにも役立つことを願っています。

よろしくお願いします!

[〜#〜] edit [〜#〜]:この質問は重複していると見なされるべきではありませんこの他の質問 の理由:

  • 特定のタイプの変数に関連するいくつかのデフォルトのキーワード/シンボルの意味を尋ねるだけでなく、さまざまなケース(コンストラクターメソッド、ジェネリックタイプの構文など)を提示します。
  • 具体的には、特定のスタックトレースを元のメソッドシグネチャと比較する方法と、それを達成するために実行する手順を尋ねます
  • 一般的な質問をするだけでなく、さまざまな状況でさまざまな例を示します
  • ところで、「VSデバッガマジックネーム」を適切な質問タイトルと見なすにはどうすればよいでしょうか。 C#のスタックトレースシンボルの意味を探すときに、別のユーザーがその質問をどのようにして見つけたのでしょうか。
26
Sergio0694

私は後でエリック・リッペルトが来てより良い答えを出すと思いますが、それが起こらない場合に備えて-私もこれに興味があります。私が得た「d」、「c」、および類似の記号の意味 this エリック・リッパーによる回答。

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は、いくつかの一意の数\カウンターです。 2つの非同期メソッドだけでdllをコンパイルすると、それらのステートマシンは_d__0_および_d__1_になるため、それは単なるカウンターだと思います。この情報に基づいて、非同期メソッドのどの部分がスローされたかを推測することはできません。

2)bは「匿名メソッド」です(上記のリンク)。私はいくつかの実験を行いましたが、最初のインデックスは匿名メソッドが使用されたメソッドに関連しており、2番目のインデックスはそれらが使用されたメソッド内の匿名メソッドのインデックスに関連しているようです。たとえば、同じクラスのコンストラクターで2つの匿名メソッドを使用し、メソッドFooで2つの匿名メソッドを使用するとします。次に:

_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)これはかなり複雑に見えます。まず、「2番目のパラメーター(トークン)が署名に表示されないのはなぜですか」と質問します。これは簡単です。問題のメソッドは、GetWatchedTaskメソッドではなく、匿名メソッドtask => task.GetAwaiter().GetResult()を表すためです。今、私はこれであなたのスタックトレースを再現できませんでしたが、それでもいくつかの情報があります。まず、_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メソッドで呼び出された匿名メソッドを示しています。

13
Evk