OzCodeでかゆいところに手が届くデバッグを

wizardVisual Studioの拡張機能としてリリースされているOzCodeを紹介します。

記事の発端はtwitterで私がぽろっとRTした内容に反応があったことで:

まさか反応があるとは思ってなかったので、秘孔を突かれた感 (;´Д`) 国内ではR#の事はよく聞くんですが、OzCodeはまだマイナーかもしれないと思い、機能を紹介してみます。

なお、有償ですがTrialできます。また、Personalで全機能使えるので個人ならPersonalでも十分、サポートが必要ならCorporateかSite licenseを選択すればよいでしょう。Personalで$79なので、効果を考えるとお安いですよ!

# 誰かさんが呟いてましたが、Academicなら$25ですよ


デバッガの強化

OzCodeはVisual Studioのデバッガ機能を強化する拡張機能です。特徴的でわかりやすく機能を絞っているので、R#とはまた違う位置づけで良い製品だと思います。

Head-up displayとMagic Glance

Head-up displayは、メソッドの引数や式の評価内容を、いちいちWatchしなくてもインプレースで可視化します。

CodeLensと同じでエディタ上に場所をとるので、最初は慣れが必要かもしれません(しばらくしたら慣れました)。個人的にはプレビューウインドウがモードレスであることと、検索ウインドウから部分的なシンボル名で串刺し検索が出来るのがとても良い感じです。

もうね、このビデオの通りだと思うのですよ…

magicGlanceそして、Magic Glanceは、式のどの部分が評価されて無視されるのかを、色分けで示してくれます。

そもそも副作用のある式を書いたりしないように注意して設計していれば、この事が問題になることはないと思いますが、あまり良い出来ではないサードパーティ製ライブラリなどを使用する場合は、うっかりこのような罠に掛かっている事が見ただけで判ったりするので便利です。

プレビューの強化 (Reveal)

いつも確認するプロパティとか、ファボっておけます。ファボるとプレビューの上位に出てくるので、いちいちスクロールする必要がないのです。

オブジェクトの比較

比較が成立するには条件がありそうです(確認していませんが、パブリックフィールド・メンバじゃないとダメかな)が、あらかじめそういう設計にしておけば、無双感あります。

Exception Trail

例外ビュアーの強化・構造化された例外(InnerException)をマルチペーンビューで簡単に追跡できます。

InnerExceptionを横に並べてくれるので、映像にあるようにいちいちツリーを展開するメンドクササが軽減されます。また、まだあまり例外クラスについての知識がない場合は、その場でGoogleなどから検索できるのもイイんじゃないでしょうか?

まとめ

ほかにも小粒で便利な機能が: この辺を見てもらったほうが早いかも。

まだ日本語化されていませんが、日本語である必要性はほとんど無い(「魅せる」タイプのアプリですね)ので問題ないと感じました。敢えてネタを挙げるとすると:

  • LINQ (IEnumerable<T>)の演算子間を流れるTの観測をしたい。かなり無茶を言ってる事は分かってるんですが、いちいちToListしたりとか、もし回避出来るならかなりいい感じかなと思いました。
  • C#のみサポート。個人的にはぜひF#もサポートしてほしい… F#にはFSharp.Compiler.Serviceがありますよー? :)

今後の機能拡張が楽しみです。

なお、彼らは日本市場にも興味を持っているようですよー アンケート投下しておいたので話題に上れば何かあるかも #あるとは言ってない


ほかのVisual Studio拡張機能については、以下の記事もどうぞ:
Visual Studio 2012/2013 の拡張機能を紹介 (1)
Visual Studio 2012/2013 の拡張機能を紹介 (2)

継続飛行 (1) – 原点

数回に分けて、継続についてのもろもろを、時系列にそって書きます。

そもそも「継続」という学問があるようなのですが、私はここ最近に至るまでそんな学問があるとも知らずに継続を扱ってきたこともあって、きっと厳密性は全く無いと思います。ただ、経験から得られた知識であれど、実際に動作させたという知見があるので、もしそういった方からのアプローチが役に立つのであればそれはそれでいいんじゃないかという妄想によりますので、控えめによろしくお願いします (;´Д`)

※そこんとこは「継続」で言うところの何某だよ、みたいなコメントがあると嬉しいです

ちなみに、近日「継続勉強会」があるようです。うぅ、東京うらやましい…


原点

2000年よりも昔、まだインターネットが大学でしか使えなかったころ、巷ではシリアル通信によるパソコン通信なるものがコミュニケーションの主流でした。そのころに私は某BBSシステムを改良したシステムをフリーソフトウェア(この頃は定義さえ怪しい)として公開して配布していました。

驚いたことに、これはいまだにベクターで公開されており、ソースコードも公開していたので、今回のネタの原点から振り返ってみることが可能でした(肝心のコードはあまりに古く、色々アレなので、ポインタについては勘弁してくださいw)。

