フリーの.NETプロファイラNProf

フリー(GPL)の.NETのプロファイラ、NProfが.NET2.0では動かないので困った。SourceforgeのMLを読むとどうやらそのうち.NET2.0対応版を出すようです。が、MLを読んでみた限りあまりやる気はなさそう。

NProfのホームはこちら。
http://nprof.sourceforge.net/Site/SiteHomeNews.html

そんなわけでつっこんでいろいろ調べてみました。.NET2.0で動かすパッチが去年の5月にメーリングリストに流れていたようです。これを当てると確かに動いて、.NET2.0アプリのプロファイリングができます。
プロファイラ向けのインターフェイスICorProfilerCallbackインターフェイス(COM)をICorProfilerCallback2にしてやると動くようで、プロファイル対象のアプリに寄生するProxyのインターフェイスを修正したパッチです。(COMってよく知らないんだけど、ICorProfilerCallbackを使い続ければ問題ないものじゃないの?)
なんてさらっと書いたけど、このパッチを見つける前にNPorfをさんざん解析しちゃった。どういう仕組みで動いてるのか、前から興味があったし、ちょうどよく.NET2で動かないしで、これはもう解析するしかないっしょって思ってしまった訳で・・・。
構造は.NETでできてるメインのGUI部分と、COMでできてるProxyの2つからなっています。.NETのデバッグとプロファイリングのインターフェイス はCOMでインターフェイスが用意してあって、ProxyはこのうちのICorProfilerCallbackの実装になってます。
解析で難関だったのがこのProxyがどうやって起動されるのかって点。はっきり記述してあるドキュメントは見つけてないんだけど、MS製のプロファイラのCLR Profilerも見てなんとか想像がつきました。どうやら、環境変数に「COR_ENABLE_PROFILING = 1」と「COR_PROFILER = ProxyのGUID」を書いてプロファイル対象アプリを起動すると、CLRがProxyを呼んでくれるようです。ASP.NETのときはレジストリに同じものを書いて、終了時に消してます。こんなCLRマジックがあったなんてぇ!

んで、パッチの話に戻って、このパッチは競合状態を解消するパッチも含んでいます。ただし、この競合状態パッチ、どうもうまくいってないようで、うちでは頻繁にデッドロックします…。なんとかしようとさらに解析してたり・・・。
パッチを0.9b-alphaに当ててコンパイルすれば動きます。といっても、COMをコンパイルしないといけないので、C# Expressではコンパイルできません。VS2005のStandard以上をお持ちなら、NProf.Applicationプロジェクトをスタートアッププロジェクトにしてコンパイルすればきっと動くと思います。NProf.Application.exe(実行ファイル)と同じディレクトリにmsvcr70.dllが必要っぽいです。古いNAntが同梱されてますがかなり古いので使わないで、VSでビルドしたほうがよさげです。

さて、うちはVS2003Proはあるけど、2005はC# Expressしかないので、コンパイルはかなり大変でした。まず考えたのはVC++2005Expressのダウンロード。ダウンロードを開始した後に、そういえばMFC/ATLが入ってないってどこかで聞いたなぁと気づいてしまって、なんとか2003でコンパイルしました。プロファイル対象は.NET2.0アプリですが、プロファイラ本体は1.1でもいいし(GUIだけ2.0でもいいけど)、ProxyはCOMが作れればそれでよかったので。
手順は複雑です。分かる人は分かると思いますが、メモ程度に書いておきます。
あらかじめ環境変数を見て、標準のincludeパスを調べておいて、.NET1.1のSDK\includeを.NET2.0のものに変えてやります。具体的には、NProf.Hookプロジェクト(Proxy)のプロパティページで、「C/C++プリプロセッサ」の「標準インクルードパスの無視」を「はい」にし、「C/C++→全般」の「追加のインクルードパス」に.NET2.0SDKのincludeパス+元の残りのパスを書きます。
これでコンパイルすると、cor.hでspecstrings.hがないってエラーが出るので、.NET2.0 SDKのincludeからcor.hを持ってきて、NProf.Hookプロジェクトに入れて、cor.hの該当行を「#include 」から「#include "specstrings.h"」にします。で、NProf.Hookプロジェクトにspecstrings.hを捏造してやります。コンパイルしながら確かめるとエラーが出るマクロは「__in」、「__out」、「__out_opt」、「__out_ecount_part_opt( X, Y )」、「__inout」の5つのマクロ。すべて「#define __in」って感じに消してしまいます。どうやらセキュリティバグ対策のチェックマクロみたいですね。本物のspecstrings.hを使いたいときはPlatform SDKに入ってるみたいなので、そちらをどうぞ。
ここまでやるとProxyのコンパイルが通ってProxyができました!お疲れ様でした。

nprof maintainers/developers wantedなんてあるけど、COMコンパイルに苦労するんで、スルーだなぁ(英語もダメだし)。VS2005Proが欲しくなってきた・・・。