1 DTraceスタート・ガイド

この項の各トピックでは、DTraceで特定の操作を実行する方法のガイダンスを示し、DTraceのインストールと使用の概要について説明します。このガイドのステップに従うことで、すぐにDTraceの使用を開始できます。これらのトピックの確認後に、「DTraceの概念」を確認すると、DTraceのしくみと、その使用方法の改善方法について理解を深めることができます。また、記載されている各種のリファレンスを使用すると、必要な作業を実行するDプログラムの作成の詳細を確認できます。

DTraceのインストール

dtraceパッケージをインストールすることで、DTraceコマンドライン・ユーティリティを使用し、Dプログラムを実行します。

  1. システムに対して適切なyumリポジトリを有効にします。

    x86_64システムの場合、DTrace v2ユーザー空間ツールは、次のいずれかのyumリポジトリまたはUnbreakable Linux Network (ULN)上の同等のチャネルからインストールできます。

    • ol8_UEKR6
    • ol8_UEKR7
    • ol9_UEKR7

    たとえば、Oracle Linux 9 x86_64システムでOracle Linux yumサーバーを使用している場合は、次を実行します:

    sudo dnf config-manager --enable ol9_UEKR7

    ノート:

    Oracleは、aarch64プラットフォーム用のbaseosリポジトリでUEKとDTraceパッケージをリリースしています。aarch64プラットフォーム用のDTraceパッケージにアクセスする場合は、その他のリポジトリを有効にする必要はありません。

  2. dtraceパッケージをインストールします。
    sudo dnf install -y dtrace
  3. DTraceが正しい場所にインストールされたことを調べて、DTraceのバージョンを確認します。
    ls -lah /usr/sbin/dtraceを実行して、DTraceユーティリティが存在することを確認します:
    ls -lah /usr/sbin/dtrace

    dtrace -Vコマンドを実行して、バージョン番号を表示します。

    dtrace -V

プローブのリストと有効化

このトピックでは、DTraceで使用可能なプローブをリストして有効にする方法について説明します。

