イベント トレース
ETW によりデバッグおよびパフォーマンス調整を改善する
Insung Park 博士 and Ricky Buch
この記事の内容 : :
- ETW アーキテクチャについて理解する
- Windows Vista の ETW の新機能
- イベント プロバイダ API を使用したプログラミング
|
この記事は次のテクノロジを使用しています:
Windows Vista
|

コンテンツ
今日のソフトウェア システムは、サイズも複雑性もますます増大しており、ソフトウェアの開発と管理は難しい課題となっています。すべての実行状態を把握することはほとんど不可能に近く、アプリケーションでは開発者が予測しなかった動作が見られることもよくあります。さらに、膨大な数のハードウェアの組み合わせや絶えず変動する作業負荷の特性により、さまざまなソフトウェア問題の診断がますます困難になっています。当然のことながら、信頼性および管理の容易性そのものが重要な機能になりつつあります。これらの機能は、インストルメンテーションの必要性をもたらします。
ソフトウェアの実行における主要なエラー状態のいくつかに対して精巧なインストルメンテーションを追加することで、問題のデバッグに費やす時間が大きく削減されます。インストルメンテーションはその他の場合にも役立ちます。大きなコンピュータ グループの場合、ソフトウェアおよびハードウェアの障害やリソース不足状態など、好ましくない状況は管理ビジネス環境で監視し、対処する必要があります。また、パフォーマンス上の問題は、外部作業負荷、構成パラメータ、および基となるハードウェアおよびソフトウェア状態に左右されるため、診断が難しい場合があります。インストルメンテーションはそのような問題の解決にも役立ちます。パフォーマンスが低下した期間中の運用コンピュータのトレースにより、開発者や管理者は、パフォーマンス不良のコンポーネントまたはサービスを特定したり、開発時には予測しなかったボトルネックを識別したりすることができます。最後に、IT 担当者はさまざまな管理ツールを使用して、トランザクション トレースからリソースの使用状況の統計情報を計算し、能力計画や傾向分析に使用します。
Event Tracing for Windows® (ETW) は、オペレーティング システムで提供される高速な汎用トレース機能です。ETW は、カーネルに実装したバッファおよびログのメカニズムを使用し、ユーザー モード アプリケーションとカーネル モード デバイス ドライバの両方によって発生するイベントのトレース メカニズムを提供します。さらに、ETW ではログ機能を動的に有効または無効にすることができ、再起動や、アプリケーションの再起動を必要とせずに、運用環境で詳細なトレースを簡単に実行できます。ログ機能のメカニズムでは、非同期の書き込みスレッドによってディスクに書き込まれるプロセッサごとのバッファを使用しています。これにより、大きな支障を来たすことなく大規模サーバー アプリケーションでイベントを書き込むことができます。
ETW は、Windows 2000 で初めて導入されました。それ以来、さまざまなコア OS およびサーバー コンポーネントがアクティビティのインストルメント化のために ETW を採用し、今日では Windows プラットフォーム上で主要なインストルメント テクノロジの 1 つとなっています。インストルメンテーションに ETW を使用するサードパーティ製アプリケーションも増えており、Windows 自体で提供されるイベントを利用しているものもあります。また ETW は、開発時のデバッグのために "printf" スタイルのメッセージをトレースする使いやすいマクロのセットを提供する Windows プリプロセッサ (WPP) ソフトウェア トレース テクノロジにも抽出されています。
Windows Vista™ では、ETW はメジャー アップグレードを済ませており、その最も重要な変更は統合イベント プロバイダ モデルと API です。つまり、新しい統一 API では、ログ トレース機能、およびイベント ビューアへの書き込み機能を組み合わせて、イベント プロバイダ用に 1 つの一貫した、使いやすいメカニズムを構成しています。同時に、開発者やエンド ユーザーによる操作性を向上させる複数の新機能も追加されました。この記事では、新しい ETW プロバイダ モデルを紹介し、Windows Vista ベースのアプリケーションで新しいモデルを採用する方法について説明します。
まず、ETW アーキテクチャおよび使用方法モデルの概要を示したうえで、新しいイベント モデルと API について説明します。続いて、イベント インストルメンテーション設計および実装に関する簡潔なガイドを提供し、最後に ETW セッションの制御、ログに記録されたイベントの処理、およびそれらの分析による高レベルのレポート生成などの付属のツールを吟味します。
Event Tracing for Windows
ETW のコア アーキテクチャを図 1 に示します。図に示したとおり、ETW の主なコンポーネント タイプには、イベント プロバイダ、コントローラ、コンシューマ、およびイベント トレース セッションの 4 つがあります。バッファおよびログは、イベント トレース セッションで発生します。イベント トレース セッションでは、イベントが受け入れられ、トレース ファイルが作成されます。ETW セッションで使用できるログ モードは複数あります。たとえば、セッションは、コンシューマ アプリケーションにイベントを直接送るように構成することも、特定サイズに到達したときにラップによって古いイベントを上書きするように構成することもできます。セッションごとに作成された別々のライター スレッドにより、ファイルまたはリアルタイムのコンシューマ アプリケーションにそれらがフラッシュされます。高パフォーマンスを実現するために、プロセッサごとのバッファを使用することにより、ログ パスでロックの必要性がなくなります。
図 1 ETW のアーキテクチャ (画像を拡大するには、ここをクリックします)
イベント プロバイダは、ETW セッションにイベントを書き込む論理エンティティです。記録可能で有意義なアクティビティはすべてイベントである可能性があり、ETW に記録されたイベントとして表現されます。イベント プロバイダは、ユーザー モード アプリケーション、管理されるアプリケーション、ドライバ、その他のソフトウェア エンティティのいずれかである可能性があります。唯一の要件は、イベント プロバイダは登録 API を使用して ETW にプロバイダ ID を登録する必要があることです。プロバイダはまず ETW に登録し、ETW ロギング API を呼び出すことにより、コード内のさまざまな位置からイベントを書き込みます。ETW コントローラ アプリケーションによってプロバイダが動的に有効にされると、ロギング API の呼び出しにより、コントローラで指定した特定のトレース セッションにイベントが送信されます。イベント プロバイダからトレース セッションに送信される各イベントは、イベント メタデータおよび追加の可変ユーザー コンテキスト データを含む固定ヘッダーで構成されます。多くの OS コンポーネントにおけるイベント インストルメンテーションの増大により、Windows Vista の単純なアプリケーションにさえもイベント プロバイダであるコンポーネントが複数含まれています。
イベントがセッションに記録されると、ETW によってユーザー提供のデータと共に、その他のデータ項目がいくつか追加されます。それらには、ログ スレッドのタイムスタンプ、プロセスおよびスレッド ID、プロセッサ番号、CPU 使用率データなどが含まれます。これらのデータ項目は、ETW イベント ヘッダーに記録され、プロバイダによって提供された可変イベント コンテンツと共にイベント コンシューマに渡されます。トレース コンシューマの多くでは、その分析においてこれらのデータ フィールドが不可欠です。
コントローラは、ETW セッションを開始および停止し、ETW セッションに対してプロバイダを有効にします。デバッグや診断などの一部のシナリオでは、詳しいトレースの収集のために必要に応じてコントローラ ツールが呼び出されます。これとは対照に、管理者を対象としたイベントのように、常にイベント ビューアに流れる必要のあるイベント (以下のセクションで定義します) の場合、プロバイダは登録時にイベント ログ サービスで自動的に有効にされます。セッションを制御するためには、コントローラには Windows Vista 上で ETW 権限が必要です。この権限は、既定では特権ユーザーの小さなグループのみに与えられます。
最後に、コンシューマとは、ログ ファイルを読み取るアプリケーション、またはセッションでリアルタイムのイベントを待機しそれらを処理するアプリケーションを指します。イベントの利用はコールバックに基づいています。コンシューマはイベント コールバックを登録します。ETW ではイベント コールバックがイベントごとに呼び出されます。イベントは発生順に ETW コンシューマに送られます。イベントをさまざまな形式にダンプする汎用のイベント コンシューマ ツールがあります。図 2 は、カーネル プロバイダによってログに記録された "Process" イベントの XML ダンプを示しています。これは Windows Vista の tracerpt.exe ツールで生成されたものです。このイベントは、メモ帳プロセスの開始を示します。イベントには、プロバイダによってログに記録されたカスタム ユーザー コンテンツが含まれるため、イベントを正しくデコードするためにはある種のメタデータが必要になります。新しい API を使用するプロバイダは、レイアウト情報と共にプロバイダが書き込むすべてのイベントを定義するイベント マニフェスト (XML ファイル) を提供する必要があります。汎用コンシューマ アプリケーションでは、トレース データ ヘルプ (TDH) API を使用してイベントのメタデータを取得し、イベントをデコードし、表示します。

