3 ユーザー・スペース・アプリケーションのトレース

この章では、ユーザー・スペース・アプリケーションをトレースする方法と、ユーザー・スペース・プログラムの例で発生している状況を調査するために使用できるDプログラムの例について説明します。

ユーザー・スペース・アプリケーションの準備

DTraceヘルパー・デバイス(/dev/dtrace/helper)を使用すると、DTraceのプローブが含まれているユーザー・スペース・アプリケーションで、プローブのプロバイダ情報をDTraceに送信できます。

root以外のユーザーによって実行されたユーザー・スペース・プロセスをトレースするには、次の例に示すように、DTraceヘルパー・デバイスのモードを変更して、ユーザーがトレース情報を記録できるようにする必要があります。

例: DTraceヘルパー・デバイスのモードの変更

次の例は、rootユーザー以外のユーザーによるユーザー・スペース・アプリケーションのトレースを有効にする方法を示しています。

# chmod 666 /dev/dtrace/helper

または、システムにaclパッケージがインストールされている場合は、ACLルールを使用して、次のように特定のユーザーへのアクセスを制限します。

# setfacl -m u:guest:rw /dev/dtrace/helper

ノート:

DTraceでプローブポイントを参照するには、ユーザーがプログラムの実行を開始する前に、デバイス上のモードを変更する必要があります。

/etc/udev/rules.d/10-dtrace.rulesなどのudevルール・ファイルを作成して、システムがブートされるたびにデバイス・ファイルの権限を変更することもできます。

次の例は、udevルール・ファイルに次の行を追加して、デバイス・ファイルのモードを変更する方法を示しています。

kernel=="dtrace/helper", MODE="0666"

次の例は、udevルール・ファイルに次のような行を追加して、デバイス・ファイルのACL設定を変更する方法を示しています。

kernel=="dtrace/helper", RUN="/usr/bin/setfacl -m u:guest:rw /dev/dtrace/helper"

システムを再起動せずにudevルールを適用するには、start_udevコマンドを実行します。

サンプル・アプリケーション

この項では、この章の以降の演習および例で使用するサンプル・アプリケーションについて説明します。この例は、簡単なプログラムを示しており、完全性や効率性ではなく、簡潔さとプローブの機会を優先しています。

ノート:

次の簡単なプログラムは、例の目的でのみ示されており、実際的な問題を効率的に解決したり、優先されるコーディング方法を示すことを目的としていません。

サンプル・プログラムにより、入力した数値の最小係数が検索されます。このプログラムは、同じ作業ディレクトリに格納されたmakefileprimelib.hprimelib.cおよびprimain.cの4つのファイルで構成されています。

makefileファイルの説明および形式

次の例は、makefileファイルの内容を示しています。

ノート:

makefileでは、makeコマンドが正しく機能するようにインデントにタブを使用する必要があります。また、ファイルをコピーしてから使用する場合は、タブが保持されていることを確認します。

default: prime

# compile the library primelib first
primelib.o: primelib.c
	gcc -c primelib.c

# compile the main program
primain.o: primain.c
	gcc -c primain.c

# link and create executable file "prime"
prime: primelib.o primain.o
	gcc primain.o primelib.o -o prime -lm

clean:
	-rm -f *.o
	-rm -f prime

primelib.hソース・ファイルの説明

次の例は、primelib.hファイルの内容を示しています。

int findMaxCheck( int inValue );
int seekFactorA( int input, int maxtry );
int seekFactorB( int input );

primelib.cソース・ファイルの説明

次の例は、primelib.cファイルの内容を示しています。

#include <stdio.h>
#include <math.h>

/*
 * utility functions which are called from the main source code
 */

// Find and return our highest value to check -- which is the square root
int findMaxCheck( int inValue )  {
  float sqRoot;
  sqRoot = sqrt( inValue );
  printf("Square root of %d is %lf\n", inValue, sqRoot); 
  return floor( sqRoot );
  return inValue/2;
}

