.NET Framework の基本クラスに対してソースレベルデバッグを行う方法 (オフライン編)

.NET Framework の Base Class Library (BCL) の一部*1ソースコードが公開されていて*2Visual Studio 2008 のソースレベルデバッグに利用することができます.
公開されているソースコードの入手方法としては Microsoft のソースサーバを利用するものが有名ですが,ここでは,アーカイブされたソースコードとシンボルファイルをまとめてダウンロードする方法を紹介します.この方法であれば,オフラインでも安心してソースレベルデバッグができますし,公開されているソースコードから興味がある部分を grep で検索することも可能です.
クリーンインストール状態の Visual Studio 2008 SP1 から以下の手順で設定を行います.

  1. Microsoft Reference Source Code Center の .NET Framework に関するソースコードダウンロードページ から,以下の 3 ファイルをダウンロード
  2. 上記 3 ファイルをインストール.Visual Studio 使用時の権限でリードアクセス可能なフォルダであれば,インストール先はどこでも場所でよい.ここでは "C:\ReferenceSource" にインストールするものとする.
  3. Visual Studio 2008 SP1 を起動し,Tool → Options を選択.
    1. Debugging → General を,以下のように設定.
      • チェックを外すもの
        • Enable Just My Code (Managed only)
        • Require source files to exactly match the original version
        • Enable .NET Framework source stepping
      • チェックを入れるもの
        • Enable source server support
    2. Debugging → Symbols を選択し,Symbol file (.pdb) locations: に "C:\ReferenceSource\Symbols" と設定.ここはインストールしたフォルダにあわせて変更する.



以上で設定は終わりです.これで BCL の中にステップインできるようになります.

おすすめ巡回先

Reference Source Code Center に関する最新情報が欲しい方におすすめの情報源.

*1:現在公開されている範囲に,LINQ や ReaderWriterLockSlim といったクラスが含まれていません.一方で WPF が利用している MIL (Media Integration Layer) のエクスポート関数や定数一覧が記されたレアなソースは入っていたりします.なかなかおもしろいことになっています.

