◆パフォーマンスモニタ returns 〜 カスタムパフォーマンスモニタの作成 その2
[ ホーム ] [ 上へ ] [ パフォーマンスモニタ returns 〜 カスタムパフォーマンスモニタの作成 その3 ]
<2.パフォーマンスDLLの作成>
レジストリにモニタ対象のアプリケーションをモニタする情報を登録して、
コントロールパネル→管理ツール→パフォーマンスから表示されるパフォーマンスデータ収集アプリを
起動すると、パフォーマンスモニタシステムは、レジストリ情報に従ってパフォーマンスDLL
をロードして、その仕様に沿ったコールバック関数を呼ぶことによって目的のデータを収集しようとします。
このコールバック関数を実装することが今回のテーマの中心になります。
システムが呼び出す関数なのでそのシグネチャは決まっていて何を実装するかも指針が示されています。
そのコールバック関数は3つあり、次のような機能を実装します。
(2a.Open関数)
パフォーマンスデータ収集アプリがRegQueryValueExを呼び出すと パフォーマンスモニタシステムはレジストリ情報を元にDLLをロードします。ただし、別のアプリが RegQueryValueExを呼んでいたらDLLはロードされています。 DLLのロード後、 1つのスレッドが最初に一度だけ呼ばれる関数がOpen関数です。プロトタイプは
DWORD WINAPI OpenPerfData(LPWSTR lpDeviceNames);
です。
引数 | lpDeviceNames | レジストリのHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\application_name\Linkage のExportエントリに格納されている文字列が渡されています。 |
戻値 | 初期化等の必要処理が正常に行われた場合は、ERROR_SUCCESSを返却するようにします。 エラーなどが発生して、以降のパフォーマンスデータ収集ができない場合は ERROR_SUCCESS以外の値を返してイベントログに記録します。この場合、システムから 以降のCollect関数、Close関数は呼ばれません。 |
(2b.Collect関数)
Open関数で初期化処理が正常に終了してERROR_SUCCESSが返されると、 システムは続いてCollect関数を呼び出します。プロトタイプは次のとおりです。
DWORD APIENTRY CollectPerfData(IN LPWSTR lpwszValue, IN OUT LPVOID *lppData, IN OUT LPDWORD lpcbTotalBytes, IN OUT LPDWORD lpNumObjectTypes);
引数 | lpwszValue | RegQueryValueExの第二引数で指定される収集しようとしているパフォーマンスオブジェクトの リストが収められている文字列(ex. "Global", "Foreign xxx", "Costly", "344 132") |
lppData | PERF_OBJECT_TYPEを先頭とした、収集したデータを含む返却すべきデータブロックの先頭位置への ポインタがシステムより提供されます.正常にデータ収集ができて、*lppDataにデータを返却できたら、 次のオブジェクトがパフォーマンスデータを返却できるように、データを格納した最終位置 (次のオブジェクトがデータを格納する最初の位置)に*lppDataの位置を移動しなければなりません。 | |
lpcbTotalBytes | Collect関数でオブジェクト/カウンタの返却データに必要だと思われる総バイト数をシステムが 推定してここの引数に提供されます。*lppDataに正常にデータが書込みできたら、 格納したバイト単位のサイズを返却します。 | |
lpNumObjectTypes | 正常にデータを取得して返却できた時、システムに返却したカウンタオブジェクトの 種類数を設定します。 | |
戻値 |
システムが推定して*lpcbTotalBytesに渡した返却用メモリ領域よりも、実際に返却するメモリ領域の
ほうが大きい場合、つまり、結果が収まらないときはERROR_MORE_DATAを返します。 それ以外、正常にデータ収集できた場合やそもそもこのカウンタオブジェクトに関連する カウンタデータを収集する必要が無い時、あるいはエラー時の場合などERROR_MORE_DATAを返す以外は すべてERROR_SUCCESSを返します。 |
先頭がPERF_DATA_BLOCKでないのはこのDLLのCollect関数では、 あたなた作ったパフォーマンスデータだけをシステムは必要としていて、最終的に RegQueryValueExの第二引数に指定した範囲のカウンタデータを取得したシステムがデータを まとめてPERF_DATA_BLOCKを先頭にして呼び出し元に返却します。 データ収集の手順は次のようになります。
|
その際注意しなければならない点は、Collect関数内のローカル変数にデータをいったん 蓄えて*lppDataにコピーする場合です。前にも書きましたが、複数のスレッドがこのDLLに アクセスする可能性があるので、AスレッドがカウンタデータをCollect関数内のローカル変数xに 取得したとします。xを*lppDataにコピーするまでにスレッドBがCollect関数を呼び出して変数xを 初期化してしまったら、Aスレッドの収集データがおかしな値になります。こんなことを避けるために 収集データは直接*lppDataにコピーするのが吉かもしれません。
(2c. Close関数)
パフォーマンスデータ収集アプリが終了してRegCloseKeyを発行するとスレッドごとに 最後に一度だけClose関数が呼ばれます。プロトタイプは次のとおりです。
DWORD WINAPI ClosePerfData();
戻値 | すべてERROR_SUCCESS |
<3.モニタリング対象アプリケーションからのデータ取得のための実装 −プロセス間通信>
後で記述する例のモニタ対象アプリケーション側では、共有メモリを作成して そこにデータをコピーをします。パフォーマンスDLL側のCollect関数でこの共有メモリから データを取得する形でプロセス間通信を行っています。プロセス間通信にはいろいろな方法がありますが、 ここでは趣旨が違うのでこれ以上書きません。 例の共有メモリを使う方法については こちらで確認してください。
<4.イベントログの実装>
DLLの実装の際、エラー報告は先に書いたようにイベントログしかないので イベントビューア等で状況を把握できるようにイベントログを記録できるようにしなければなりませんが、 これも、3.のプロセス間通信と同様今回の趣旨と異なるので コードの掲載と簡単な説明に留めておきます。また、機会があれば書きたいと思います。
カスタムパフォーマンスモニタの実装でやるべきことの大筋は上記のような感じなので、 次から実践してみます。