まず、この質問が長く続いたことをお詫び申し上げます。
私はIronSchemeの作者です。最近私は「ネイティブ」の.NETデバッガーを使用できるように、まともなデバッグ情報を出力するように努力しています。
これは部分的に成功していますが、歯が生える問題に直面しています。
最初の問題はステッピングに関連しています。
Schemeが式言語であるため、ステートメント(または行)ベースのように見える主要な.NET言語とは異なり、すべてが括弧で囲まれる傾向があります。
元のコード(Scheme)は次のようになります。
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
私はわざとそれぞれの表現を改行でレイアウトしています。
生成されたコードは(ILSpyを介して)C#に変換されます。
public static object ::baz(object x)
{
if (x == null)
{
return x;
}
if (x is Cons)
{
return Builtins.Car(x);
}
return #.ironscheme.exceptions::assertion-violation+(
RuntimeHelpers.False, "nooo", Builtins.List(x));
}
ご覧のとおり、非常に単純です。
注:コードがC#で条件式(?:)に変換された場合、全体は1つのデバッグステップにすぎないことに注意してください。
これは、ソース番号と行番号を含むIL出力です。
.method public static object '::baz'(object x) cil managed
{
// Code size 56 (0x38)
.maxstack 6
.line 15,15 : 1,2 ''
//000014:
//000015: (define (baz x)
IL_0000: nop
.line 17,17 : 6,15 ''
//000016: (cond
//000017: [(null? x)
IL_0001: ldarg.0
IL_0002: brtrue IL_0009
.line 18,18 : 7,8 ''
//000018: x]
IL_0007: ldarg.0
IL_0008: ret
.line 19,19 : 6,15 ''
//000019: [(pair? x)
.line 19,19 : 6,15 ''
IL_0009: ldarg.0
IL_000a: isinst [IronScheme]IronScheme.Runtime.Cons
IL_000f: ldnull
IL_0010: cgt.un
IL_0012: brfalse IL_0020
IL_0017: ldarg.0
.line 20,20 : 7,14 ''
//000020: (car x)]
IL_0018: tail.
IL_001a: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_001f: ret
IL_0020: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_0025: ldstr "nooo"
IL_002a: ldarg.0
IL_002b: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
//000021: [else
//000022: (assertion-violation #f "nooo" x)]))
IL_0030: tail.
IL_0032: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_0037: ret
} // end of method 'eval-core(033)'::'::baz'
注:デバッガーがメソッド全体を単に強調表示しないようにするために、メソッドのエントリポイントの幅を1列だけにします。
ご覧のとおり、各式は行に正しくマッピングされています。
ステッピングの問題(VS2010でテスト済みですが、VS2008でも同じ/類似の問題):
これらはIgnoreSymbolStoreSequencePoints
適用されていません。
- null引数を指定してbazを呼び出すと、正しく機能します。(null?x)の後にxが続きます。
- Cons argでbazを呼び出します。正しく機能します。(null?x)それから(pair?x)それから(car x)
- 他の引数でbazを呼び出すと失敗します。(null?x)それから(pair?x)それから(car x)そして(assertion-violation ...)
申請時IgnoreSymbolStoreSequencePoints
(推奨):
- null引数を指定してbazを呼び出すと、正しく機能します。(null?x)の後にxが続きます。
- Cons argでbazを呼び出すと失敗します。(null?x)それから(pair?x)。
- 他の引数でbazを呼び出すと失敗します。(null?x)それから(pair?x)それから(car x)そして(assertion-violation ...)
また、このモードでは、一部の線(ここには表示されていません)が誤って強調表示されており、1つずれています。
原因となる可能性のあるアイデアを以下に示します。
- Tailcallsがデバッガーを混乱させる
- 重複する場所(ここには表示されていません)は、デバッガーを混乱させます(ブレークポイントを設定するときに非常にうまく機能します)
- ????
2番目の、しかし深刻な問題は、デバッガーがブレークポイントのブレーク/ヒットに失敗することです。
デバッガーを正しく(そして一貫して)ブレークさせる唯一の場所は、メソッドのエントリポイントです。
をIgnoreSymbolStoreSequencePoints
適用しない場合、状況は少し良くなります。
結論
それはVSデバッガが単なるバグがあるかもしれません:(
参照:
更新1:
Mdbgは64ビットアセンブリでは機能しません。これで終わりです。テストする32ビットマシンはもうありません。更新:これは大きな問題ではないと確信していますが、誰か修正はありますか?編集:はい、ばかげた私、x64コマンドプロンプトでmdbgを起動してください:)
アップデート2:
C#アプリを作成し、ライン情報を分析しようとしました。
私の発見:
brXXX
命令の後にシーケンスポイントを設定する必要があります(有効でない場合は「#line hidden」とも呼ばれ、を発行しますnop
)。brXXX
命令の前に、 '#line hidden'とを発行しnop
ます。
これを適用しても、問題は修正されません(単独?)。
しかし、以下を追加すると、望ましい結果が得られます:)
- の後
ret
、「#line hidden」とを発行しnop
ます。
これはIgnoreSymbolStoreSequencePoints
適用されないモードを使用しています。適用しても、一部の手順はスキップされます:(
上記が適用された場合のIL出力は次のとおりです。
.method public static object '::baz'(object x) cil managed
{
// Code size 63 (0x3f)
.maxstack 6
.line 15,15 : 1,2 ''
IL_0000: nop
.line 17,17 : 6,15 ''
IL_0001: ldarg.0
.line 16707566,16707566 : 0,0 ''
IL_0002: nop
IL_0003: brtrue IL_000c
.line 16707566,16707566 : 0,0 ''
IL_0008: nop
.line 18,18 : 7,8 ''
IL_0009: ldarg.0
IL_000a: ret
.line 16707566,16707566 : 0,0 ''
IL_000b: nop
.line 19,19 : 6,15 ''
.line 19,19 : 6,15 ''
IL_000c: ldarg.0
IL_000d: isinst [IronScheme]IronScheme.Runtime.Cons
IL_0012: ldnull
IL_0013: cgt.un
.line 16707566,16707566 : 0,0 ''
IL_0015: nop
IL_0016: brfalse IL_0026
.line 16707566,16707566 : 0,0 ''
IL_001b: nop
IL_001c: ldarg.0
.line 20,20 : 7,14 ''
IL_001d: tail.
IL_001f: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_0024: ret
.line 16707566,16707566 : 0,0 ''
IL_0025: nop
IL_0026: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_002b: ldstr "nooo"
IL_0030: ldarg.0
IL_0031: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
IL_0036: tail.
IL_0038: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_003d: ret
.line 16707566,16707566 : 0,0 ''
IL_003e: nop
} // end of method 'eval-core(033)'::'::baz'
更新3:
上記の「半修正」の問題。するPEverifyが原因にすべてのメソッドにエラーを報告nop
した後ret
。私は本当に問題を理解していません。どのようにしてnop
検証を中断できますかret
。それはデッドコードのようなものです(コードでさえないことを除いて)...まあ、実験は続いています。
アップデート4:
家に戻って、「検証できない」コードを削除し、VS2008で実行すると、事態はさらに悪化します。おそらく、適切なデバッグのために検証不可能なコードを実行することが答えになるかもしれません。「リリース」モードでも、すべての出力は引き続き検証可能です。
アップデート5:
私は今、上記のアイデアが今のところ唯一の実行可能なオプションであると判断しました。生成されたコードは検証できませんが、まだ何も見つかりませんVerificationException
。このシナリオでは、エンドユーザーにどのような影響があるかわかりません。
おまけとして、私の2番目の問題も解決されました。:)
ここでは少しあるスクリーンキャスト私がなってしまったかのは。ブレークポイントに到達し、適切なステップイン(イン/アウト/オーバー)などを行います。全体として、望ましい効果です。
しかし、私はそれを行う方法としてこれをまだ受け入れていません。それは私に過度にハックを感じます。本当の問題を確認していただければ幸いです。
アップデート6:
VS2010でコードをテストするための変更があっただけで、いくつかの問題があるようです:
最初の呼び出しが正しく行われなくなりました。(アサーション違反...)がヒットしました。他のケースは正常に動作します。いくつかの古いコードは不必要な位置を出しました。コードを削除し、期待どおりに動作しました。:)- さらに深刻なのは、プログラムの2回目の呼び出しでブレークポイントが失敗することです(メモリ内コンパイルを使用すると、アセンブリをファイルにダンプすると、ブレークポイントが再び満足するようになります)。
これらのケースはどちらもVS2008では正しく機能します。主な違いは、VS2010ではアプリケーション全体が.NET 4用にコンパイルされ、VS2008では.NET 2にコンパイルされることです。どちらも64ビットで実行されます。
アップデート7:
前述のように、64ビットでmdbgを実行しました。残念ながら、プログラムを再実行するとブレークに失敗するというブレークポイントの問題もあります(これは、プログラムが再コンパイルされるため、同じアセンブリを使用せず、同じソースを使用していることを意味します)。
アップデート8:
私がしているバグを提出したブレークポイントの問題に関してMS Connectサイトで。
更新:修正済み
アップデート9:
長い間考えた結果、デバッガーを幸せにする唯一の方法はSSAを実行することです。そのため、すべてのステップを分離して順次実行できます。私はまだこの考えを証明していません。しかし、それは理にかなっているようです。明らかに、SSAからtempをクリーンアップするとデバッグが中断しますが、これは簡単に切り替えることができ、それらを残してもオーバーヘッドはあまりありません。
nop
sがないと、ステッピングは失敗します(念のため、もう一度確認します)。それは私がしなければならないと思う犠牲です。それは、VSが管理者権限なしでも実行できるようなものではありません:)ところで、DLRを介してReflection.Emitを使用します(非常にハッキングされた初期の分岐型)。