*2:ライセンス形態は [http://referencesource.microsoft.com/referencesourcelicensing.aspx:title=Microsoft Reference Source License (MS-RSL)] と書かれていますが,"The source code for the .NET Framework libraries are released under a modified Microsoft Reference Source License." の "modified" が微妙に気になります.

Mono 2.0 の Expression Trees と HashSet を Xbox 360 環境に移植してみた (可能な範囲で)

ネタ元は『XNAにLINQがきた……が何に使えばいいんだこれ - ABAの日誌』.
XNA Creators Club FAQ より.

Can I use the new C# 3.0 features with XNA Game Studio 3.0?

The new C# 3.0 language features are fully available for all platforms in the Beta release, with the exception of LINQ Expression Trees, which are available only on Windows. You can’t deploy/run your Xbox 360 game in the Beta, but you can still compile your code for Xbox 360, including for scenarios that use C# 3.0 features.

なるほど確かに XNA GS 3.0 beta の Xbox 360 環境には Expression Trees が存在しない.HashSet<T> や ReaderWriterLockSlim も未収録.オリジナルの System.Core.dll から Linq to Object 関係だけ引っ張ってきたという感じだ.単に .NET Compact Framework 3.5 と同じと言えばそれまでなんだけど.
というわけで,いつものように Mono 2.0 Preview のソースコード を拝借.さしあたって Expression Trees と HashSet<T> を移植してみたものが以下.ソースも同梱.
http://www.dwahan.net/nyaruru/hatena/System.Core.Xna.20080919.zip
上記ファイルに含まれる System.Core.Xna.dll を参照設定に加えると,Xbox 360 環境でも (可能な範囲で) Expression Trees が使えるようになる.
Xbox 360 環境では Lightweigt Code Gen (LCG) が使えないため,これに依存する部分は全て省略してある. Expression Trees の実行時コンパイルはできない.
対応策として,Xbox 360 環境でも動く Expression Trees のインタプリタなら作れそうな気がする.うまくいけばちょっと面白いが,詳細は詰めていないのでよく分からない.作りはじめてみたものの,途中でどうしようもない壁にぶち当たりそうではある.要暇.
ReaderWriterLockSlim は Mono 2.0 にも存在しなかったので見送り.
ついでに Achiral の最新版Xbox 360 向けにビルドできるようにしておいた.

.NET Generics の variance サポートと曖昧性

を読んでちょいと気になったこと.
まず基本..NET の型システムは,以下のような型を許す.

public interface ICountable<T>
{
    int Count{ get; }
}

public class MyCollection : ICountable<string>, ICountable<FileStream>
{
    int ICountable<string>.Count
    {
        get { return 1; }
    }
    int ICountable<FileStream>.Count
    {
        get { return 2; }
    }
}

このように定義された MyCollection は,string として数えられたときには 1 個で,FileStream として数えられたときには 2 個となる.では C# が仮にワイルドカード ICountable<?> をサポートするとして,どちらの実装がバインドされるべき?
実際上の例は .NET の型システムが既に抱え込んでしまっている曖昧性である……ように見える..NET Generics は,ジェネリックデリゲートとジェネリックインターフェイスに関する variance をサポートするわけで,仮に C# 4.0 で,型引数の covariant/contravariant 修飾が解禁されるとして,以下のようなコードはどうしたものか.

public interface ICountable<+T> // Tに関して Covariant
{
    int Count{ get; }
}

public class MyCollection : ICountable<string>, ICountable<FileStream>
{
    int ICountable<string>.Count
    {
        get { return 1; }
    }
    int ICountable<FileStream>.Count
    {
        get { return 2; }
    }
}

public static class Program
{
    static void Main(string[] args)
    {
        var col = new MyCollection();

        // どちらの ICountable<> 実装を使うか明示する IL 表現は存在するの?
        var countable = (ICountable<object>)(object)col;
        Console.WriteLine(countable.Count);
    }
}

気になっているのは,上記コードのような曖昧性を回避したくなったときにどうするか*1.ざっと仕様書を眺め直した範囲では,曖昧なときの意味論とか,曖昧性回避を意図した IL 表現が見つからなかったんだけど,はてさて.
どなたかご存じで?

*1:全部デリゲートにしてしまうというのはひとつの手かも

LL Future, tracing jit, IBM Java Just-in-Time Compiler

寝過ごして事実上午後から参加の LL Future.終了後は,さくら水産にてみなさんと晩ご飯.魚類は控えめ.
そろそろ寝ないとまた明日(というか今日)も寝過ごしそうなのですが,記憶が飛ぶ前に tracing jit 絡みの話だけメモ.
要は私が読み飛ばしていた omo さんのエントリがあって,今回 LL Future で「なるほどそういう話だったのか」と把握できたという話なんですが.

つまり, こんな AS のコードから...:

 ##GooglePrettify
 public class Foo {
    public function bar() : void {
     doSomething();
    }
 }
 
 ...
 foo = new Foo();
 ...
 foo.bar();

こんなかんじのネイティブコードができると考えればいい:

 ##GooglePrettify
 // クラス定義は省略
 ...
 Foo* foo = new (gc) Foo();
 ...
 // 呼び出すメソッドをチェックする. vtbl は仮想関数テーブルをあらわす空想上の変数
 if (Foo::vtbl.bar != foo->vtbl->bar) { goto side_exit_at_path_1; }
 doSomething(); // インライン化された bar() の実装. ほんとは更にこいつもインライン化されるはず. 
 ...

ガードを入れながらトレースするだけの至ってシンプルな仕組みで, 仮想関数がインライン化された. JIT の力を痛感する.

インライン化されれば関数をまたいだ最適化の恩恵にも与れる. また tamarin-devel での議論をみていると, 将来の改善ではガードをループの外に追い出すような高速化も目論みにあるようす. ABC のメソッドだけでなく, forth をコンパイルしてできた IL ランタイムも同様にインライン展開される.

なお Sun の Hotspot Java VM は, クラス階層を分析することでチェックのいらない仮想関数をインライン化するという. いまどき仮想関数テーブルとかいってるのは C++ だけかもしらん. あー, JIT いいなあ...

MS CLR でも似た最適化をやっているという記事があるのでご紹介.

ここでの主役はインターフェイス経由の callvirt.愚直な CLI 実装では 2 回も仮想関数テーブルのルックアップが必要なところを,理想的なケースでのコードパスを「比較1回+ジャンプ1回」に変換してやるぜ,というお話.

One of the interface implementations is called considerably more frequently than others, at a given call site. This can be determined by dynamically profiling and patching the code or by some kind of hint from the programmer. (See http://www.sable.mcgill.ca/publications/papers/2005-4/sable-paper-2005-4.ps and http://www.research.ibm.com/journal/sj/391/suganuma.html for more information on the topic in general and its JVM implementation in particular.) In this case, the interface method dispatch for that specific interface can be modified to direct dispatch or even inlined, as follows:

if (obj->MethodTable == expectedMethodTable) {
      // Inlined code of "hot" method
}
else {
      // Normal interface method dispatch code
}

上のあたりに IBM Java Just-in-Time Compiler の話がでてくる.上記の例にはインライン化とあるけどこれは JVM 実装の話っぽい.では CLR だとどうかというのがこれ.

Summarized in pseudo-code, this looks somewhat like the following:

start: if (obj->Type == expectedType) {
      // Jump to the expected implementation
}
else {
      expectedType = obj->Type;
      goto start;
}

This is the final behavior that we get for the entire course of the program. It means that per call site, we get a one-shot counter (initialized to 100) which counts the times the "hot" implementation was missed. After the counter decays below 0, JIT back-patching is triggered and the code is replaced with the version we just saw, that alternates the "hot" implementation with each miss.

こっちが MS CLR (x86) の話と.カウンタを用意して予想が外れまくった場合に備えているあたりが特色なんですかね.
とまあメモ完了なのでお休みなさいのお時間.皆様お休みなさいませ.

CoreCLR 一区切り

MSDN Magazine August 2008 の CoreCLR 記事を読む限り,次の 3 年後がそろそろ始まるみたいな感じですな.


CoreCLR エンジンの秘密

CoreCLR の設計は、2005 年 10 月に CLR の Version 2.0 をリリースした直後から開始され、サイズと互換性の 2 つの設計目標が設定されました。プログラマの立場から考えると CLR に関するコーディングは常に同じであるべきですが、ユーザーの立場から考えるとダウンロードするサイズを大幅に小さくする必要がありました。

なんとなく 3 年というタイムスパンは普遍的な気がする.C# 3.0 にしてもそうだけど.実際に手を動かし始めてから 3 年はかかると思ってもいいのかも.

クロス プラットフォームの実現

(略)

私たちは、CoreCLR の設計と開発の全体を通じて、開発者が今までに習得したスキルとツールを再利用して、サイズが小さく安全なランタイムに対応するリッチ コンテンツを開発できる環境を提供することに力を入れました。リッチなインターネット アプリケーションのシナリオを減らしたことによって下すことができた決断がほとんどですが、設計の中には過去の作業の中で得た経験が活かされているものもあります。CoreCLR に関して下した決断の中には、最終的にデスクトップに還元できるものもあります。たとえば、次のバージョンのデスクトップ CLR は、プロセス内で別のバージョンの CLR と同時に実行できるようになるでしょう。また、透過的セキュリティ モデルの改善点のほとんども、次の CLR に活かせるはずです。

つくづく世界はインクリメンタルだよなぁ.急に世界は変わらない.でも決して1箇所にとどまらない.

Dynamic Compilation - WPF Shader Effects

Shader Effects BuildTask and Project Templates

.NET Framework 3.5 SP1 では PixelShader 2.0 を用いてポストエフェクトがかけられるようになりましたが,ユーザ定義エフェクトの作成は少しややこしい準備が必要です.この作業を簡略化するカスタムビルドタスクとプロジェクトテンプレートが,.NET Framework 3.5 SP1 のリリースに合わせて CodePlex にて公開されました.作者は Microsoft WPF team の Greg Schechter & Gerhard Schneider コンビです.

このカスタムビルドタスクを用いると,プロジェクトをビルド時にエフェクトファイルのコンパイルも一緒に行われます.XNA Game Studio の Content Pipeline の簡易版とも言えます.
このコンパイル部分ですが,C++/CLI で実装されています.実は出所の怪しい D3DX の静的リンクライブラリが同梱されていて*1,d3dx9_xx.dll が見つからない場合でも単独動作可能という特徴があるようです.

ID3DXEffectCompiler

さて,d3dx9_xx.dll の存在を前提にできるなら*2,アンマネージコードとの相互運用を C# だけで完結させることが可能です.
d3dx9_xx.dll がエクスポートする D3DXCreateEffectCompiler に注目します.このエクスポート関数が返す ID3DXEffectCompiler インターフェイスは,動作に IDirect3DDevice9 を必要とせず,バックグラウンドでエフェクトファイルを取り扱うのに便利です.
ID3DXEffectCompiler を基点として,エフェクトファイルに含まれるシェーダコードをコンパイルしたり,エフェクトファイルに含まれる要素に対するリフレクションを行うことができます.

エフェクトファイルの実行時コンパイルサンプル

ちょうど手元に書きかけの ID3DXEffectCompiler ラッパーライブラリがあったので,少し書き足してそこそこ使える形にまとめてみました.それを使って出来たのが以下のサンプルです.このサンプルは,実行時にエフェクトファイルのコンパイルを行い,動的に WPF のシェーダエフェクトを生成します.

  • Binary and source code
  • 実行方法
    • .NET Framework 3.5 SP1 以降が必要です.
    • 起動したら,"Open Movie" ボタンを押して適当に動画ファイルを選択して下さい.
    • 左の Compile ボタンを押すと,表示されている HLSL をコンパイル・実行します.
    • 適用される PixelShader の関数名は main にハードコードしてあります.
    • WPF の仕様で,PixelShader 2.0 に固定されています.
    • elapsedSeconds 変数には,動画ファイル先頭からの経過時間がバインドされます.

ID3DXEffectCompiler ラッパーライブラリの使い方

上記サンプルコードに含まれる ID3DXEffectCompiler ラッパーライブラリの使い方をごく簡単に紹介しておきます.
文字列型のエフェクトファイルソースコードから,シェーダのバイトコードを得るには次のようにします.

//D3DX Effect Format
const string SourceCode = 
@"sampler2D implicitInput : register(s0);
float elapsedSeconds : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float phase = 10.0f*(uv.y + elapsedSeconds);
    float2 offset = 0.1f * float2(cos(phase), 0.0f);
    return tex2D(implicitInput, uv + offset);
}
";

var effect = CompiledEffect.FromString(SourceCode, CompilerOptions.None);
if(effect.CompileSucceeded)
{
    var shader = effect.CompileShader("main", "ps_2_0", CompilerOptions.None);
    if(shader.CompileSucceeded)
    {
        var shader = new PixelShader();
        shader.SetStreamSource(new MemoryStream(shader.Data));

        .......

ラッパーの本体は EffectCompiler.cs にまとめてあります.コピー・改変等ご自由にお使い下さい.

更新履歴

  • 2008年8月15日
    • EffectCompiler.cs: PreserveSig 属性が設定されていなかった COM 呼び出し全てに PreserveSig 属性を付けた.
  • 2010年10月2日

*1:WPFWindows Vista 用のカスタムビルドの可能性があります

*2:一般的には [http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=2da43d38-db71-4c1b-bc6a-9b6652cd92a3:title=DirectX の Web Installer] を使ってもらうことになるでしょう

.NET Framework 2.0 で LINQ を使う方法

うわー,うちの『C# 3.0 による .NET 2.0 アプリケーション開発 - XNA で LINQ を使おう - NyaRuRuの日記』がご迷惑をおかけしたようで.id:atsushieno さんにフォローしていただいていたので大丈夫かと思っていたのですが,確かにトラックバックだと気付きにくいかもしれませんね.ちゃんと本文にも追記しておきます.



それはそれとして,LINQ (to Object) を .NET Framework 2.0 でという話は結構耳にします.私が知っている先行事例だけでも列挙しておきましょう.

  • LINQBridge
  • Poor Man's Linq
    • C# 2.0 で LINQ 風メソッドチェーンを可能にしたもの
    • System.Linq.Enumerable の実装には Mono 実装を流用
  • NLinq
    • C# 2.0 で LINQ のクエリ式を可能にしたもの
    • クエリ式は文字列で与え,NLinq 内蔵のパーサによって処理される
    • 実用性は考えないことにして,遊びで作ったものらしい*1

*1:個人的にこういうのは好きだ