int debugFlag = 0;

// Search for a factor to the input value, proving prime on return of zero
int seekFactorA( int input, int maxtry )  {
  int divisor, factor = 0;
  for( divisor=2; divisor<=maxtry; ++divisor ) {
    if( 0 == input%divisor ) {
      factor = divisor;
      break;
    }
    else if ( debugFlag != 0 )
      printf( "modulo %d yields: %d\n", divisor, input%divisor );
  }
  return factor;
}

// Search for a factor to the input value, proving prime on return of zero
// This is a different method than "A", using one argument
int seekFactorB( int input )  {
  int divisor, factor = 0;
  if( 0 == input%2 ) return 2;
  for( divisor=3; divisor<=input/2; divisor+=2 ) {
    if( 0 == input%divisor ) {
      factor = divisor;
      break;
    }
  }
  return factor;
}

primain.cソース・ファイルの説明

次の例は、primain.cファイルの内容を示しています。

#include <stdio.h>
#include "primelib.h"

/*
 * Nominal C program churning to provide a code base we might want to
 * instrument with D
*/

// Search for a divisor -- thereby proving composite value of the input.
int main()  {
  int targVal, divisor, factorA=0, factorB=0;

  printf( "Enter a positive target integer to test for prime status: " );
  scanf( "%d", &targVal );

  // Check that the user input is valid
  if( targVal < 2 ) {
    printf( "Invalid input value -- exiting now\n" );
    return -2;
  }

  // Search for a divisor using method and function A
  int lastCheck;
  lastCheck = findMaxCheck( targVal );
  printf( "%d highest value to check as divisor\n", lastCheck );
  factorA = seekFactorA( targVal, lastCheck );

  // Search for a divisor using method and function B
  factorB = seekFactorB( targVal );

  // Warn if the methods give different results
  if (factorA != factorB)
    printf( "%d does not equal %d! How can this be?\n", factorA, factorB );

  // Print results
  if( !factorA )
    printf( "%d is a prime number\n", targVal );
  else
    printf( "%d is not prime because there is a factor %d\n",
	    targVal, factorA );
  return 0;
}

プログラムのコンパイルおよびprime実行可能ファイルの実行

前に説明した同じ作業ディレクトリに配置されている4つのファイルを使用して、次のようにmakeコマンドを実行し、プログラムをコンパイルします。

# make
gcc -c primelib.c
gcc -c primain.c
gcc primain.o primelib.o -o prime -lm

makeコマンドを実行すると、primeという名前の実行可能ファイルが作成されます。次の2つの例に示すように、このファイルを実行して、入力の最小のprime値を検索できます。

# ./prime 
Enter a positive target integer to test for prime status: 5099
Square root of 5099 is 71.407280
71 highest value to check as divisor
5099 is a prime number
# ./prime
Enter a positive target integer to test for prime status: 95099
Square root of 95099 is 308.381256
308 highest value to check as divisor
95099 is not prime because there is a factor 61

プログラムをコンパイルしてprime実行可能ファイルを実行した後、「アプリケーションに対するUSDTプローブの追加」の説明に従って、アプリケーションに対するUSDTプローブの追加の演習を行います。

アプリケーションに対するUSDTプローブの追加

この項では、アプリケーションに対するUSDTプローブの追加の演習を行います。バックグラウンド情報およびその他の詳細は、Oracle Linux: DTraceリファレンス・ガイドアプリケーションに対するプローブの追加に関する項を参照してください。

開始するには、Oracle Linux: DTraceリファレンス・ガイドプロバイダおよびプローブの定義に関する項の説明に従って、.dファイルを作成する必要があります。

ノート:

この.dファイルは、このチュートリアルの前の例と同じ方法で実行されるスクリプトではなく、アプリケーションのコンパイルおよびリンク時に使用する.dソース・ファイルです。混同しないように、このファイルにはスクリプトに使用するものとは異なるネーミング規則を使用してください。

