言語エレメント

このセクションでは、Vue 言語の基本構文、および多くの Vue スクリプトに共通している言語エレメントを定義します。

Variables

Vue 言語は、C-89 仕様で許可される、ほとんどの従来の C データ型をサポートしています。 また、Vue には強力な動的トレース・プログラムを簡単に作成できる拡張機能があります。

Vue は、次の 3 つの異なるスコープ・ルールをもつ変数をサポートしています。

  • 1 つのアクション・ブロックのみにローカルな変数
  • グローバル・スコープをもつ変数
  • スレッド・ローカル・スコープをもつ変数

さらに、Vue はプローブされるカーネルまたはアプリケーションのユーザー・データのグローバル変数のような、外部スコープをもつ変数にもアクセスできます。

通常、変数はスクリプト内で最初に使用される前に宣言されなければなりません。ただし、Vue は非常に限定された形式の暗黙型認識もサポートしています。 アクション・ブロック内の変数宣言ステートメントは、実行可能ステートメントの前に置く必要があります。 これらのステートメントは、if 文のような、ネストされたブロック内に置くことはできません。 アクション・ブロック外で変数を宣言できる場合もありますが、このケースではこのような宣言はすべて最初のアクション・ブロックの前に置く必要があります。

変数クラス

Vue はスコープ、初期化方法、更新の有無、型の判別方法に関してさまざまなルールをもつ変数のクラスをいくつかサポートしています。 C 言語のように、変数の宣言ステートメントは、スクリプト内で最初に使用される前 (文字通り) に置く必要があります。

Vue では、宣言される変数のクラスを示すために宣言ステートメントに追加する特殊な型修飾子が用意されています。 例えば、__global キーワードは、宣言される変数に「global」クラスを指定するために宣言ステートメントに含めるクラス修飾子です。

次の例では、foobar の両方がグローバル・クラスの変数であると宣言されています。

__global int foo, bar;

また、Vue はスクリプト内での最初の使用に基づいた変数の型の暗黙認識もサポートしています。 この場合、宣言ステートメントはありませんが、次のようにスクリプト内の最初の参照でクラス修飾子を変数に直接付加することで変数のクラスを指定できます。

global:count = 5;	/* First reference to variable count in the script */

前述の例の global: キーワードは、count 変数をグローバル・クラスの変数に指定する修飾子です。 この変数には int 型も暗黙的に代入されます。これはこの変数に対する最初の参照が、右辺が整数定数である代入式であるためです。

注: 宣言ステートメントでクラス修飾子を指定する場合は __global キーワードを使用する必要がありますが、スクリプト内での変数の最初の使用でこれを定義する場合は global: キーワードを使用する必要があります。 この構文ルールは、Vue でサポートされる他のクラス修飾子に対しても同様です。

自動クラス変数

自動変数は節固有であり、C の自動変数またはスタック変数と似ています。これは節のアクション・ブロック部分内のみにスコープがあり、ここでこの変数が定義または使用され、アクション・ブロックの呼び出しごとに再作成されます。 自動変数はアクション・ブロックの先頭で常に未定義になっています。自動変数を式または他の実行可能ステートメントで使用するには、事前に代入ステートメントを使用して自動変数を初期化する必要があります。

自動変数は auto: 接頭部を使用して識別されます。例えば、auto:lticks は自動変数を示します。 また、自動変数は __auto 宣言ステートメントを使用しても宣言できます。この場合、auto: 接頭部は省略できます。

自動クラス変数は、Vue 節の述部セクションでは使用できません。

次のスクリプトは、__auto 宣言ステートメントの例です。

	__auto int i;      /* Explicit declaration */
	auto:j = 0;        /* Implicit declaration */

スレッド・ローカル・クラス変数

スレッド・ローカル変数は、値を変数に代入するアクション・ブロックを最初に実行するときに、トレースされるスレッドごとにインスタンス化されます。 スレッド・ローカル変数は、作成されると、Vue スクリプトがアクティブで、トレースされるスレッドが終了しない限り存在します。 スレッド・ローカル変数の値は、スレッド固有であり、同じプログラムのいずれの節の実行後も保持されます。 すなわち、このクラスの変数は Vue スクリプトのすべての部分に表示されます。 ただし、Vue スクリプトを実行する各スレッドはそれ自体の変数のコピーを取得し、各コピーの変数はこれらの変数をインスタンス化したスレッドでのみスクリプト内のどの場所でもアクセスと変更が可能です。

スレッド・ローカル変数は、thread: 接頭部を使用して識別されます。 例えば、thread:count はスレッド・ローカル変数を示します。 また、スレッド・ローカル変数は __thread 宣言ステートメントを使用しても宣言できます。この場合、thread: 接頭部は省略できます。ただし、次の例外があります。

スレッド・ローカル変数は、インスタンス化の前であっても、Vue 節の述部セクションで使用できます。 インスタンス化されないスレッド・ローカル変数をもつ述部は、必ず FALSE の値に評価されます。 thread: 接頭部は、述部セクションで使用する場合、必ずスレッド・ローカル変数として含める必要があります。

次のスクリプトは、__thread 宣言ステートメントの例です。

	__thread int i;      /* Explicit declaration */
	thread:j = 0;        /* Implicit declaration */
注: スレッド・ローカルは @@BEGIN@@END プローブ内で宣言できますが、これらの特殊なプローブ内にあるスレッド・ローカルへの他の参照により未定義の動作が発生する可能性があります。 宣言ステートメント単独では、スレッド・ローカル変数はインスタンス化されません。

グローバル・クラス変数

グローバル・クラスの変数にはグローバル・スコープがあり、この変数は Vue スクリプトのすべての部分に表示されます。 グローバル変数は、Vue スクリプトの 1 つ以上の節で使用できます。 また、グローバル変数は、文字通り最初の節の前の先頭で宣言して分かりやすくすることもできます。 グローバル変数は、必要に応じてゼロまたは NULL に初期化されます。

デフォルトでは、Vue スクリプト内のすべての変数にグローバル・クラスが代入されます。ただし、グローバル・クラス以外の指定子が明示的に宣言の前に置かれている場合を除きます。 また、グローバル変数を明示的に宣言するには、変数を宣言するときに __global クラス指定子を使用することもできます。 リスト変数は、定義上、常にグローバル・クラスの変数として作成されます。

グローバル変数の読み取りと更新は、リスト型でない限り直列化されません。 プローブが同時に実行された場合、データ・レースに関する保証はありません。 リスト型でないグローバル変数は、プロファイルとその他の統計情報を収集するのに有用です。

グローバル変数は、Vue 節の述部セクションで使用できます。

次のスクリプトの例では、グローバル変数を初期化して使用しています。

int wcount;			/* Global variable declared before first clause */

	@@BEGIN
	{
		int f_count;		/* Global variable declared inside @@BEGIN */
		__global int z_count;	/* Global variable declared with __global prefix */

		f_count = 12;
	}

	@@syscall:*:read:entry 
		when (z_count == 0)
	{
		int m_count;		/* Global variable declared inside a probe */
		m_count += f_count;	/* f_count already declared in earlier probe */
		printf("m_count = %d¥n", m_count);
		if (wcount == 1)
			exit();
	}
	

	@@syscall:*:write:entry
	{
		m_count++;		/* m_count already declared in earlier probe */

	}

	@@syscall:*:write:exit
	{
		wcount = 1;		/* w_count declared globally */
	}

カーネル・グローバル・クラス変数

ProbeVue では、特権ユーザーはすべての Vue 節のアクション・ブロック内にあるカーネル・グローバル変数にアクセスできます。uft プローブ・ポイントのようなユーザー・スペースのプローブ・ポイントのものでも同様です。 Vue スクリプトでカーネル変数を使用したり参照したりする場合は、事前に __kernel 宣言ステートメントを使用してこの変数を明示的に宣言する必要があります。 カーネルでエクスポートされた変数 (/unix のエクスポート・リストにある変数) のみアクセス可能です。

整数型のカーネル変数、構造体/共用体およびポインターのカーネル変数にアクセスできます。 さらに、Vue スクリプトのカーネル構造体および共用体のメンバー名も参照できます。 カーネル配列にもアクセスできますが、カーネルの文字データを ProbeVue 文字列にコピーすることはできません。

固定されたカーネル変数のみにアクセスします。 カーネル変数を含むページがメモリーにない (ページアウトされている) 場合は、ProbeVue がその変数に対してゼロの値を戻します。

Vue スクリプトでカーネル変数を宣言して使用する例については、

カーネル変数は、節の述部セクションには表示されません。 カーネル変数は、Vue スクリプトで常に読み取り専用の変数として処理されます。 カーネル変数への書き込みを試行すると、構文エラーが発生するか、失敗してスクリプト中止メッセージが表示されます。

エントリー・クラス変数

システム・コールまたはユーザー関数のエントリー・ロケーション・ポイントにあるプローブ・ポイントに関連した節は、プローブされるシステム・コールまたは関数に渡される引数にアクセスできます。

エントリー・ロケーション・ポイントのプローブは、システム・コールとユーザー関数のトレース・プローブ・マネージャーでサポートされます。 例えば、読み取りシステム・コールは、ファイル・ディスクリプター ID、ユーザー・バッファーに対するポインター、および読み取るデータのバイト数の値の 3 つの引数を取ります。 これらの 3 つの引数の値にアクセスできるのは、プローブ指定が読み取りシステム・コールのエントリー・ポイントのプローブを指定する @@syscall:*:read:entry である場合です。

関数に対するパラメーターは、特別な組み込みエントリー・クラス変数名を使用して参照します。これらの変数名は、__arg1__arg2__arg3 と関数に渡される引数の数までの名前です。 例えば、読み取りシステム・コールのエントリー・ポイントに関連した節では、__arg1 はファイル・ディスクリプター ID パラメーターの値、__arg2 はバッファー・ポインター・パラメーターの値、および __arg3 は読み取るデータのサイズを示します。
注: 1 つ以上のプローブ・ポイント組が指定されている場合、__arg <x> 変数はアクション・ブロックで使用できません。これは次の例のようにエラーとなります。
@@syscall:*:read:entry,@@syscall:*:write:entry
{
        char *argument;        
        argument=__arg2;  -> Not Allowed.
}
Probevue は次のエラー・メッセージを表示して終了します: arg builtin cannot be used. (arg 組み込み変数は使用できません) No defined function. (定義されている関数はありません)

Vue 節でエントリー・クラス変数の使用が有効なのは、プローブされる関数の C 形式の宣言、特に関数に渡されるパラメーターのデータ型が Vue スクリプトにも指定されている場合のみです。 この変数は、エントリー節を参照する最初の Vue 節の前に置く必要があります。 宣言は Vue スクリプトの先頭に、文字通り Vue 節の前に置いてください。

次のスクリプトの例では、エントリー・クラス変数を使用しています。

	int read(int fd, char *buf, unsigned long size);

	@@syscall:*:read:entry 
	{
		printf("Number of bytes to read = %d¥n", __arg3);
	}
注: 前述の例では、スクリプトに指定された読み取りシステム・コール関数の定義は、/usr/include/unistd.h ファイルに指定されたものとは正確に一致しませんが、同様に機能します。

2 つ目の要件は、節に関連したプローブ指定が固有のプローブ・ポイントを識別するということです。 エントリー・クラス変数は、プローブ指定に複数のプローブ・ポイントが指定された Vue 節では使用できません。これは、プローブされる関数が同一であったり、同様の関数プロトタイプを持っていても関係ありません。 次のスクリプトは無効です。このプローブ指定には 2 つのプローブ・ポイントがあるため、ProbeVue コンパイラーは構文エラーで失敗します。

	int read(int fd, char *buf, unsigned long size);
	int write(int fd, char *buf, unsigned long size);

	@@syscall:*:read:entry, @@syscall:*:write:entry 
	{
		/* Cannot use __arg3 in here, as this clause has multiple probe 
		 * points associated with it. This script will fail with a
		 * syntax error in the compilation phase of the probevue command.
		 */
		printf("Number of bytes to read/write = %d¥n", __arg3);
	}

次のように変更されたスクリプトは機能します。

	int read(int fd, char *buf, unsigned long size);
	int write(int fd, char *buf, unsigned long size);

	@@syscall:*:read:entry
	{
		printf("Number of bytes to read = %d¥n", __arg3);
	}
	@@syscall:*:write:entry 
	{
		printf("Number of bytes to write = %d¥n", __arg3);
	}

終了クラス変数

システム・コールまたはユーザー関数の終了ロケーション・ポイントにあるプローブ・ポイントに関連した節は、そのシステム・コールまたはユーザー関数の戻り値にアクセスできます。