Figure 2 プロセス開始イベントの XML ダンプ
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Guid="{9e814aad-3204-11d2-9a82-006008a86939}" />
<EventID>0</EventID>
<Version>2</Version>
<Level>0</Level>
<Task>0</Task>
<Opcode>1</Opcode>
<Keywords>0x0</Keywords>
<TimeCreated SystemTime="2006-12-18T12:26:27.887309500Z" />
<Correlation
ActivityID="{00000000-0000-0000-0000-000000000000}" />
<Execution ProcessID="3396" ThreadID="3260" ProcessorID="0"
KernelTime="390" UserTime="195" />
<Channel />
<Computer />
</System>
<EventData>
<Data Name="UniqueProcessKey">0xFFFFFA800143FA80</Data>
<Data Name="ProcessId">0x10EC</Data>
<Data Name="ParentId">0xD44</Data>
<Data Name="SessionId">1</Data>
<Data Name="ExitStatus">0</Data>
<Data Name="UserSID">guest</Data>
<Data Name="ImageFileName">notepad.exe</Data>
<Data Name="CommandLine">notepad</Data>
</EventData>
<RenderingInfo Culture="en-US">
<Opcode>Start</Opcode>
<Provider>MSNT_SystemTrace</Provider>
<EventName xmlns=
"http://schemas.microsoft.com/win/2004/08/events/trace">
Process</EventName>
</RenderingInfo>
<ExtendedTracingInfo xmlns="
http://schemas.microsoft.com/win/2004/08/events/trace">
<EventGuid>{3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c}</EventGuid>
</ExtendedTracingInfo>
</Event>
多くのユーザーにとっては、トレースは特定のプロバイダからイベントを収集することを意味します。この考え方では、イベント トレース セッションは概念的コレクション全体で 1 つまたは複数のプロバイダに関連付けられており、セッション自体 (ログ エンジン) は無視されることがよくあります。ETW アーキテクチャでは、より動的で柔軟なトレースおよびイベント管理が可能です。ここでは、セッションおよびプロバイダは別々の領域に存在します。ETW セッションを開始または停止したり、セッションに対して動的にプロバイダを有効にしたりするのはコントローラです。したがって、コントローラはセッションに対してプロバイダのグループを有効にしたり、しばらくしてからその一部を無効にしたり、さらに同じセッションに対して別のプロバイダを後から有効にしたりすることができます。セッションはカーネル内で実行され、プロバイダに静的には関連付けられていません。同様に、プロバイダは通常イベントがどのセッションに記録されるかは認識していません。同時にプロバイダ、コントローラ、およびコンシューマである大規模なアプリケーションとサービスがあります。API は、プロバイダ、コントローラ、およびコンシューマのすべての動作に対して提供されており、アプリケーションはそれらを任意で組み合わせた役割を担うことができます。ただし、通常開発者はイベント プロバイダのみを実装し、付属のツールを使用してトレースを収集し、表示します。
プロバイダとトレース セッションの分離の利点の 1 つは、トレースがクラッシュやハングなどのアプリケーション問題に対して免疫を持つ (影響を受けなくなる) ようになることです。クラッシュの前にプロバイダによってログに記録されたイベントは、トレース ファイルに既に存在しなければカーネル メモリに入れられます。これにより、アプリケーションの異常のデバッグにおいて特に役立ちます。
前に説明したように、イベントは開発者、IT 管理者、および管理ツール開発者によって、デバッグ、監視、診断、能力計画などに使用されます。イベントに基づく一般的な分析方法論は、以下の手法に分類できます。
検索する ユーザーはイベント ダンプで単一の重要なイベント、または既知のイベントの小さなパターンを検索できます。これは通常、エンドユーザーの問題に対応して障害のケースをデバッグする場合や、イベント ログ内で重大な障害を検索する場合に行います。
デルタ分析 ETW では、フォームの単純なデルタ分析など、各イベントのタイムスタンプと CPU 使用率の数値がキャプチャされるため、次のコード サンプルでは、
Property (Event B)-Property (Event A)
アプリケーション アクティビティの応答時間と CPU 使用率統計が考慮されています。2 つのイベントがアクティビティの開始と終了をマークする場合、運用モードのアプリケーションから収集したイベントの大きなセットは、この方法で処理することにより、応答時間と CPU 使用率統計の概要を生成できます。
統計分析 特定のイベントを考慮するだけで、ソフトウェアの動作をさらに理解することができます。
ステート マシンおよびリソースの追跡 イベント セットが十分な数になると、ステート マシンの構築が有効になり、それに続いてトレースに基づくシミュレーションも有効になります。たとえば、コア OS アクティビティの大半は、ETW イベントによってインストルメント化されているため、OS トレースを使用して、スケジューラ、メモリ、I/O アクティビティなどを追跡管理するステート マシンを構築できます。
エンドツーエンド トレース 大型のアプリケーションは、複雑な相互接続によって統合されているいくつもの分散型コンポーネントで構成されていることがよくあります。それらのアプリケーションは、それぞれ異なる役割を担う複数のマシンを含んでいる場合がよくあります。要求追求を対象とするインストルメンテーションは、そのような環境で問題を診断するうえでの困難を克服するためのアプローチの 1 つです。このフレームワークでは、アプリケーションを通して、現在処理中の要求の一意の ID と共にアクティビティを記録するインストルメンテーション ポイントが追加されました。トレースを収集したら、イベントの利用時に同じ要求に対応するイベントが関連付けられるので、そのアクティビティと進行状況を追跡できます。後で、特定の要求で、異なるサービス段階における問題を個別に調べることができますが、要求のグループを統計分析によって要約することもできます。
Microsoft ダウンロード センターから入手できるサーバー パフォーマンス アドバイザ (SPA) は、エンドツーエンド トレース用に設計されたサーバー インストルメンテーションを採用したサーバー管理および診断ツールです。
統一イベント プロバイダ モデルおよび API
Windows Vista では、さらに多くの機能と拡張セキュリティ オプションを備え、かつ使いやすくなった新しいイベント プロバイダ API セットを紹介しました。新しい API は、イベント ビューアへの書き込みにも使用され、トレースとイベント ログを 1 つの一貫した API セットに統合します。このセクションでは、モデルと API についてさらに詳しく説明します。新しい API と既存のものとの違いは、必要に応じて指摘します。
ETW プロバイダになるためには、EventRegister API を使用してソフトウェア コンポーネントを ETW に登録する必要があります。EventRegister では、プロバイダを一意に識別する PvoviderId という GUID が必要になります。プロバイダは OS エンティティにバインドしている必要はないので、ソフトウェア エンティティ (アプリケーション、共有 DLL、またはドライバ) はすべて、プロバイダとして登録できます。登録は通常、DLL アタッチ ルーチンやドライバ エントリなど、コンポーネントへのエントリ ポイントで行います。登録ハンドルが返され、後続のロギング API 呼び出しで使用されます。最後に、プロバイダ実行の終了時に EventUnregister が呼び出されます。
既存のプロバイダ API では、プロバイダは通知の有効化/無効化のコールバックを提供する必要があります。つまり、コントローラでプロバイダが有効にされた場合、登録されたコールバック関数が有効な設定で呼び出されます。コールバックのパラメータの 1 つは、プロバイダが有効にされているセッションのハンドルです。有効コールバックを受け取ると、プロバイダはグローバル変数 (TracingOn など) を設定し、トレース機能がオンかオフかを指定し、セッション ハンドルも保存します。その後、コールバックから取得したこのセッション ハンドルを、ロギング API 呼び出しで使用します (TracingOn の値に基づく)。
新しい ETW プロバイダ モデルでは、ETW がプロバイダに代わって有効設定を呼び出します。つまり、プロバイダはロギング呼び出しを登録し、呼び出します。それらが現在有効か無効かをチェックする必要はありません。ロギング API 内では、ETW は有効設定をすばやく確認し、有効な場合のみイベントをセッションに送信します。有効でない場合、ロギング呼び出しは破棄されます。そのため、新しいモデルでは有効/無効コールバックはオプションです。ただし、シナリオによっては有効コールバックがまだ必要な場合もあります。たとえば、ステート マシン構築のためのインストルメンテーションでは、トレースの開始と終了時にスナップショットまたは状態停止イベントが必要な場合がよくあります。
ロギング API である EventWrite は登録ハンドル (古いモデルにおけるセッション ハンドルに代わる) を必要とします。ロギング呼び出しで登録ハンドルを使用すると、有効設定がプロバイダに対して透過的になります。新しいモデルでは、ログで使用するハンドルが非透過ハンドルであるため、ETW ではイベントのマルチキャストが可能になりました。つまり、プロバイダを複数の ETW セッションに対して有効にすることができます。これはセッション ハンドルを使用していた古いモデルではできなかったことです。
ETW は、プロバイダが有効かどうかをテストする EventEnabled と EventProviderEnabled という別々の APIを提供します。ロギング API は、イベントを書き込む前に有効設定を確認しますが、トレースが有効な場合、プロバイダで追加の作業が必要になる場合があります。そのようなシナリオの 1 つに、プログラム実行には必ずしも必要ではない情報提供目的のイベント データの収集と構成が含まれます。これらの API を使用すると、プロバイダはトレースが有効かどうかを必要に応じていつでも確認できます。
前に説明したように、ユーザーは変数コンテキスト データを各イベントに追加できます。ロギング API では、イベント固有のデータ項目の取得に拡散/収集メカニズムを使用しています。呼び出し元は、データ記述子の配列を構築することにより、追加のイベント データ項目を渡します。データ記述子とは、ポインタおよびサイズ フィールドを含んでいる構造体です。したがって、ユーザーはログに記録するデータ項目ごとに 1 つのデータ記述子を追加します。データ記述子の構築を簡単にするためにマクロ (EventDataDescCreate) が提供されています。その後、ログ記録時に ETW はユーザーが提供したコンテンツをセッション バッファにコピーします。
対応するイベント マニフェストでは、<Template> タグを使用してイベントのレイアウトを指定する必要があります。テンプレートは、各イベントに含まれるユーザー指定のコンテキスト データを記述します。テンプレートは、レイアウトを定義できます。このレイアウトには、整数型や文字列型などの個々のデータ フィールド、または構造体の配列などの複雑なデータ構造を含めることができます。テンプレートは、必ずしもすべてのイベントに必要ではありません。テンプレートが指定されていない場合は、そのイベントにユーザーが提供したデータがないものと見なすことができます。マニフェストでは、テンプレートは開始および停止イベントなど、同じコンテキスト情報を持つ複数のイベント間で共有できます。コンシューマ アプリケーションがイベントに遭遇すると、TDH API を使用してイベント テンプレートが検索され、それに従って可変イベント データがデコードされます。古い API を使用しているプロバイダは、Windows Management Instrumentation (WMI) Managed Object Format (MOF) によりレイアウト情報を提供します。
図 3 は、登録とログ記録に新しいイベント API を使用するサンプルのプロバイダ コードを示しています。これは、ユーザー モード プロバイダ API を採用するユーザー モード プロバイダですが、対応するカーネル モード プロバイダ API のセットも使用できます。図 3 の最初のイベントは、ユーザーが提供した 2 つのデータ項目を書き込みます。1 つは ULONG 型で、もう 1 つは Null で終わる WCHAR 文字列です。EventDataDescCreate は、適切なデータ記述子の配列を構築するために呼び出します。さらに、ここに示した EventWrite API に加え、ロギング API が後 2 つあります。EventWriteString と EventWriteTransfer です。EventWriteString では、非マニフェスト文字列の簡単なログ記録が可能です。EventWriteString が呼び出されると、ETW は、イベント データが Null で終わる単一の WCHAR 文字列であることを示すマークをヘッダーに付けます。ヘッダーでこれを確認したコンシューマは、TDH でイベント スキーマを検索しなくても、ユーザー データを文字列として処理します。EventWriteString は、マニフェストを変更せずに文字列のすばやいログ記録を有効にします。

