ソースの活用

Visual Studio 2005 におけるバグへの対処でのソース サーバーの活用

John Robbins


この記事で取り上げる話題:

  • ソース サーバー
  • シンボル サーバー
  • WinDBG および Visual Studio のデバッグ
この記事で使用する技術:
Visual Studio 2005、Visual SourceSafe
サンプルコードのダウンロード:
Bugslayer2006_08.exe (181KB)

目次

ソース サーバーでの作業
前提条件
簡単なシナリオ
インデックス付けの開始
サブプロジェクトでの作業
WinDBG を使用したデバッグ
Visual Studio 2005 でのデバッグ
インデックス付けの向上と簡素化
まとめ


捕らえどころのないバグを見つけ出そうとしながら、ミニダンプあるいはライブのデバッグ セッションを検討してみると、問題のビットの正確な出所を明らかにする必要が生じます。Microsoft .NET あるいはネイティブ C++ のどちらの開発を行っている場合でも、正しいシンボルおよびソース コードへのアクセスが不可欠です。 そうしないと、アセンブリ言語レベルでデバッグすることになります。 実際、時間給で勤務している方であれば、新車の購入費用を稼ぐ絶好のチャンスになるかもしれません。 しかし、時間をもっと有効に過ごす方法があります。

以前私が執筆したコラム (https://msdn.microsoft.com/en-us/magazine/cc301459.aspx)では、必要なシンボルをデバッガに持ち込むにはどうすればよいかについて述べました。 シンボル サーバーが必要なのは明らかです。 それがいかに重要かは、いくら力説してもしすぎることはありません。 ご自分の開発環境でそれをまだセットアップしていなければ、すぐに用意してください。

的確なシンボル セットアップにアクセスできるようになったら、的確なソースが必要になります。 バージョン管理にぬかりのないチームであれば、どのバージョンのソース コードがどのバイナリに入っているかは正確に把握されています。 しかし、デバッガがまさにその PDB ファイルをロードしようとしたときに、正確なソース コードが自動的に取り出されたら便利だと思いませんか。 見当をつける必要もなく、デバッグはもっと簡単になるはずです。

この記事では、ソース サーバーにソース コードが必ず持ち込まれるようにする方法を解き明かします。 最新のリリースの WinDBG および Visual Studio では、ソース サーバーの使用法が的確に捕らえられているので、その利点を .NET およびネイティブの C++ の両方の開発者が活用することができます。

ソース サーバーでの作業

シンボル サーバーとは、ファイル システムを使用するデータベースのことです。FOO.PDB をロードすると、キャッシュ ディレクトリから始まり、開くファイルを付加し、ファイル バージョンの固有性を特定する GUID で終わるディレクトリ パスがデバッガによって構築されます。 そのディレクトリ パス内に、デバッガにとって必要な実際の PDB ファイルがあります (図 1 を参照)。


図 1 パスの構築

シンボル サーバーが PDB を保管するのとは対照的に、ソース サーバーはソース コードをシンボル サーバーとして保管しません。 最近のコンパイラのいずれかを使用して作成された PDB ファイルは、バイナリの作成で使用された各ソース ファイル (コンパイランド) ごとに、完全パスを持っています。 バイナリに対してソース サーバーを実行すると、PDB ファイルにセクションが追加されます。 そこには、バージョン管理システムから報告されたファイルの正確なバージョンと、バージョン管理システムからそのファイルを取り出すのに使用された実際のコマンドが組み込まれます。 このプロセスをソース コードのインデックス付けと呼びます。

ソース コードにインデックスを付け終わったら (バージョン管理システムにアクセスできるという想定のもとで)、デバッガからバージョン管理システムに正しいファイル バージョンが問い合わせられます。PDB ファイル内のそのようなすべての情報を確認する方法は、この記事の後半で取り上げます。

バージョン管理システムをどのように使用するかをソース サーバーに知らせる必要があります。 ソース サーバーの最新バイナリでは、Perforce, Subversion、Concurrent Versions System (CVS)、および Microsoft Visual SourceSafe のサポートが用意されています。 残念ながら、新規の Visual Studio Team Foundation System のバージョン管理システムのすぐに使用可能なサポートはありません。Visual Studio Team Foundation System あるいは別のバージョン管理製品のサポートを付け加えたい場合、ソース サーバーのツールを拡張することができます。 ご使用のバージョン管理システムが、バージョン情報の取得およびプルの実行用のコマンド行ツールをサポートする限り、モジュールを作成して、ご使用のシステムを操作することができます。 ソース サーバーのインストール ディレクトリ内の SRCSRV.DOC ファイルに、独自のモジュールを作成して統合する方法が説明されています。

ソース サーバーのワークフローは単純明快です。 まず、アプリケーションのデバッグおよびリリース用のビルドを作成します。 ビルドが万全であることを確認するための自動スモーク テストの実行後、ソース サーバー プログラムを使用して、バージョン管理情報に合わせて PDB ファイルにインデックスを付けます。 最後のステップでは、そのインデックス付きの PDB ファイルをシンボル サーバーに保管します。 図 2に、そのプロセスが図示されています。


Figure 2 ソース サーバーのワークフロー

前提条件

システムを起動して実行するには、何が必要かを見てみましょう。 例として、この記事の執筆中に使用した構成について述べることにします。

バージョン管理には Visual SourceSafe を使用しました。これは、Windows 開発で最もよく使用されるバージョン管理システムだからです。ただし、ここで取り上げている概念は、最も一般的なバージョン管理ソリューションにも当てはまります。 デバッグでは、x86 および x64 のどちらにも WinDBG 6.6.6.0 を使用しました。 バージョン 6.4.7.2 にまでさかのぼる各バージョンが、ソース サーバーに対応します。

Visual Studio 2005 の Professional Edition および Team Edition のみがソース サーバーを認識します。Visual Studio .NET 2003 あるいは Visual C++ 6.0 を使用しているネイティブ C++ 開発者の場合、Visual Studio 2005 を使用してバイナリをデバッグすることができます。 (ただし、注意してください。Visual Studio 2005 内のシンボル サーバーおよびソース サーバーのサポートや、その他の強化機能をいったん目にしたら、恒久的なアップグレードを真剣に検討することになるかもしれません。)

実際にインデックス付けを行うソース サーバーのバイナリは、Windows パッケージ用のデバッグ ツールの一部であり、Microsoft の Web サイト (microsoft.com/whdc/devtools/debugging/default.mspx) から無償でダウンロードできます。64 ビット システムで実行する予定の場合、64 ビット バージョンを使用できますが、32 ビット バージョンも x64 上で順調に作動します。Visual Studio 2005 には、Windows 用のデバッグ ツールの 6.6.6.0 バージョンに付属しているものより古いバージョンの SRCSRV.DLL が付属しています。これは、Visual SourceSafe の使用時に一部のバグを修正します。x86 バージョンの SRCSRV.DLL を、Windows 用のデバッグ ツールのディレクトリから、Visual Studio.NET のインストール ディレクトリ \Common7\IDE にコピーし、旧バージョンを上書きすることができます。

ソースのインデックス付けを行うのに必要なソース サーバー バイナリの場合、既定でのインストールは行われません。Debugging Tools for Windows の SDK 部分をカスタム インストールする必要があります。 すると、Debugging Tools for Windows のディレクトリの下に SDK ディレクトリが追加されます。 そのディレクトリには、必要なものすべてが収まった SRCSRV ディレクトリが置かれています。 そこから移動してそこを指し示したり、あるいはどこか他の所にそれをコピーすることはできますが、ツールを収めたディレクトリがパス内になければなりません。

Debugging Tools for Windows のインストール後、Perl をインストールする必要があります。 www.cpan.org/src/5.0 の Perl 5.8.7 を使用しています。Visual Studio .NET 2003 を使用してソースからバイナリを作成しました。ソース コードは、Visual Studio 2005 コンパイラを使用して作成されていないからです。

ソース サーバー ディレクトリを見て、主要な作業の大半は、Perl で行われていることに注目してください。SSINDEX.CMD ファイルは、PERL.EXE を呼び出して Perl コードを標準入力を通して引き渡す単なるバッチ ファイルです。.PM ファイルは、バージョン管理システムと対話できる Perl モジュールです。つまり、SSINDEX.CMD はこのモジュールをロードしてから、標準インターフェイスを通して呼び出しを行って、システムの相違を特定します。VSSINDEX.CMD などの他の CMD ファイルは、特定のバージョン管理システムと連係して作動するためのラッパーです。SSINDEX.CMD の場合、コマンド行上にバージョン管理システムがなければならないからです。SRCSRV ディレクトリ内の PDBSTR.EXE ファイルと SRCTOOL.EXE ファイルは、PDB ファイルの読み取りおよび書き込みを行います。VSSDUMP.EXE プログラムは、Visual SourceSafe への統合用のヘルパです。Debugging Tools for Windows パッケージの x86 バージョンでのみ、このプログラムが一緒にインストールされます。

SSINDEX.CMD を実行すると、パラメータの検証後、ターゲット プロジェクトとその下のすべてのソース ファイル (およびそれぞれのバージョン番号) のリストが、バージョン管理システムに対して要求されます。 その後、PDB ファイルが再帰的に検索されます。PDB ファイルが見つかるごとに、Perl コードが SRCTOOL.EXE を呼び出して、ソース ファイルのリストを抽出します。 このコードは、PDB ファイル内のソース ファイルを順に調べて、事前のバージョン管理検索で保管されたファイルのいずれかに一致するかどうかを確かめます。一致するものが見つかった場合、このコードはそのファイル情報を保管します。 コードは、PDB ファイル内のすべてのソース ファイルの検索を完了したら、次に PDBSTR.EXE を呼び出して、SRCSRVというインデックス ストリームを PDB ファイルに書き込みます。 デバッグ時には、デバッガはそのストリームを検索します。 それが見つかると、デバッガは、関与するソース サーバーがあることを認識し、SRCSRV.DLL を呼び出してバージョン管理システムを実行し、必ず正しいファイルへのアクセスが行われるようにします。

以上が、ソース コードのインデックス付けの概要です。SSINDEX.CMD とその関連機能を使用した作業の詳細は、該当コードあるいは SRCSRV.DOC を参照してください。 気を付けていただく点として、ご使用のバージョン管理システムがサポートされていない場合、SRCSRV.DOC に指定されているインターフェイスに準拠した Perl モジュールを作成する必要があります。

簡単なシナリオ

次に、ソース サーバーを稼働させましょう。CppApp および NetApp という、2 つのコンソール アプリケーションを構成する単純なソース ツリーを取り上げます。CppApp はネイティブ C++ アプリケーションであり、NetApp は C# で書かれています。 プロジェクトは C:\Code\CppApp and C:\Code\NetApp に置かれています。 すべての出力は C:\Code\Debug に対して構築されます。Visual SourceSafe のバージョン管理は共有の \\Critias\BugslayerSource に置かれていて、CppApp および NetApp は、$/ (Visual SourceSafe のルートです) のプロジェクトとして置かれています。$/ プロジェクトは、作業ディレクトリとして C:\Code を使用するように設定されていて、$/CppApp および $/NetApp をそれぞれ作業ディレクトリ C:\Code\CppApp および C:\Code\NetApp にします。Visual SourceSafe を使用してソース ファイルにインデックスを付けるには、プロジェクト上で作業ディレクトリを設定する必要があることに注意してください。 また、バージョン管理のラベル "First Version" をプロジェクトに付けました。 最後に、両方のプロジェクトを作成しました。 これで、両者ともインデックスを付ける準備が整いました。

まず、Visual SourceSafe プログラム、SS.EXE、Perl、およびソース サーバー プログラムがすべて、使用するコマンド シェルのパス内にあることを確認する必要があります。 バージョン管理プログラムがこのパス内にないと、ソース ファイルの取り出しは自動的に失敗し、わけの分からないまま混合表示ウィンドウが表示されます。

使用するサーバーをコマンド行に指定できると、ソース サーバーのヘルプに示されますが、それは Visual SourceSafe では実際には指定できないことに注意してください。SSDIR 環境変数を Visual SourceSafe データベースに設定する必要があります。 ここの例では、次のようなコマンド プロンプトを発行します。

set SSDIR=\\Critias\BugslayerSource\

Visual SourceSafe での別の問題として、現在のプロジェクトの概念があります。それは、SourceSafe Explorer GUI の末尾で選択したプロジェクトです。Visual SourceSafe のソースのインデックス付けでは、現在のプロジェクトが「優先」されます。それは、作業対象とする予定のプロジェクトが、現在のプロジェクトであると想定されることを意味します。バージョン管理システム内にあるプロジェクトが 1 つのときは問題ないのですが、実際はそうはいきません。Visual SourceSafe のインデックス付けを正常に機能させるには、Visual SourceSafe の現在のプロジェクトを、インデックスを付けようとしているプロジェクトに設定する必要があります。コマンド行でのインデックス付けの場合、SS.EXE cp オプションを使用して設定することができます。ここの例では、次のようなコマンドを実行します。それは、ルート プロジェクトを処理しようとしているからです。

ss cp $/

次に、C:\Code で、サーバーおよびサーバー パス用の論理名を定義することによって、新規の SRCSRV.INI を作成します。Debugging Tools for Windows の \SDK\SRCSRV ディレクトリ内の例 SRCSRV.INI に、それに関する詳細が示されています。ここでの Visual SourceSafe データベースの場合、SRCSRV.INI は次のようになります。

[variables]
BSSRC=\\critias\BugslayerSource\

(Visual SourceSafe では、等号の右側の値は、末尾の円記号 (\) も含めて、SSDIR 環境変数内の値と正確に一致する必要があります。)

 

ほぼあらゆるケースで、インデックス付けには SRCSRV.INI ファイルしか必要ありません。 そこには、処理対象の PDB ファイル内のサーバーの BSSRC 論理名とロケーションが保管されます。 マシン CRITIAS が停止し、バージョン管理を移動する必要が生じた場合、デバッガの SRCSRV.DLL と同じディレクトリにこの SRCSRV.INI を移動します。すると、このファイル内の設定によって、PDB ファイル内に組み込まれているインデックス付けが指定変更されます。 これで、PDB ファイルのインデックス付けをやり直さなくて済みます。

また、デバッグ中に SRCSRV.INI を使用して、読み取りアクセス専用のミラーリングされたバージョン管理システムをデバッガで指し示すこともできます。[variables] セクションの下の SRCSRV.INI 内に社内のすべてのバージョン管理システムを取り込んでもかまいません。

SRCSRV.INI は、いくつかの場所に置くことができます。 ソース サーバー コード ディレクトリをマシン上のそれ独自のスポットにコピーした場合、そこに各自のバージョンを置くことができます。インデックス付けツールは、それを自動的に選出します。 あるいは、インデックス付けコマンドの実行場所である現行ディレクトリ内に置いてもかまいません。 また、SRCSRV.INI がどこにあるかをツールに知らせるには、コマンド行オプションを使用するか、あるいは、そこを指し示すように SRCSRV_INI 環境変数を設定します。 ここの例では、SRCSRV.INI をソース コード用のルート ディレクトリ (C:\Code) に置いて、そこからすべてのインデックス付けコマンドを実行します。

インデックス付けの開始

インデックス付けを開始するときには、ソースやシンボルなどがどこにあるかをツールに知らせるために、コマンド行オプションを設定する方が多いと思われます。 ここでは、Visual SourceSafe を使用するので、VSSINDEX.CMD を実行してインデックス付けを行います。 また、SSINDEX.CMD /System=VSS を使用することもできました。 コマンド行オプションには、-? および -?? の 2 つのヘルプ オプションがあります。疑問符の数が多いほど、詳しいヘルプが表示されます。 このヘルプには、すべての種類のスイッチと環境変数が示されます。

どのバージョン管理システムにとっても本当に重要なコマンド行オプションが 4 つあり、5 番目のオプションは、Visual SourceSafe 用です。 第 1 のオプションは /Ini です。これは、使用する SRCSRV.INI ファイルを指定します。 上述のとおり、このファイルは、所定のロケーションに置かれていれば、自動的に見つけ出されます。 しかし、具体的に指示したい場合は、/Ini を使用してロケーションを手動で設定します。

/Source スイッチは、ソース コードのルートがどこから開始するかをインデックス付けツールに指示します。 既定値は現行ディレクトリです。 インデックス付けスクリプトは、指定のディレクトリとその下で見つかったすべてのソース ファイルを、バージョン管理システム内のファイルに関連付けようと試みます。

/Symbols スイッチは、PDB ファイルの検索時に巡回されるルート ディレクトリを示します。 バージョン管理システムの 1 つ以上のソース コード ファイルが PDB ファイルに入っている場合、その PDB ファイルには、SRCSRV ストリームのインデックスが付けられます。これについては、後で取り上げます。

Visual SourceSafe の場合のみ、/Label コマンドが必要です。Visual SourceSafe における制限事項が原因で、ソース インデックス付け機能は、特定のディレクトリ内のソース ファイルのバージョンの見当を付けることができません。 したがって、まず、Visual SourceSafe を使用してプロジェクトのルートにラベルを設定してから、その名前をソース インデックス付けツールに渡す必要があります。 バージョン管理を正常に保つためには、常にビルドにラベルを付ける必要がありますが、その作業が面倒になりすぎてはなりません。

最後のコマンド行オプション /debug が最も重要です。 ソースのインデックス付けツールには大きな欠点があります。すなわち、何らかの問題が検出された場合に、PDB ファイルにインデックスが付いていないと、何か不都合なことが起きてもそれは通知されません。 すべて順調に見えていながら、デバッグを開始すると何も首尾よく運ばなくなります。 そのような場面で、/debug が役に立ちます。 常に、/debug を指定してインデックス付けコマンドを実行すれば、何が順調で何が順調でないかを確かめることができます。

次に、インデックス付けを実行してみましょう。 コマンド シェルが開かれていて、現行ディレクトリは C:\Code であるとします。 これがソース コードおよびシンボルのルートであるので、コマンド行オプションを何も設定する必要はありません。SRCSRV.INI ファイルもそのディレクトリ内にあるので、そのコマンド行値を設定する必要もありません。 ラベル "First Version" は、既に SourceSafe 内で適用済みです。 最後に、サーバーを指定するように SSDIR 環境変数を設定しました。 VSSINDEX /Label="First Version" /debug を実行すると、[図 3](https://msdn.microsoft.com/ja-jp/ee679802#fig3)に示されている出力が生成されます。

お分かりのとおり、VSSINDEX.CMD はいずれかの PDB ファイルを見つけ出し、それにインデックスを付けました。 技術的には、これらはすべて首尾よく運びます。 vc80.pdb と記入された行までスキャンしていくと、次の行には、ゼロ個のソース ファイルが見つかったと示されています。 これは、どの VC?0.PDB ファイルの場合でも正常です。なぜなら、それらは処分可能なファイルであるからです。 ただし、必要な PDB ファイルのうちの 1 つでも、ゼロ個のソース ファイルが見つかったと報告した場合は深刻な問題になります。 しかし、インデックス付けエラーが起きても、インデックス付けツールは停止しません。 それは、/debug オプションを指定したうえで、すべての PDB ファイルが正しくインデックスを付けられているかどうかを慎重に調べる必要があることを意味します。

[図 3](https://msdn.microsoft.com/ja-jp/ee679802#fig3)の出力は、インデックス付けのパフォーマンスを低下させる可能性のある別の問題を示しています。 /Symbols スイッチは既定値に設定したままにしました。その場合、現行ディレクトリは、すべての PDB ファイルのルートとして扱われます。 インデックスを付けられた最後の PDB ファイルは、ファイル C:\Code\NetApp\obj\Debug\NetApp.pdb でした。これは、.NET アプリケーションが作成されてから、指定の出力ディレクトリにコピーされた場所です。 これらのファイルにインデックスを付けると、インデックス付けの時間は 2 倍かかります。

保守を簡単にするためには、中央ディレクトリあるいはディレクトリ ツリーに対してアプリケーションを作成するのが最善策です。 ここの例では、あらゆるものが、C:\Code\Debug ディレクトリに対して作成されています。 したがって、コマンド行 VSSINDEX /Label= "First Version" /symbols=.\Debug /debug を実行した場合、シンボル サーバーに入れた PDB ファイルのみが VSSINDEX によってインデックスを付けられます。

ドライブのルート ディレクトリとの直接の結び付きを、すべてのソースのディレクトリが失った場合、ソースのインデックス付けは機能しないことに注意してください。Source Server ツールを初めて使用したときに行ったセットアップは、そのようなものであったので、どの PDB ファイルにもインデックスを付けることはできませんでした。 ここの例では、ソース コードを故意に C:\Code ディレクトリに置いて、そのような問題に対処しました。 どのソース ディレクトリもルートに結び付いていない場合、ビルド システムを変更して、ソース コードを少なくとも 1 つ下のレベルに置く必要があります。

サブプロジェクトでの作業

インデックス付けしようとしているプロジェクトが、バージョン管理システム内のサブプロジェクトである場合、また別の発見があります。 たとえば、ここのサンプルには、$/Happy という 1 つのルート プロジェクトがあり、その下に $/Happy/Foo および $Happy/Bar という 2 つのサブプロジェクトがあると仮定します。 さらにその下には、コードのすべての部分にとって必要なすべてのサブプロジェクトがあります。 次に、$/Happy/Foo のソース インデックス付けのみを担当したとし、作業ディレクトリは D:\Dev\Happy\Foo であると仮定します。 インデックス付けコマンド vssindex /debug /Symbols=.\Debug を実行すると、ゼロ個のソース コード ファイルがインデックス付けされたことが示されます。

なぜでしょうか。 最初の想定が間違っていたからです。ソースのインデックス付けコマンドでは、現行ディレクトリが検討の対象となり、それに対応してバージョン管理プロジェクトが突き合わせられると考えたのですが、それは誤りでした。 ソースのインデックス付けの場合の既定のプロジェクトは、バージョン管理システムのルートです。 (ここのテストではすべて、Visual SourceSafe を使用していることを思い起こしてください。 他のバージョン管理システムの場合、何らかのテストを実施する必要があります。)

コマンド行で vssindex /debug /Symbols=.\Debug /Project=Happy/Foo を使用する必要があります。Visual SourceSafe のソースのインデックス付けコードは、前に付ける必要のある $/ を SS.EXE に付けます。 ルートの場合でも、常に /Project コマンド行オプションを指定するように心がけてください。 (この場合も、さまざまなシナリオをテストして、ご自分の環境では何が功を奏するかを確かめる必要があります。)

.STREAM ファイルが PDB ファイルに書き込まれたことを確認できれば、何も問題はないということです。 このストリームを検証するために、PDBSTR.EXE を使用してその内容を確かめます。PDBSTR.EXE は少し扱いにくいので、それに配慮しながらコマンド行オプションを配備し、出力では、PDB の中には何もストリームはないように見えるようにする必要があります。 最初のパラメータは ?r (PDB の読み取り) でなければならず、 2 番目のパラメータは ?p:file.PDB (表示する PDB)、 そして最後のパラメータは、表示したいストリーム (常に ?s:srcsrv) です。 [図 4](https://msdn.microsoft.com/ja-jp/ee679802#fig4) は、CPPAPP.PDB に対して PDBSTR.EXE を実行して得られた出力を示しています。

ini セクションでは、ソース インデックス付けバージョン、使用するバージョン管理システム、およびインデックス付けが実行された時刻を指定します。 デバッガは、変数セクション内の SRCSRVCMD 値を見つけ出して、% 記号が前後に付いた値を、ビルドする変数として扱います。 [図 4](https://msdn.microsoft.com/ja-jp/ee679802#fig4)では、抽出先として適したファイルとディレクトリを指定してバージョン管理システムを呼び出すためにビルドされるコマンド行が VSS_EXTRACT_CMD に収められています。 ソース ファイル セクションには、変数に埋め込むときに使用する値が示されています。 どの行でも、1 つのソース ファイルに対するデータがそれぞれアスタリスクで示されています。 最初の値は、PDB から持ち越されたソース ファイルへのパスです。2 番目の値は、この場合は BSSRC ですが、使用するバージョン管理データベースを示します。3 番目の値はバージョン管理プロジェクトであり、最後の値はバージョン管理内のファイル バージョンです。 それらがすべて、SRCSRV.DOC に解説されています。

この時点で、デバッグを開始する準備は既にできています。ただし、ソースのインデックス付けを正常に進めるのに必要な手の込んだすべてのステップが明らかになったという前提で、[図 5](https://msdn.microsoft.com/ja-jp/ee679802#fig5)のチェックリストに従って、ソース サーバーの真髄にせまりたいと思います。 それによって、準備作業に要する時間が節約されることを期待しています。

WinDBG を使用したデバッグ

ついに、待ちに待った瞬間がきました。 デバッガでどのようにソース サーバーを使用できるかを見てみましょう。 ソース サーバーのサポートを WinDBG、NTSD.EXE、あるいは CDB.EXE でオンにするのは、シンボル サーバーのサポートをオンにするのに似ています。 すべての WinDBG セッションを対象にこれをグローバルにオンにするには、デバッグ対象を開かないまま WinDBG を開始してから、ファイル メニューでソース ファイル パスを選択し、[Source Search Path] ダイアログを開きます。 あるいは、_NT_SOURCE_PATH 環境変数を設定してもかまいません。 どちらの場合も、値は SRV* Cache Dirでなければなりません。

ソース サーバーの使用が予定されていることが、SRV からデバッガに知らされます。さらに、アスタリスクの後のビットは、すべてのファイルのローカル キャッシュとして使用される予定のディレクトリを指定していることも知らされます。 ダウンロード キャッシュを設定することを強くお勧めします。 その設定を行わないと、ソースは、Debugging Tools for Windows のインストール ディレクトリ内の Src ディレクトリにプルされてしまいます。 また、規定のディレクトリは Program Files 内にあるので、ユーザー アクセスの最も少ない環境で正しく実行している場合は、そのプルは中止されます。 ソース パスのグローバル設定以外に、.srcpath コマンドを使用してデバッグ セッション中にいつでもパスを設定することができます。

WinDBG でのデバッグを開始した後、ソース コードを必要とするどの操作でも、図 6に示されているダイアログがポップアップします。 [Perform this action each time from now on] を選択すると、その決定は HKCU\Software\Microsoft\Source Server\Warning 内の規定値に書き込まれるので、このセキュリティ ダイアログは再度表示されなくなります。


図 6 セキュリティ アラートをオフにする

おそらく、セキュリティ警告をオフにして、ソースを取得するためのバージョン管理コマンドの実行に WinDBG を直接ジャンプさせるのが望ましいとお考えでしょう。 ソース サーバーを使用すれば、デバッガが真っ先にソースを探す場所はそこになります。 高速のバージョン管理システムの場合、それは問題になりませんが、誰でも高速のシステムを持っているとは限りません。

セキュリティ ダイアログを無効にする代わりの方法として、SRCSRV.INI のコピーを Debugging Tools for Windows プログラム ディレクトリに入れておきます。 そこは、SRCSRV.DLL が置かれている場所であるので、ファイルをそこで探せばよいことが分かります。SRCSRV.INI が持っているバージョン管理システムが [trusted commands] セクション内にある場合、セキュリティ ダイアログはスキップされます。 信頼されている各コマンドごとに、左側にバージョン管理システムのコマンド行プログラムのベース名を指定します。 右側では、バージョン管理システムのコマンド行プログラムへの完全パスを指定します。 ここのシナリオでは、[trusted commands] セクションは次のようになります。

[trusted commands]
ss.exe=C:\VSNET8\Microsoft Visual SourceSafe\ss.exe

ここの例では、SRV*C:\Code\Source のソース パスを使用しました。 図 6 では、C:\Code\Source\BSSRC\CppApp\CppApp.cpp\First Version という、バージョン管理がファイルを抽出する先のパス全体が表示されています。C:\Code\Source\ の部分は自明ですが、この文字列の残りの部分は、特定のファイルを識別しています。BSSRC ディレクトリは、PDB ファイルのインデックス付けに使用した SRCSRV.INI ファイルに指定したタグ値に一致します。CppApp ディレクトリは、バージョン管理プロジェクトを識別し、CppApp.cpp は、ソース ファイルを識別します。 この例では、/Label= "First Version" コマンド行オプションを指定することで、ソースにインデックスを付けました。Visual SourceSafe 以外のバージョン管理システムの場合に、ラベルを指定しないと、RAW ファイル バージョン番号が示されます。 シンボル サーバーと同様に、ソース サーバーは、ファイル システムをデータベースとして使用して、必要とするファイルのバージョンを固有識別します。SRCSRV.DLL 内のソース サーバー コードは、抽出ディレクトリ内にファイルが既に存在することを検出すると、バージョン管理に問い合せないで、ソース ファイルをそこからロードします。

ソース サーバーのキャッシュ ディレクトリを、シンボル サーバーのキャッシュ ディレクトリの下のディレクトリに設定しようと考えています。 そのシンボル サーバーのディレクトリが C:\SYMBOLS であるとすると、ソース サーバーのキャッシュ ディレクトリは、C:\SYMBOLS\SOURCE となります。 このようにして、すべてのキャッシュは同じ場所に置かれることになり、ディスク スペースの再利用時には、単にすべてを消去するだけで最初からやり直すことができます。

Microsoft ソース サーバーの [Security Alert] ダイアログの [Yes] ボタンをクリックした場合、すべて順調であれば、ソース コードと共に WinDBG がポップアップします。 問題がある場合、.srcnoisy コマンドを使用して、バージョン管理コマンド行ツールの実行結果が WinDBG からレポートされるようにします。

Visual Studio 2005 でのデバッグ

Visual Studio 2005 でのソース サーバーのデバッグのセットアップは、WinDBG におけると同様に簡単です。 環境変数を使用する代わりに、[Options] ダイアログの機能を使用します。 [Options] ダイアログを表示したら、[Debugging] > [General] にナビゲートします (図 7 を参照)。 [Enable source server support] および [Print source server diagnostic messages to the Output window] オプションにチェックをつけます。 次に、[OK] をクリックします。すると、ソース サーバーの使用は、セキュリティ上の潜在的な問題の原因となることが通知されます。 [Yes] をクリックして、先に進みます。

Visual Studio を使用してデバッグを開始すると、図 8に示されているようなセキュリティ ダイアログが表示されます。 そこで、プログラムを実行するかどうかを選択することができます。 残念なことに、シンボル サーバーを信頼するのでこのダイアログを再度表示する必要はないことを知らせるオプションはありません。そのため、各ソース ファイルごとに上記のダイアログが表示されます。 ただし、バージョン管理システムを使用して [trusted commands] セクションを定義する SRCSRV.INI ファイルを Visual Studio のインストール ディレクトリ\Common7\IDE ディレクトリに入れるオプションはあるので、このセキュリティ ダイアログは今後は表示されません。


図 7 Visual Studio でソース サーバーをオンにする


図 8 デバッグのセキュリティ検査

Visual Studio がソース サーバーのダウンロード キャッシュを置く場所の見当が付かない方もおられるかもしれません。 既定のロケーションは C:\Documents and Settings\Username\Local Settings\Application Data\SourceServer です。 これが優れているのは、適切なセキュリティが配備されるので、ログインしたユーザーしかディレクトリを表示できなくなるからです。 ここでのログイン アカウントは、管理者アカウントでは決してありませんが、ソース サーバーのキャッシュ ディレクトリを WinDBG と共有するのが望ましいので、デバッガどうしの間でソースを共有しています。 WinDBG ソース パスを SRV* C:\Documents and Settings\Username\Local Settings\Application Data\SourceServer に変更できる一方で、ソース ファイルをシンボル サーバー ディレクトリの下に置いておきたいと思います。

この問題から、Visual Studio がどこでキャッシュ ディレクトリを設定するかを突き止める探索が始まります。 レジストリを調べると、HKCU\Software\Microsoft\VisualStudio\8.0\Debugger の下でようやく SourceServerExtractToDirectory が見つかりました。ここが、キャッシュ ディレクトリの設定が置かれている場所です。

レジストリ キーを手動で変更するつもりはないので、JavaScript プログラム VsSourceServerCache.JS (これは、この記事のダウンロード用ソース コードの一部です) を作成しました。 コマンド行オプションを渡さない場合、プログラムは単に現在の設定を示すだけです。 有効なディレクトリをコマンド行で渡した場合、プログラムはソース サーバーのキャッシュ ディレクトリを変更します。

さいわい、Visual Studio のインポートおよびエクスポートの設定ウィザードを使用すれば、マシンどうしの間で設定が正しくエクスポートおよびインポートされます。 注意してください。キャッシュ ディレクトリを指定しないで新規のマシンに設定をインポートすると、ソース サーバーのファイルのプルは自動的に失敗してしまいます。 キャッシュ ディレクトリが存在することを常に確認してください。

Visual Studio でのソース サーバーの使用でトラブルが発生した場合、必ず出力ウィンドウを確認します。 そこには、ソース サーバーでのすべての問題のレポートが表示されます。またそこは、異常な動作が発生したときに最初に調べる場所でもあります。 さらに、Visual Studio のソース ファイル タブの上をマウスで移動して、ソース ファイルがどこに置かれているかを検証することもできます。つまり、開かれているファイルへの完全パスがツール ヒント上に表示されます。 最後に、WinDBG と同様に、Visual Studio はソース サーバーを通してソース コードを取り込んでから、生パスを PDB ファイル内で検索します。

Visual Studio 2005 を使用したデバッグに関する最後のヒントとして、一般的に WinDBG は年あたり 2 つのパブリック リリースを保有するのに対して、Visual Studio のほうがその数は少ないということです。 この記事の冒頭で述べたとおり、WinDBG の 6.6.6.0 バージョンには、Visual Studio のものより新しいバージョンの SRCSRV.DLL が備えられています。 常に最新の x86 バージョンの WinDBG を Visual Studio のインストール ディレクトリ\Common7\IDE ディレクトリにコピーしているので、常に最新バージョンを使用しています。Visual Studio 2005 は 32 ビットの x86 プログラムであるため、64 ビットの Windows を使用している方でも、32 ビット バージョンが必要です。

インデックス付けの向上と簡素化

ここまでで、ソース サーバーを使用するために必要なすべての作業を解説しました。 しかし、もし私と同じ考えをする方なら、CMD ファイルをビルドに組み込んでいる最中に、ソースのインデックス付けの出力を通して見るよりも気のきいたやり方があることにすぐに気付くかもしれません。 たとえば、プロジェクト内で 5 つのバイナリがヒットした場合、目がうつろになって、重要な点を見落としてしまいます。 生じうるエラーの検索のための自動化された手段があれば役に立ちます。 言うまでもなく、ソースのインデックス付けは、ビルドの一部でなければならないので、さらに別の MSBUILD タスクに取り組むのはたいへんです。 最近私が執筆したコラム (https://msdn.microsoft.com/en-us/magazine/cc163643.aspx)では、単体テストおよびコード カバレッジのいくつかの MSBUILD タスクを紹介しました。 そのコードを基盤とし、タスクを使用してビルドを行って、ソースのインデックス付けを処理しようと思います。

この場合の要件は単純です。 ソース ファイルのインデックス付け中に何らかの問題が検出されたら、プロセスにおいてエラーを生成し、MSBUILD が停止するようにします。 そのような問題は、いくつかの経緯で明らかになります。明らかになるのは、たとえば、SSINDEX.CMD からの何らかの種類の [ERROR] 出力や、ゼロ個のソース ファイルが見つかったことを報告する PDB ファイルのインデックス付けの場合や、さらには、インデックスが付けられているソース ファイルがまったくない (不注意から生じる問題であることは確かです) 場合です。

私の計画は、使用可能なベース タスク SourceIndexTask の作成でした。それを、すべてのバージョン管理製品別のタスクの継承元にすることができます。 また、VssSourceIndexTask も作成する予定でした。 ソースサーバーでサポートされるバージョン管理システム用のタスクを作成した方がいれば、それを私宛てにお送りください。お送りいただければ、それを Bugslayer.Build.Tasks.DLL に組み入れて、そのコードを MSDNMagazineの読者向けに掲載します。

分かりやすくするために、ソースのインデックス付けツールはパス内にあると想定とします。 ベースの SourceIndexTask は、そのパス内でツールを探します。MSBUILD は、実行の前提として完全パスを必要とするからです。 第 2 の想定として、ディレクトリのセットアップが、バージョン管理プロジェクト名に一致しているとします。 製品別のインデックス付けスクリプトでは、プロジェクトおよびクライアントのパラメータを設定できますが、最上位のディレクトリでインデックス付けスクリプトを実行できるように、ディレクトリ名とプロジェクト名を並べておくほうが望ましいと思われます。 不一致があった場合、各ディレクトリごとにソースのインデックス付けを別々に実行する必要があります。

SourceIndexTask には、symbols、sources、および VersionControlSystem という、PROJ ファイル内で設定する必要のある 3 つのプロパティがあります。 symbols は、PDB ファイルが置かれているディレクトリを定義します (これは、PROJ ファイルからの相対パスでかまいません)。 ここでは sources を必須プロパティにしました。なぜなら、ソース ディレクトリへのタスクの切り替えが実行前に分かったほうが、インデックス付けコマンドの働きが向上するからです。VersionControlSystem パラメータには、RequiredAttribute のマークは付けられていません。それは、VssSourceIndexTask などの派生クラスが、バージョン管理システムを重複して指定する必要が生じるのは望ましくないと考えたからです。 ただし、VersionControlSystem を定義しないと、あたかもそれが必須であったものとして、タスクはエラーになってしまいます。

SourceIndexTask の最初のオプション プロパティは IniFile です。 これを使用して、SRCSRV.INI ファイルを明示的に指定できます。AdditionalCommandLineOptions プロパティを使用すれば、派生タイプの範疇にはない任意のコマンドを追加できます。 独自のタスク クラスを持っていないバージョン管理システムの使用時に、すぐにインデックス付けを開始したい場合にも、これは便利です。 バージョン管理別のファイルを AdditionalCommandLineOptions に追加し、すぐに起動して実行することができます。 明らかに、AdditionalCommandLineOptions 内の値のエラー検査は行われません。 最後のオプション プロパティは IgnorePdbFiles です。 不首尾を表す「ゼロ個のソース ファイルが見つかりました」というエラー メッセージをタスクで無視したい場合に、PDB ファイルをこの ITaskItem 配列に入れておきます。 これが起きる可能性があるのは、自動生成のバイナリや、前述の VC?0.PDB タイプのファイルなどの項目の場合です。SourceIndexTask は VC?0.PDB を無視することを既に承知していますが、このプロパティによって、エラー検査の対象にするつもりのないさらに別の PDB を追加する場所が用意されます。

Visual SourceSafe を処理するタスクである VssSourceIndexTask は、Server および Label という 2 つの必須パラメータを定義します。 Server は、バージョン管理システムを収めています。 タスクは、SSDIR 環境変数を設定してから、インデックス付けコマンドを実行して、Visual SourceSafe の処理中のバグに対処します。

Label プロパティはインデックス付けコマンドを設定して、個々の Visual SourceSafe ラベルを使用します。Visual SourceSafe の最上位レベルの下にあるプロジェクトから取り込んだソース コードにインデックスを付けると、そのインデックス付けは成功しません。Project プロパティが設定されていないと、VssSourceIndexTask は Source プロパティを取り出して、それを完全パスに拡張し、そのパスを Visual SourceSafe プロジェクトに変換します。 とりあえず必要な処置として Visual SourceSafe プロジェクトをファイル ディレクトリにマップすると、自分の側では何もしなくても、/Project オプションが自動的に設定されます。Project プロパティを設定した場合、その値は手付かずになってタスクによって変更されることはありません。そのため、VssSourceIndexTask はそれをソース インデックス付けツールに直接渡します。

Project プロパティにまつわる問題の 1 つとして、$/ (Visual SourceSafe のルート) とは直接関わらないプロジェクトで作業する場合、MSBUILD プロジェクトにおいて Project プロパティを手動で / に設定する必要があります。 サブプロジェクトの取り扱い方法を考え抜いたすえの、結論としての最善策がこれです。 もっと良いアイデアのある方は、ぜひ連絡してください。

前述のとおり、Visual SourceSafe 用のソースのインデックス付けツールでは、現在のプロジェクトが規定のプロジェクトになります。そのため、別のプロジェクトに対してソースのインデックス付けを実行すると、失敗してしまいます。 この問題に対処するために、VssSourceIndexTask は cp コマンド行を使用して SS.EXE を呼び出し、現在のプロジェクトを Project 値に設定します。 これで、現在のプロジェクトはどれかを考える必要はなくなり、ソース コードのインデックス付けはもっと楽な作業になります。

作業がすべて順調であることを確かめるには、ソース コードのルート ディレクトリ内の MSBUILD プロジェクト Index.Proj を調べます。 このプロジェクトは、この記事のソース コードにインデックスを付けます。

実装という点から見ると、それほど目立った出来事はありません。 ソースのインデックス付けの出力内でエラーを探す方法を模索するのが、最もやりがいのある作業でした。 当初、出力をファイルにリダイレクトし、とっておきの正規表現スキルを使用して、すべての問題を解析しようと考えていました。 しかし、コマンド行ツールの killer クラスである Microsoft.Build.Utilities.ToolTask から SourceIndexTask を派生したときに、このクラスが標準の I/O リーダーをセットアップすることが確認されました。 コマンド行ツールが出力行を書き出すごとに、ToolTask は指定変更可能な LogEventsFromTextOutput メソッドを呼び出します。 つまり、必要な作業は、エラー、警告、処理中の PDB ファイル、および処理結果を見つけ出すための小さい状態マシンをセットアップするだけだったということです。 大掛かりな作業は、ソースのインデックス付けのツールでのすべての問題を見つけ出してそれを解説することでした。

まとめ

この記事を読んで、ソース サーバーの重要性がお分かりになったと思います。 シンボル サーバーとの併用によって、最も詳しい情報を使用してあらゆるデバッグ作業に取り組む態勢が整います。 いくつかの対処すべき問題点はあっても、生産性が向上したことをすぐに実感されるはずです。 なぜ今までこれなしにデバッグしていたのかと思われるまでに、そんなに時間はかかりません。


John Robbins 氏は、.NET および Windows プラットフォームを専門とするソフトウェアのコンサルティング、教育、および開発に携わる Wintellect 社の設立者の 1 人です。 同氏の最新の出版物は、「Debugging Applications for Microsoft .NET and Microsoft Windows」(Microsoft Press、2003 年版) です。 同氏の連絡先は、www.wintellect.comです。


 この記事は、 MSDN マガジン - 2006 年 8 月号からの翻訳です。 .


Back to top