Vue 言語で定義される終了クラス変数は 1 つだけあります。 これは関数またはシステム・コールからの戻り値で、この戻り値にアクセスするには特別な組み込み変数名 __rv を使用します。

終了ロケーション・ポイントのプローブは、システム・コールのプローブ・マネージャーでサポートされます。 例えば、読み取りシステム・コールは、読み取られた実際のバイト数またはエラー戻りコード -1 を戻します。 この戻り値には、@@syscall:*:read:exit プローブ・ポイントでアクセスできます。このプローブ・ポイントは、読み取りシステム・コールからのすべての終了ポイントを識別します。

エントリー・クラス変数と同様、Vue 節で終了クラス変数の使用が有効なのは、節に関連したプローブ指定が固有のプローブ・ポイントを識別している場合のみです。 このため、プローブ指定に複数のプローブ・ポイントが指定された Vue 節で __rv を使用することはできません。 また、プローブされる関数の C 形式の宣言、特に戻り値のデータ型を Vue スクリプトに明示的に指定する必要があります。 実際、戻りの型を指定しないで関数宣言を指定するとエラーになります。

終了クラス変数は、節の述部セクションで使用できます。

次のスクリプトは無効です。read 関数の戻りの型が指定されていないため、ProbeVue コンパイラーは構文エラーで失敗します。

/* Bad example.  */

	int read(int fd, char *buf, unsigned long size);

	@@syscall:*:read:exit
		when (__rv > 0)
	{
		/* Entered on read success: return value = # of bytes read */
		printf("Number of bytes read = %d¥n", __rv);
	}

次のように変更されたスクリプトは機能します。

/* Good example.  */

	int read(int fd, char *buf, unsigned long size);

	@@syscall:*:read:exit
		when (__rv > 0)
	{
		/* Entered on read success: return value = # of bytes read */
		printf("Number of bytes read = %d¥n", __rv);
	}

組み込みクラス変数

特別な組み込み変数 __arg1 から __arg32__rv 以外にも、Vue は一連の汎用組み込み変数も定義します。 これらの汎用組み込み変数については、このセクションで詳しく説明します。一部のプローブ・マネージャー固有の組み込み変数については、それぞれのプローブ・マネージャーのセクションで説明します。組み込みクラス変数は関数ですが、ProbeVue では変数として処理されます。 したがって、これらの組み込み変数は Vue 節の述部セクションで使用できます。

Vue では、以下の組み込み変数がサポートされています。

__tid
トレースされるスレッドのスレッド ID。
__pid
トレースされるスレッドのプロセス ID。
__ppid
トレースされるスレッドの親プロセス ID。
__pgid
トレースされるスレッドのプロセス・グループ ID。
__pname
トレースされるスレッドのプロセス名。
__uid, __euid
トレースされるスレッドの実ユーザー ID と実効ユーザー ID。
__trcid
トレース・プロセスのプロセス ID (すなわち、probevue コマンドの ID)
__errno
トレースされるスレッドの現行 errno 値。
__kernelmode
現在の実行可能モード。1 (カーネル・モード) または 0 (ユーザー・モード)。
__r3, ..., __r10
汎用レジスター値 (関数パラメーターまたは戻り値用)。
__curthread
現行スレッド。
__curproc
現行プロセス。
__ublock
現行プロセスのユーザー域。
__mst
現行スレッドのマシン状態保存域 (MST) のハードウェア・レジスターの内容にアクセスするための組み込み変数。
__isISR
現行コンテキストが割り込みサービス・ルーチンであるかどうかを識別する組み込み変数。

次のスクリプトの例では、組み込み変数を使用しています。

	@@syscall:*:read:entry
	{
		printf("Thread ID:%d, Process ID:%d, Parent Process ID:%d¥n",
				__tid, __pid, __ppid);
		printf("Process Group ID: %d¥n", __pgid);
		printf("Process name = %s¥n", __pname);
	
		printf("Real UID=%d, Effective UID=%d¥n", __uid, __euid);S
	       
		printf("probevue command process ID = %d¥n", __trcid);
	
		printf("Errno = %d¥n", __errno);
		printf("Mode = %s¥n", __kernelmode == 1 ? "kernel" : "user");"kernel" : "user");
	
		printf("Current values of GPRs: r3=0x%016llx, r4=0x%016llx, r5=0x%016llx¥n",
				__r3, __r4, __r5);
		printf("                        r6=0x%016llx, r7=0x%016llx, r8=0x%016llx¥n",
				__r6, __r7, __r8);
		printf("                        r9=0x%016llx, r10=0x%016llx¥n",
				__r9, __r10);
	}

__curthread 組み込み変数

__curthread は特殊な組み込み変数であり、ユーザーはこれを使用して、現行スレッドのスレッド関連情報の一部にアクセスできます。 この情報には __curthread 組み込み変数で -> 演算子を使用してアクセスできます。 この組み込み変数は、systraceBEGIN、および END プローブでは使用できません。 また、これは、PID が示されている場合のみに間隔プローブで使用できます。 この組み込み変数は基本的には getthrds/getthrds64 と同様の機能を提供しますが、現行スレッドのみを対象とします。 アクセスできるデータは以下のとおりです。

tid
スレッド ID
pid
プロセス ID
ポリシー
スケジューリング・ポリシー
pri
優先順位
cpusage
CPU 使用率
cpuid
現行スレッドのバインド先のプロセッサー
sigmask
スレッドでブロックされているシグナル
lockcount
スレッドに取得されているカーネル・ロックの数
ptid
このスレッドの pthread ID (カーネル・スレッドの場合は 0、単一スレッド・アプリケーションの場合は 1)
homecpu
スレッドのホーム CPU。
homesrad
スレッドのホーム srad

使用例

Tid of the current thread can be accessed using  __curthread->tid.

__curproc 組み込み変数

__curproc は特殊な組み込み変数であり、ユーザーはこれを使用して、現行プロセスのプロセス関連情報の一部にアクセスできます。 この情報には __curproc 組み込み変数で -> 演算子を使用してアクセスできます。 この組み込み変数は、systraceBEGIN、および END プローブでは使用できません。 また、これは、PID が示されている場合のみに間隔プローブで使用できます。 この組み込み変数は基本的には getproc と同様の機能を提供しますが、現行プロセスのみを対象とします。 アクセスできるデータは以下のとおりです。

pid
プロセス ID。
ppid
親プロセス ID
pgid
プロセス・グループ ID
uid
実ユーザー ID
suid
保存されているユーザー ID
pri
優先順位
nice
nice の値
cpu
プロセッサー使用率
adspace
プロセスのアドレス・スペース
majflt
入出力ページ不在
minflt
非入出力ページ不在
size
ページ単位のイメージのサイズ
sigpend
プロセスで処理中のシグナル
sigignore
プロセスに無視されているシグナル
sigcatch
プロセスにキャッチされているシグナル
forktime
プロセスの作成時間
threadcount
プロセス内のスレッド数
cwd
現行作業ディレクトリー。 フリー・ページ・フォールト・コンテキストが使用不可の場合、CPU ごとの計算スタック・サイズが 96 KB 未満の場合、またはページ・フォールトが許可されないプローブ (例えば、間隔プローブなど) の場合、この組み込み変数はヌル・ストリングを返します。

使用例

Parent process id of the current process can be accessed using  __curproc->ppid.

__ublock 組み込み変数

__ublock は特殊な組み込み変数であり、ユーザーはこれを使用して、現行プロセスのプロセス関連情報の一部にアクセスできます。 この組み込み変数は、systraceBEGIN、および END プローブでは使用できません。 また、これは、PID が示されている場合のみに間隔プローブで使用できます。 この情報には __ublock 組み込み変数で -> 演算子を使用してアクセスできます。 アクセスできるデータは以下のとおりです。

text
テキスト開始
tsize
文字サイズ (バイト数)
データ
データの開始
sdata
現行データ・サイズ (バイト数)
mdata
最大データ・サイズ (バイト数)
stack
スタックの開始
stkmax
スタックの最大サイズ (バイト数)
euid
有効ユーザー ID
uid
実ユーザー ID
egid
有効グループ ID
gid
実グループ ID
utime
プロセスのユーザー・リソース使用時間 (秒数)
stime
プロセスのシステム・リソース使用時間 (秒数)
maxfd
ユーザー内の最大 fd 値
is64u
64 ビット・プロセスのコンテキストであれば、1 に設定

使用例

Start of the text for the current process can be accessed using  __ublock->text.

__mst 組み込み変数

__mst は特殊な組み込み変数で、ユーザーはこれを使用して現行スレッドのハードウェア・レジスターの内容にアクセスできます。 この組み込み変数は、systrace プローブ、BEGIN プローブ、および END プローブでは使用できません。 また、この組み込み変数は、PID が示されている場合にのみ、間隔プローブで使用できます。 この情報には、__ublock 組み込み変数で -> 演算子を使用することによりアクセスできます。 アクセスできるレジスターは以下のとおりです。

r1-r10
汎用レジスター r1 から r10
r14-r31
汎用レジスター r14 から r31
iar
命令アドレス・レジスター
lr
リンク・レジスター

使用例

プローブで lr 値にアクセスするには、次のコマンドを使用します。
__mst->lr

値および型の代入

前セクションの分類は、Vue スクリプトの変数を考察する 1 つの方法です。 変数クラスは異なる視点 (これらの値の派生方法) から考察できます。 この視点では、これらの変数を 2 つのカテゴリーに分類できます。

外部変数

カーネル・クラス変数、エントリーおよび終了クラス変数と組み込み変数はすべて外部変数です。 これらの変数は、ProbeVue フレームワークからは独立して存在し、Vue スクリプトのコンテキスト外でその値を派生させます。 ProbeVue では、外部変数の現行値を Vue スクリプト内で使用可能にすることができます。 これらの変数は、Vue スクリプトのコンテキスト内では常に読み取り専用です。 プログラムのステートメントが外部変数の値を変更しようとすると、コンパイラーはこのステートメントに無効なステートメントのフラグを立てます。

外部変数には定義済みの型がありますが、ProbeVue では外部変数にアクセスする Vue スクリプト内ですべての外部変数 (組み込み変数を除く) を明示的に宣言する必要があります。 次の表では、外部変数の型がどのように判別されるかについて説明します。

変数
カーネル・グローバル・クラス カーネル変数の __kernel 宣言ステートメントから。
エントリー・クラス Vue スクリプトの関数プロトタイプの宣言から。 Vue スクリプトで使用される各引数のデータ型を指定する必要があります。
カーネル関数からの戻り値 Vue スクリプトの関数プロトタイプの宣言から。 戻り値の型を指定する必要があります。
組み込み 通常、基本のカーネル変数に依存します。 これらの定義済み型および同等の ProbeVue 型を以下に示します。
組み込み 定義済み型 ProbeVue
__tid tid_t long long
__pid pid_t long long
__ppid pid_t long long
__pgid pid_t long long
__pname char [32] String [32]
__uid uid_t unsigned int
__euid uid_t unsigned int
__trcid pid_t long long
__errno int int
__kernelmode int int
__r3..__r10 32 ビット・プロセスでは 32 ビット

64 ビット・プロセスでは 64 ビット

unsigned long
__curthread N/A すべてのメンバーは long long です。
__curproc N/A cwd を除くすべてのメンバーは long long です。 cwd メンバーの型は文字列です。
__ublock N/A すべてのメンバーは long long です。
注: 戻りデータの最大サイズは、型のサイズよりも小さくなることがあります。 例えば、AIX® のプロセス ID は 32 ビット整数に適合する一方で、pid_t データ型は 64 ビット・プロセスとカーネルでは 64 ビット整数です。

スクリプト変数

スクリプト変数は、自動、スレッド・ローカル、またはグローバル・クラス変数のいずれかです。 スクリプト変数は Vue スクリプトのコンテキスト内にのみ指定され、これらの値はスクリプトから代入されます。 また、これらの変数を定義するスクリプト内でのみアクセスまたは変更可能です。

通常、スクリプト変数のデータ型は宣言ステートメントを使用して明示的に宣言する必要があります。 ただし、変数への最初の参照が代入演算子の左辺に変数をもつ代入操作である場合は、コンパイラーでプログラム変数のデータ型を暗黙的に判別することができます。

整数型の暗黙型指定

整数型を代入するには、代入の右辺が次のいずれかの状態でなければなりません。

  • 定数。
  • 整数型の別の変数 (組み込み変数を含む)。 型が不明な変数からの代入はエラーになります。
  • 整数型を戻す Vue 関数 (diff_time 関数など)。
  • 右辺の式を整数型にキャストする。ただし、警告が出される場合があります。
  • 前述の状態に関連した式。

