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

ミニマムなプロバイダー

Win32 ETW

前回はミニマムなプロバイダーを作りました。
しかし、あくまでイベント ビューアーで見るためのミニマム コードであって、ETW 的に本当に最小限のコードではありません。

今回は本当にミニマムなコードを示すとともに、(イベント ビューアーでは見ることができませんので)他のツールを紹介します。
コードは GitHub を参照してください。

マニフェスト以外は前回のコードからほとんど変わっていません。

ProviderManifest0.man

<?xml version="1.0" encoding="utf-8"?>
<instrumentationManifest
  xmlns="http://schemas.microsoft.com/win/2004/08/events"
  xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <instrumentation>
    <events>
      <provider
        guid="{FF5D07C6-03BA-481F-B07F-97CB8A51FADF}"
        name="SampleProvider0"
        symbol="PROVIDERID_SampleProvider0"
        resourceFileName="Provider0.exe"
        messageFileName="Provider0.exe"
        parameterFileName="Provider0.exe">

        <events>
          <event value="1" symbol="EVENTDESC_Hello"/>
        </events>

      </provider>
    </events>
  </instrumentation>

</instrumentationManifest>

channel も template も stringTable も削りました。
ETW 的にはこれが最低限と言っていいでしょう。
もちろん、ちゃんと機能はします。

trace0.cmd

プロジェクトをビルドすると出力フォルダーに trace0.cmd というファイルができます。

logman create trace %name% -p %guid% -o "%~dp0%name%.etl" -ets

Provider0.exe

logman stop "%name%" -ets

tracerpt "%~dp0%name%.etl" -o "%~dp0%name%.xml"

発行したイベントの確認にイベント ビューアーが使えませんので、別のツールを使います。
今回は logman と tracerpt というツールを使いました。どちらも Windows に標準で入っているツールです。

logman はコントローラーです。
create trace コマンドでトレース セッションを作成し、イベントを trace_test0.etl というファイルに保存します。
プロバイダー(Provider0.exe)がイベントを発行し終えたら、stop コマンドでセッションを停止します。

tracerpt はコンシューマーです。
trace_test0.etl ファイルは人間には読めないバイナリファイルなので、読みやすい xml 形式に変換します。

trace_test0.xml

trace0.cmd を実行すると作成される結果ファイルです。

注目して欲しいのは、RegisterProvider0.cmd でプロバイダーを登録していなくても、trace0.cmd は実行可能だということです。
試してみてください。

プロバイダーを登録していない場合の出力はこうなります。

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Guid="{ff5d07c6-03ba-481f-b07f-97cb8a51fadf}" />
    <EventID>1</EventID>
    <Version>0</Version>
    <Level>0</Level>
    <Task>0</Task>
    <Opcode>0</Opcode>
    <Keywords>0x0</Keywords>
    <TimeCreated SystemTime="2014-10-06T18:44:43.260005800Z" />
    <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
    <Execution ProcessID="20936" ThreadID="16692" ProcessorID="3" KernelTime="0" UserTime="0" />
    <Channel />
    <Computer />
  </System>
  <BinaryEventData></BinaryEventData>
</Event>

マニフェストで何も指定していないので、Version、Level、Task、Opcode、Keywords がすべて 0 になっています。

RegisterProvider0.cmd でプロバイダーを登録していると、こうなります。

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="SampleProvider0" Guid="{ff5d07c6-03ba-481f-b07f-97cb8a51fadf}" />
    <EventID>1</EventID>
    <Version>0</Version>
    <Level>0</Level>
    <Task>0</Task>
    <Opcode>0</Opcode>
    <Keywords>0x0</Keywords>
    <TimeCreated SystemTime="2014-10-06T18:46:16.106633400Z" />
    <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
    <Execution ProcessID="14284" ThreadID="5096" ProcessorID="3" KernelTime="0" UserTime="0" />
    <Channel />
    <Computer />
  </System>
  <EventData>
  </EventData>
  <RenderingInfo Culture="ja-JP">
  </RenderingInfo>
</Event>

違いがわかるでしょうか。

登録していない場合、Event 要素には System 要素と BinaryEventData 要素が含まれます。
登録している場合は、System 要素、EventData 要素、RenderingInfo 要素が含まれています。

マニフェストを登録する作業は何のために必要かと言いますと、イベント データを解析するためなのです。
登録していない場合、単なるバイナリ データ(BinaryEventData)としてしか取得できません。
マニフェストがあると、もう少しわかりやすい形式で表示することができます。
ただし、今回は付随データを何も出力していないため、EventData 要素は空っぽです。

なお、System 要素の内容はどのプロバイダーでも同じなので、マニフェストがなくても解析できます。

次回予告

コントローラーとコンシューマーを作ってみましょうか。