Figure 3 ETW プロバイダ
#include <myevents.h> // Header generated from manifest.
// Contains MyProviderId and event descriptors.
REGHANDLE MyProvRegHandle;
ULONG MyInteger;
PWCHAR MyString;
ULONG MyStringLength;
EVENT_DATA_DESCRIPTOR DataDescriptor[2];
...
// Register the ETW provider.
Status = EventRegister(&MyProviderId, // ProviderId (GUID)
NULL, // Optional Callback
NULL, // OPtioanl Callback Context
&MyProvRegHandle); // Registration Handle
...
// Construct DataDescriptor and write an event with
// MyInteger and MyString.
EventDataDescCreate(&DataDescriptor[0], // DataDescriptor
&MyInteger, // Pointer to the data
sizeof(ULONG)); // Size of data
EventDataDescCreate(&DataDescriptor[1], &MyString, MyStringLength);
Status = EventWrite(MyProvRegHandle, // Registration Handle
MyEventDescriptor1, // EventDescriptor
2, // DataDescriptor array size
DataDescriptor); // DataDescriptor array
...
// Write another event with no user data.
if (EventEnabled(MyProvRegHandle, MyEventDescriptor2)) {
// Do extra work if enabled and write event.
...
Status = EventWrite(MyProvRegHandle, MyEventDescriptor2, 0, NULL);
}
...
// Unregister the ETW provider.
Status = EventUnregister(MyProvRegHandle);
EventWriteTransfer および EventActivityIdControl API は、エンドツーエンド トレース インストルメンテーションに対応しています。前に説明したように、エンドツーエンド トレースは、複数のユーザー要求に対して異なるアクティビティを同時に実行するサーバー アプリケーションを対象とするインストルメンテーション方法論です。たとえば、Web ページでのスクリプト実行の要求は、クライアント マシンから送信され、サーバー上のネットワーク レイヤに到達します。その後、HTTP ドライバ、IIS、ASP.NET エンジンを通過し、さらに別のマシン上の Exchange Server も通過する可能性があります。エンドツーエンド トレースの目的は、後ほどデバッグやパフォーマンス分析に使用するために、ETW イベントを介してこの要求に関するすべてのアクティビティを記録することです。これには個々の要求を識別できる一意の ID が必要です。関連付けは、使用中にこの一意のアクティビティ ID を使用して実行できます。
ETW では、各イベントで ActivityId を使用することにより、このニーズに対応しています。新しい API で記録されたすべてのイベントは、実行中のスレッドに保存された現在のアクティビティ ID を自動的に取得します。アクティビティ ID は <System> セクションの <Correlation ActivityId> タグの XML ダンプに表示されます。プロバイダは、EventActivityIdControl API を使用して実行中のスレッドのアクティビティ ID を取得、設定、および作成できます。アクティビティ ID は、要求と共に複数のコンポーネント間を移動できます。あいにく、パブリック プロトコルの制約や設計上の制約などにより、アクティビティ ID を適用できないケースもあります。EventWriteTransfer API は、アクティビティ ID の転送を示す転送イベントを書き込みます。EventWrite のすべてのパラメータに加え、EventWriteTransfer は ActivityId と RelatedActivityId という 2 つの引数をとります。
すべてのイベントはプロバイダ ID でスタンプされ、標準的なイベント情報を定義し、追加の識別情報とセマンティクスを提供するイベント記述子というエンティティが割り当てられます。開発者は、インストルメンテーション設計段階でインストルメンテーション ポイントのイベント記述子を定義し、対応するエントリをイベント マニフェストに書き込みます。開発環境のメッセージ コンパイラは、指定されたイベント マニフェストからヘッダー ファイルにイベント記述子を生成します。それらのイベント記述子は、その後ソース ファイルに含められ使用されます。プログラム上、イベント記述子は、ID、バージョン、チャネル、レベル、オペレーション コード、タスク、およびキーワードのフィールドで構成される構造体です。
typedef struct _EVENT_DESCRIPTOR {
USHORT Id;
UCHAR Version;
UCHAR Channel;
UCHAR Level;
UCHAR Opcode;
USHORT Task;
ULONGLONG Keyword;
} EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR;
イベント ID は、プロバイダのイベントを一意的に識別するために使用します。イベントがマニフェストで定義されている場合、入力が必要なのはイベント ID のみです。イベントに遭遇すると、コンシューマはプロバイダ ID (GUID) とイベント ID (USHORT) を使用してそのマニフェストを検索します。同様に、バージョンは、同じセマンティクスとイベント ID を維持しながら、後のリリースで変更および拡張するイベントを提供します。したがって、イベント ID とバージョンは、プロバイダ ID と共に、イベントを一意に識別できます。
チャネルは、対象ユーザーのイベントのグループを定義します。チャネルは、管理者、運用、分析およびデバッグのいずれかのタイプに属します。管理チャネルで発生するイベントは、処理可能イベントです。管理者は、イベントを受け取った時点で、イベントの発生理由、およびその処理方法を即座に知る必要があります。運用チャネルで発生したイベントは、高レベルの監視ツールおよびサポート スタッフを対象としています。これらのイベントは詳細なコンテキストを提供し、管理者チャネル イベントよりも頻繁に発生します。管理者および運用チャネルにグループ化されたイベントは、自動的にイベント ログに送られ、イベント ビューアに表示されます。分析チャネルは、エキスパート レベルのサポート担当者、または詳細な診断ツールおよびトラブルシューティング ツールを対象とする従来のトレース用です。デバッグ チャネルは、デバッグ メッセージに使用し、開発者が利用するイベントを含みます。既定では分析とデバッグ チャネル イベントは有効ではありません。チャネルにより、異なる目的の、異なるユーザーを対象としたイベントを 1 つの API セットで追加できます。
プロバイダを有効にする際、コントローラはレベル (1 バイトの整数) とキーワード (8 バイトのビット マスク) を指定できます。レベルとキーワードは、ETW インストルメンテーションに次元を追加するために使用されます。レベルは、イベントの重大度または詳細レベルに基づいてフィルタ機能を有効にするように設計されています。キーワードは、プロバイダのサブコンポーネントを示すように設計されています。たとえば、開発者はイベントを情報イベントと重大なエラーのイベントに分けることができます。また、アプリケーションのサブコンポーネントに異なるキーワードを割り当てることもできます。異なるレベルとキーワードを使用して選択的にプロバイダを有効にすることにより、トレース コントローラは、サブコンポーネント B のエラー イベントのみのログや、サブ コンポーネント A および C のすべてのイベントのログなどのためにプロバイダを有効にすることができます。古い API を使用しているプロバイダの場合、キーワードは 4 バイトで、レベルとキーワードによるフィルタは、プロバイダ コードで明示的に行う必要があります。コントローラが特定のレベルを有効にすると、コントローラが指定した値以下 (重大度が高いまたは同等) のレベルの値のイベントもすべて有効になります。レベルとキーワードは開発者がカスタム設計してイベントに割り当てることができますが、図 4 に示したように、あらかじめ定義されたものもあります。

