Sun Studio 12: パフォーマンスアナライザ

データ収集と解析のためのプログラムの準備

データ収集と解析の準備のためにプログラムに対して行う特別な作業はありません。次の処理のうち、いずれか 1 つでも行うプログラムの場合には、後述の該当する節を読んでください。

また、データ収集をプログラムから制御する場合も、該当する節を読んでください。

動的割り当てメモリーの利用

多くのプログラムは、次のような機能を使用して、動的に割り当てられたメモリーに依存しています。

初期値の設定としてメモリーの割り当て方法が明示的に規定されていないかぎり、プログラムが動的に割り当てられるメモリーの初期内容に依存しないように注意する必要があります。たとえば、malloc(3C) のマニュアルページにある callocmalloc の説明を比較してください。

動的に割り当てられるメモリーを使用するプログラムを単独で実行すると、正常に機能しているように見えることがありますが、パフォーマンスデータの収集を有効にした状態で実行すると、問題が起きることがあります。そのときの症状には、予期しない浮動小数点演算動作、セグメント例外、またはアプリケーション固有のエラーメッセージなどが含まれる場合があります。

こうした症状は、アプリケーションが単独で実行されたときには、初期化されていないメモリーの値が動作に影響ないものであっても、パフォーマンスデータの収集ツールとの組み合わせで実行されたときに別の値が設定されることによって、発生する場合があります。この場合は、パフォーマンスツールの問題ではありません。動的に割り当てられたメモリーの内容に依存するアプリケーションのどれにも、潜在的な問題があります。別途、明示的に規定されていないかぎり、オペレーティングシステムは、どのような内容であれ、動的に割り当てられたメモリー上の内容の提供を自由に行えます。現在のオペレーティングシステムが動的に割り当てられるメモリーに必ず特定の値を設定するようになっていたとしても、将来オペレーティングシステムのリリースが変わったとき、あるいはプログラムを別のオペレーティングシステムに移植した場合には、こうした潜在的な問題によって、予期しない動作が発生する可能性があります。

次のツールが、こうした潜在的な問題の発見に役立ちます。

システムライブラリの使用

コレクタは、さまざまなシステムライブラリの関数に割り込み、トレースデータを収集し、データ収集の完全性を確保します。コレクタがライブラリ関数の呼び出しに割り込む状況を次に示します。

次のような環境では、割り込みが成功しません。

コレクタが割り込み処理を行えなかった場合には、パフォーマンスデータが消去されたり無効になったりする可能性があります。

シグナルハンドラの使用

コレクタは、シグナルを 2 つ使用してプロファイルデータを収集します。全実験用の SIGPROF と、ハードウェアカウンタ実験専用の SIGEMT です。コレクタはこれらのシグナルのそれぞれを対象としてシグナルハンドラをインストールします。シグナルハンドラは自身のシグナルをインターセプトして処理しますが、ほかのシグナルは、インストールされているほかのシグナルハンドラに引き渡します。プログラムがこれらのシグナル用に独自のシグナルハンドラをインストールすると、コレクタは自分のシグナルハンドラをプライマリハンドラとして再インストールし、それによって完全なパフォーマンスデータが確保されます。

collect コマンドでは、ユーザー指定のシグナルを使用してデータ収集の一時停止と再開、および標本の記録を行えます。それらのシグナルはコレクタによって保護されませんが、ユーザーハンドラがインストールされている場合は、実験に警告が書き出されます。コレクタとアプリケーションによる指定シグナルの使用が互いに競合しないように、ユーザーが責任を持って確認する必要があります。

コレクタによってインストールされたシグナルハンドラは、システムコールがシグナル配信のために中断されないようにするためのフラグを設定します。この方法では、プログラムのシグナルハンドラがシステムコールの中断を許可するようにフラグを設定した場合に、プログラムの動作が変わる可能性があります。動作が変化する重要な例としては、非同期キャンセル処理に SIGPROF を使用し、システムコールの中断を行う非同期入出力ライブラリ libaio.so があります。コレクタライブラリ libcollector.so がインストールされている場合は必ず、キャンセルシグナルの到着が非同期入出力操作の取り消しに間に合わないほど遅れます。

コレクタライブラリを事前読み込みしないままプロセスに dbx を接続してパフォーマンスデータ収集を有効にし、そのあとでプログラムが自分のシグナルハンドラをインストールすると、コレクタは自分のシグナルハンドラを再インストールしません。この場合、プログラムのシグナルハンドラは、 パフォーマンスデータが失われないように、SIGPROFSIGEMT のシグナルが確実に渡されるようにする必要があります。プログラムのシグナルハンドラがシステムコールを中断した場合のプログラムの動作とプロファイルの動作は、コレクタライブラリが事前読み込みされた場合の動作と異なります。

setuid の使用

動的ローダーによって課される制約は、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++ インタフェース

C/C++ インタフェースにアクセスする方法は 2 通りあります。

Fortran インタフェース

Fortran API の libfcollector.h ファイルには、ライブラリに対する Fortran インタフェースが定義されています。このライブラリを使用するには、アプリケーションを -lcollectorAPI にリンクする必要があります。このライブラリには、下位互換性を維持するため、-lfcollector というもう 1 つの名前も用意されています。動的関数とスレッドによる呼び出しの一時停止と再開を除けば、Fortran API は C/C++ API と同じ機能を提供します。

Fortran の場合、API 関数を使用するには、次の文を挿入します。


include "libfcollector.h"

注 –

どんな言語を使用している場合も、プログラムを -lcollector とリンクしないでください。リンクした場合、コレクタが予期しない動作をすることがあります。


Java インタフェース

次の文を使用して、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 つのマスタースレッドで、それ自体を含むすべてのスレッドのすべての呼び出しを行う方法と、各スレッドで自身のみの呼び出しを行う方法があります。その他の使用方法では、結果が予測できないものになる可能性があります。

C/C++、Fortran、および Java API 関数

ここでは、データ収集に関係する API 関数について説明します。

動的な関数とモジュール

使用している 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 関数について説明します。

collector_func_load()

実験への記録のため、動的にコンパイルされた関数に関する情報をコレクタに渡します。パラメータリストを次の表に示します。

表 3–1 collector_func_load() のパラメータリスト

パラメータ 

定義 

name

パフォーマンスツールで使用する、動的にコンパイルされた関数の名前。実際の関数名でなくてもかまいません。この名前は関数の通常の命名規則に従っている必要はありませんが、空白文字や引用符は含めないようにします。 

alias

関数の説明に使用する任意の文字列。NULL を使用できます。この文字列が解釈の対象となることはありません。 空白文字を含めることができます。アナライザの「概要」タブに表示されます。何の関数であるか、またはなぜ関数が動的に構築されたかを示すために使用できます。

sourcename

関数の構築元であるソースファイルのパス。NULL を使用できます。このソースファイルは、注釈付きソースリストに使用されます。

vaddr

関数が読み込まれたアドレス。 

size

バイト数による関数のサイズ。 

lntsize

行番号テーブルのエントリの数を示すカウント。行番号情報がない場合には、ゼロとなります。 

lntable

lntsize エントリが入っているテーブル。各エントリは、整数対です。第 1 整数はオフセット、第 2 整数は行番号です。あるエントリのオフセットと次のエントリのオフセットとの間の命令はすべて、最初のエントリの行番号に対応します。オフセットは数字の昇順にする必要があります。 行番号の順序は任意です。lntableNULL である場合、関数のソースリストは利用できませんが、逆アセンブリリストは利用できます。

collector_func_unload()

アドレス vaddr にある動的関数が読み込み解除されたことをコレクタに通知します。