DTraceプロバイダは、DTraceに使用可能なプローブを公開することで、プローブの起動時に関数を実行できるようにします。dtraceコマンドは、使用可能なすべてのプローブのリスト表示や、プローブの有効化に使用できます。

  1. 使用可能なプローブをリスト表示します。

    使用可能なすべてのプローブをリストするには、次を実行します:

    sudo dtrace -l

    ノート:

    DTraceのほとんどの使用には、root権限が必要です。このドキュメントでは、コマンドは適切な権限で実行することを前提としています。このドキュメントに示したコマンドの実行前に、sudoコマンドを使用してrootユーザー権限に昇格します。

    コマンドは以下のような出力を返します。

    DTrace 2.0.0 [Pre-Release with limited functionality]
        ID   PROVIDER            MODULE                          FUNCTION NAME
         1     dtrace                                                     BEGIN
         2     dtrace                                                     END
         3     dtrace                                                     ERROR
         4        fbt           vmlinux        __traceiter_initcall_level entry
         5        fbt           vmlinux        __traceiter_initcall_level return
         6        fbt           vmlinux        __traceiter_initcall_start entry
         7        fbt           vmlinux        __traceiter_initcall_start return
         8        fbt           vmlinux       __traceiter_initcall_finish entry
         9        fbt           vmlinux       __traceiter_initcall_finish return
    ...
    144917        sdt               rtc                                   rtc_set_time
    144918        sdt               i2c                                   i2c_result
    144919        sdt               i2c                                   i2c_reply
    144920        sdt               i2c                                   i2c_read
    144921        sdt               i2c                                   i2c_write
    144922        sdt             smbus                                   smbus_result
    144923        sdt             smbus                                   smbus_reply
    144924        sdt             smbus                                   smbus_read
    144925        sdt             smbus                                   smbus_write
    144926        sdt             hwmon                                   hwmon_attr_show_string
    144927        sdt             hwmon                                   hwmon_attr_store
    144928        sdt             hwmon                                   hwmon_attr_show
    144929        sdt           thermal                                   thermal_zone_trip
    144930        sdt           thermal                                   cdev_update
    144931        sdt           thermal                                   thermal_temperature
    144932        sdt            bcache    
    ...
    145763    syscall           vmlinux                            listen entry
    145764    syscall           vmlinux                              bind return
    145765    syscall           vmlinux                              bind entry
    145766    syscall           vmlinux                        socketpair return
    145767    syscall           vmlinux                        socketpair entry
    145768    syscall           vmlinux                            socket return
    145769    syscall           vmlinux                            socket entry

    ヒント:

    DTraceで使用可能なプロバイダ固有のリストを取得するには、次を実行します:
    sudo dtrace -l|tail -n +3|awk '{print $2}'|uniq
    -Pオプションは、プローブのリストを特定のプロバイダに限定するために使用できます。-mオプションを使用して、特定のモジュールに限定することもできます。次に例を示します:
    sudo dtrace -l -P sdt
    sudo dtrace -l -m thermal
  2. コマンドライン・ユーティリティを使用して指定のプローブを有効にするには、dtrace -nを実行します。
    名前に一致するプローブを有効にできます。プローブの完全名の名前部分のみを指定することもできますが、完全名を使用することで予期しない動作を回避できます。
    sudo dtrace -n dtrace:::BEGIN

    次のような出力が表示されます。

    dtrace: description 'dtrace:::BEGIN' matched 1 probe
    CPU     ID                    FUNCTION:NAME
      2      1                           :BEGIN
    dtrace:::BEGINプローブは、新しいトレース・リクエストを開始するたびに1回起動します。表形式の出力に、プローブが起動したCPUと、プローブのID、関数、および名前が表示されます。

    DTraceの実行は継続され、別のプローブが起動するのを待機します。終了するには、[Ctrl]キーを押しながら[C]キーを押します。

  3. リクエスト内で複数のプローブを連鎖させることで、複数のプローブを有効にします。

    DTraceリクエストは、任意の数のプローブと関数を使用して作成できます。たとえば、BEGINプローブとENDプローブを追加して、2つのプローブを使用したリクエストを作成します。

    次のコマンドを入力してから、BEGINプローブの出力行が表示された後に、再度シェルでCtrl-Cを押してください。

    sudo dtrace -n dtrace:::BEGIN -n dtrace:::END 
    dtrace: description 'dtrace:::BEGIN' matched 1 probe
    dtrace: description 'dtrace:::END' matched 1 probe
    CPU     ID                    FUNCTION:NAME
      0      1                           :BEGIN 
    ^C
      1      2                             :END

    dtrace:::BEGINプローブは、トレース・リクエストの開始時に起動します。DTraceは、Ctrl-Cを押して終了するまで、さらにアクティブになるプローブを待機します。トレースの完了時に、dtrace:::ENDプローブが1回アクティブ化されます。dtraceコマンドは、このプローブの起動を終了前に報告します。

  4. ある関数のすべてのプローブを有効にするには-fオプションを使用し、あるモジュールのすべてのプローブを有効にするには-mオプションを使用します。
    関数またはモジュール全体のプローブを一致させて有効にできます。たとえば、syscall:vmlinux:socket関数のentryプローブとreturnプローブの両方を有効にするには、次を実行します:
    sudo dtrace -f syscall:vmlinux:socket

    モジュール全体のプローブを有効にすることもできます。たとえば、sdt:tcpモジュールのすべてのプローブを有効にするには、次を実行します:

    sudo dtrace -m sdt:tcp

DTraceスクリプトの作成

このチュートリアルでは、DTraceスクリプトの作成方法について説明します。このチュートリアルでは、Dプログラミング言語の理解を深め、DTraceを実際に使用するためのステップを示します。

DTraceがシステムにインストールされていて、プローブのリスト表示と有効化ができることを確認します。「DTraceのインストール」と「プローブのリストと有効化」を参照してください。