コードはTurbo PASCALで書かれており、一部がアセンブラ(x86)です。プラットフォームはMS-DOSであり、RS-232C(シリアル)のドライバも一から書かなければならない頃です。そのBBSシステムは、多チャンネルに対応していたため、複数の電話回線から複数のモデムを通じてのセッションを同時に裁く必要があります。UNIXでいえば、物理的なシリアル何回線かに対して、シリアルコンソールでgettyに接続する感じでしょうか。

現代的なコードであれば、スレッドを使って個々の接続を個別のスレッドで処理するような感じです。しかし相手はMS-DOS、もちろんスレッドなんてありませんし、プロセスは分離されず子プロセスのみでコンカレント動作はありません。MS-DOSを知らない方には想像つかないかもしれませんが…

となると、多チャンネルの操作は、ステートマシンの手動実装、つまり「ステート値」とその値に依存する「巨大なswitch-case」のような実装、そして細切れにされた各ステートの処理の乱立、という、酷すぎて保守したくないコードのようなものにならざるを得ません。

しかし、このシステムには画期的な、ある小さなコードの断片がありました。

code	segment byte public
	assume cs:code, ds:data

public	transfer

trans_stack	struc	; arguments of transfer.
tr_bp	dw	?
return	dd	?
proc2	dd	?
proc1	dd	?
trans_stack	ends

transfer proc far
	push	bp
	mov	bp, sp
	les	di, [bp].proc1
	mov	es:[di], sp	;現在のスタックポインタをproc1が示すtrans_stackに保存
	mov	es:[di+2],ss	
	cli
	mov	sp, word ptr [bp].proc2	; proc2が示すtrans_stackのポインタをスタックポインタに設定
	mov	ss, word ptr [bp].proc2+2
	sti
	pop	bp
	ret	8
transfer endp

code	ends
	end

このコードはx86のアセンブラで、「transfer」と呼ばれるメソッドが定義されています。引数に指定されている「trans_stack」構造体へのポインタを使って、現在のスタックポインタを「無理やり別のスタック位置に入れ替える」操作を行います。

初めてこのコードを見たときは「衝撃」でした(今でもよく覚えている。いまだにこの方面のことに興味があるのは、これのせいかもしれない)。

何しろ動作中にスタックポインタを書き換えるのです。しかもこのコードでは、proc2がどこからやってくるのかがよくわからないため、スタックポインタがどのような値になるのか想像もつきません。更にスタックポインタを書き換えた状態で、「pop」とか「ret」を実行しているのです。一体なにがpopされるのか、その後retでどこに戻るというのか… 頭がパニックになります。

「機械語」 – 先日のILのキホンでも言ったんですが、このような低レベルのコードは、「書いたとおりに動作する」のです。ILの場合は明らかに不正なコード片はCLRが止めてしまいますが、機械語の場合はほぼ書いたとおりに実行されます。

スタックポインタがどこを指しているのかわかりませんが、その「どこか」から値をpopし、更に値を取得してそこに処理が遷移する(ret)のです。特にretは、その動作から、以下のようなジャンプ命令のように振る舞います。

	; @tempというレジスタがあったと仮定して:
	pop	@temp
	jmp	@temp

つまり、あらかじめ「新しいスタックポインタ」が示すメモリ領域に、bpに復元すべき値とジャンプ先のアドレスを書き込んでおき、そこを指すようにtrans_stack構造体を初期化しておいてtransferを呼び出せば、見事ジャンプ先に遷移することになります。

で、こんなめんどくさくてわかりにくい事を何故行うのかというと、一度遷移に成功すれば、再びtransferを呼び出すことで、「以前使っていたスタックポインタの位置から処理を継続できる」からです。

transferは「call命令」で呼び出されることを前提にしています。つまり、transferに遷移してきたとき、すでに戻るべき処理位置へのポインタはスタックに積まれており、これが次回のtransferからretするときに復元されます。元コードはTurbo PASCALで書かれていましたが、Cで書くと以下のような疑似的なコードとなります。

// 疑似コードです。動きません
extern void transfer(trans_stack* fromtr, trans_stack* totr);

static trans_stack main_tr;
static trans_stack sub_tr;

// subのtransfer-processエントリポイント
static void sub()
{
  auto i = 1000;
  while (1)
  {
    printf("sub(): %d\n", i++);

    // subからmainに転送
    transfer(&sub_tr, &main_tr);
  }
}

// main(のtransfer-process)
void main()
{
  // bpの初期値は0で良い
  sub_tr.tr_bp = 0;
  // transfer-processの初期位置はsub関数の先頭
  sub_tr.return = &sub;
  // subのためのスタックを確保
  sub_tr.proc2 = malloc(0x1000);

  for (auto i = 0; i < 10000; i++)
  {
    printf("main(): %d\n", i);

    // mainからsubに転送
    transfer(&main_tr, &sub_tr);
  }
}

よーく見てください。このコードを実行すると、mainのprintfとsubのprintfを交互に実行します。そして、mainで10000回処理を行った後はプログラムが終了します。subは無限ループなので、終了しても放置されていますね。いずれにしても、このプログラムに「スレッド」はありません。環境はMS-DOSです。しかしながら、「transfer」を呼び出す必要があることを除けば、まるでスレッドが存在するかのようです。