Figure 4 セキュリティ レベル
<levels>
<level name="win:LogAlways" symbol="WINEVENT_LEVEL_LOG_ALWAYS"
value="0" message="$(string.level.LogAlways)"> Log Always
</level>
<level name="win:Critical" symbol="WINEVENT_LEVEL_CRITICAL" value="1"
message="$(string.level.Critical)"> Only critical errors </level>
<level name="win:Error" symbol="WINEVENT_LEVEL_ERROR" value="2"
message="$(string.level.Error)"> All errors, includes win:
Critical </level>
<level name="win:Warning" symbol="WINEVENT_LEVEL_WARNING" value="3"
message="$(string.level.Warning)"> All warnings, includes
win:Error </level>
<level name="win:Informational" symbol="WINEVENT_LEVEL_INFO"
value="4"
message="$(string.level.Informational)"> All informational
content, including win:Warning </level>
<level name="win:Verbose" symbol="WINEVENT_LEVEL_VERBOSE" value="5"
message="$(string.level.Verbose)"> All tracing, including
previous levels </level>
</levels>
タスクとオペレーション コードは、各イベントに追加情報をアタッチするために使用します。タスクは、インストルメント化する共通の論理コンポーネントまたはタスクを指定します。タスクは、コンポーネントが目的を達成するために行う主要な高レベルのステップを表すことがよくあります。オペレーション コードは、イベントの書き込み時に実行する特定の操作を示します。たとえば、Windows カーネル プロバイダでは、すべての I/O 操作イベントを "FileIO" タスクのグループにまとめます。オペレーション コードは、作成、開く、読み取り、書き込みなどの操作を示します。ID、バージョン、チャネル、レベル、およびキーワードとは異なり、タスクとオペレーション コードは情報の追加の目的のみに使用し、インストルメンテーションの制御やメタデータの検索には影響しません。
イベント記述子とレイアウトはイベント マニフェストで指定されます。開発者は、インストルメンテーションの設計時にイベント マニフェストを作成します。イベント マニフェストは XML で作成します。XML ではユーザー定義のチャネル、タスク、オペレーション コード、レベル、およびキーワードが適切な XML タグで指定されます。ETW であらかじめ定義したチャネル、レベル、およびオペレーション コードも使用できます。ただし、事前定義されたチャネルはグローバル チャネルを参照するため、管理者対象のイベントのみに使用してください。各イベントに対して異なるメタデータ フィールドが組み合わせられ、イベント ID に一意的に関連付けた <Event> タグで定義されます。また、イベントには、コンシューマがイベントを読み取る際、イベント データからの代入値と共に表示できるメッセージ文字列を含めることもできます。図 5 は、イベント マニフェスト サンプルの XML 断片を示しています。イベント インストルメンテーションの設計とインストルメンテーションの手順を追ったガイドラインについては、後ほど説明します。