このチュートリアルでは、システムについての有用な情報を収集するために使用できるDTraceスクリプトを開発する一連のステップを示します。このチュートリアルは、今後、DTrace用の別のスクリプトを作成するためのフレームワークとして使用できます。

  1. テキスト・エディタで、hello.dという名前のファイルを作成して、dtrace:::BEGINプローブに応じて起動するDTrace節を記述します。

    エディタに次のテキストを入力します:

    dtrace:::BEGIN
    {
      trace("hello, world");
      exit(0);
    }

    ファイルを保存します。

  2. dtrace -sコマンドを使用して、hello.dプログラムを実行します。
    sudo dtrace -s hello.d

    次のような出力が表示されます。

    dtrace: script 'hello.d' matched 1 probe
    CPU     ID                    FUNCTION:NAME
      0      1                           :BEGIN   hello, world    

    このプログラムでは、BEGINプローブにexit関数を指定したため、Ctrl-cを押して終了する必要はありません。

  3. テキスト・エディタでhello.dを開いて、スクリプトの先頭にインタプリタ行を追加します。

    ファイルを編集し、ファイルの先頭に次のテキスト行を追加します。

    #!/usr/sbin/dtrace -s
    完全なスクリプトは次のとおりです:
    #!/usr/sbin/dtrace -s
    dtrace:::BEGIN
    {
      trace("hello, world");
      exit(0);
    }

    ファイルを保存します。

  4. hello.dファイルの権限を変更して、実行可能にします。

    chmodコマンドを実行して、ファイルのアクセス権を更新します:

    chmod a+rx hello.d
  5. 新しい実行可能スクリプト・ファイルを実行します。
    sudoコマンドを使用して、DTraceスクリプトがroot権限のまま実行されるようにして、すべてのDTrace機能にアクセスできるようにします。
    sudo ./hello.d

    プログラムの先頭にインタプリタ行を含めると、dtraceコマンドの指定なしでもスクリプトを実行できます。

  6. 外部マクロ変数を使用するように、スクリプトを変更します。

    スクリプトの引数として名前を指定したときに、名前で人に挨拶するようにファイルを編集します。

    #!/usr/sbin/dtrace -s
    dtrace:::BEGIN
    {
      printf("hello, %s", $$1);
      exit(0);
    }

    trace関数がprintf()関数に置き換えられて、変数置換を使用して文字列にマクロ変数$1を挿入できるようになりました。$$構文は、文字列値として表すマクロ変数を参照するときに使用します。

  7. このスクリプトを実行して、変更による動作の変化を確認します。

    次のコマンドを使用して、前述のようにスクリプトを実行します:

    sudo ./hello.d

    次のようなエラーが生成されます:

    dtrace: failed to compile script ./hello.d: line 4: macro argument $$1 is not defined
    このスクリプトの実行時には、引数も指定する必要があるため、エラーが生成されます。スクリプトを再度実行してみますが、今回は名前を指定します:
    sudo ./hello.d bob

    このスクリプトは次のような出力を返します:

    dtrace: script './hello.d' matched 1 probe
    CPU     ID                    FUNCTION:NAME
      3      1                           :BEGIN hello, bob
  8. プラグマ文を使用するように、スクリプトを変更します。

    スクリプトの冗長性を減らして、出力を節が機能的に返す内容のみに制限するために、実行時のquietオプションを設定するプラグマ文を追加します。次のようにスクリプトを編集して、プラグマ文を追加します:

    #!/usr/sbin/dtrace -s
     #pragma D option quiet
     dtrace:::BEGIN
     {
       printf("hello, %s", $$1);
       exit(0);
     }
  9. このスクリプトを実行して、変更による動作の変化を確認します。

    次のコマンドを使用して、前述のようにスクリプトを実行します:

    sudo ./hello.d sally

    スクリプトの出力は、printf()関数によって返されるもののみに削減されます。

  10. この節を処理するタイミングを制御するための述語を使用するように、スクリプトを変更します。

    述語を使用することで、特定の条件がtrueの場合にのみ実行されるようにスクリプトを制御できます。スクリプトを編集して述語行を追加し、次のようにマクロ変数の文字列値が'bob'と等しいかどうかを評価します:

    #!/usr/sbin/dtrace -s
     #pragma D option quiet
    
     dtrace:::BEGIN
     /$$1=="bob"/
     {
       printf("hello, %s", $$1);
       exit(0);
     }
  11. このスクリプトを実行して、変更による動作の変化を確認します。

    次のコマンドを使用して、前述のようにスクリプトを実行します:

    sudo ./hello.d sally

    このスクリプトは終了しません。Ctrl-Cを押してプロセスを強制終了する必要があります。これは、スクリプトの最初の引数が'bob'と等しいかどうかを評価する節の一部にexit()関数があるためです。引数としてbobを使用して、スクリプトを再度実行してみます。

    sudo ./hello.d bob

    スクリプトは以前と同様に実行され、述語が機能していることを実証しています。

制御フローのための述語の使用

ランタイムの安全性については、Dと他のプログラミング言語(C、C++およびJavaプログラミング言語など)の顕著な違いの1つは、Dでは、if文やループなどの制御フロー構成が存在しない点にあります。Dプログラム節は、固定量のデータ(オプション)をトレースする単一の直線的な文のリストとして作成されます。Dは、述語と呼ばれる論理式を使用して、条件付きでデータをトレースし、制御フローを変更する機能を提供します。このチュートリアルでは、Dプログラムの制御に述語を使用する方法を示します。