変数はその型と値を右辺の式に基づいて取ります。 また、変数のクラスを変数の前に指定することで変数に代入することができます。 次のスクリプトでは、いくつかの例を示します。

/*
 * File:	implicit2.e
 * Usage:	Demonstrates implicit assignment for integer types 
 */

int read(int fd, char *p, long size);

@@BEGIN
{
	count = 404;			/* count: int of global class */
	zcount = 2 * (count - 4);	/* zcount: int of global class */
	llcount = 33459182089021LL;  	/* lcount: long long of global class */
	lxcount = 0xF00000000245B20LL;	/* xcount: long long of global class */

}

@@syscall:$1:read:entry
{
	__auto probev_timestamp_t ts1, ts2;
	int gsize;
	ts1 = timestamp();
	auto:dcount = llcount - lxcount;  /* dcount: long long of auto class */ 

	auto:mypid = __pid;	/* mypid:  pid_t (64-bit integer) of automatic class */
	fd = __arg1;		/* fd: int of global class */

	/* The following cast will likely cause a compiler warning
	 * but can be ignored here
	 */
	global:bufaddr = (long)__arg2;	/* bufaddr: long of global class */

	gsize = __arg3;
	thread:size =  gsize + 400;	/* size: int of thread-local class */

	printf("count = %d, zcount = %lld¥n", count, zcount);
	printf("llcount = %lld, lxcount = 0x%016llx, diff = %lld¥n",
			llcount, lxcount, dcount);
	printf("mypid = %ld, fd = %d, size = %d¥n", mypid, fd, size);
	printf("bufaddr = 0x%08x¥n", bufaddr);
	ts2 = timestamp();
	
	auto:diff = diff_time(ts1, ts2, MICROSECONDS);	/* diff: int of automatic class */
	
	printf("Time to execute = %d microseconds¥n", diff);
	
	exit();
}
注: 前述のスクリプトには、@@syscall:$1:read:entry プローブ指定にシェルの定位置パラメーターのような $1 シンボルがあります。 syscall プローブ・マネージャーでは、2 番目のフィールドにプロセス ID を指定して、システム・コールのプローブ・ポイントを特定のプロセスに対してのみ使用可能にすることができます。 特定のプロセス ID をハードコーディングするのではなく、このスクリプトでは 2 番目のフィールドがシェルの定位置パラメーターに設定されていて、スクリプトの実行時に実際のプロセス ID が引数として渡されることを許可します。 probevue コマンドは、スクリプト内のシェルの定位置パラメーターをコマンド行で渡される各引数に置き換えます。

プロセス ID が 250000 のプロセスをプローブすると仮定して、次のスクリプトでは implicit2.e スクリプトの実行例を示します。

# probevue implicit2.e 250000
WRN-100: Line:29 Column:26 Incompatible cast
count = 404, zcount = 800
llcount = 33459182089021, lxcount = 0x0f00000000245b20, diff = -1080830451389212643
mypid = 250000, fd = 10, size = 4496
bufaddr = 0x20033c00
Time to execute = 11 microseconds

前述の例では、スクリプト内の $1 シンボルが "250000" に自動的に置き換えられ、プロセス ID が 250000 であるプロセスに、読み取りシステム・コールのエントリー・プローブ・ポイントが限定されます。

文字列型の暗黙型指定

文字列型を代入するには、代入の右辺が次のいずれかの状態でなければなりません。

  • 文字列リテラル。二重引用符で囲まれた 一連の文字列。
  • string (文字列) 型の別の変数 (組み込み変数を含む)。
  • 文字列を戻す Vue 関数 (et_userstring 関数など)。
  • 前述の状態に関連した式。

次の例では、暗黙の文字列型の代入を示します。

/*
 * File:	implicit3.e
 * Usage:	Demonstrates implicit assignment for string types 
 */

int write(int fd, char *p, long size);

@@BEGIN
{
	s1 = "Write system call:¥n";
}

@@syscall:$1:write:entry
{
	String s2[40];
	
	wbuf = get_userstring(__arg2, __arg3);

	s2 = s1;

	zbuf = s2;

	pstring = zbuf + wbuf;

	printf("%s¥n", pstring);
}

@@syscall:$1:write:exit
{
	ename = __pname;
	printf("Exec name = %s¥n", ename);
	exit();
}

$1 シェル定位置パラメーター変数を置き換えるには、スクリプトの実行時にプロセス ID を引数としてスクリプトに渡す必要があります。

リスト型の暗黙型指定

リスト型を代入するには、代入の右辺が list() 関数でなければなりません。 list() 関数はどの節からでも使用できます。

有用なカーネル変数

次の表では、Vue スクリプト内からアクセスできる有用なカーネル変数の例をいくつか示します。 これらの変数の名前または意味が他の AIX リリースで変更される可能性があるため (可能性は低いですが)、Vue スクリプトでこれらの変数を使用する場合はご注意ください。 これらのカーネル変数はメモリーに固定され、カーネルからエクスポートされます。

カーネル変数 説明 関連ヘッダー・ファイル
struct system_configuration _system_configuration システム構成構造。 sys/systemcfg.h
struct var v 基本のカーネルのチューナブル (およびその他の) パラメーター。 sys/var.h
struct timestruc_t tod メモリー・マップされた時刻機構。 エポックからの秒数およびナノ秒数。 sys/time.h
cpu_t high_cpuid オンラインの最高論理 CPU ID。 sys/encap.h
struct vminfo vmminfo vmstat コマンドで表示される情報を含むデータ構造体。 sys/vminfo.h
time_t lbolt 最後のブート以降のティック数。 sys/time.h
char spurr_version 現行システムで SPURR レジスターをサポートするかを指定します。0= SPURR なし、1= CPU に SPURR あり。 sys/sysinfo.h
struct utsname utsname システム名の構造体 (オペレーティング・システム名、ノード名、リリース・レベルなど)。 sys/utsname.h

32 ビットおよび 64 ビット・プロセスのデータ・モデル

AIX は 32 ビットと 64 ビットの 2 つの開発環境をサポートしています。 このため、AIX のコンパイラーは次の 2 つのプログラミング・モデルを提供します。

ILP32
ILP32 (整数、long、およびポインター 32 の頭字語) は、AIX の 32 ビット・プログラミング環境です。 ILP32 データ・モデルは、4 GB の理論的メモリー制限付きで 32 ビット・アドレス・スペースを提供します。
LP64
LP64 (long およびポインター 64 の頭字語) は、AIX の 64 ビット・プログラミング環境です。 LP64 はデータ型サイズと位置合わせを除いて、ILP32 モデルと同じプログラミング機能をサポートしており、最も広く使用されている int データ型と後方互換性があります。

したがって、AIX のプログラムは、32 ビット・プログラムまたは 64 ビット・プログラムのどちらでも実行されるようにコンパイルできます。 32 ビットまたは 64 ビット・モードで実行されるプロセスに対して同じ Vue スクリプトを実行できます。 データ・モデルの仕様どおり、プローブ (トレース) されるプロセスが 32 ビット・プロセスである場合、Vue スクリプトでアクセスされる long 型の外部変数は 4 バイト長として処理されなければなりません。 プローブされるプロセスが 64 ビット・プロセスである場合は、8 バイト長として同じ変数が処理されなければなりません。 構造体または共用体 (ポインターまたは long 変数のメンバーを含む) のレイアウトとサイズは、32 ビット・プロセスまたは 64 ビット・プロセスのどちらの視点から判別するかによって異なります。 混乱が生じないように、Vue には変数のクラスに基づいて論理的で一貫した方法で 2 つの異なるデータ・モデルを処理するための意味規則があります。

サイズ不変の変数型

次の変数型はサイズ不変です。 これらのサイズは、宣言された変数のクラスに関係なく、32 ビット・モードでも 64 ビット・モードでも常に同じです。

サイズ
long long 8
int 4
short 2
char 1

サイズ可変の変数型

次の変数型には 32 ビット・モードと 64 ビット・モードの両方があります。

32 ビットのサイズ 64 ビットのサイズ
long 4 8
ポインター型 4 8

上記の表では、ポインター型は char *int *struct foo *unsigned long * などの型を示します。

前述の型 (「long」や「ポインター」など) で定義される変数には、以下の意味規則が適用されます。 これらの規則は、変数が構造体または共用体のどちらのメンバーであろうと、また変数が個々の変数として宣言されても適用されます。

自動クラス
変数のモードは、プローブされるプロセスのモード (32 または 64) によって異なります。
スレッド・ローカル・クラス
変数のモードは、プローブされるプロセスのモード (32 または 64) によって異なります。
グローバル・クラス
プローブされるプロセスのモードに関係なく、変数は常に 64 ビット・モードで処理されます。 これにより、データを失うことなく、32 ビット・プロセスと 64 ビット・プロセスの両方で変数を正常に使用できます。
カーネル・グローバル・クラス
AIX 6.1 以降でサポートされるカーネルは 64 ビット・カーネルのみであるため、long またはポインターのカーネル変数は常に 64 ビット・モードです。
エントリー・クラス
関数に対するパラメーターの関数プロトタイプに long またはポインター型が定義されている場合は、対応するエントリー・クラス変数 (__arg1 から __arg32) のモードは、プローブされるプロセスのモード (32 または 64) によって決まります。
終了クラス
関数の戻り値の型として関数プロトタイプに long またはポインター型が定義されている場合は、終了クラス変数 (__rv) のモードは、プローブされるプロセスのモード (32 または 64) によって決まります。
組み込みクラス
通常、この変数はサイズ不変の型を持っています。ただし、__r3 から __r10 までの組み込み変数は除きます。これらの変数には符号なし long 型が定義されているため、32 ビット・プロセスでは 32 ビット long、64 ビット・プロセスでは 64 ビット long です。

@@BEGIN プローブと @@END プローブは、常に 64 ビット・モードで実行されます。

Vue のデータ型

Vue 言語は、従来の C-89 データ型に加えて、特殊な 3 つのデータ型を受け入れます。

C 言語から派生したデータ型

Vue 言語は、C-89 仕様で定義される、ほとんどのデータ型をサポートしています。 これには符号付きと符号なしの整数データ型 charshortintlong、および long long が含まれます。 「プレーン」の char は符号なしとして処理され、その他の整数型は (非修飾の場合) 符号付きとして処理されます。 これは PowerPC® での C の実装と同じです。 Vue 言語は、浮動小数点型 floatdouble もサポートしています。 これらの C 言語の基本の型に加えて、Vue は配列などの派生型、構造体、共用体、およびポインター型、列挙型、および void などの不完全型もサポートしています。

Vue のこれらの型は C 言語の同等の型と同じ構文と意味を持っていますが、以下の例外があります。

浮動小数点型
浮動小数点型は、単純代入式と printf のような Vue 関数の引数としてのみ使用できます。 特に、代入演算子以外の単項演算子または 2 項演算子のオペランドとして浮動小数点変数を使用することはできません。
ポインター型
ポインターを使用してカーネルまたはアプリケーション・データを間接参照することができます。 ただし、Vue スクリプト変数に対するポインターを宣言したり、そのアドレスを取ることはできません。
文字配列
文字配列は C のように文字列として使用することはできませんが、文字列データ型を使用する必要があります
不完全型
サイズが不明な配列型は使用できません。
ビット・フィールド型
Vue コンパイラーはビット・フィールドの宣言を無視し、ビット・フィールドであるメンバーを含む構造体または共用体の型のレイアウトは未定義となります。
ILP32 および LP64 データ・モデル
通常、C プログラムは ILP32 データ・モデルに従う 32 ビット・モードまたは LP64 モデルに従う 64 ビット・モードのどちらでもコンパイルできます。 32 ビット・プロセスと 64 ビット・プロセスの両方で同じ Vue 節を実行できるため、Vue は同時に両方のモデルを内部でサポートしています。

範囲データ型およびバケット・データ型

Vue における範囲データ型は、一定の定義済み範囲のデータ・ポイントの分布を処理するように設計されています。範囲データ型の定義済み変数の各範囲には、対応する範囲値内に収まるエレメント数のカウントが表示されます。範囲データ型は、範囲の整数型および文字列型をサポートします。 整数範囲の範囲値の分布は、2 の累乗分布または線形分布のいずれかです。範囲値の線形分布および 2 の累乗分布の例を以下に示します。

線形分布:

範囲 カウント
0 - 5 2
5 - 10 4
10 - 15 1
Others 20

2 の累乗分布:

範囲 カウント
1 - 2 2
2 - 4 9
4 - 8 2005
8 - 16 4
16 - 32 1999
32 - 64 7
Others 5