Figure 5 イベント マニフェストの断片
<provider name="Microsoft-Windows-Kernel-Registry"
guid="{70eb4f03-c1de-4f73-a051-33d13d5413bd}"
symbol="RegistryProvGuid"
resourceFileName="%SystemRoot%\System32\advapi32.dll"
messageFileName="%SystemRoot%\System32\advapi32.dll">
<channels>
<channel name="Microsoft-Windows-Kernel-Registry/Analytic"
chid="RegistryEvents" symbol="REG_Events" type="Analytic"
isolation="System">This channel contains registry
events.</channel>
</channels>
<opcodes>
<opcode value="32" name="CreateKey" symbol="" />
...
</opcodes>
<keywords>
<keyword name="CreateKey" symbol="" mask="0x1000" />
...
</keywords>
<templates>
<template tid="tid_RegOpenCreate">
<data name="BaseObject" inType="win:Pointer"
outType="win:HexInt64" />
<data name="KeyObject" inType="win:Pointer"
outType="win:HexInt64" />
<data name="Status" inType="win:UInt32"
outType="win:HexInt32" />
<data name="Disposition" inType="win:UInt32" />
<data name="BaseName" inType="win:UnicodeString"
outType="xs:string" />
<data name="RelativeName" inType="win:UnicodeString"
outType="xs:string" />
</template>
...
</templates>
<events>
<event value="1" symbol="ETW_REGISTRY_EVENT_CREATE_KEY"
template="tid_RegOpenCreate" opcode="CreateKey"
channel="RegistryEvents" level="win:Informational"
keywords="CreateKey"
message="$(string.Registry.RegOpenCreate)"/>
...
</events>
</provider>
...
<localization>
<resources culture="en-US">
<stringTable>
<string id="Registry.RegOpenCreate"
value="Registry key %6 was created with status %3." />
</stringTable>
</resources>
</localization>
マネージ ETW プロバイダ API は、Microsoft® .NET Framework 3.5 (コード名 "Orcas") でも使用可能です。管理コンシューマおよびコントローラ API は現在計画段階です。System.Diagnostics.Eventing には EventProvider クラスが含まれます。EventProvider は、上記のすべての機能のメソッドをネイティブ アプリケーション用に提供します。ユーザーは、プロバイダ GUID によってクラスをインスタンス化し、そのクラス インスタンスを使用してイベントをログに記録する必要があります。EventDescriptor は、ネイティブ API のイベント記述子に相当する構造体です。ネイティブ ケースとは異なり、EventDescriptors は管理されるアプリケーションに対しては生成されません。ただし、パフォーマンスと検証機能の向上を目的としたコードを生成できるツールが現在検討されています。図 6 は EventProvider の使用例を示しています。