述語の動作を理解するために、10秒カウントダウン・タイマーを実装するDプログラムを作成します。このプログラムを実行すると、10からカウント・ダウンし、メッセージを出力して終了します。このプログラムでは、変数と述語を使用して、経過時間と出力内容を評価します。

  1. このプログラムの論理フローを設計します。

    プログラム自体を記述する前に、プログラムの論理フローを設計することを検討してください。フローが明確に定義されていると、条件の構造を個別の節と述語に変換できます。プログラムの論理フローは次のようになります:

    i = 10
    once per second,
      if i is greater than zero
        trace(i--);
      if i is equal to zero
        trace("blastoff!");
        exit(0);

    述語と関数が異なる同じプローブ記述の節を2つ作成することで、このプログラムに必要な論理フローを実現できます。

  2. プローブの起動時に、指定されたプローブ記述の関数の実行を許可するかどうかを決定するために、述語を使用したプログラム・コードを記述します。

    次に、プログラムのソース・コードを示します。このコードをコピーして、countdown.dという名前のファイルに保存します。

    dtrace:::BEGIN 
    {
      i = 10;
    }
    
    profile:::tick-1sec
    /i > 0/
    {
      trace(i--);
    }
    
    profile:::tick-1sec
    /i == 0/
    {
      trace("blastoff!");
      exit(0);
    }
  3. プログラムを実行します。
    sudo dtrace -s countdown.d

    次のような出力が表示されます。

    dtrace: script 'countdown.d' matched 3 probes
    CPU     ID                    FUNCTION:NAME
      0    638                       :tick-1sec        10
      0    638                       :tick-1sec         9
      0    638                       :tick-1sec         8
      0    638                       :tick-1sec         7
      0    638                       :tick-1sec         6
      0    638                       :tick-1sec         5
      0    638                       :tick-1sec         4
      0    638                       :tick-1sec         3
      0    638                       :tick-1sec         2
      0    638                       :tick-1sec         1
      0    638                       :tick-1sec   blastoff!       
    #

このチュートリアルでは、BEGINプローブを使用して、変数整数iをカウントダウン開始の10に初期化します。次に、tick-1secプローブを使用して、プログラムに毎秒1回起動するタイマーを実装します。countdown.dでは、tick-1secプローブ記述が、述語と関数リストの異なる2つの個別の節で使用されています。述語は、前後をスラッシュ//で囲まれた論理式で、プローブ名と、節の文リストが入っている中カッコ{}の間にあります。

最初の述語は、iがゼロより大きいかどうか(タイマーがまだ実行中かどうか)をテストします。

profile:::tick-1sec
/i > 0/
{
  trace(i--);
}

関係演算子>は、「より大きい」を意味し、偽の場合は整数値0、真の場合は1を返します。iがまだ0になっていない場合、スクリプトではiをトレースし、--演算子を使用して1ずつ値を減らします。

2番目の述語は、==演算子を使用して、iが0 (カウントダウンが完了した)の場合に真を返します。

profile:::tick-1sec
/i == 0/
{
  trace("blastoff!");
  exit(0);
}

2番目の節では、文字列定数と呼ばれる二重引用符で囲まれた一連の文字にtrace関数を使用して、カウントダウンが完了したときの最後のメッセージを出力します。次に、exit関数を使用して、すべてのトレースを終了し、最終的なデータの消費、集積体の出力(必要な場合)、シェル・プロンプトに戻る前のクリーン・アップの実行などの残りのタスクを実行します。

例1-1 あるプロセスIDのシステム・コールを監視するために述語を使用する方法

あるプロセスIDのシステム・コールをトレースするDプログラムを作成するには、述語を使用して、トレースするプロセスIDと一致するようにデフォルトのトレース関数を制限します。

syscall:::entry
/pid == 2860/
{
}

この例では、組込み変数pidと特定のID (この例では2860)の一致が評価されます。さらに、このスクリプトはシェルのマクロ変数を利用するように変更すると、拡張性が向上して、実行時に任意のプロセスIDに対して実行できるようになります。次のようにスクリプトを編集して、strace.dsというファイルに保存します:

#!/usr/sbin/dtrace -s

syscall:::entry
/pid == $1/
{
}

ファイル・モードを変更して、実行可能にします。

sudo chmod +x strace.ds

これで、このスクリプトを使用して、システムでいずれかのプロセスによって行われたすべてのシステム・コールを監視できます。たとえば、このスクリプトを実行して、cronデーモンによるシステム・コールを監視できます:

sudo ./strace.ds $(pidof /usr/sbin/crond)