1 DTraceについて

DTraceに備えられた動的トレースでは、実行中のオペレーティング・システム・カーネルをインストゥルメントできます。

DTraceを使用すると、スタック・トレース、関数の引数、タイムスタンプおよび統計集積の収集や出力などのアクションを、ランタイム・イベントまたはソース・コードの場所であるプローブに関連付けることができます。D言語は強力ですが、シンプルです。DTraceは動的で、オーバーヘッドが低く、本番システムで使用しても安全です。これにより、ユーザー・プログラムおよびオペレーティング・システムの動作を検査し、システムの動作状況を把握し、パフォーマンスの問題を検出し、異常な動作の原因を特定できます。

DTraceは、コンシューマによって読み取られるバッファにデータを動的にトレースするカーネル・フレームワークです。Oracle Linuxでは、通常、1つのコンシューマ(dtraceコマンドライン・ユーティリティ)のみを使用します。これには、フレームワークの機能へのフル・アクセスを許可するD言語が含まれています。

このガイドの大部分はリファレンス・マニュアルです。DTraceの使用方法の詳細とステップバイステップの例は、Oracle Linux: DTraceチュートリアルを参照してください。

DTraceスタート・ガイド

ノート:

DTraceのほとんどの使用には、root権限が必要です。

dtrace_utilsパッケージをインストールする前に、実行中のUEKカーネルに対応するULNチャネルをサブスクライブしている状態にします。たとえば、UEK R5でOracle Linux 7を実行している場合、dtrace_utilsパッケージはol7_UEKR5チャネルで使用できます。ULN上のチャネルのサブスクライブの詳細は、『Oracle Linux Unbreakable Linux Networkユーザーズ・ガイド for Oracle Linux 6 and Oracle Linux 7』を参照してください。

Oracle LinuxまたはUEKリリースの更新の詳細は、https://docs.oracle.com/en/operating-systems/linux.htmlにあるドキュメントを参照してください。

dtrace-utilsパッケージをインストールします。

# yum install dtrace-utils

libdtraceコンシューマを実装する場合は、次のようにします。

# yum install dtrace-utils-devel

DTraceプロバイダを開発する場合は、次のようにします。

# yum install dtrace-modules-provider-headers

dtraceがシステムに正しくインストールされ、自分に必要なすべての権限があることを確認するには、dtrace -lコマンドを使用します。このコマンドを実行すると、必要なカーネル・モジュールのいずれかがロードされ、出力に使用可能なプローブが示されます。

ノート:

dtrace-utilsパッケージは、dtrace/usr/sbin/dtraceにインストールします。使用するパスで、systemtap-sdt-develパッケージによってインストールされる/usr/bin/dtraceにある同様の名前が付けられたユーティリティではなく、このパスが検出されるようにします。

プロバイダは、特定の種類のインストゥルメンテーションを持つ一連のプローブです。

ノート:

プロバイダのプローブを使用するには、そのプロバイダをサポートするカーネル・モジュールをロードする必要があります。通常、dtraceはこれを自動的に処理します。最初の使用時に、dtraceモジュールと/etc/dtrace-modulesにリストされているすべてのモジュールがロードされ、システム管理者が編集できます。

場合によっては、次のように、目的のプロバイダをサポートするカーネル・モジュールを手動でロードする必要があります。

# more /etc/dtrace-modules
sdt
systrace
profile
fasttrap
# modprobe sdt
# modprobe systrace
# modprobe profile
# modprobe fasttrap

これらの必要なモジュールは、プロバイダのプローブによってインストゥルメントされてdtrace -lの出力で検出されるモジュール(存在する場合)とは異なります。たとえば、procプローブをサポートするために必要なモジュールはsdtですが、次の出力に示すように、これらのプローブでインストゥルメントされるモジュールはvmlinuxです。

# dtrace -l -P proc
   ID   PROVIDER    MODULE          FUNCTION NAME
  197       proc   vmlinux          _do_fork lwp-create
  198       proc   vmlinux          _do_fork create
  225       proc   vmlinux           do_exit lwp-exit
  226       proc   vmlinux           do_exit exit
  275       proc   vmlinux   do_sigtimedwait signal-clear
...

ランタイム・イベントまたはソース・コードの場所であるプローブで実行されるアクションを動的に割り当てます。DTrace内のすべてのプローブには、プローブがロードされると割り当てられる一意の整数IDと人間が読める文字列名の2つの名前があります。DTraceに関する学習を開始するには、BEGINという名前のプローブを使用する非常に単純なリクエストを作成します。BEGINプローブは、新しいトレース・リクエストを開始するたびに1回起動します。

-nオプションを指定してdtraceコマンドを使用し、その名前を指定してプローブを有効にします。

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

前述の例のデフォルト出力には、一致したプローブと列ヘッダーが表示され、その後プローブが起動するたびに1行が表示されます。行ごとのデフォルトは、プローブが起動されたCPUと、起動されたプローブに関する情報です。DTraceは一時停止したまま、他のプローブの起動を待機します。終了するには、[Ctrl]キーを押しながら[C]キーを押します。

任意の数のプローブおよびアクションを使用して、DTraceリクエストを作成できます。たとえば、前の例に示すコマンドにENDプローブを追加して、2つのプローブを使用して簡単なリクエストを作成します。ENDプローブは、トレースが完了したときに1回起動します。

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

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

[Ctrl]キーを押しながら[C]キーを押してdtraceを終了すると、ENDプローブがトリガーされます。dtraceコマンドは、終了前にこのプローブの起動を報告します。

コマンドラインにDTrace実行文を入力するのみでなく、Dプログラミング言語を使用してDTrace実行文をテキスト・ファイルに記述することもできます。

