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コマンドを実行します。
サンプル・アプリケーション
この項では、この章の以降の演習および例で使用するサンプル・アプリケーションについて説明します。この例は、簡単なプログラムを示しており、完全性や効率性ではなく、簡潔さとプローブの機会を優先しています。
ノート:
次の簡単なプログラムは、例の目的でのみ示されており、実際的な問題を効率的に解決したり、優先されるコーディング方法を示すことを目的としていません。
サンプル・プログラムにより、入力した数値の最小係数が検索されます。このプログラムは、同じ作業ディレクトリに格納されたmakefile
、primelib.h
、primelib.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 ユーザー入力が完了してチェックされた後に使用するプローブ・ポイント
説明 | プローブ |
---|---|
ユーザー入力が完了し、チェックされました |
|
|
|
|
|
プログラム終了直前 |
|
演習: 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ファイルの再作成を指示する
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機能のセットが表示されます。