こうすれば、各「transfer-process」の暗黙のステートは、それぞれのスタック(mainのスタックと、mallocで確保されたメモリ)に隠されており、独立性を保ち、不要なステートマシンを作る必要がなくなります。当然、各シリアル制御のコードは大幅に簡略化されるでしょう。実際、transferがなければまともなコードにはならなかったはずです。

例えば、上記のsub関数内の自動変数「i」は、mallocで確保されたスタックメモリ領域内にその値が格納されています。コードのどこにも、明示的にmallocのメモリを読み書きしている個所がありませんが、これはスタックなのです。そして自動変数はスタック内に割り当てられます。つまり、subを実行するtransfer-processは、subの実行に必要な暗黙のステートを、スタック内で管理しているのです。そして、transferを呼び出すたびに、このスタックは切り替えられます。

このような構造を「コルーチン(coroutine)」と呼びます。

もちろん、この時にはこれが「継続」と呼ばれる何かだとは、全く気が付いていませんでした。

真・ILのキホン – 第六回 Center CLR 勉強会

WP_20160319_11_10_36_Pro_LIILについての勉強会を、第六回 Center CLR 勉強会でやってきました!

前回色々進行がダメだったので仕切り直しと言う事で、最初に少し解説を加えたりしてみました。


導入

il1扱っているネタが極めて低レベルと言う事もあり、前回同様出題内容を各自で解いてみるという形式で進行しました。
その前に導入として:

  • 「System.Reflection.Emit」名前空間を覚えておくこと。
    System.Reflection.Emit.OpCodesクラスがあり、ここにILのオプコード(OpCode)が定義されているよ。
  • OpCodeとは
    CLRが理解できる中間言語(バイトコード)
  • OpCodeにはいくつか種類がある事。
  • スタック操作・スタックマシンとは:
    CLRがバイトコード由来で動作するための基礎となる構造で、JVMのような類似技術もある。リアルなCPUにはあまり採用されていない事。

を説明しました。


実践して覚える

今回も、前回と同様GitHubのテンプレートとなるコードを元に課題にチャレンジします。

果たして…今回はPart5まで到達出来ました!! Part5では、インスタンスメソッドを扱い、その後のPartでクラス内のフィールドへのアクセス等を扱う予定でしたが、時間足らず。スライドは上げておくので、残りのPartを「忘れないうちに」取り組んでみる事をお勧めします。

# 多分しばらくはILネタのセッションは無いですww


Intermediate Languageを覚えることの意義

ILの基本と言う事で2回も長時間セッションを行ったわけですが、ILを覚えたところで一体何の役に立つのか?と言う疑問を持つ方もいるかも知れません。ILをやっていて重要だなと思うポイントを2点挙げておきます。

  • 参照型と値型の明確な区別
    ChalkTalk CLR – 動的コード生成技術(式木・IL等)でもやりましたが、CLRには大別して「参照型」と「値型」という2つの型の相違があります。これらはCLR内でのメモリの扱われ方が違う上、IL上でも異なる扱われ方をされます。特に参照型の値と、値型の値と、値型が参照される場合、など、IL上でも正しく区別が必要です。これらの区別がC#やVB.netでも正しく出来ていない・苦手という人は多いと思いますが、ILでどのように扱うのかがしっかり理解できれば(しかもこれは単純な法則でもあるので、抽象的な概念などは不要で実は覚えやすい)、C#などでコードを書く場合でも、自信をもって書けるはずです。また、これが分かると、ボクシング(Boxing)・アンボクシング(Unboxing)のコストと、これらがいつ生じるのか、と言う事が正しく理解できます。暗に気が付かないうちにこれらを発生させてしまい、パフォーマンスの低下を招くという事を回避できるようになるはずです。
  • アセンブリメタデータの構造観
    普段はコンパイラが自動的によしなにやってくれる、アセンブリメタデータの構造についての直観的な理解が得られます。なぜC#の言語構造はこうなっているのか、の(すべてではありませんが)ILから見た姿が分かります。記述した型がどこでどのように使われるのか、あるいはメソッドの定義はどこからやってくるのか、ILとどのように関連付けされるのかが分かります。これが分かると、どんな場合にアセンブリを分割すべきなのか、あるいはリフレクションを使用して解決すべき課題なのかそうではないのか、と言ったことを判断できます。こういった判断は、最終的にシステム全体の構造にも影響を与える可能性があるので、システム設計を行う上では無視できない要素の一つだと思います。

学問で言えば本当に基礎的な部分に相当するので、これを習得するメリットが見えにくいのは事実ですが、覚えておいて損はないと思います。大体、ILなんて「単純」なので、難しそうで実は簡単なんですよ!(きわめて抽象度の高い概念を覚える事に比べたら、どんな人でも覚えれる可能性があります)。

Let’s IL!!

それではまた。