読者です 読者をやめる 読者になる 読者になる

メッセージを表示する

Win32 ETW

思い出した頃に更新することで定評のある ETW ブログのお時間です。

前々回までで、プロバイダー、コントローラー、コンシューマーと、ETW のコンポーネントを一通り作ってみました。
が、今までのプロバイダーは、イベントに付随するデータが何もありませんでした。
そのため、「何かが起きた」ことはわかっても、「何が起きたか」はわかりません。
それでは実用になりませんので、今回から、イベントに様々なデータを付随させて、実用的なサンプルにして行きたいと思います。

ま、今回はその準備段階のようなものですが。

例によってコードは GitHub に。
今回はプロバイダーコンシューマーをいじっています。
コントローラーはこれまで作ったものでも logman でも構いませんが、.etl ファイルの名前には注意してください(trace3.cmd を使うとファイルを生成できます)。

メッセージとは

今回のテーマは「メッセージを表示する」です。
メッセージとは何かと言いますと、プロバイダー マニフェストであらゆる要素に定義されている message 属性のことです。
provider 要素にも、channel 要素にも、task 要素にも、opcode にも level にも event にも、様々な要素に message という属性があります。

この message 要素には、表示するメッセージをそのまま書くのではなく、(多言語対応のために)文字列テーブルへの参照を書きます。
まず、文字列テーブルを見てみましょう。

一つの言語につき一つの resources 要素を用意します。
string 要素の ID は、今回は種別.識別子のような形式にしましたが、別にこうするという決まりはありません。

続いて、これを参照するマニフェストの方ですが、

このように、$(string.文字列テーブルのID)という形式で指定します。

その他の要素にも同じように書いてあります。全文GitHub を見てください。

ロケールの設定

割と忘れがちなので気を付けたいポイント。

これをやっておかないと、日本語が表示されなかったり文字化けしたりします。

イベント データへのアクセス

コードは抜粋です。

TdhGetEventInformation 関数でイベント情報を取得します。
この関数を呼び出すためには、プロバイダー マニフェストが登録されている必要があります(RegisterProvider3.cmd を使ってください)。

最初に必要なバッファのサイズを取得し、バッファを確保してから再度呼び出しています。*1

イベント情報は TRACE_EVENT_INFO 構造体の形式で得られます。
この構造体には多数の、なんとかOffset という名前のメンバーがありますが、これらがそれぞれ、メッセージの場所を指し示しています。
このオフセット値は、この構造体の先頭の場所から何バイト先にメッセージがあるかということを表していますので、構造体のアドレスにオフセット値を足してやる必要があります。

構造体のイメージはこんな感じです(値は例です)。

メンバー オフセット
ProviderGuid +0 {824E8551-...
...
ProviderNameOffset 136
LevelNameOffset 168
ChannelNameOffset 176
...
+136 "SampleProvider3\0"
+168 "やばい\0"
+176 "おぺれーしょなる\0"

オフセット値が 0 ということは、そのメッセージは存在しないということですので、0 でないかどうかはチェックする必要があります。

ところで、Admin チャンネルの方に出している win:Informational のレベル名として「情報」という文字列が取得できません。
うーむ。レベル 0 ~ 15 はシステム規定だから決め打ちで何とかしろってことなのかなあ…。でもそれでは多言語化できないし…。どうするんだろう。

イベント ビューアーで見てみる

スクリーンショットは載せませんが、サンプルを動かしてみたら、是非、ご自分の環境でイベント ビューアーを開いてみてください。
いろいろなところが日本語化されていますよ。

次回予告

今回、マニフェストがぐっと複雑になりましたので、次回はそれに関してちょっと補足説明の回を設けます。
その次で、イベントにプロパティを付与してみます。

*1:malloc/free とか __try/__finally なんていう C++ 屋さんに見つかったらマサカリで頭かち割られそうなコードを書いているのは、単なる趣味です