上記の分布は、範囲値の下限より大か等しく、かつ上限より小さいエレメント数のカウントを示しています。例えば、2 の累乗分布では、4 以上 8 未満のデータのカウントは 2005 です。定義された範囲に入らない値のカウントは「Others」の範囲に表示されます。

文字列範囲の例

例:

範囲 カウント
Read、write、open 87
Close、foo1 3
foo2 1
Others 51

前の例では、分布は、範囲値内に特定の文字列が現れる回数を示します。 この例では、read、write、および open が 87 回呼び出されています。

範囲データ型の宣言と初期化:

範囲データ型はキーワード range_t を使用して宣言できます。例えば、Vue スクリプトで次のように宣言すると、2 つの範囲データ型変数が定義されます。

range_t T1, T2;  // T1 and T2 are of Range data type variables.

ルーチン set_range および add_range は、いずれの特定の範囲データ型変数についても整数範囲および文字列範囲を初期化するのに使用されます。

整数範囲データ型の初期化: 整数範囲の初期化には、ルーチン set_range が使用されます。set_range の構文は、範囲値の線形分布の場合と 2 の累乗分布の場合で異なります。線形分布の場合のルーチン set_range の構文は、以下のようになります。

void set_range(range_t range_data, LINEAR, int min, int max, int step); 

例:

set_range(T1, LINEAR, 0, 100, 10);

前の例では、ルーチン set_range は範囲データ T1 を初期化します。範囲データ T1 は値の線形分布を持ちます。T1 の下限は 0 で、上限は 100 です。 各範囲のサイズは 10 です。前の例の分布は次のようになります。

範囲 カウント
0 - 10 ...
10 - 20 ...
20 - 30 ...
... ...
... ...
90 - 100 ...

2 の累乗分布の初期化の構文は、以下のようになります。

set_range(range_t range_data, POWER, 2);

例:

set_range(T2, POWER, 2);

この例では、ルーチンにより、範囲型データ T2 が 2 の累乗分布範囲型として初期化されます。

文字列範囲データ型の初期化: 文字列範囲データ型の初期化は、ルーチン add_range によって行われます。

構文:

void add_range(range_t range_data , String S1, String S2, ..., String Sn);

例:

add_range(T1, “read”, “write”, “open”);

このルーチンにより、文字列 readwrite、および openrange_t データ T1 の 1 つのスロットに追加されます。同じ range_t データ T1 に対して add_range をもう 1 つ使用すると、文字列は次のスロットに追加されます。

add_range(T1, “close”, “func1”, “func2”);

このルーチンでは、文字列 closefunc1、および func2 が次のスロットの range_t データ T1 に追加されます。

注: range_t 範囲データ型は Vue 用の特殊なデータ型であり、連想配列内の値として保管するためにのみ使用できます。range_t データ型に対するその他の演算 (算術演算、論理演算、ビット単位演算、関係演算など) はいずれも失敗し、エラーとなります。
注: ここでは、range_t データ型の各種の使用法および初期化ルーチンについて説明しています。
  1. range_t データ型の宣言は @@BEGIN 節でのみ実行できます。
  2. ルーチン set_range の初期化は @@BEGIN 節内でのみ使用できます。
  3. 範囲値が整数である範囲データ型は 1 回のみ初期化できます。同じ変数を 2 回初期化することはできません。

    例:

    set_range(T1, LINEAR, 0, 50, 5);        // Valid syntax
        set_range(T1, LINERA, 10, 100, 10); // Error, cannot initialize an already
                                            // initialized T1.
        set_range(T1, POWER, 2);            // Error, T1 has already initialized.
        add_range(T1, “read”, “write”);     // Error, T1 has already initialized.
  4. パラメーター minmax、および step は、set_range ルーチンでは整数定数でなければなりません。

範囲データ型の保管および出力:

qrange ルーチンを使用すると、範囲データ型を連想配列内に値として保管できます。qrange ルーチンは、頻度およびカウントを増分する必要があるスロット番号を検出します。

例:

この例では、T1 は range_t データ型であり、その範囲値は整数型です。

qrange(aso[“read”], T1, time_spent);

この例では、qrange ルーチンにより、time_spent が失敗するスロット番号が検出され、そのスロット番号のカウントは、キー「read」に対応する連想配列 aso について増分されます。

以下の例では、T2 は range_t データ型であり、範囲値は文字列型です。

qrange(aso[“function usage”], T2,get_function());

この例では、qrange ルーチンにより、3 つ目の引数として渡される関数が失敗するスロット番号が検出され、そのスロット番号のカウントは、キー function usage に対応する連想配列 aso について増分されます。

注:
  1. どの ASO についても、値として保管できる range_t 型変数は 1 つのみです。同じ ASO について 2 つの異なる型の range_t 変数型を指定すると、qrange は失敗します。

    例:

    
    qrange(aso[“read”], T1,time_spent);  // Correct syntax.
    qrange(aso[“read”], T2,time_spent);  // Error. Two different range_t types
                                           // cannot be used for the same ASO.

    値の型が range_t である連想配列の関数 quantize および lquantize により、範囲の頻度およびカウントのビジュアル量子化が示されます。

  2. 文字列範囲の表示では、特定の 1 スロットについて最大 40 文字 (コンマを含む) を表示できます。スロット内の文字列が 40 文字を超える場合は、文字列範囲の切り捨てが行われ、最後の 3 文字がドット (…) である出力が表示されます。

範囲データ型と qrange ルーチンの例:

@@BEGIN
{
  __thread start ;
  range_t T1;
  set_range(T1, LINEAR, 0, 150, 10) ;
}
@@syscall :$__CPID :read :entry
{
  thread :tracing = 1 ;
  start = timestamp() ;
}
@@syscall :$__CPID :read :exit
         when(thread :tracing == 1)
{
  __auto long time_spent;
  currtime = timestamp() ;
  time_spent = diff_time(start, currtime, MICROSECONDS);
  qrange(aso[“read”], T1, time_spent);
}
@@END
{
  print(aso);
  quantize(aso);
}

この例で期待される出力:

Key                                     Value

Read           Range                    count
               0-11	                     4
               10-20                     6
               60-70                     7
               Others                   32

Key                                     Value

Read           Range                    count
               0-10                       4      ===
               10-20                      6      ====
               60-70                      7      =====
               Others                    32      ================

スタック・トレース型

stktrace_t 型の変数は、現行のスタック・トレースを返す ProbeVue 関数 get_stktrace からの戻り値を保持するために使用されます。 返されるスタック・トレースは、現行スレッドのスタック・トレースです。 この変数は、キーまたは値として連想配列に保管することもできます。 stktrace_t 型は抽象データ型であり、この変数を標準 C の単項または 2 項演算子と直接使用することはできません。 内部では、この変数は unsigned long の配列です。

Vue は、スタック・トレース型変数に関して、以下の特性および演算をサポートしています。

スタック・トレース型変数の宣言

以下のようにスクリプトで宣言することにより、変数はスタック・トレース型であると宣言することができます。
      stktrace_t  st;              // st is a stktrace_t variable.
      st = get_stktrace(5);        //  Get the stack trace up to five levels. 
      a_st[0] = get_stktrace(-1);  // Get the stack trace up to the extent possible and
                                  // store in the associative array a_st as value.
signed、unsigned、register、static、auto、thread、kernel、および const 修飾子は、stktrace_t 型変数ではサポートされていません。

代入演算

代入 (=) 演算子では、stktrace_t 型変数を別の stktrace_t 型変数に割り当てることが許可されます。ターゲットの stktrace_t 変数内にある、元の値は破棄されます。 stktrace_t 変数からの型キャスト、またはこの変数への型キャストは許可されません。 以下の例で、スタック・トレース t1 の内容は、t2 に割り当てられます。
      stktrace_t     t1, t2;          // Declares two stack trace variables.
      t1 = get_stktrace();            // Get the current stack trace in t1.
      t2 = t1 ;                       // Get the content of t1 into t2.

比較演算

stktrace_t 変数では、等価 (==) 演算子と不等価 (! =) 演算子のみが許可されます。 これらの演算子の結果は、stktrace_t 変数の全体のエントリーに基づいて、True(1) または False(0) のいずれかになります。 stktrace_t 変数の個別のエントリーの比較は許可されません。 他の比較演算子 (>=>< または =<) は、stktrace_t 型変数では許可されません。
            if( t1 == t2)  // comparing two stktrace_t type variables.
                     printf(“Entries are similar”);
              else
                     printf(“Entries are not similar”);

スタック・トレース型変数の表示

stktrace_t 変数は、Vue の printf 関数の %t フォーマット指定子を使用して表示できます。 出力は、この変数に保存されるスレッドのシンボリック・スタック・トレースです。 アドレス付きシンボル (シンボルとアドレス) は、stktrace_t 変数に対応するスレッドが実行状態の場合にのみ表示されます。それ以外の場合は、その変数に関して、アドレスとしてのスタック・トレースのみが表示されます。

キーまたは値として連想配列に保管された stktrace_t 型変数は、連想配列の print 関数を使用して表示することができます。 連想配列に保管された stktrace_t 型に対応するスレッドが実行状態の場合、シンボル付きアドレス (シンボル名とオフセット) が表示されます。それ以外の場合は、アドレスのみが表示されます。
    stktrace_t t1;
    t1 = get_stktrace (5);
    printf (“%t”, t1);       // Displays the stack trace stored in variable t1.
    a[__tid] = t1;           // Store t1 as value in an associative array a.
     print(a) ;              // Print associative array a, whose value 
																			//	type is stktrace_t variable. 
                                                     

スタック・トレース型変数の制限

  • stktrace_t 変数の配列は宣言できません。
  • stktrace_t 変数は、struct または union のメンバーとして使用できません。
  • スタック・トレースの個別のエントリーへのアクセスは許可されません。
  • stktrace_t 型変数の演算 (代入、比較、および印刷) は、systrace プローブではサポートされません。

特殊なデータ型

従来の C-89 データ型に加えて、Vue 言語は特殊な 7 つのデータ型も受け入れます。

文字列型

文字列データ型は、文字列リテラルの表記です。 C とは異なり、Vue では文字列が基本のデータ型です。 文字列型を指定すると、文字列型をサポートしない C での混乱がいくらか回避されますが、文字列が char 型に対するポインターと文字配列の両方で表記されます。

文字列変数は、文字列宣言ステートメントを使用して明示的に宣言できます。 明示的に宣言された文字列変数には、文字列の最大の長さも指定する必要があります (C での文字配列の宣言方法に似ています)。 C とは異なり、Vue の文字列はヌル文字では明示的に終了されず、このためにスペースを予約する必要はありません。

   String s[40];		/* Defines a string 's' of length 40 */
   s = "probevue";

また、二重引用符で囲まれた C 形式の文字列リテラルに、文字列データ型が自動的に代入されます。 Vue は必要に応じて、C 形式の文字データ型 (char * または char[]) として宣言される外部変数を文字列データ型に自動的に変換します。

文字列データ型には、次の演算子を使用できます。

  • 連結演算子: "+"
  • 代入演算子: "="
  • 比較文字列 "=="、"!="、">"、">="、"<"、および "<=" の相対演算子。

文字列変数を空文字列に設定するには、次の例のように "" を代入します。

s = "";		/* Sets s to an empty string */

C 言語とは異なり、隣接する文字列リテラルは自動的に連結されません。 次の例のように、連結演算子 (+) を明示的に適用する必要があります。

String s[12];

	// s = "abc" "def";	
   /* ERROR: Commented out as this will result in a syntax error */
	s = "abc" + "def";	/* Correct way to concatenate strings */

Vue は文字列データ型をパラメーターとして受け入れる関数、または文字列データ型をもつ値を戻す関数をいくつかサポートしています。

リスト型

リスト型の変数は、整数型の値のセットを収集します。 リスト型は抽象データ型であり、リスト変数を標準 C の単項または 2 項演算子と直接使用することはできません。 リスト型には、次の演算を使用できます。

  • 新規リスト変数を作成するためのコンストラクター関数 list() (定義されていない場合)。この変数が既に定義されている場合は、クリアされます。
  • 項目をリストに追加したり、2 つのリストを結合したりするための連結関数 append
  • リストを別のリストに代入する「=」演算子。
  • リスト変数で機能し、スカラー (整数) 値を戻す集約関数のセット (sumavgminmax など)。

リスト変数を使用して整数値を収集することはできますが、値は常に 64 ビットの符号付き整数として保存されます。

list() 関数は、リスト型の変数に代入される新しい空のリストを戻します。 これにより、代入演算子の左辺のリスト変数がリストに代入されていない場合は、新しいリスト型の変数が作成されます。 ターゲット・リストに収集された値が廃棄された場合は、これが既存のリスト変数に代入される場合もあります。 また、Vue スクリプトの任意の位置で次のように変数を宣言すると、リスト型の変数を宣言できます。

       __list l_opens;