.dファイルを作成した後、次の例で使用する必須のプローブ・ポイントを作成する必要があります。この情報は、primain.cソース・ファイルに追加されます。この演習で使用するプローブ・ポイントを、次の表に示します。これらのプローブは一連の操作を表し、ユーザー・エントリが完了してチェックされた後に使用されます。

表3-1 ユーザー入力が完了してチェックされた後に使用するプローブ・ポイント

説明 プローブ

ユーザー入力が完了し、チェックされました

userentry( int )

seekFactorA()に戻って入力します

factorreturnA( int, int )

seekFactorB()に戻って入力します

factorreturnB( int, int )

プログラム終了直前

final()

演習: dprime.dファイルの作成

前に説明したプローブ・ポイントとデータを反映するには、dprime.dという名前のファイルを作成し、そのファイルを他のソース・ファイルと同じ作業ディレクトリに格納します。

ノート:

通常、Oracle Linux: DTraceリファレンス・ガイドで以前に参照した詳細に従って、.dファイルに追加情報(安定性属性など)を指定します。簡潔化、迅速化、および単純化するために、これらの詳細は、この初歩的な例には含まれていません。

(予想完了時間: 5分未満)

演習の回答: dprime.dファイルの作成

provider primeget
{
  probe query__userentry( int );
  probe query__maxcheckval( int, int );
  probe query__factorreturnA( int, int );
  probe query__factorreturnB( int, int );
  probe query__final();
};

例: dprime.dファイルからの.hファイルの作成

次のステップでは、ここに示すように、dprime.dファイルから.hファイルを作成します。

# dtrace -h -s dprime.d

作成されたdprime.hファイルには、dprime.dファイルに定義されている各プローブ・ポイントへの参照が含まれています。

次に、アプリケーション・ソース・ファイルprimain.cで、#include "dprime.h"ファイルへの参照を追加し、適切な場所に適切なプローブ・マクロを追加します。

結果のprimain.cファイルでは、プローブ・マクロ(例の目的でのみ太字フォントで表示しています)は大文字で表示されるため、簡単に認識できます。

#include <stdio.h>
#include "primelib.h"
#include "dprime.h"

/*
 * Nominal C program churning to provide a code base we might want to
 * instrument with D
*/

// Search for a divisor -- thereby proving composite value of the input.
int main()  {
  int targVal, divisor, factorA=0, factorB=0;

  printf( "Enter a positive target integer to test for prime status: " );
  scanf( "%d", &targVal );

  // Check that the user input is valid
  if( targVal < 2 ) {
    printf( "Invalid input value -- exiting now\n" );
    return -2;
  }
  if (PRIMEGET_QUERY_USERENTRY_ENABLED())
    PRIMEGET_QUERY_USERENTRY(targVal);

  // Search for a divisor using method and function A
  int lastCheck;
  lastCheck = findMaxCheck( targVal );
  printf( "%d highest value to check as divisor\n", lastCheck );
  if (PRIMEGET_QUERY_MAXCHECKVAL_ENABLED())
    PRIMEGET_QUERY_MAXCHECKVAL(lastCheck, targVal);

  factorA = seekFactorA( targVal, lastCheck );
  if (PRIMEGET_QUERY_FACTORRETURNA_ENABLED())
    PRIMEGET_QUERY_FACTORRETURNA(factorA, targVal);

  // Search for a divisor using method and function B
  factorB = seekFactorB( targVal );
 if (PRIMEGET_QUERY_FACTORRETURNB_ENABLED())
    PRIMEGET_QUERY_FACTORRETURNB(factorB, targVal);

  // Warn if the methods give different results
  if (factorA != factorB)
    printf( "%d does not equal %d! How can this be?\n", factorA, factorB );

  // Print results
  if( !factorA )
    printf( "%d is a prime number\n", targVal );
  else
    printf( "%d is not prime because there is a factor %d\n",
	    targVal, factorA );
  if (PRIMEGET_QUERY_FINAL_ENABLED())
    PRIMEGET_QUERY_FINAL();

  return 0;
}