Figure 6 マネージ ETW プロバイダ
using System.Diagnostics.Eventing;
...
static void Main(string[] ArgRead)
{
int MyInteger;
string MyString;
...
// Construct event descriptor.
EventDescriptor Event1 = new EventDescriptor(5, 0, 0, 2, 0, 0, 0);
// Instantiate event provider.
EventProvider etwProvider = new EventProvider(
new Guid("d58c126f-b309-11d1-969e-0000f875a5bc"));
...
// Write an event with MyInteger and MyString.
etwProvider.WriteEvent(ref Event1, MyInteger, MyString);
...
}
さらに、Windows Vista 上の ETW では、プロバイダに対するセキュリティ オプションが改善されました。既定では、どのプロバイダでもイベントを登録し、書き込むことができます。ただし開発者は、承認されたユーザーのみが GUID にプロバイダを登録できるように、その GUID に制限を加えることができます。プロバイダは、誰がそれを有効にできるかを指定することもできます。さらに ETW では、コントローラがセッションを安全と宣言できます。つまり、そのセッションは特定のユーザー グループのみからイベントを受け取ることを意味します。
イベント プロバイダ インストルメンテーションでバイナリがコンパイルされると、プロバイダがインストールされます。ユーザーは、付属のコントローラ アプリケーションである logman ツールを使用して、そのプロバイダからイベントを収集できます。下記の logman.exe コマンドでは、mysession というセッションを開始し、プロバイダを有効にする 2 つの ETW コントロール API 呼び出しが発行されます。
> logman start mysession -p <provider name> -o mytest.etl -ets
mysession セッションは、mytest.etl というファイルにイベントを書き込みます。ログのモードやバッファ構成などをカスタマイズする logman.exe オプションは多数あります。ここで、<provider name> にはマニフェストのプロバイダ名、または EventRegister API を使用してプロバイダが登録に使用した GUID のプロバイダ名を使用できます。プロバイダ GUID が 11223344-5566-7788-99aa-bbccddeeff00 の場合、実際のコマンド ラインは次のようになります。
> logman start mysession
-p {11223344-5566-7788-99aa-bbccddeeff00}
-o mytest.etl -ets
ETW では、コントローラはプロバイダを登録して実行する前に、それをあらかじめ有効にすることができます。これによりユーザーは、バイナリ イメージの読み込みの制御が難しい DLL およびドライバの起動トレースを収集できます。コントローラはセッションを開始し、プロバイダの実行が開始される前にそれを有効にします。プロバイダは登録と同時に有効になります。
必要に応じてイベントを収集したら、logman.exe の停止オプションを使用してセッションを停止できます。
> logman stop mysession -ets
mytest.etl というトレース ファイルは、logman コマンドを実行したディレクトリと同じディレクトリに保存されます。Tracerpt.exe を使用すると、このファイルのイベントのダンプを取得できます。
このコマンドでは、ダンプ ファイル (既定では dumpfile.xml) とテキスト形式の概要ファイル (既定では summary.txt) が生成されます。概要ファイルには、イベント統計の簡単な概要が含まれます。ダンプ ファイルには図 2 に示したようなイベントの XML ダンプが含まれます。イベント ヘッダーのデータ フィールドは、<System> セクションに示されます。プロセス ID、スレッド ID、プロセッサ ID、および CPU 使用率は、<System> 内の <Execution> タグで指定します。さらに、<System> セクションでは、イベントのイベント記述子フィールドの値も示されます。出力ファイル名は、tracerpt.exe コマンドのオプションとしてユーザーが指定できます。Windows Vista には、トレースの収集や表示が可能なその他の GUI ツールがありますが、それらについては後ほど説明します。
Windows Vista には既に数百のイベント プロバイダがインストールされています。PerfMon またはイベント ビューア、あるいは logman.exe コマンドにより、それらのプロバイダの一覧を表示できます。
設計と実装のガイドライン
このセクションでは、イベントを定義するマニフェスト ファイルを作成し、ETW API 呼び出しでコードをインストルメント化し、イベント プロバイダを構築してインストールする手順について説明します。
インストルメンテーション マニフェストを設計する インストルメンテーション マニフェストは、イベント プロバイダ、およびそのイベント プロバイダが ETW に記録するイベントを定義する XML 形式のファイルです。各イベントには、標準メタデータおよび可変イベント データ セクションがあります。マニフェストは手動で作成することも、SDK (ecmangen.exe) に含まれるマニフェスト ジェネレータ ツールを使用して作成することもできます。次に手順を示します。
- イベント プロバイダを作成し、プロバイダ ID (GUID) を指定します。
- コンポーネントの論理タスク、およびそれらの論理タスク内の特定のインストルメンテーション ポイントを定義します。これにより、タスク、およびプロバイダが作成するイベントのオペレーション コードが定義されます。
- イベントをログに記録するサブコンポーネントを定義し、各サブコンポーネントにキーワードを割り当てます。これにより、コントローラはイベントを選択的に有効にすることができます。
- 設計するイベントの対象ユーザーを評価し、各対象ユーザーのチャネルを定義します。
- 可変ユーザー コンテキスト データを定義する異なる構造体を作成します。これは <Templates> セクションで行います。
- マニフェストの <Events> セクションで、すべてをまとめてイベントを定義します。各イベントにシンボリック識別子、重大度、キーワード、チャネル、タスク、オペレーション コード、およびテンプレートを割り当てます。ローカライズ可能な、イベント固有のメッセージを追加することもできます。
ETW API を使用してコードをインストルメント化する マニフェストでイベントが定義されたら、統一 API を使用して ETW にイベントを書き込みます。ネイティブ アプリケーションの場合は、まずマニフェストからイベント定義を含んだヘッダー ファイルを作成し、プロバイダ コードに含めることができるようにします。その後、ETW API を使用して前に設計したイベントを書き込みます。このプロセスは次のとおりです。
- 上記で作成したマニフェストでメッセージ コンパイラ (mc.exe) を実行します。これにより、イベントのイベント記述子構造体を含むヘッダー ファイルが生成されます。各構造体インスタンスは、イベントのシンボリック識別子と同じ名前を持ちます。メッセージ コンパイラはリソース文字列ファイルも生成します。生成されたヘッダー ファイルはプロバイダ コードに含める必要があります。
- イベントを発行するコードを書き込む前に、プロバイダは EventRegister 関数を呼び出すことにより、マニフェストで定義されたプロバイダ ID で登録する必要があります。ロギング API 呼び出しや対応する EventUnregister 呼び出しで使用するためには、EventRegister から返された登録ハンドルを保存する必要があります。
- マニフェストで定義したイベントをログに記録するには、EventWrite を呼び出します。イベント固有のデータは、図 3 に示したようにデータ記述子配列を使用して渡されます (開発者は、データ フィールドがマニフェストで指定された対応するイベント テンプレートと正確に一致することを確認する必要があります)。イベントのヘッダー ファイルで定義された正しいイベント記述子構造体を渡してください。
- 必要なイベントをすべて記録したら、EventUnregister を呼び出し、登録ハンドルを渡します。
インストルメント化したバイナリをコンパイルする イベントを定義するマニフェスト情報は、構築プロセス時にコンパイルし、リソースとしてプロバイダ実行可能バイナリにアタッチする必要があります。ローカライズ可能な文字列はすべて、このコンパイルしたマニフェストから取り出し、別個にコンパイルしてリンクする必要のある別のリソース ファイルに含められます。プロバイダ バイナリを展開する場合、コントローラとコンシューマの両方がイベント プロバイダとイベント定義を検索できるように、マニフェスト情報がシステムにインストールされている必要があります。必要なツールはすべて Windows Vista でネイティブに、または SDK から使用可能です。必要な手順は次のとおりです。
- イベントにローカライズ可能なメッセージ文字列がある場合、rc.exe を実行してリソース ファイルを .res にコンパイルする必要が発生することがあります。
- Visual Studio® を使用した場合、開発者はあらかじめ構築されたコマンドを含めて rc.exe を実行することにより、.res コンパイル リソースを生成し、コンパイルされたこのリソース ファイルをプロジェクトに含めることができます。
- cl.exe または link.exe を使用して、コンパイル済みのリソース ファイルをリンクし (Visual Studio のユーザーの場合は、これはステップ 2 で完了しています)、アプリケーションまたはドライバをコンパイルします。
- プロバイダをインストールする場合、-im オプションで wevtutil.exe ツールを実行し、イベントを定義するマニフェストをインストールします。これにより、コントローラはイベント プロバイダを検出できるようになるほか、利用しているツールは、イベントを正しくデコードするために必要な情報を検出できます。
ツールとサポート
これまでのところ、コマンド ライン コントローラの logman.exe、および一般的なコンシューマ ツールの tracerpt.exe を使用しました。Logman.exe には、リモート マシンでのコレクションをスケジュールするオプションもあります。また、Tracerpt.exe は CSV 形式のイベントをダンプできるほか、既知のコア OS イベントの一部で -report オプションを使用してリソース利用レポートを生成できます。図 7 は、ステート マシン構築技法に基づき、既知のコア OS の一部を処理して関連付けた後で、tracerpt.exe から生成されたホット ファイル テーブルを示しています。
図 7 Tracerpt で生成されたホット ファイル レポート (画像を拡大するには、ここをクリックします)
信頼性とパフォーマンス モニタ (RPM) (Windows Vista の PerfMon を含む) では、トレースの収集のためのグラフィカル インターフェイスが提供されています。イベント トレース セッション インターフェイスを使用すると、トレース セッションの設定、有効にするプロバイダの選択、および ETW セッションの開始と停止が可能です。図 8 は、ユーザーが有効にするプロバイダの一覧を表示しているときの RPM を示しています。RPM では、イベント トレース、パフォーマンス カウンタ、および構成 (レジストリと WMI クラス) データ コレクションが単一のコレクション セットに統合されるデータ コレクタ セットの概念を導入しています。ユーザーは、トレースおよびカウンタ データに対してプロバイダが有効になっているデータ コレクション セットを作成できます。作成されたコレクション セット設定は、RPM に保存されるので、ユーザーは毎回ソースを指定しなくても、データ コレクションを簡単に開始および停止することができます。この機能は、ユーザーが既知の診断シナリオのためにトレースとカウンタを収集するプロバイダを指定できる共通の診断シナリオをサポートしています。
図 8 RPM のイベント トレース プロバイダ (画像を拡大するには、ここをクリックします)
イベント ビューアによって、イベントの収集と表示のための別の手段が提供されます。新しいイベント API は、イベント ログへ含めるイベントのためにも設計されており、管理者および運用チャネル イベントは自動的にイベント ビューアに送られ、表示され、重要なソフトウェア状態が監視されます。さらに、イベント ビューアは、分析イベントやデバッグ イベントにも対応してその機能を拡張しています。[表示] オプションにより、使用可能なすべての分析チャネルおよびデバッグ チャネルを表示でき、ユーザーは右クリックにより、トレース コレクションを有効にすることができます。図 9 では、ユーザーがレジストリ イベント プロバイダの分析チャネルを右クリックし、トレース コレクションを有効にしています。
サンプル コードは、プロバイダ、コントローラ、およびコンシューマ API のソフトウェア開発キットに含まれています。新しいプロバイダ API を使用するサンプル プロバイダには、ログ対象のイベントのマニフェストが含まれます。ドライバ サンプルも使用可能です。
図 9 分析イベント コレクションのためのイベント ビューア インターフェイス (画像を拡大するには、ここをクリックします)
まとめ
Windows Vista で新しく使用可能になった ETW の統一イベント プロバイダ モデルおよび API を紹介しました。この記事の目的は、開発者に API を紹介し、使用例と一般的ガイドラインを提供することです。トレースは、開発者の多くにとってまだ使い慣れていないテクノロジですが、この記事によって Windows プラットフォームにおける主要なインストルメンテーションおよび診断のインフラストラクチャの 1 つについての見識を深めることができることを期待しています。Windows 2000 オペレーティング システムにおいて一般的なトレース テクノロジとして導入されてから数年内に、多数のコンポーネントやアプリケーションで、トレースおよび管理容易性機能の基本として ETW が採用されてきましたが、この傾向は今後も継続しそうです。
より高い信頼性とより優れたパフォーマンスを併せ持ち、かつ管理しやすいアプリケーションを実現するために、イベント インストルメンテーション テクノロジを採用する開発者がますます増大することが予想されます。インストルメンテーションは、開発者によるアプリケーションのデバッグを助けるだけではなく、アプリケーションの展開および管理を担当するユーザーにも役立ちます。さらに、インストルメンテーションは能力計画、状況分析、およびパフォーマンス ボトルネックの特定にも役立ちます。ETW は、アプリケーションでインストルメンテーションを提供して、上記すべてのシナリオに対応できるように、有用なトレース インフラストラクチャを提供すると共に、アプリケーションをより管理しやすく、診断しやすいものにする取り組みにおいて、このテクノロジが、コア ビルディング ブロックとして基本的な役割を担うことを期待しています。
Insung Park 博士は、Windows インストルメンテーション プラットフォーム チームの開発担当者です。パフォーマンス分析、要求の追跡、インストルメンテーション テクノロジ、およびプログラミングの方法論とサポートについて、多数の論文を発表しています。Insung の連絡先は
insungp@microsoft.com (英語) です。
Ricky Buchは、Windows インストルメンテーション プラットフォーム チームのプログラム マネージャです。彼は、Event Tracing for Windows およびパフォーマンス カウンタ ライブラリの両方のテクノロジに携わっています。Ricky の連絡先は
ricky.buch@microsoft.com (英語) です。