この結果、list() 関数が @@BEGIN プローブで呼び出されて、戻り値がこのリスト変数に代入されたかのようになります。

次の例では、l_opens という新しいリスト変数を作成します。

l_opens = list();

list 関数はどの節からでも呼び出し可能です。 list 関数を呼び出すときに既存のリスト名を指定した場合は、その既存のリストがクリアされます。

append() 関数を使用すると、リスト変数に値を追加できます。append 関数への呼び出しごとに、リスト変数に既に保存されている値のセットに新しい値が追加されます。次の例では、append 関数への呼び出しごとにリスト変数のサイズがどのように大きくなるかを示します。

	append(l_opens, n_opens1); /* l_opens = {n_opens1} */ 
	append(l_opens, n_opens2); /* l_opens = {n_opens1, n_opens2} */ 
	append(l_opens, n_opens3); /* l_opens = {n_opens1, n_opens2, n_opens3} */ 
	append(l_opens, n_opens4); /* l_opens = {n_opens1, n_opens2, n_opens3, n_opens4} */ 

append() 関数に対する 2 番目のパラメーターには、最初のパラメーターで指定されたターゲット・リストにすべての値を追加するリスト型の変数も指定できます。このため、append を使用して 2 つのリストを結合することもできます。

次の例では、リスト b の内容がリスト a に追加されます。

a=list()
b=list()
append(a,b)
注: リストに追加する値は、整数またはリスト型のパラメーターでなければなりません。変数 n_opens1 -n_opens4 が整数型でない場合は、エラーが発生します。 long long より小さい型 (short や int など) は、long long 型に自動的にプロモートされます。

また、append を使用して 2 つのリストを結合することもできます。 最初の引数がターゲット・リストで、2 番目の引数がソース・リストです。 次の例では、リスト b の内容がリスト a に追加されます。

a=list()
b=list()
append(a,b)

append() 関数には戻り値がありません。

代入演算子を使用して、リストを別のリストに代入できます。 ターゲット・リストの元の値は破棄されます。 次の例では、l_opens2 リストの内容が失われ (項目が除去される)、l_opens リストの内容が l_opens2 リスト上にコピーされます。

	l_opens2 = list(); 
	append(l_opens2, n_opens5); 

	l_opens2 = l_opens; 
   /* l_opens and l_opens2 => {n_opens1, n_opens2, n_opens3, n_opens4}  */ 

集約関数は次の例のようにリスト変数に適用できます。

	/* below we assume n_opens1=4, n_opens2=6, n_opens3=2 and n_opens4 = 4
	 * at the time they were added to the l_opens list variable 
	 */ 
	x = avg(l_opens); /* this will set x to 4 */ 
	y = min(l_opens); /* this will set y to 2 */ 
	z = sum(l_opens); /* this will set z to 16 */ 
	a = count(l_opens) /* this will set a to 4 */ 

リスト変数は、正確な集約値を記録する必要がある場合に有用です。 リスト変数は原子的に更新されるため、必要な場合のみ使用してください。この変数は通常の変数よりも効率的ではありません。

連想配列型

連想配列は、キーとその関連値の集合からなるマッピング・テーブルまたはルックアップ・テーブルです。 キーと値の間は 1 対 1 にマッピングされています。連想配列は、Perl、ksh93、およびその他のいくつかの言語でサポートされます。

Vue では、連想配列のキーまたは索引は、文字列型、タイム・スタンプ型、スタック・トレース型、整数型、または浮動小数点型のいずれかでなければなりません。関連付けられる値は、文字列型、タイム・スタンプ型、整数型、浮動小数点型、スタック・トレース型、リスト型、および範囲型のいずれかです。

連想配列は、Vue では抽象データ型です。:

  • キーの値へのバインド: このアクションは、キーがまだ存在していない場合はキーを連想配列に追加します。キーが既に存在している場合は、単に、キーの古い値を新しい値で置き換えます。バインドされていないキーは、デフォルト値の 0、すなわち空ストリングになります。
    
    count["ksh"] = 1;

    ASO のキーについては、次のようないくつかの方法で LIST 値をバインドできます。

    1. LIST 変数を割り当てる方法:
      
         assoc_array["ksh"]=ll  /* copies ll list into associative array */
         assoc_array["ksh"]=assoc_array["abc"]; /* copies a list in ASO to another list in ASO.
                                                          Here the value type of assoc_array is LIST */ 
    2. list() コンストラクター関数によって返される空のリストを割り当てる方法:
      assoc_array["ksh"]=list(); /* assigns an empty list */ 
    3. list 値または整数値を付加する方法
      
      append(assoc_array["ksh"], 5);	/* integral value 5 is appended to the list in ASO */
      append(assoc_array["ksh"], ll);	/* appends the contents of LIST variable ll to the list in ASO*/
      append(assoc_array["ksh"], assoc_array["abc"]); /* appends the contents of list in ASO to another list in ASO */
  • キーのアンバインディングと削除: キーを連想配列から削除するには、delete() 関数を使用します。アンバインドされるキーは、値が 0 または空ストリングであると想定されます。
    delete(count, "ksh");
  • キーの削除: キーを削除すると、要素全体、すなわちキーと値の両方が連想配列から削除されます。この操作は、単にキーの値を 0 または NULL に設定することと同じではありません。例えば、連想配列の要素をループでチェックすると、削除された要素は表示されません。

    delete() 関数は、指定されたキーが存在し、そのキーが削除できた場合は値 0 を返し、それ以外の場合は値 1 を返します。

  • キーの値の検索: この操作は、指定されたキーにバインドされている値を検索します。

    
    total = count["ksh"] + count["csh"];

    キーの LIST 値は、連想配列にキーを索引付けすることにより取り出すことができます。連想配列のリストでは、すべての LIST 関数、つまり sum()min()max()count()、および avg() を使用できます。連想配列内のリストを LIST 変数に割り当てることもできます。

    例:
    /* copies associative array list into list variable "ll" */
    ll=assoc_array["ksh"];
    /* prints the sum of all elements of list in associative array indexed with ksh" */
    printf("sum of assoc_array %d¥n",sum(assoc_array["ksh"]) ); 
    /* prints the minimum value */
    printf("min of assoc_array %d¥n",min(assoc_array["ksh"]) );
    /* prints the maximum value */
    printf("max of assoc_array %d¥n",max(assoc_array["ksh"]) ); 
    /* prints the number of values in list */
    printf("count of assoc_array %d¥n",count(assoc_array["ksh"]) );
    /* prints average value of the list */
    printf("avg of assoc_array %d¥n",avg(assoc_array["ksh"]) );  
  • キーの存在検査: exists () 関数は、連想配列内にキーが存在するかどうかを検査します。
    if (exists(count, "ksh"))
    printf("Number of ksh calls = %d¥n", count["ksh"]);

指定されたキーが存在する場合、exists() 関数は 1 を返し、それ以外の場合は 0 を返します。

  • 増分/減分演算: この演算を使用して、連想配列値を増分または減分することができます。これらの演算は、LIST 値型の連想配列には実行できません。

  1. printf(“Incremented value = %d¥n”, ++count[“ksh”]);
  2. printf(“Incremented value = %d¥n”, count[“ksh”]++);
  3. printf(“Decremented value = %d¥n”, --count[“ksh”]);
  4. printf (“Decremented value = %d¥n”, count[“ksh”]--);

例 1 では、キー ksh に対応する値は増分され、その増分された値が出力されます。

例 2 では、ksh に対応する値がまず出力されてから、次に、その値が増分されます。減分演算も同様に働きます。ただし、増分/減分演算は、値の型が整数である連想配列のみで実行できます。 増分演算または減分演算は、集約機能としても使用できます。この場合、連想配列の値の型は、デフォルトで整数として設定されます。例えば、a[100]++ というステートメントが最初に検出されると、整数キー・タイプおよび整数値型を持つ連想配列 a が作成されます。キー 100 に対して保管される値は 1 です。しかし、a[100]-- では、キー 100 の値として -1 が保管されます。同じ連想配列 a について後続の増分演算または減分演算が検出されると、指定されたキーの値に対して増分演算および減分演算が実行されます。

  • 連想配列の内容の出力: この演算は、連想配列に保管されている <key, value> 対を出力します。以下の出力オプションを指定できます。

連想配列の出力オプション 説明 指定できる値 デフォルト値
num-of-entries 指定した数のキーと値を先頭から出力するよう指定します。 n>=0。(0 の場合、すべてのエントリーが表示されます。) 0
sort-type ソート順を指定します。 SORT_TYPE_ASCEND、 SORT_TYPE_DESCEND SORT_TYPE_ASCEND
sort-by キー順または値順のいずれでソートするかを指定します。 SORT_BY_KEY、 SORT_BY_VALUE SORT_BY_KEY
list-value 連想配列の値がリスト型の場合に、ソートまたは量子化にどの LIST 属性を使用するかを指定します。 USE_LIST_SUM、 USE_LIST_MIN、 USE_LIST_MAX、 USE_LIST_COUNT、 USE_LIST_AVG USE_LIST_AVG

ソート順フラグが SORT_BY_KEY, SORT_BY_VALUE であり、キーと値の対が、ソートができない型である場合、num-of-entries オプションおよびその他の出力オプションが、個別のキーと値の対の出力に適用されます (適用可能な場合)。 例えば、ソートが範囲型順の場合、num-of-entries オプションおよびその他の出力オプションが各範囲のスロット用に予約されます。

連想配列のデフォルトの出力オプションは、BEGIN プローブで関数 set_aso_print_options() を使用して変更できます。

例:
set_aso_print_options (10, SORT_TYPE_DESCEND|SORT_BY_VALUE);

この例が示すように、vertical bar symbol を間に挿入すれば、複数のフラグを指定できます。

print() 関数は、デフォルトの出力オプションを使用して、連想配列のキーと値の対を出力します。連想配列の内容を別の形式で表示したい場合は、print() 関数の追加パラメーターとして num-of-entries オプションと出力オプション・フラグを指定します。

例:

/* uses default print options to display the contents of associative array ‘count’ */
print(count); 	
/* prints the first 10 entries of sorted associative array ‘count’. 
Default sort-by and sort-type options are used */
print(count, 10);	
/* sorts the associative array ‘count’ in descending order of values and 
displays the first 10 entries of ‘count’ */ 
print(count, 10, SORT_BY_VALUE|SORT_TYPE_DESCEND);
  • clear ルーチンは、連想配列にあるすべての <key, value> 変数をクリアするため、またはキーをクリアせずに値をリセットするために使用されます。 このルーチンは、連想配列の内容を正常にクリアすると 0 を返し、それ以外の場合は 1 を返します。
    clear(count);               // count is an associative array.
    連想配列型の 1 つの引数のみが指定された前述のルーチンは、連想配列 count にあるすべての鍵ペアをクリアします。 前述のクリア操作の後、連想配列 count は空になります。
    clear(count, RESET_VALUE);       // count is an associative array. 
    前述の clear ルーチンは、キーをクリアせずに、連想配列内のすべての鍵ペアの値をリセットします。 次のデフォルト値は、連想配列の値の型に基づいてリセットされます。
デフォルト値
整数型 (int、long、short、long long) 0
LIST
float および double 0.0000000
文字列
stktrace_t
probev_timestamp_t 0
  • 連想配列での Quantize: この演算は、指定された連想配列のキーと値の対を、<key-value> の対の値に基づいてグラフィカル形式で出力します。
    
    quantize(count);
    count は連想配列であり、以下の内容を出力します。
    
    key                  value
    1                     1             ========
    2                     2             =========
    3                     3             ==========
    4                     4             ===========
    5                     5             ============
    6                     6             =============

print() 関数の場合と同様に、quantize() 関数のオプションを指定して、デフォルトの出力オプションを指定変更できます。

例:

/* sorts the associative array ‘count’ in descending order of values and displays 
the first 10 entries of ‘count’ in graphical format*/
quantize(count, 10, _BY_VALUE|SORT_TYPE_DESCEND);
  • 連想配列での lquantize の実行: これは、指定された連想配列のキーと値の対を、<key, value> 対の値の対数値に基づいてグラフィカル形式で出力します。
    lquantize (count);
    ここで、count は連想配列であり、以下の内容が出力されます。
    key                    value
    500                     500           ====
    1000                   1000           ====
    2000                   2000           =====
    4000                   4000           =====
    8000                   8000           ======
    16000                 16000           ======
    32000                 32000           =======
    64000                 64000           =======

print() 関数の場合と同様に、lquantize() 関数の出力オプションを指定して、デフォルトの出力オプションを指定変更できます。