ノート:

すべての* _ENABLED()プローブは、関連するプローブが有効になっている(一部のコンシューマが使用している)場合に真の値に変換され、関連するプローブが有効になっていない場合には偽の値に変換されます。

続行する前に、プローブが有効であり、dprime.hファイルに一覧表示されているマクロとして表示されることを確認します。Oracle Linux: DTraceリファレンス・ガイドプローブが有効かどうかのテストに関する項を参照してください。

ノート:

必ず、必要なすべての値(存在する場合)をマクロに含めて、プローブでもそれらの値を識別できるようにします。

次に、makefileファイルを変更する必要があります。ステップごとの指示は、Oracle Linux: DTraceリファレンス・ガイドプローブを使用したアプリケーションの構築に関する項を参照してください。

演習: makefileにdprime.hファイルの再作成を指示する

dtraceに、後でdprime.dファイルが変更された場合にdprime.hファイルを再作成するよう指示するターゲットを追加します。このステップにより、なんらかの変更が行われた場合にdtrace -h -s dprime.dコマンドを手動で実行する必要がなくなります。

この演習では、dtraceprime.oファイルを作成することも指示します。

(予想完了時間: 10分)

演習の回答: makefileにdprime.hファイルの再作成を指示する

default: prime

# re-create new dprime.h if dprime.d file has been changed
dprime.h: dprime.d
	dtrace -h -s dprime.d

# compile the library primelib first
primelib.o: primelib.c
	gcc -c primelib.c

# compile the main program
primain.o: primain.c dprime.h
	gcc -c primain.c

# have dtrace post-process the object files
prime.o: dprime.d primelib.o primain.o
	dtrace -G -s dprime.d primelib.o primain.o -o prime.o

# link and create executable file "prime"
prime: prime.o
	gcc -Wl,--export-dynamic,--strip-all -o prime prime.o primelib.o primain.o dprime.h -lm


clean:
	-rm -f *.o
	-rm -f prime
	-rm -f dprime.h

例: プログラムのテスト

新たにビルドを作成した後、実行可能ファイルが引き続き期待どおりに動作することをテストします。

# make clean
rm -f *.o
rm -f prime
rm -f dprime.h


# make
gcc -c primelib.c
dtrace -h -s dprime.d
gcc -c primain.c
dtrace -G -s dprime.d primelib.o primain.o -o prime.o
gcc -Wl,--export-dynamic,--strip-all -o prime prime.o primelib.o primain.o dprime.h -lm
# ./prime
Enter a positive target integer to test for prime status: 6799
Square root of 6799 is 82.456047
82 highest value to check as divisor
6799 is not prime because there is a factor 13

USDTプローブの使用

この項では、「アプリケーションに対するUSDTプローブの追加」で作成したUSDTプローブの公称の使用方法で、いくつかの演習を行います。

最初は、次の出力に示すように、アプリケーションがプローブで実行されていないため、プローブは表示されません。

# dtrace -l -P 'prime*'
  ID   PROVIDER            MODULE                          FUNCTION NAME
dtrace: failed to match prime*:::: No probe matches description

アプリケーションを起動しますが、プローブが一覧表示されるまで値を入力しないでください。

# ./prime 
Enter a positive target integer to test for prime status:

別のコマンドラインから、プローブ・リストを発行します。

# dtrace -l -P 'prime*'
   ID      PROVIDER            MODULE                          FUNCTION NAME
 2475 primeget26556             prime                              main query-factorreturnA
 2476 primeget26556             prime                              main query-factorreturnB
 2477 primeget26556             prime                              main query-final
 2478 primeget26556             prime                              main query-maxcheckval
 2479 primeget26556             prime                              main query-userentry

ノート:

プロバイダ名は、dprime.dファイルから定義されたprovider primegetと、実行中のアプリケーションprimeのPIDの組合せです。次のコマンドの出力には、primeのPIDが表示されます。

# ps aux | grep prime
root 26556 0.0 0.0 7404 1692 pts/0 S+ 21:50 0:00 ./prime

root以外のユーザーに対してUSDTスクリプトを実行できるようにするには、ヘルパー・デバイスに適切な権限が必要です。または、rootユーザーとして、プローブを使用してプログラムを実行することもできます。DTraceヘルパー・デバイスのモードの変更の詳細は、「例: DTraceヘルパー・デバイスのモードの変更」を参照してください。

これらの権限を取得する方法の1つは、次のコマンドを実行して、rootユーザー以外のユーザーがプローブ・プロバイダ情報をDTraceに送信できるように構成を変更することです。

# setfacl -m u:guest:rw /dev/dtrace/helper

アプリケーションを再起動しますが、プローブが一覧表示されるまで値を入力しないでください。

# ./prime 
Enter a positive target integer to test for prime status: 

別のコマンドラインから、プローブ・リストを発行します。

# dtrace -l -P 'prime*'
   ID     PROVIDER            MODULE                    FUNCTION NAME
 2456 primeget2069             prime                        main query-factorreturnA
 2457 primeget2069             prime                        main query-factorreturnB
 2458 primeget2069             prime                        main query-final
 2459 primeget2069             prime                        main query-maxcheckval
 2460 primeget2069             prime                        main query-userentry

例: simpleTimeProbe.dを使用した2つのプローブ間の経過時間の表示

次の例は、最初のプローブと2番目のプローブ(query-userentryからquery-maxcheckval)の間の経過時間を測定する、単純なスクリプトを作成する方法を示しています。

/* simpleTimeProbe.d */

/* Show how much time elapses between two probes */

primeget*:::query-userentry
{
  self->t = timestamp; /* Initialize a thread-local variable with the time */
}

primeget*:::query-maxcheckval
/self->t != 0/
{
  timeNow = timestamp;
  /* Divide by 1000 for microseconds */
  printf("%s (pid=%d) spent %d microseconds between userentry & maxcheckval\n",
         execname, pid, ((timeNow - self->t)/1000));

  self->t = 0; /* Reset the variable */
}

ターゲット・アプリケーションの実行を開始します。

# ./prime
Enter a positive target integer to test for prime status:

次に、別のウィンドウからDTraceスクリプトを実行します。

# dtrace -q -s simpleTimeProbe.d

アプリケーションの実行中に、スクリプトの出力も並列して実行されます。

# ./prime 
Enter a positive target integer to test for prime status: 7921
Square root of 7921 is 89.000000
89 highest value to check as divisor
7921 is not prime because there is a factor 89
# ./prime 
Enter a positive target integer to test for prime status: 995099
Square root of 995099 is 997.546509
997 highest value to check as divisor
995099 is not prime because there is a factor 7
# ./prime 
Enter a positive target integer to test for prime status: 7921
Square root of 7921 is 89.000000
89 highest value to check as divisor
7921 is not prime because there is a factor 89

スクリプトが実行されているコマンドラインに、次のような出力が表示されます。

# dtrace -q -s simpleTimeProbe.d
prime (pid=2328) spent 45 microseconds between userentry & maxcheckval
prime (pid=2330) spent 41 microseconds between userentry & maxcheckval
prime (pid=2331) spent 89 microseconds between userentry & maxcheckval
^C

例: timeTweenprobes.dを使用した各プローブ間の経過時間の表示

スクリプトを拡大して、アプリケーションの次のすべてのプローブをモニターできます。

  • query-userentry

  • query-maxcheckval

  • query-factorreturnA

  • query-factorreturnB

  • query-final

/* timeTweenProbes.d */

/* show how much time elapses between each probe */

BEGIN
{
  iterationCount = 0;
}


