データ収集と解析の準備のためにプログラムに対して行う特別な作業はありません。次の処理のうち、いずれか 1 つでも行うプログラムの場合には、後述の該当する節を読んでください。
シグナルハンドラをインストールする
システムライブラリを明示的かつ動的に読み込む
関数を動的にコンパイルする
プロファイルする派生プロセスを作成する
非同期 I/O ライブラリを使用する
プロファイルタイマーまたはハードウェアカウンタ API を直接使用する
setuid(2) を呼び出すか、setuid ファイルを実行する
また、データ収集をプログラムから制御する場合も、該当する節を読んでください。
多くのプログラムは、次のような機能を使用して、動的に割り当てられたメモリーに依存しています。
malloc、valloc、alloca (C/C++)
new (C++)
スタック局所変数 (Fortran)
MALLOC、MALLOC64 (Fortran)
初期値の設定としてメモリーの割り当て方法が明示的に規定されていないかぎり、プログラムが動的に割り当てられるメモリーの初期内容に依存しないように注意する必要があります。たとえば、malloc(3C) のマニュアルページにある calloc と malloc の説明を比較してください。
動的に割り当てられるメモリーを使用するプログラムを単独で実行すると、正常に機能しているように見えることがありますが、パフォーマンスデータの収集を有効にした状態で実行すると、問題が起きることがあります。そのときの症状には、予期しない浮動小数点演算動作、セグメント例外、またはアプリケーション固有のエラーメッセージなどが含まれる場合があります。
こうした症状は、アプリケーションが単独で実行されたときには、初期化されていないメモリーの値が動作に影響ないものであっても、パフォーマンスデータの収集ツールとの組み合わせで実行されたときに別の値が設定されることによって、発生する場合があります。この場合は、パフォーマンスツールの問題ではありません。動的に割り当てられたメモリーの内容に依存するアプリケーションのどれにも、潜在的な問題があります。別途、明示的に規定されていないかぎり、オペレーティングシステムは、どのような内容であれ、動的に割り当てられたメモリー上の内容の提供を自由に行えます。現在のオペレーティングシステムが動的に割り当てられるメモリーに必ず特定の値を設定するようになっていたとしても、将来オペレーティングシステムのリリースが変わったとき、あるいはプログラムを別のオペレーティングシステムに移植した場合には、こうした潜在的な問題によって、予期しない動作が発生する可能性があります。
次のツールが、こうした潜在的な問題の発見に役立ちます。
f95 -xcheck=init_local
詳細は、『Fortran ユーザーズガイド』または f95(1) のマニュアルページを参照してください。
lint ユーティリティー
詳細は、『C ユーザーズガイド』または lint(1) のマニュアルページを参照してください。
dbx 下での実行時チェック
詳細は、『dbx コマンドによるデバッグ 』または dbx(1) のマニュアルページを参照してください。
Rational Purify
コレクタは、さまざまなシステムライブラリの関数に割り込み、トレースデータを収集し、データ収集の完全性を確保します。コレクタがライブラリ関数の呼び出しに割り込む状況を次に示します。
同期待ちトレースデータの収集。コレクタは、Solaris 9 OS で Solaris スレッドライブラリ libthread.so の関数に対して、また Solaris 10 OS で Solaris C ライブラリ libc.so の関数に対して割り込み処理を行います。
ヒープトレースデータの収集。コレクタは、malloc、realloc、memalign、free の関数に割り込みます。これらの関数は、C 標準ライブラリ libc.so のほか、libmalloc.so や libmtmalloc.so などのライブラリにあります。
MPI トレースデータの収集。コレクタは、Solaris MPI ライブラリ libmpi.so の関数に割り込みます。
時間データの完全性の確保。コレクタは setitimer に割り込み、プログラムがプロファイルタイマーを使用しないようにします。
ハードウェアカウンタデータの完全性の確保。コレクタはハードウェアカウンタライブラリ libcpc.so の関数に割り込み、プログラムがカウンタを使用しないようにします。プログラムからこのライブラリの関数への呼び出しは、値 -1 を返します。
派生プロセスに対するデータ収集の有効化。コレクタは、fork(2)、fork1(2)、vfork(2)、fork(3F)、system(3C)、system(3F)、sh(3F)、popen(3C)、exec(2) の関数とそのバリアントに割り込みます。 vfork の呼び出しは、内部で fork1 の呼び出しに置き換えられます。これらの割り込み処理が行われるのは、collect コマンドの場合だけです。
コレクタによる SIGPROF シグナルおよび SIGEMT シグナルの処理の保証。コレクタは sigaction に割り込み、シグナルハンドラがこれらのシグナル用のプライマリシグナルハンドラであるかどうかを確認します。
次のような環境では、割り込みが成功しません。
割り込み対象関数が入っているライブラリとプログラムを静的にリンクした場合。
コレクタライブラリが事前読み込みされていない実行中アプリケーションに dbx を接続した場合。
これらのライブラリのいずれか 1 つを動的に読み込み、このライブラリの中でだけ検索することによってシンボルを解決する場合。
コレクタが割り込み処理を行えなかった場合には、パフォーマンスデータが消去されたり無効になったりする可能性があります。
コレクタは、シグナルを 2 つ使用してプロファイルデータを収集します。全実験用の SIGPROF と、ハードウェアカウンタ実験専用の SIGEMT です。コレクタはこれらのシグナルのそれぞれを対象としてシグナルハンドラをインストールします。シグナルハンドラは自身のシグナルをインターセプトして処理しますが、ほかのシグナルは、インストールされているほかのシグナルハンドラに引き渡します。プログラムがこれらのシグナル用に独自のシグナルハンドラをインストールすると、コレクタは自分のシグナルハンドラをプライマリハンドラとして再インストールし、それによって完全なパフォーマンスデータが確保されます。
collect コマンドでは、ユーザー指定のシグナルを使用してデータ収集の一時停止と再開、および標本の記録を行えます。それらのシグナルはコレクタによって保護されませんが、ユーザーハンドラがインストールされている場合は、実験に警告が書き出されます。コレクタとアプリケーションによる指定シグナルの使用が互いに競合しないように、ユーザーが責任を持って確認する必要があります。
コレクタによってインストールされたシグナルハンドラは、システムコールがシグナル配信のために中断されないようにするためのフラグを設定します。この方法では、プログラムのシグナルハンドラがシステムコールの中断を許可するようにフラグを設定した場合に、プログラムの動作が変わる可能性があります。動作が変化する重要な例としては、非同期キャンセル処理に SIGPROF を使用し、システムコールの中断を行う非同期入出力ライブラリ libaio.so があります。コレクタライブラリ libcollector.so がインストールされている場合は必ず、キャンセルシグナルの到着が非同期入出力操作の取り消しに間に合わないほど遅れます。
コレクタライブラリを事前読み込みしないままプロセスに dbx を接続してパフォーマンスデータ収集を有効にし、そのあとでプログラムが自分のシグナルハンドラをインストールすると、コレクタは自分のシグナルハンドラを再インストールしません。この場合、プログラムのシグナルハンドラは、 パフォーマンスデータが失われないように、SIGPROF と SIGEMT のシグナルが確実に渡されるようにする必要があります。プログラムのシグナルハンドラがシステムコールを中断した場合のプログラムの動作とプロファイルの動作は、コレクタライブラリが事前読み込みされた場合の動作と異なります。
動的ローダーによって課される制約は、setuid(2) の使用とパフォーマンスデータの収集を困難にします。プログラムが setuid を呼び出すか setuid ファイルを実行する場合、コレクタは新しいユーザー ID に必要なアクセス権がないために、実験ファイルに書き込めない可能性が高くなります。
この問題は、プロセスが実行される可能性があるすべての UID または GID に書き込み権を与えるように umask を設定することで回避できます。ID には、実験の書き込み権が必要です。
プログラムからデータ収集を制御するには、コレクタ共有ライブラリ libcollector.so に入っている API 関数をプログラムで使用します。 これらの関数は C で記述されており、Fortran インタフェースも用意されています。ライブラリとともに提供されるヘッダーファイルに、C インタフェースと Fortran インタフェースの両方が定義されています。
API 関数は、次のように定義されます。
void collector_sample(char *name); void collector_pause(void); void collector_resume(void); void collector_thread_pause(unsigned int t); void collector_thread_resume(unsigned int t); void collector_terminate_expt(void); |
CollectorAPI クラスに、JavaTM プログラム用の類似の機能が用意されており、これについては、「Java インタフェース」で説明しています。
C/C++ インタフェースにアクセスする方法は 2 通りあります。
collectorAPI.h を取り込み、基になっている libcollector.so API 関数の有無をチェックする実関数が含まれている -lcollectorAPI とリンクする。
この方法では、API ライブラリとリンクする必要があります。この方法は、あらゆる環境下で有効です。有効な実験がない場合、API 呼び出しは無視されます。
基になっている libcollector.so API 関数の有無をチェックするマクロを含む、libcollector.h を取り込む。
この方法は、メインの実行可能ファイル内で使用する場合と、プログラムの起動と同時にデータ収集を開始する場合にのみ有効です。dbx を使用してプロセスに接続する場合や、プロセスが dlopen する共有ライブラリ内で使用した場合は、必ずしも機能しません。この方法は、下位互換性を維持する目的で提供されており、ほかの目的での使用は推奨されません。
どんな言語を使用している場合も、プログラムを -lcollector とリンクしないでください。リンクした場合、コレクタが予期しない動作をすることがあります。
Fortran API の libfcollector.h ファイルには、ライブラリに対する Fortran インタフェースが定義されています。このライブラリを使用するには、アプリケーションを -lcollectorAPI にリンクする必要があります。このライブラリには、下位互換性を維持するため、-lfcollector というもう 1 つの名前も用意されています。動的関数とスレッドによる呼び出しの一時停止と再開を除けば、Fortran API は C/C++ API と同じ機能を提供します。
Fortran の場合、API 関数を使用するには、次の文を挿入します。
include "libfcollector.h" |
どんな言語を使用している場合も、プログラムを -lcollector とリンクしないでください。リンクした場合、コレクタが予期しない動作をすることがあります。
次の文を使用して、CollectorAPI クラスをインポートし、Java API にアクセスできます。ただし、アプリケーションは / installation_directory/lib/collector.jar (ここで、installation-directory は Sun Studio ソフトウェアがインストールされているディレクトリ) を指すクラスパスがある状態で呼び出される必要があります。
import com.sun.forte.st.collector.CollectorAPI; |
Java CollectorAPI メソッドは、次のように定義されます。
CollectorAPI.sample(String name) CollectorAPI.pause() CollectorAPI.resume() CollectorAPI.threadPause(Thread thread) CollectorAPI.threadResume(Thread thread) CollectorAPI.terminate() |
Java API には、動的関数 API 以外の C および C++ API と同じ関数が含まれています。
C インクルードファイルの libcollector.h には、データが収集されていないときには実際の API 関数の呼び出しを迂回するマクロが入っています。この場合、関数は動的に読み込まれません。ただし、一部の環境ではうまく機能しないことがあるため、これらのマクロを使用するのは危険です。collectorAPI.h はマクロを使用していないため、このファイルを利用する方が安全です。このファイルでは、関数が直接参照されます。
Fortran API サブルーチンはパフォーマンスデータが収集されているときには C API 関数を呼び出し、そうでないときには復帰します。チェック処理のオーバーヘッドは非常に小さいので、プログラムのパフォーマンスにはあまり影響がないはずです。
パフォーマンスデータを収集するには、この章で後述するように、コレクタを使用してプログラムを実行する必要があります。API 関数への呼び出しを挿入することによって、データ収集が有効になることはありません。
マルチスレッドプログラムで API 関数を使用する場合には、これらの関数が 1 つのスレッドによってのみ呼び出されるようにする必要があります。collector_thread_pause() および collector_thread_resume() 以外は、API 関数が行うアクションの対象はプロセスであって、個々のスレッドではありません。各スレッドが API 関数を呼び出すと、記録されたデータが期待したものにならない可能性があります。たとえば、あるスレッドが collector_pause() や collector_terminate_expt() を呼び出したときに、ほかのスレッドがまだプログラム内のそのポイントに達していない場合、すべてのスレッドについて収集が一時停止または停止され、この API 呼び出しの前にコードを実行していたスレッドのデータが失われる可能性があります。データ収集を個々のスレッドレベルで制御するには、collector_thread_pause() 関数と collector_thread_resume() 関数を使用します。これらの関数の使用方法として、1 つのマスタースレッドで、それ自体を含むすべてのスレッドのすべての呼び出しを行う方法と、各スレッドで自身のみの呼び出しを行う方法があります。その他の使用方法では、結果が予測できないものになる可能性があります。
ここでは、データ収集に関係する API 関数について説明します。
C と C++: collector_sample(char *name)
Fortran: collector_sample(string name)
Java: CollectorAPI.sample(String name)
標本パケットを記録し、その標本に指定された名前または文字列をラベルとして付けます。ラベルは、「パフォーマンスアナライザ」の「イベント」タブで表示されます。Fortran の引数 string の型は、character です。
標本ポイントに含まれるデータは、プロセスに関するものであり、個々のスレッドに関するものではありません。マルチスレッドアプリケーションの場合、collector_sample() API 関数は、標本の記録中に別の呼び出しが行われても、1 つの標本だけが書き込まれるようにします。記録される標本の数は、呼び出しを行うスレッドの数よりも少なくなります。
パフォーマンスアナライザは、別々のメカニズムによって記録された標本同士を区別しません。API 呼び出しによって記録された標本だけを見たい場合には、パフォーマンスデータの記録時にほかのあらゆる標本モードを停止します。
C、C++、Fortran: collector_pause()
Java: CollectorAPI.pause()
実験へのイベント固有データの書き込みを停止します。実験はオープン状態のままであり、大域データの書き込みは続けられます。有効な実験がない場合やデータの記録がすでに停止されている場合には、呼び出しは無視されます。この関数は、たとえすべてのイベント固有データの書き込みが collector_thread_resume() 関数によって特定のスレッドに対して有効にされていたとしても、その書き込みを停止します。
C、C++、Fortran: collector_resume()
Java: CollectorAPI.resume()
collector_pause() を呼び出したあとに、実験へのイベント固有データの書き込みを再開します。有効な実験がない場合やデータの記録が有効である場合には、呼び出しは無視されます。
C と C++ のみ: collector_thread_pause(unsigned int t)
Java: CollectorAPI.threadPause(Thread t)
引数リストで指定したスレッドから実験へのイベント固有データの書き込みを停止します。引数 t は、C/C++ プログラムの場合は POSIX スレッド識別子、Java プログラムの場合は Java スレッドです。実験がすでに終了したか、実験が有効でないか、あるいは、そのスレッドに対するデータの書き込みがすでにオフになっている場合、呼び出しは無視されます。この関数は、たとえデータの書き込みが大域的に有効でも、指定したスレッドからのデータの書き込みを停止します。デフォルトでは、個々のスレッドのデータの記録がオンに設定されます。
C と C++ のみ: collector_thread_resume(unsigned int t)
Java: CollectorAPI.threadResume(Thread t)
引数リストで指定したスレッドから実験へのイベント固有データの書き込みを再開します。引数 t は、C/C++ プログラムの場合は POSIX スレッド識別子、Java プログラムの場合は Java スレッドです。実験がすでに終了したか、実験が有効でないか、あるいは、そのスレッドに対するデータの書き込みがすでにオンになっている場合、呼び出しは無視されます。データは、データの書き込みが大域的に有効にされ、スレッドに対して有効にされているときのみ、実験に書き込まれます。
C、C++、Fortran: collector_terminate_expt()
Java: CollectorAPI.terminate
データを収集している実験を終了します。 以降、データの収集は行われませんが、プログラムは正常に動作を続けます。有効な実験がない場合は、呼び出しは無視されます。
使用している C または C++ プログラムが、コンパイル時にそのデータ空間に関数を動的に取り込む場合、動的関数やモジュールのデータをパフォーマンスアナライザで見るには、コレクタに情報を与える必要があります。この情報は、コレクタ API 関数の呼び出しによって渡されます。API 関数の定義は、次のとおりです。
void collector_func_load(char *name, char *alias, char *sourcename, void *vaddr, int size, int lntsize, Lineno *lntable); void collector_func_unload(void *vaddr); |
Java HotSpotTM 仮想マシンによってコンパイルされる Java メソッドには別のインタフェースが使用されるので、これらの API 関数を使用する必要はありません。Java インタフェースは、コンパイルされたメソッドの名前をコレクタに知らせます。Java コンパイル済みメソッドの関数データと注釈付き逆アセンブリのリストを見ることはできますが、注釈付きソースリストを見ることはできません。
ここでは、データ収集に関係する API 関数について説明します。
実験への記録のため、動的にコンパイルされた関数に関する情報をコレクタに渡します。パラメータリストを次の表に示します。
表 3–1 collector_func_load() のパラメータリスト
パラメータ |
定義 |
---|---|
name |
パフォーマンスツールで使用する、動的にコンパイルされた関数の名前。実際の関数名でなくてもかまいません。この名前は関数の通常の命名規則に従っている必要はありませんが、空白文字や引用符は含めないようにします。 |
alias |
関数の説明に使用する任意の文字列。NULL を使用できます。この文字列が解釈の対象となることはありません。 空白文字を含めることができます。アナライザの「概要」タブに表示されます。何の関数であるか、またはなぜ関数が動的に構築されたかを示すために使用できます。 |
sourcename |
関数の構築元であるソースファイルのパス。NULL を使用できます。このソースファイルは、注釈付きソースリストに使用されます。 |
vaddr |
関数が読み込まれたアドレス。 |
size |
バイト数による関数のサイズ。 |
lntsize |
行番号テーブルのエントリの数を示すカウント。行番号情報がない場合には、ゼロとなります。 |
lntable |
lntsize エントリが入っているテーブル。各エントリは、整数対です。第 1 整数はオフセット、第 2 整数は行番号です。あるエントリのオフセットと次のエントリのオフセットとの間の命令はすべて、最初のエントリの行番号に対応します。オフセットは数字の昇順にする必要があります。 行番号の順序は任意です。lntable が NULL である場合、関数のソースリストは利用できませんが、逆アセンブリリストは利用できます。 |
アドレス vaddr にある動的関数が読み込み解除されたことをコレクタに通知します。