リアルタイム モード

今回はリアルタイム モードについて説明します。

ソースコードは例によって GitHub に。
今回はコントローラーコンシューマーをまとめて説明します。

ソースコード

ソースコードの変更はわずかです。


コントローラーは EVENT_TRACE_PROPERTIES 構造体の LogFileMode メンバーに EVENT_TRACE_REAL_TIME_MODE を追加します。
コンシューマーは EVENT_TRACE_LOGFILE 構造体の ProcessTraceMode メンバーに PROCESS_TRACE_MODE_REAL_TIME を追加し、ファイル名の代わりにセッション名を指定しています。
それ以外は前回までのサンプルとまったく同じです*1

ファイル モード

リアルタイム モードとは何かを説明する前に、これまでやってきた、リアルタイムでないモードについて復習しましょう。
ちなみに、リアルタイムでないモードには、何か特段の呼び名があるわけではないようですが、ここでは便宜上、ファイルモードと呼びます。

ファイルモードは、プロバイダーが発行したイベントのデータを、一旦ファイルに溜めておいて、コンシューマーが後からそれを再生するというモードです。
もう少し細かく時系列で言いますと、こんな感じになります。

  1. コントローラーがセッションを開始する
  2. プロバイダーがイベントを発行する(イベントはトレース データ ファイルに記録される)
  3. コントローラーがセッションを終了する
  4. コンシューマーがトレース データ ファイルを開く
  5. コンシューマーがイベントを処理する
  6. コンシューマーがファイルを閉じる

このモードの特徴は、

  • セッションの寿命とコンシューマーの動作タイミングが完全に分かれており、非同期的
  • コンシューマーは同じイベントを何度でも再生できる
  • アプリケーションの現在の処理の進行状況などを追うには向かない

といったところでしょうか。
トラブルシューティングに向いていると言ってもいいかもしれません。

リアルタイム モード

対してリアルタイム モードは、こんな感じになります。

  1. コントローラーがセッションを開始する
  2. コンシューマーがセッションを開く
  3. プロバイダーがイベントを発行する
  4. コンシューマーがイベントを処理する
  5. コントローラーがセッションを終了する
  6. コンシューマーがセッションを閉じる

プロバイダーがイベントを発行するそばから、コンシューマーにイベントが流れて来ます。それ故のリアルタイム モードというわけです。
このモードの特徴は、ちょうどファイル モードの逆に当たり、

  • セッションの寿命とコンシューマーの動作タイミングが重なっており、同期的*2
  • コンシューマーは同じイベントを一度しか処理できない
  • アプリケーションの現在の処理の進行状況などを追うのに向いている

となります。
進行状況をモニタリングしたり、他のログ機構(テキスト ファイルとかデータベースとか)にログを転記し保存したりするのに適していると言えるでしょう。

サンプル プログラムを動かす際には、

  1. Controller2
  2. Consumer2
  3. Provider0

の順で(それぞれ別のコンソールで)実行してください。

なお、ファイル モードでは、コンシューマーが ProcessTrace 関数を呼ぶと、ファイル中のすべての(もしくは、ProcessTrace 関数のパラメーターで指定した条件に合致する)イベントを処理するまで、処理が戻って来ませんでした。
対してリアルタイム モードでは、コントローラーがセッションを終了するまで、ProcessTrace 関数は制御を返しません。

注意事項

MSDN にも記載がありますが、リアルタイム モード セッションは、リアルタイム モード コンシューマーがいる場合のみ作成するようにすべきです。

Windows Vista より前では、リアルタイム モード コンシューマーがいない場合、プロバイダーが発行したイベントは捨てられてしまっていました。
Windows Vista 以降では、リアルタイム モード コンシューマーがいない場合、イベントは一時的にファイルに保存されます。*3
そして、リアルタイム モード コンシューマーが動き出したとき、まず一時ファイルに保存されているイベントが消化されてから、リアルタイムのイベント処理が始まります。
つまり、イベントの取りこぼしが軽減されるようになったわけです。

しかし、一時ファイルのサイズには上限があります。
コンシューマーがいないまま、プロバイダーが大量のイベントを発行し続け、一時ファイルがいっぱいになってしまうと、それ以降、プロバイダーがイベントを発行できなくなってしまいます。
そのため、リアルタイム セッションを開始したら、可能な限り早く、リアルタイム コンシューマーを実行すべきです。

注意すべきは、あるプロバイダーから発行されたイベントは、複数のセッションで受信される可能性があるにもかかわらず、このような問題がどこか 1 つのセッションで発生すると、すべてのセッションで記録されなくなってしまうということです。

なお、EVENT_TRACE_PROPERTIES 構造体の LogFileMode メンバーに EVENT_TRACE_INDEPENDENT_SESSION_MODE を含めることで、あるセッションでの書きこみ失敗が他のセッションに影響しない(他のセッションでは書き込みに成功する)ようにすることができます。
が、この場合、どこかのセッションで問題が発生しているということに気づきにくくなるという側面もありますので、安易に使ってよいものではないと思います。
この場合、一時ファイルがいっぱいになったセッションでは、それ以上、新しいイベントは記録されません。

また、EVENT_TRACE_FILE_MODE_SEQUENTIAL の代わりに EVENT_TRACE_FILE_MODE_CIRCULAR を指定することでも、エラーになるのを防ぐことができます。
この場合、一時ファイルがいっぱいになると、古いイベントから順に捨てられていきます。

ハイブリッド モード

これも正式に呼ばれているわけではないのですが、便宜上こう命名しました。
要するに、ファイル モードとリアルタイム モードの両立です。

というのも、ファイル モードとリアルタイム モードは排他的ではありません。
実際、今回のサンプルを動かしてみるとわかりますが、ファイルは作られます。

リアルタイム モードというのは、イベントをリアルタイムにコンシューマーに流すか否かというだけであって、ファイルを作るか作らないかには関与しません。
リアルタイム モードでファイルを作らないためには、コントローラーで EVENT_TRACE_PROPERTIES 構造体の LogFileNameOffset メンバーに 0 を指定します。

ただし、リアルタイム モード コンシューマーでは、ファイルを読み取ることはできません。
コントローラーはファイル モードとリアルタイム モードを兼ねることができますが、コンシューマーはどちらか一方を選ぶ必要があります。

今回のサンプルで作られた Controller2.etl ファイルは、前回のコンシューマー(ファイル名は決め打ちなので適宜書き換えてください)や tracerpt で読み取ることができます。

次回予告

これまでプロバイダーは最小限のものを使ってきましたが、これは非実用的でした。
次回からはプロバイダーがより多くのデータを出力するようにするとともに、コンシューマーもそれを解釈できるように改良していきます。

*1:ファイル名とセッション名は排他的だったことに後から気付いたので、前回の記事を訂正しています

*2:あくまでイメージです。コンシューマーがイベントの処理を終えるまでプロバイダーが制御を返さないとかいうことはありません。

*3:これは EVENT_TRACE_PROPERTIES 構造体の LogFileNameOffset メンバーで指定するファイルとは別のもので、保存される場所はよくわかりませんが、最大サイズは MaximumFileSize メンバーの影響を受けるようです。