primeget*:::query-userentry
{
  printf("%s (pid=%d) running\n", execname, pid);
  self->t = timestamp; /* Initialize a thread-local variable with time */
}

primeget*:::query-maxcheckval
/self->t != 0/
{
  timeNow = timestamp;
  printf(" maxcheckval spent %d microseconds since userentry\n",
         ((timeNow - self->t)/1000));  /* Divide by 1000 for microseconds */
  self->t = timeNow; /* set the time to recent sample */
}

primeget*:::query-factorreturnA
/self->t != 0/
{
  timeNow = timestamp;
  printf(" factorreturnA spent %d microseconds since maxcheckval\n",
         ((timeNow - self->t)/1000));  /* Divide by 1000 for microseconds */
  self->t = timeNow; /* set the time to recent sample */
}

primeget*:::query-factorreturnB
/self->t != 0/
{
  timeNow = timestamp;
  printf(" factorreturnB spent %d microseconds since factorreturnA\n",
         ((timeNow - self->t)/1000));  /* Divide by 1000 for microseconds */
  self->t = timeNow; /* set the time to recent sample */
}

primeget*:::query-final
/self->t != 0/
{
  printf(" prime spent %d microseconds from factorreturnB until ending\n",
         ((timestamp - self->t)/1000));
  self->t = 0; /* Reset the variable */
  iterationCount++;
}

END
{
  trace(iterationCount);
}

再度、最初にターゲット・アプリケーションの実行を開始してから、別のウィンドウからスクリプトを実行します。

# ./prime 
Enter a positive target integer to test for prime status: 995099
Square root of 995099 is 997.546509
997 highest value to check as divisor
995099 is not prime because there is a factor 7
# ./prime 
Enter a positive target integer to test for prime status: 7921
Square root of 7921 is 89.000000
89 highest value to check as divisor
7921 is not prime because there is a factor 89
# ./prime 
Enter a positive target integer to test for prime status: 95099
Square root of 95099 is 308.381256
308 highest value to check as divisor
95099 is not prime because there is a factor 61
# ./prime 
Enter a positive target integer to test for prime status: 95099
Square root of 95099 is 308.381256
308 highest value to check as divisor
95099 is not prime because there is a factor 61
# ./prime 
Enter a positive target integer to test for prime status: 5099         
Square root of 5099 is 71.407280
71 highest value to check as divisor
5099 is a prime number

スクリプトからの対応する出力は、次のようになります。

# dtrace -q -s ./timeTweenProbes.d
prime (pid=2437) running
 maxcheckval spent 96 microseconds since userentry
 factorreturnA spent 9 microseconds since maxcheckval
 factorreturnB spent 6 microseconds since factorreturnA
 prime spent 9 microseconds from factorreturnB until ending
prime (pid=2439) running
 maxcheckval spent 45 microseconds since userentry
 factorreturnA spent 10 microseconds since maxcheckval
 factorreturnB spent 7 microseconds since factorreturnA
 prime spent 9 microseconds from factorreturnB until ending
prime (pid=2440) running
 maxcheckval spent 43 microseconds since userentry
 factorreturnA spent 11 microseconds since maxcheckval
 factorreturnB spent 8 microseconds since factorreturnA
 prime spent 10 microseconds from factorreturnB until ending
prime (pid=2441) running
 maxcheckval spent 53 microseconds since userentry
 factorreturnA spent 10 microseconds since maxcheckval
 factorreturnB spent 7 microseconds since factorreturnA
 prime spent 10 microseconds from factorreturnB until ending
prime (pid=2442) running
 maxcheckval spent 40 microseconds since userentry
 factorreturnA spent 9 microseconds since maxcheckval
 factorreturnB spent 48 microseconds since factorreturnA
 prime spent 10 microseconds from factorreturnB until ending

^C
5

前の例と同様に、作成されたプローブで使用できるDTrace機能のセットが表示されます。