例:

/* sorts the associative array ‘count’ in descending order of values, and displays 
the first 10 entries of ‘count’ in graphical 
format based on the logarithmic value*/
lquantize(count, 10, _BY_VALUE|SORT_TYPE_DESCEND);

例:

# Trace all the alloc- related calls and store the entry 
# Time in ‘entry_time’ associative array
#
@@uft:$__CPID:*:"/alloc/":entry
{
        entry_time[get_function()]=timestamp();
}
#
# At exit, first check if entry for this function was traced
# If so, delete the entry time from ‘entry_time’ associative array 
# To ensure that next time no action is taken on exit if entry was not traced. 

@@uft:$__CPID:*:"/alloc/":exit
{
   func =get_function();
   if(exists(entry_time, func) )
   {
             append(time_taken[func],
                   diff_time(timestamp(),entry_time[func],MICROSECONDS));
	            delete(entry_time, func);
	  }
}
#
# Print the list attributes sum, min, max, count, and avg time taken in every 
# Alloc function.
#
@@syscall:$__CPID:exit:entry
{
  print(time_taken);
	 exit();
}
注: この方法では、複数のリスト変数を明示的に定義しなくても、連想配列を使用して LIST の完全な機能を利用できます。

タイム・スタンプ・データ型

probev_timestamp_t 型の変数は、内部 AIX フォーマットのタイム・スタンプを戻す timestamp ProbeVue 関数からの戻り値を保持します。この変数は後で、2 つのタイム・スタンプの時間差を戻す diff_time 関数にパラメーターとして渡すことができます。このデータ型は、キーまたは値として連想配列に保管することもできます。

ProbeVue コンパイラーは、タイム・スタンプを保管するために probev_timestamp_t データ型ではなく、long データ型を使用しても型の検査を行いませんが、可能であれば、このような使用は避けてください。

probev_timestamp_t 型の変数では、以下の操作が受け入れられます。

  • 明示的にゼロに初期化できます。
    注: グローバル・クラスまたはスレッド・ローカル・クラスのタイム・スタンプ変数が、ProbeVue セッションの開始時にゼロに初期化されます。
  • ゼロと比較できます。 timestamp 関数で戻されるタイム・スタンプ値は、常にゼロより大きいです。
  • 別のタイム・スタンプ変数と比較できます。 遅い方のタイム・スタンプは必ず早い方のタイム・スタンプよりも大きくなります。
  • diff_time 関数にパラメーターとして渡すことができます。
  • printf または trace 関数を使用して印刷できます。

ファイル・パス・データ型

path_t 型の変数を使用すると、__file->path の値 (I/O プローブ・マネージャーに組み込まれている __file を指す) または function fd_path() の値を保持することができます。path_t 型のローカル変数またはグローバル変数のみがサポートされます。この型の変数は、連想配列内のキーまたは値にもすることができます。

ファイル・パス変数の宣言

path_t pth; // global variable of type path_t
auto:pth2 = fd_path(fd); // store in a local path_t variable
my_aso[__file->fname] = __file->path; // store in associative array

signed、unsigned、register、static、thread、および kernel の各修飾子は、path_t 型変数ではサポートされていません。

代入演算

代入 (=) 演算子では、path_t 変数を別の path_t 変数に割り当てることが許可されます。この変数の元の値は上書きされます。

次の例では、代入後に p1 および p2 は同じファイル・パスを参照します。
path_t p1, p2;
p1 = fd_path(fd); // fd is a valid file descriptor value 
p2 = p1;

比較演算

path_t 変数では、等価 (==) 演算子と不等価 (! =) 演算子のみが許可されます。等価演算子の結果は、両方が同じ絶対ファイル・パスを表している場合は true (1) であり、そうでない場合は false (0) です。

非等価演算子は、この動作を正確に補完します。他の比較演算子 (>=、>、<、または =<) は、path_t 型変数では許可されません。

ファイル・パス型変数の表示

path_t 型変数は、printf() 関数の「%p」フォーマット指定子を使用して表示できます。

ファイル・パスのこの表示には、対応するファイルシステムで時間のかかるファイル検索操作が含まれます。そのため、Vue スクリプトで慎重に使用する必要があります。

注: printf() メッセージが表示されたときに存在しなくなっている可能性がある一時ファイルの場合、ヌル文字列はファイル・パスとして表示されます。
path_t 型変数をキーまたは値 (もしくはその両方) として持つ連想配列は、print() 関数を使用して表示できます。
printf(“file path=[%p]¥n”, __file->path); 
my_aso[0] = fd_path(fd); // fd is valid file descriptor value 
print(my_aso);
ファイル・パス型変数の制限
  • path_t 変数の配列は宣言できません。
  • path_t 変数を struct または union のメンバーとして使用できません。
  • path_t 変数を指すポインターは使用できません。
  • 他のいずれかの型への path_t 変数の型キャスト、または path_t 型への他のいずれかの型の型キャストは許可されません。
  • 算術演算子 (+、-、*、/、++、-- など) を path_t 型変数と一緒に使用できません。

MAC アドレス・データ型

mac_addr_t 型の変数は、MAC アドレスの値を保持するのに使用されます (mac_addr_t 型変数の使用法については、ネットワーク・プローブ・マネージャーのセクションで __etherhdr 組み込み変数および __arphdr 組み込み変数を参照してください)。

この抽象データ型は、標準 C の単項演算子および 2 項演算子と直接使用することはできません。mac_addr_t 型のローカル変数またはグローバル変数のみがサポートされます。

この型の変数は、キーまたは値として連想配列に保管することもできます。

Vue は、MAC アドレス型変数に関して、以下の特性および演算をサポートしています。

MAC アドレス変数の宣言

mac_addr_t m1;           																				// global variable of type 
__auto mac_addr_t m2;                             // auto variable of type 
m2 = __etherhdr->src_addr;                        // store source MAC address in a local variable
mac_aso[“src_mac_addr”] = __etherhdr->src_addr ;  // store in an associative array. 

signed、unsigned、register、static、thread、および kernel の各修飾子は、mac_addr_t 型変数ではサポートされていません。

代入演算

代入 (=)> 演算子では、mac_addr_t 型変数を別の mac_addr_t 型変数に割り当てることが許可されます。この変数の元の値は上書きされます。mac_addr_t 変数からの型キャスト、またはこの変数への型キャストは許可されません。

以下の例で、mac_addr_t m1 の内容は、m2 に割り当てられます。

mac_addr_t m1, m2;              // Declares two MAC address variables. 
m1 = __etherhdr->src_addr;      // Get the source MAC address of the packet in m1. 
m2 = m1 ;                       // Get the content of m1 into m2.

比較演算

mac_addr_t 変数では、等価 (==) 演算子と不等価 (! =) 演算子のみが許可されます。等価演算子の結果は、両方に同じ MAC アドレス値が含まれる場合は True (1) であり、そうでない場合は False (0) です。

非等価演算子は、この動作を正確に補完します。他の比較演算子 (>=、>、<、または =<) は、mac_addr_t 型変数では許可されません。
if ( m1 == m2) // comparing two mac_addr_t type variables. 
printf(“Mac addresses are equal”); else printf(“Mac addresses are not equal”);

MAC アドレス型変数の表示

mac_addr_t 型変数は、Vue の printf() 関数の「%M」フォーマット指定子を使用して表示できます。 mac_addr_t 型変数をキーまたは値 (もしくはその両方) として持つ連想配列は、print() 関数を使用して表示できます。
printf(“ Source MAC address=[%M]¥n”, __etherhdr->src_addr); 
mac_aso[“src_mac_address”] = __etherhdr->src_addr ; // Store source MAC address as value in an associative array mac_aso. 
print(mac_aso);
MAC アドレス型変数の制限
  • mac_addr_t 変数の配列は宣言できません。
  • mac_addr_t 変数は、struct または union のメンバーとして使用できません。
  • mac_addr_t 変数を指すポインターは使用できません。
  • 他のいずれかの型への mac_addr_t 変数の型キャスト、または mac_addr_t 型への他のいずれかの型の型キャストは許可されません。
  • 算術演算子 (+、-、*、/、++、-- など) を mac_addr_t 型変数と一緒に使用することはできません。

IP アドレス・データ型

ip_addr_t 型の変数は、IP アドレスの値を保持するのに使用されます (ip_addr_t 型変数の使用法については、ネットワーク・プローブ・マネージャーのセクションで __ip4hdr、__ip6hdr および __proto_info の各組み込み変数を参照してください)。

これは抽象データ型であり、標準 C の単項演算子および 2 項演算子と直接使用することはできません。ip_addr_t 型のローカル変数またはグローバル変数のみがサポートされます。この型の変数は、キーまたは値として連想配列に保管することもできます。

Vue は、IP アドレス型変数に関して、以下の特性および演算をサポートしています。

IP アドレス変数の宣言
ip_addr_t i1;                               // global variable of type ip_addr_t 
__auto ip_addr_t i2;                        // auto variable of type 
ip_addr_t i2 = __ip4hdr->src_addr;          // store source IP address in a local ip_addr_t variable. 
ip_aso[“src_ip_addr”] = __ip4hdr->src_addr; // store in an associative array.

signed、unsigned、register、static、thread、および kernel の各修飾子は、ip_addr_t 型変数ではサポートされていません。

代入演算

代入 (=) 演算子では、ip_addr_t 型変数を別の ip_addr_t 型変数に割り当てることが許可され、定数の IP アドレスまたはホスト名を ip_addr_t 型変数に割り当てることも許可されます。この変数の元の値は上書きされます。ip_addr_t 変数からの型キャスト、またはこの変数への型キャストは許可されません。

以下の例で、ip_addr_t i1 の内容は i2 に割り当てられます。
ip_addr_t i1, i2;                   // Declares two IP address variables. 
ip_addr_t i3, i4, i5;               // Declares three IP address variables.
i1 = __ip4hdr->src_addr;            // Get the source IP address of the packet in i1. 
i2 = i1 ;                           // Get the content of i1 into i2.
i3 = “10.10.10.1”;                  // Assign the constant IPv4 address to i3 variable.
i4 = “fe80::2c0c:33ff:fe48:f903”;   // Assign the Ipv6 address to i4 variable.
i5 = “example.com”;                 // Assign the hostname to i5 variable.
                                   // Get the content of i1 into i2.

比較演算

ip_addr_t 型変数では、等価 (==) 演算子と不等価 (! =) 演算子のみが許可されます。この比較では、2 つの ip_addr_t 型変数間の比較および定数文字列型との比較のみが可能です (IP アドレスまたはホスト名は二重引用符で囲んで「192.168.1.1」または「example.com」のように指定します)。

等価演算子の結果は、両方に同じ IP アドレス型 (IPv4 または IPv6) および値が含まれる場合は True (1)であり、そうでない場合は False (0) です。非等価演算子は、それを正確に補完します。他の比較演算子 (>=、>、<、または =<) は、ip_addr_t 型変数では許可されません。

if( i1 == i2)                     // comparing two ip_addr_t type variables.
																											//IP address string	 
printf(“IP addresses are equal”); 
else printf(“IP addresses are not equal”); 
or 
if( i1 == “192.168.1.1”)          // comparing ip_addr_t type variable and constant string.
 printf(“IP addresses are equal”); 
else printf(“IP addresses are not equal”);
or
if (i1 = “example.com”)          // comparing ip_addr_t type variable and constant       
                                 //IP address string
printf(“IP addresses are equal”); 
else printf(“IP addresses are not equal”);
 
IP アドレス型変数の表示

ip_addr_t 型変数は、IP アドレスをドット 10 進形式または 16 進形式で表示する場合は「%I」フォーマット指定子を使用して表示でき、Vue の printf() 関数でホスト名を表示する場合は「%H」フォーマット指定子を使用して表示できます。 このホスト名の表示には、時間のかかる DNS ルックアップ操作が含まれます。そのため、VUE スクリプトで慎重に使用する必要があります。

注: ユーザーがフォーマット指定子「%H」を使用して、DNS に存在しない可能性がある IP アドレスのホスト名を表示する場合、ホスト名ではなく、ドット 10 進/16 進形式で IP アドレスを表示します。
ip_addr_t 型変数をキーまたは値 (もしくはその両方) として持つ連想配列は、print() 関数を使用して表示できます。

printf(“ Source IP address=[%I]¥n”, __ip4hdr->src_addr); 
ip_aso[“src_ip_address”] = __ip4hdr->src_addr ; // Store source IP address as value in an associative array 
print(ip_aso); 
IP アドレス型変数の制限
  • ip_addr_t 変数の配列は宣言できません。
  • ip_addr_t 変数を指すポインターは使用できません。
  • 他のいずれかの型への ip_addr_t 変数の型キャスト、または ip_addr_t 型への他のいずれかの型の型キャストは許可されません。
  • 算術演算子 (+、-、*、/、++、-- など) を ip_addr_t 型変数と一緒に使用することはできません。