テキスト・エディタで、hello.dという名前の新しいファイルを作成し、最初のDプログラムを入力します。

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

プログラムを保存したら、次の例に示すように、dtrace -sコマンドを使用してプログラムを実行できます。

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

dtraceコマンドでは、前述の例と同じ出力の後に「hello, world」というテキストが出力されます。ただし、前の例とは異なり、しばらく待機してから[Ctrl]キーを押しながら[C]キーを押す必要はありません。これらの変化は、hello.dBEGINプローブにアクションが指定されたためです。

何が行われたかを把握するために、Dプログラムの構造についてより詳細に調べましょう。

  • 各Dプログラムは、一連の節(各節は、有効にする1つ以上のプローブについて記述します)、およびプローブの起動時に実行するオプションの一連のアクションで構成されています。

  • これらのアクションは、中カッコ({})で囲まれた一連の文としてプローブ名の後にリストされます。各文は、セミコロン(;)で終わります。

  • 最初の文では、trace()関数を使用して、BEGINプローブが起動したときに、指定された引数(文字列「hello, world」)を記録し、出力するようDTraceに指示しています。

  • 2番目の文では、exit()関数を使用してトレースを中止し、dtraceコマンドを終了するようDTraceに指示しています。

DTraceには、Dプログラム内でコールできるtrace()exit()などの一連の便利な関数が用意されています。

関数をコールするには、名前を指定し、その後ろにカッコで囲んだ引数のリストを追加します。D関数の完全セットの詳細は、「アクションおよびサブルーチン」を参照してください。

Cプログラミング言語の知識がある方はDTraceのDプログラミング言語がCと非常に類似していることに気付くでしょう。実際に、Dは、トレースを簡単に実行できるように、Cの大規模なサブセットに特殊な関数および変数を組み合せたものから派生した言語です。これらの機能は、後続の章で詳細に説明されています。Cプログラムを以前に作成したことがあれば、知識の大部分をDでのトレース・プログラムの作成にそのまま流用できます。Cプログラムを作成したことがなくても、Dは比較的簡単に学習できます。すべての構文は、この章で学習できます。まず、詳細な言語ルールに進む前に、DTraceの仕組みについて詳しく学習しましょう。次に、このガイドの後半で、より興味深いDプログラムを作成する方法を学習します。

プロバイダおよびプローブ

前の例では、BEGINおよびENDという名前の2つの簡単なプローブを使用する方法について学習しました。DTraceプローブは、プロバイダと呼ばれるセットに含まれており、それぞれがプローブを作成するために特定のインストゥルメンテーションを実行します。DTraceを使用する場合、各プロバイダには、DTraceフレームワークに提供するプローブを発行する機会が与えられます。これにより、発行されている任意のプローブに対してトレース・アクションを有効化およびバインドできるようになります。

次のコマンドを入力して、システムで使用可能なすべてのプローブをリストできます。

# dtrace -l 
   ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR
    4    syscall           vmlinux                              read entry
    5    syscall           vmlinux                              read return
    6    syscall           vmlinux                             write entry
    7    syscall           vmlinux                             write return
    ...

すべての出力が表示されるまで時間がかかる場合があります。

すべてのプローブをカウントするには、次のコマンドを入力します。

# dtrace -l | wc -l
4097

使用しているシステムによって表示される合計が異なる可能性があります。これは、オペレーティング・プラットフォーム、インストールしたソフトウェア、およびロードしたプロバイダ・モジュールによってプローブの数が異なる場合があるためです。この出力は完全なリストではないことにも注意してください。後述するように、一部のプロバイダでは、トレース・リクエストに基づいて新しいプローブをオンザフライで作成でき、これにより、実際のDTraceプローブの数は実質的に無制限になります。各プローブには、前に説明した、整数IDと人間が読める名前の2つの名前があります。人間が読める名前は、dtraceの出力で個別の列として表示される4つのパートで構成され、次のようになります。

プロバイダ

このプローブを発行したDTraceプロバイダの名前。

モジュール

このプローブが特定のプログラムの場所に対応している場合は、プローブが位置するカーネル・モジュール、ライブラリまたはユーザー空間プログラムの名前。

関数

このプローブが特定のプログラムの場所に対応している場合は、プローブが位置するプログラム関数の名前。

名前

プローブのセマンティックな意味の理解に役立つ名前(BEGINENDなど)。

人間が読めるプローブ名を完全に入力するには、次のように、名前の4つの部分すべてをコロンで区切って入力します。

provider:module:function:name

前に使用したBEGINおよびENDプローブのように、リスト内の一部のプローブにはモジュールや関数がありません。一部のプローブでは、特定のインストゥルメンテーション付きプログラムの関数または場所に対応していないため、これら2つのフィールドは空白のままになります。かわりに、これらのプローブは、「トレース・リクエストの終了」のような、より抽象的な概念を表します。

慣例により、プローブ名の一部のフィールドを省略して指定する場合、リクエストは、指定した名前の部分に一致する値を持つすべてのプローブに一致します。つまり、前の演習でプローブ名BEGINを使用した場合、実際には、プロバイダ、モジュールおよび関数の各フィールドの値とは関係なく、名前フィールドがBEGINであるプローブをすべて検出するようDTraceに指示したことになります。記述に一致するプローブが1個しかないため、結果は同じになります。BEGINプローブの正式な名前はdtrace:::BEGINであることがこれまでにわかりました。この名前が示すように、このプローブはDTraceフレームワーク自体から提供され、関数に固有ではありません。したがって、hello.dプログラムは、次のように作成することもできます。この場合、同じ結果が得られます。

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