C/C++プログラマのためのDTrace入門

第6回 独自プロバイダの定義[1]

この記事を読むのに必要な時間:およそ 3 分

これまでの連載における説明では,基本的にpidプロバイダが提供するプローブentryおよびreturnを使用して,関数の呼び出し/復帰に関して情報を採取してきました。

しかし,実際のプログラム開発においては,情報を採取したい場所が,必ずしも関数呼び出しとは直接関係無い場合も多々あります。

そこで,今回と次回の2回に渡って,関数呼び出しの境界以外から情報を採取するための,独自プロバイダの定義とその利用について説明します。

今回は,単純な値を採取する独自プロバイダについて説明します。

ソースファイルの準備

以下の手順で必要となるソースファイルを準備します。

プロバイダの定義

何をおいても,まずは独自プロバイダを定義する必要があります。

リスト1 独自プロバイダの定義 (checkpoint.d)

provider checkpoint {
    pass(const char* filename, int lineno);
};

プロバイダcheckpointが提供するプローブpassは,以下のような用途を想定しています。

プログラムの要所要所に埋め込まれたプローブ位置において,ファイル名と行番号情報を採取することで,実行時にどのような経路を通過したのかを知る

つまり,関数フローよりもさらに細かい粒度で,実行フローを採取することができるわけです。

なおプロバイダ定義を記述する際には,ブロック末尾("{ }" の後ろ)にセミコロンが必要です。普段のDスクリプトでは不要な記述であることから,このセミコロンは忘れがちになりますので注意してください。

ヘッダファイルの生成

独自プロバイダを定義したならば,図1に示す要領でdtraceコマンドを実行してください。

図1 ヘッダファイルの生成 (checkpoint.h)

$ dtrace -s checkpoint.d -h

独自プロバイダを定義したDスクリプトの指定("-s" オプション)と共に,"-h" オプションを指定することで,リスト2のような内容を持ったヘッダファイルcheckpoint.hが生成されます。

リスト2 生成されたヘッダファイルの内容

          :
#define CHECKPOINT_PASS(arg0, arg1) \
        __dtrace_checkpoint___pass(arg0, arg1)
          :
          :
extern void __dtrace_checkpoint___pass(char *, int);
          :

"-h"オプション指定によってdtraceコマンドが生成するヘッダファイルでは,各プローブごとに「プロバイダ名」「プローブ名」の形式でマクロが定義されます(名称は共に大文字に変換されます)⁠

通常は,ここで生成されたヘッダファイル中のマクロを使用して,対象ソースファイルにプローブの埋め込みを行います。

リスト2の例に見られるように,マクロそのものは別途宣言されている関数(この例では __dtrace_checkpoint___pass()を呼び出すだけの単純なものですが,名称の長さの点などからも,直接関数を呼ぶのではなくマクロを使う方が良いでしょう。

ユーティリティヘッダの定義

先述したように,通常はdtraceコマンドによって生成されたヘッダファイルと,そこで定義されているマクロを直接使いますが,今回のpassのようなプローブの場合,実装時におけるファイル名や行番号指定の手間を軽減するために,リスト3のようなマクロ定義を含むヘッダファイルcheckpoint_impl.hを定義しましょう。

リスト3 ユーティリティヘッダの作成 (checkpoint_impl.h)

#include "checkpoint.h"

#define DTRACE_CHECKPOINT_PASS() \
    CHECKPOINT_PASS(__FILE__, __LINE__)

CHECKPOINT_PASS()マクロを直接使用するのではなく,新たに定義したDTRACE_CHECKPOINT_PASS()マクロを使うことで,対象プログラムに対するプローブの埋め込みの手間が(幾分)軽減されます。

Cプログラムの実装

今回独自に定義したpassプローブの埋め込み対象として,リスト4に示す処理をmain()に持つプログラムを想定します。

リスト4 プローブ埋め込み対象プログラム (branch_by_arg.c)

    if(argc < 2){
        DTRACE_CHECKPOINT_PASS();
    }
    else{
        int val = atol(argv[1]);

        DTRACE_CHECKPOINT_PASS();

        if(val < 10){
            DTRACE_CHECKPOINT_PASS();
        }
        else{
            DTRACE_CHECKPOINT_PASS();
        }
    }

    DTRACE_CHECKPOINT_PASS();

この実装では与えられた引数に応じて条件分岐を行いますが,あくまで関数内に閉じた処理であり,他の関数を呼び出すわけでもありませんから,これまで使用してきたpidプロバイダによる関数フロー採取では,どのような経路が実行されたのかを知ることができません。

そこで,今回新たに定義したpassプローブを使用することで,どのファイルのどの行が実行されたのかを採取するわけです。

著者プロフィール

藤原克則(ふじわらかつのり)

Mercurial三昧の日々が嵩じて, いつの間にやら『入門Mercurial Linux/Windows対応』を上梓。凝り性なのが災いして,年がら年中アップアップな一介の実装屋。最近は仕事の縁が元で,OpenSolarisに入れ込む毎日。

コメント

コメントの記入