net_info_t データ型

net_info_t 変数は構造体または複合変数であり、sockfd_netinfo Vue 関数を通じて特定のソケット・ディスクリプターから、ネットワークの 4 タプル (ローカルおよびリモートの IP アドレスとポート番号) 情報を保持するのに使用されます。この構造体のメンバーには、Vue スクリプト内の他のユーザー定義構造体のようにアクセスされます。net_info_t 型は抽象データ型であり、この変数を標準 C の単項または 2 項演算子と直接使用することはできません。この変数は、4 タプル情報を含む構造体です。この変数エレメントには、C 構造体エレメントのように「.」演算子を使用してアクセスできます。

net_info_t データ型のエレメントは次のとおりです。
net_info_t 
{ 
    int local_port; 
    int remote_port; 
    ip_addr_t local_addr; 
    ip_addr_t remote_addr;
};

Vue は、net_info_t 型変数に関して、以下の特性および演算をサポートしています。

net_info_t 型変数の宣言

net_info_t n1,n2                       
// n1 is variable of type net_info_t
sockfd_netinfo(fd, n1);           
// fd is socket descriptor and n1 contains network 
// four tuple information from sockfd_netinfo Vue function.                                   
n2.local_addr = __ip4hdr->src_addr; 
n2.remote_addr = __ip4hdr->dst_addr; 
n1.local_port = __tcphdr->src_port; 
n1.remote_port = __tcphdr->dst_port;
signed、unsigned、register、static、thread、local、global、および kernel の各修飾子は、net_info_t 型変数ではサポートされていません。

net_info_t 型変数の制限

  • 構造体と共用体のメンバー変数をサポートできません。
  • net_info_t 変数を指すポインターは宣言できません。
  • この変数は連想配列ではサポートされません。
  • net_info_t 変数の配列は宣言できません。
  • 他のいずれかの型への net_info_t 変数の型キャスト、または net_info_t 型への他のいずれかの型の型キャストは許可されません。
  • 算術演算子 (+、-、*、/、++、-- など) を net_info_t 型変数と一緒に使用できません。

Vue 関数

C または FORTRAN で書かれたプログラム、あるいはネイティブ言語で書かれたプログラムとは異なり、Vue で作成されたスクリプトは、AIX システム・ライブラリーまたはユーザー・ライブラリーで提供されるサブルーチンにアクセスできません。 しかし、Vue は動的トレース・プログラムに役立つ、独自の特殊な内部関数ライブラリーをサポートしています。

トレース固有の関数
get_function
現行プローブを囲む関数の名前を戻します。 get_function 関数は、interval、systrace、BEGIN、および END 節から呼び出されると、空のストリングを返します。
タイム・スタンプ
現在のタイム・スタンプを戻します。
diff_time
2 つのタイム・スタンプの時間差をマイクロ秒またはミリ秒で検出します。
トレース収集関数
printf
変数と式の値をフォーマットして印刷します。
trace
データをフォーマットしないで印刷します。
stktrace
スタック・トレースをフォーマットして印刷します。
リスト関数
list
リスト変数をインスタンス化します。
append
新しい項目をリストに追加します。
sum、max、min、avg、count
リスト変数に適用可能な集約関数。
C ライブラリー関数
atoi、strstr
標準の文字列関数。
一時トレースをサポートする関数
start_tentative、end_tentative
一時トレースの開始と終了を示すインディケーター。
commit_tentative、discard_tentative
一時トレースのデータをコミットまたは廃棄します。
その他の関数
exit
Vue スクリプトを終了します。
get_userstring
ユーザー・メモリーから文字列 (またはデータ) を読み取ります。
ptree
プローブしているプロセスのプロセス・ツリーを表示します。

Vue 文字列関数は、ポインター変数ではなく、文字列型の変数にのみ適用できます。 strcpystrcat などの標準の文字列関数は、言語の構文自体でサポートされているため、Vue では必要ありません。

ProbeVue コンパイラーは、Vue 関数に渡されるパラメーターのデータ型を検証します。

printf 関数の場合、フォーマット文字列に指定されたフォーマット指定子ごとに、printf 関数に引数が提供されているかどうかを検査するための妥当性検査が行われます。 フォーマット指定子の合計数と printf 関数に渡される引数の合計数は等しくなければなりません。 これに加え、渡された引数の型が、フォーマット文字列のフォーマット指定子として指定された実際の型と互換性があるかどうかを突き合わせるための妥当性検査も行われます。 これらの検査に失敗すると、Probevue はエラー・メッセージをスローします。

例えば、
printf(“hello world %s, %d¥n”, str);
では、%d の引数が渡されないため、コンパイラーからエラー・メッセージがスローされます。同様に、
Printf(“The total count of elements is %d¥n”, str);
でもエラー・メッセージがスローされます。その理由は、指定されたフォーマットが %d であるのに対し、渡された引数 str 変数が文字列であるためです。

その他の機能の関数

ただし、次のように指定した場合、
printf (“The total count of elements is %lld¥n”, i);
(ここで、i は int 型の変数です) エラー・メッセージはスローされません。なぜならば、変数 i は、要求されたフォーマット指定子の互換型だからです。 そのため、厳密な型の検査は行われませんが、互換型の検査が行われます。

関数は、Vue 節の述部セクションでは使用できません。

述部

プローブ・ポイントの節の実行を条件付きで行う必要がある場合は、述部を使用できません。 述部セクションは、プローブ指定セクションのすぐ後にある when キーワードによって識別されます。 述部自体は、通常の C 形式の条件式と括弧で構成されます。

述部セクション内の式には、いくつかの制限があります。

  • カーネル・クラス変数は述部で許可されていません。
  • 自動クラス変数は述部で許可されていません。
  • 浮動小数点型変数は述部で許可されていません。
  • Vue 関数は述部で許可されていません。
  • 副次作用は述部で許可されていません。このため、= 代入演算子と +=|= のような複合代入演算子は許可されていません。
  • 関数に渡される 9 番目以上のパラメーター (エントリー・クラス変数 __arg9__arg10 など) は述部で許可されていません。

if ... else ステートメントを使用して、節の特定のアクションを条件付きで実行できます。これは、C の類似のステートメントと同様に機能します。ただし、その節全体を条件付きで実行する場合は、代わりに述部を使用する方が望ましいです。ProbeVue は、述部の実行を最適化するように設計されています。

注: 1 つのプローブ・ポイントが複数のプロセスに対して起動する場合は、プローブの使用による全体のパフォーマンスへの影響を減らすために述部でスレッド・ローカル変数を使用することは適切な方法です。 if 文を使用するよりも、述部で条件付き検査を使用する方が適切です。

以下のスクリプトでは、述部でスレッド・ローカル変数を使用して、特定の文字列が特定のファイルに書き込まれたときを効率的に検出します。 また、述部を使用して節のアクション・ブロック内で if 文を使用する例も示します。 ファイル名と文字列は両方とも、シェルの定位置パラメーターを使用してパラメーターとしてスクリプトに渡されます。

/*
 * Filename : chkfilewrite.e
 *
 * Capture when someone writes a particular word to a specific file 
 * takes 2 arguments: filename and word
 *
 * assumes file name is < 128
 *
 * Usage: probevue chkfilewrite.e ¥"<filename>¥" ¥"<string>¥"
 *
 *	The backslashes above are necessary to prevent shell
 *	from stripping the double quotation mark.
 */

int open(char *fname, int m, int p);
int write(int fd, char *s, int size);

@@syscall:*:open:entry
{
	__auto String fname[128];

	fname = get_userstring(__arg1, -1);

	if (fname == $1)
		thread:opening = 1;
}

@@syscall:*:open:exit
	when (thread:opening == 1)
{
	thread:fd = __rv;
	thread:opening = 0;

}

@@syscall:*:write:entry
	when (thread:fd == __arg1)
{
	__auto String buf[128];

	if (__arg3 < 128)
		buf = get_userstring(__arg2, __arg3);
	else
		buf = get_userstring(__arg2, 128);

	if (strstr(buf, $2)) {
		printf("%d wrote word to file.¥n", __pid);
		exit();
	}
}

このプログラムを実行して「Error」の文字列が foo.log ファイルに書き込まれたときを検査するには、次のコマンドを実行できます。

probevue chkfilewrite.e ¥"foo.log¥" ¥"Error¥"
注: close プローブを追加して、ファイルが閉じたときを検出できるように前述のスクリプトを強化できます。こうすると、元のファイルが閉じ、新しいファイルが開いたとき、スクリプトがこの用語を取得しないようにでき、同じファイル・ディスクリプター番号は再使用されます。

シンボリック定数

Vue は AIX プログラミングでよく使用される定義済みのシンボリック定数をいくつかサポートしています。 Vue では、これらの定数はキーワードとして扱われます。 コンパイル時に、これらの定数はシステムのヘッダー・ファイルでその定義に置き換えられます。プローブ・マネージャー固有のシンボリック定数は、それぞれのセクションで説明されています。一般的なシンボリック定数は次のとおりです。

AF_INET
タイプ IPv4 のアドレス・ファミリーを指定します。これにより、確実に IPv4 タイプのデータを使用できます。
AF_INET6
タイプ IPv6 のアドレス・ファミリーを指定します。これにより、確実に IPV6 タイプのデータを使用できます。
NULL
ポインター型を NULL またはゼロ値に設定します。 NULL を使用して、文字列変数を空文字列に設定することはできません。
エラー番号または「errno」名
EPERMEAGAINESRCH、または ENOENT などの標準のエラー名です。これらは POSIX および ANSI 規格で定められ、/usr/include/sys/errno.h ヘッダー・ファイルに定義されています。

次のスクリプトでは、bind システム・コールが EADDRINUSE (既に使用中のアドレス) に設定された errno で失敗したときをトレースします。


/*
 * File: bind.e
 */

/*
 * Okay to use void for parameters since we are not planning to
 * access them in this script.
 */
int bind(void);

@@syscall:*:bind:exit
	when (__rv == -1)
{
	/*
	 * The following check could also be moved to the predicate,
	 * although it may not buy a lot because we are already in an
	 * error path that should be executed only rarely
	 */
	if (__errno == EADDRINUSE) 
 /* This check could also be moved to the predicate */
		printf("%d failed with EADDRINUSE for bind() call.¥n", __pid);
}
シグナル名
SIGSEGVSIGHUPSIGILL、または SIGABRT などの標準のシグナル名です。これらは ANSI 規格で定められ、/usr/include/sys/signal.h ヘッダー・ファイルに定義されています。

次のスクリプトでは、「誰」が特定のシグナルを送って特定のプロセスを終了したかをデバッグする方法を示します。

/*
 * File: signal.e
 *
 * Who sent SIGKILL to my process ?
 */

/* Process IDs are < 2^32, so using an 'int' here instead of pid_t is
 * good enough
 */
int kill(int pid, int signo);

@@syscall:*:kill:entry
	when (__arg1 == $1 && __arg2 == SIGKILL)
{
	/* Trace sender of SIGKILL */
	printf("Stack trace of %s: (PID = %d)¥n", __pname, __pid);
	stktrace(PRINT_SYMBOLS|GET_USER_TRACE, -1);
	exit();
}
FUNCTION_ENTRY
プローブ・ポイントが関数エントリー・ポイントであるかを識別します。 get_location_point 関数と一緒に使用されます。
FUNCTION_EXIT
プローブ・ポイントが関数終了ポイントであるかを識別します。 get_location_point 関数と一緒に使用されます。

ヘッダー・ファイル

probevue コマンドの -I オプションを使用して、ヘッダー・ファイルを Vue スクリプトに含めることができます。 ヘッダー・ファイルは、C 言語の構文で作成する必要があります。 これには C-89 仕様に準拠した型、構造体および共用体の定義を含めることができます。 ただし、C の実行可能ステートメントを含めることはできません。 通常、C プリプロセッサー演算子およびディレクティブがある場合は無視されますが、#ifdef#if などの演算子により未定義の動作が発生する可能性があります。 このため、/usr/include ディレクトリーからの標準の AIX ヘッダー・ファイルは直接含めることはできません。 その代わりに、関連ヘッダー・ファイルのセットに直接 C プリプロセッサーを実行して、後処理されたヘッダー・ファイルを生成し、probevue コマンドに渡します。 別のオプションとして、Vue スクリプトで使用する型定義と関数宣言を含めるようにヘッダー・ファイルをハンド・コーディングする方法があります。

次の例では、Vue スクリプトで使用される typedef 定義を使用してハンド・コーディングされたヘッダー・ファイルを示します。

/* My header file : myheader.i */

	typedef int myid_t;

次の Vue スクリプトでは、この typedef 定義を使用しています。

/* Program name: myscript.e */
	@@BEGIN
	{
		myid_t id;
		id = 0;
	}

次の probevue コマンドを実行できます。

probevue -I myheader.i myscript.e

コマンド行で複数のヘッダー・ファイルを指定するには、コンマでヘッダー・ファイルを区切るか (コンマとファイル名の間にはスペースなし)、-I フラグでそれぞれを別々に指定します。 次の 2 つの例は同等です。

probevue -I myheader.i,myheader2.i myscript.e
probevue -I myheader.i -I myheader2.i myscript.e

struct/class の定義では C++ ヘッダー・ファイルを組み込むことができ、これにより、probevue スクリプトが、ポインターを介して struct/class データ・フィールドにアクセスできます。 #include ディレクティブを使用すると、すべての C++ ヘッダー・ファイルを、ProbeVue スクリプト内の ##C++ ディレクティブと ##Vue ディレクティブの間にリストできます。 このオプションを使用する場合、IBM® C++ コンパイラーをシステムにインストールする必要があります。 C++ ヘッダー・ファイルを組み込むためのもう 1 つの選択肢は、最初に –probevueP オプションを使用して C++ ヘッダー・ファイルをプリプロセスし、次にプリプロセスしたファイルを –probevueI オプションを使用して組み込む方法です。 –P オプションを使用すると、probevue は入力 C++ ヘッダー・ファイルと同じ名前に接尾部 .Vue を付けた名前で出力ファイルを生成します。

プリプロセスした C++ ヘッダー・ファイルに –I オプションを使用する利点は、システムに IBM C++ コンパイラーをインストールする必要がないことです。

次のコマンドを実行すると、C++ ヘッダー・ファイルをプリプロセスできます。

probevue –P myheader.h    
注: 上記のコマンドを実行するには、IBM C++ コンパイラーが前提条件です。

上記のコマンドは、myheader.Vue というファイルを生成します。 このファイルはさらに、別のシステムに送り、probevue–I オプションで組み込むことによって C++ アプリケーションをプローブするために使用できます。 送られたプリプロセス済み C++ ヘッダー・ファイルを使用している間、プリプロセス済み C++ ヘッダー・ファイルの生成に使用されるシステムと、C++ アプリケーションのプローブ用に probevue–I オプションでプリプロセス済みヘッダー・ファイルを組み込むために使用されるシステムでは、システム環境が同じでなければなりません。

–P オプションでのプリコンパイルに使用される C++ ヘッダー・ファイル、または ##C++##Vue の間に組み込まれる C++ ヘッダー・ファイルには、標準の入出力 C++ ヘッダー・ファイルを組み込むための .h 拡張子が必要です。 IOstream ヘッダーを組み込む場合、#include<iostream> ではなく #include<iostream.h> を使用してください。

C++ アプリケーションをプローブするには、cpp_executable という C++ 実行可能ファイルおよび myscript.e というスクリプトで、次のコマンドを実行できます。

probevue –I myheader.Vue –X cpp_executable myscript.e  
注: 上記のコマンドを実行するには、IBM C++ コンパイラーは前提条件ではありません。

サポートされるシェル・エレメント

Vue 言語の構文は、エクスポートされたシェル変数や定位置パラメーター (スクリプトに対する引数) のような $ 接頭部で識別されるシェル変数をサポートしています。

Vue シェル変数は、Vue スクリプトのすべての部分に表示されます。 これらのシェル変数はプローブの一部として指定され、述部またはアクション・ブロックのステートメント内で使用できます。 ただし、シェル・スクリプトとは異なり、これらの変数は二重引用符付きの文字列内で使用された場合は拡張されません。

コマンド行からスクリプトに渡される引数は、スクリプト内で $1$2$3 などとして参照されます。 次の Vue スクリプトを考えてみましょう。

/* Program name: myscript.e */
	@@syscall:*:read:entry
		when (__pid == $1)

	{
		int count;
		count++;
	}

次の例では、myprog プログラムを実行するプロセスのプロセス ID が、前述のスクリプトの $1 と置き換わります。 これはプロセス名が指定されたプロセス ID を印刷する prgrep シェル・プログラムを使用して Vue スクリプトを呼び出すことを仮定しています。

probevue myscript.e `prgrep myprog`

シェルからエクスポートされる環境変数は、$ 演算子を使用するスクリプトでも参照されます。 次の Vue スクリプトを考えてみましょう。

/* Program name: myscript2.e */
	@@syscall:*:read:entry
		when (__pid == $PID)

	{
		int count;
		count++;
	}

	/* program to be traced has a function called 'foo' */
	
	@@uft:$PID:*:foo:entry
	{
		printf("Read system call was invoked %d times¥n", count);
	}

次の例では、3243 が前述のスクリプトの $PID と置き換わります。

PID=3423 probevue myscript2.e

ProbeVue スクリプト内で 環境変数を文字列として認識する必要がある場合、 環境変数の値は、それが文字列であることを示す二重引用符で囲んだ上で 含める必要があります。例えば、次のスクリプトでは、システムで特定のファイルが開いたときにトレース出力を取得します。

/* Program name: stringshell.e */
	int open(char *path, int oflag);
	@@syscall:*:open:entry
	{
		String s[40]; 
		s = get_userstring(__arg1, -1);
		if (s == $FILE_NAME) {
			printf("pid %d (uid %d) opened %s¥n",__pid,__uid, s);
			exit();
		}
	}

このスクリプトでは、 $FILE_NAME はエクスポートされたシェル環境変数の名前であると予期しており、 シェル環境変数の値には二重引用符が含まれます。次にスクリプトの例を示します。

	export FILE_NAME=¥"/etc/passwd¥"
	probevue stringshell.e

スクリプトで、値に二重引用符が含まれない、 既存の環境変数の値が必要な場合は、 既存の環境変数を二重引用符で囲んで新しい環境変数を構成する 必要があります。次にスクリプトの例を示します。

	export FILE_NAME=¥"$HOME¥"
	probevue stringshell.e

Vue では、プローブされるプロセスを -X フラグを使用して probevue コマンド自体で開始する場合に役立つ 2 つの特別な環境変数をサポートしています。 $__CPID 環境変数は probevue コマンドで作成された子プロセスのプロセス ID を表し、$__CTID 環境変数はそのスレッド ID を表します。 -X フラグは、特にデバッグ目的で一時的なプロセスをプローブする場合に役立ちます。

Vue スクリプトをシェル・スクリプトのように直接実行するには、最初の行を次のスクリプトに設定します。

#!/usr/bin/probevue

また、probevue コマンドはシェルのように標準入力からも Vue スクリプトを読み取れます。 これを行うには、コマンド行からスクリプトのファイル名を省略します。 これは短いスクリプトをテストする場合に役立ちます。

Vue はシェルにより内部で作成される $$$@ などの特別なシェル・パラメーターはサポートしていません。

トレース収集機能

ProbeVue は広範なトレース収集機能をサポートしています。 基本的なトレース収集アクションは、アクション・ブロックの一部としてどのようなプローブからも呼び出し可能な printf 関数を使用して実行されます。 Vue バージョンの printf 関数は、C ライブラリー・バージョンのほとんどの機能を備えています。 2 つ目のトレース収集関数は trace 関数です。 trace 関数は、パラメーターとして 1 つの変数を受け入れ、その値を印刷可能な 16 進形式でトレース・バッファーにコピーします。 この関数は、特に文字列と構造体の内容をダンプする場合に役立ちます。 stktrace 関数は、現行プローブ・ポイントにあるトレースされたスレッドのスタック・トレースを収集するトレース収集関数です。

内部スクリプト変数の値以外にも、カーネル・グローバル変数のような外部変数、プローブされる関数に対するパラメーターのようなコンテキスト固有データ、関数からの戻り値なども収集でき、これらのトレース収集関数を介して表示できます。

トレース報告プログラムにより、トレース・データが発生時刻順に表示され、異なる CPU から収集されたデータが出力される前に内部でソートされます。

一時トレース

一時トレースは、トレース・データを条件付きで収集するための ProbeVue の機能です。 start_tentative 関数をアクション・ブロック内で使用して、一時的なトレース・データの収集の開始を示します。 end_tentative 関数は、アクション・ブロック内で一時的なトレース収集の終了を示します。 end_tentative 関数は、アクション・ブロックの終了で、そのブロックで開始された一時トレースの終了が自動的に示される場合は省略することができます。 一時トレースが使用可能であるときに収集されたトレースはすべて一時トレース・データとしてマークが付きます。 すなわち、これらの開始と終了の一時トレース関数により、アクション・ブロック内に一時トレース・セクションが設定され、このアクション・ブロック内では収集されたすべてのトレース・データが一時的と見なされます。

一時的に収集されるトレース・データは、トレース・コンシューマーは使用できませんが、コミットまたは廃棄されるまでトレース・バッファー内に留まります。 commit_tentative 関数は、収集された一時トレース・データ全体をコミットし、トレース・コンシューマーが使用できるようにします。 その反対に、discard_tentative 関数は、一時的に収集されたトレース・データをすべて廃棄し、トレース・バッファーのスペースを解放します。

一時でない通常のデータのトレースも一時トレースと並行して許可されています。 トレース・コンシューマーに対してすべてのトレース・データがタイム・スタンプ順で使用可能になるため、コミットも廃棄もされていない一時トレース・データにより、通常のトレース・データは、トレース・コンシューマーに表示されない場合があります。 最悪の場合、トレース・バッファーがコミットまたは廃棄されていない一時トレース・データで満たされてしまい、通常のトレース・データの収集が停止する可能性もあります。

一時トレース関数は、一時トレース・データを収集する ID を識別する文字列を取ります。 これにより、収集された一時トレース・データをコミットまたは廃棄するときの制御が行いやすくなります。 複数のトレース収集アクションを 1 つ以上のアクション・ブロック内に開始と終了の一時トレース関数 (同じ一時トレース ID が渡される) を使用して囲むことができます。 これにより、複数のロケーションからの一時トレース・データを 1 つのブロックでコミットまたは廃棄できます。 Vue では、一時トレース ID として文字列リテラルを使用する必要があります。

一時トレースではデータのインテリジェント・フィルタリングが可能であり、分析用にユーザーに対して表示されるトレース・データの実際の量が削減されます。 これにより、一時的に収集されたデータを早い段階で廃棄またはコミットできる場合は、バッファー・オーバーフローの問題を回避できるという副次効果があります。

次のスクリプトの例では、一時トレース関数を使用して、必要な場合のみトレース・データを収集しています。

/* 
 * File: tentative.e
 *
 *	Print details when write system call takes longer than a
 *	specified number of microseconds
 *
 *	Usage: probevue tentative.e <processID> <microseconds> 
 */
int write(int fd, char *buf, int size);

@@BEGIN
{
	probev_timestamp_t  ts1, ts2;
}

@@syscall:$1:write:entry 
{
	__auto String buf[256];

	if (__arg3 < 256)
		buf = get_userstring(buf, __arg3);
	else
		buf = get_userstring(buf, 256);

	start_tentative("write");

	/* print out all the data associated with the write */
	stktrace(PRINT_SYMBOLS|GET_USER_TRACE, -1);

	printf("fd = %d, size = %d¥n", __arg1, __arg3);

	/* Prints 256 bytes of buf, even though size may be < 256 */
	trace(buf);

	end_tentative("write");

	/* Get timestamp for when we entered write: do this at the end of
	 * the probe to reduce probe effect
	 */
	ts1 = timestamp();
}
	
/* If we started probing in the middle of write, ts1 will be zero,
 * ignore that case with a predicate
 */
@@syscall:$1:write:exit
	when (ts1 != 0)
{
	/* diff_time() may return up to a 64-bit value, but we 
	 * use an int here since we don't expect the difference to
	 * larger than a few hundred microseconds at the most.
	 */
	int micros;

	/* Get timestamp for when we exited write: do this at the beginning of
	 * the probe to reduce probe effect
	 */
	ts2 = timestamp();

	micros = diff_time(ts1, ts2, MICROSECONDS);

	start_tentative("write");
	printf("Return value from write = %d¥n", __rv);
	end_tentative("write");

	if (micros > $2) {
		/* Can mix normal trace with tentative also  */
		printf("Time to write = %d, limit =%d micro seconds¥n",
			micros, $2);
		commit_tentative("write");
		exit();
	}
	else
		discard_tentative("write");
}