スレッドアナライザは、マルチスレッドプログラムの実行の分析に使用できるツールです。スレッドアナライザは、POSIX スレッド API、Solaris スレッド API、OpenMP 指令、またはこれらの組み合わせを使用して作成されたコード内でデータの競合やデッドロックなどのマルチスレッドプログラミングのエラーを検出できます。
この章では、次の内容について説明します。
スレッドアナライザは、次のハードウェアおよびオペレーティングシステムをサポートします。
SPARC(R) v8plus、v8plusa、v8plusb、v9、v9a、および v9b アーキテクチャー
Intel(R) x86 および AMD(R) x64 プラットフォーム
Oracle Solaris 10 1/06 および以降の更新、OpenSolaris 2008.11 および 2009.06
Oracle Enterprise Linux 5.3、SuSE Linux Enterprise Server 10、RedHat Enterprise Linux 5、CentOS 5
スレッドアナライザは、マルチスレッドプロセスの実行中に生じたデータの競合を検出します。データの競合は、次のすべての条件に当てはまるときに生じます。
単一プロセス内の 2 つ以上のスレッドが、同じメモリー位置に同時にアクセスする
少なくとも 1 つが書き込みのためのアクセスである
どのスレッドも、そのメモリーへのアクセスを制御するための排他的ロックを使用していない
この3つの条件が揃うとアクセス順序が定まらないため、実行するたびにその時の順序によって計算結果が異なる可能性があります。データの競合には、害のないもの (メモリーアクセスをビジーウェイトに使用するときなど) もありますが、データの競合の多くはプログラムのバグによるものです。
スレッドアナライザは、POSIX スレッド API、Solaris スレッド API、OpenMP、またはこれらの組み合わせを使用して作成されたマルチスレッドプログラムで動作します。
デッドロックとは、2 つ以上のスレッドが互いを待機しているために処理がまったく進まない状況を指します。デッドロックの原因は多数あります。スレッドアナライザは、相互排他ロックの不適切な使用によって生じたデッドロックを検出します。この種のデッドロックは、マルチスレッドアプリケーションでよく生じます。
2 つ以上のスレッドから成るプロセスは、次の条件がすべて揃うとデッドロックを生じることがあります。
すでにロックを保持しているスレッドが新しいロックを要求する
新しいロックの要求が同時に行われる
チェーン内の次のスレッドで保持されているロックを各スレッドが待機するという巡回チェーンを、2 つ以上のスレッドが形成する
デッドロック状況の簡単な例を次に示します。
スレッド 1 はロック A を保持し、ロック B を要求する
スレッド 2 はロック B を保持し、ロック A を要求する
デッドロックには潜在的デッドロックと実デッドロックの 2 種類があります。潜在的デッドロックは、所定の実行で必ず起きるわけではありませんが、スレッドのスケジュールや、スレッドによって要求されたロックのタイミングに依存したプログラムの実行で起きる可能性があります。実デッドロックは、プログラムの実行中に発生するものです。実デッドロックでは、関係するスレッドの実行は滞りますが、プロセス全体の実行は滞ることもあれば、そうでないこともあります。
次の手順は、スレッドアナライザでマルチスレッドプログラムの問題を解決するプロセスを示しています。
データの競合の検出を行う場合、プログラムを計測します。
データの競合の検出またはデッドロック検出の実験を作成します。
実験結果を検討し、スレッドアナライザで明らかになったマルチスレッドプログラミングの競合が、正当なバグまたは影響のない現象であるかどうかを判断します。
本物のバグを修正し、入力データ、スレッド数、ループスケジュール、さらにはハードウェアなど、さまざまな要素を変更しつつ追加実験 (前述の手順 2) を作成します。これを繰り返すことで、決定論的ではない問題の突き止めに役立ちます。
前述の手順 1 から 3 については、以降の節で説明します。
データの競合を検出するには、次の 3 つの手順を実行する必要があります。
データの競合の検出を有効にするコードを計測する
計測したコードで実験を作成する
データの競合の実験結果を検討する
アプリケーションでデータの競合の検出を可能にするには、実行時にメモリーアクセスを監視するコードをあらかじめ計測しておく必要があります。コードの計測方法としては、コンパイル中にアプリケーションのソースレベルで行う場合もあれば、バイナリに対し追加ツールを実行することによってアプリケーションのバイナリレベルで行う場合もあります。
ソースレベルの計測は、コンパイルに特別なオプションを指定して行います。また、使用する最適化レベルおよびその他のコンパイラオプションを指定できます。ソースレベルの計測は、コンパイラが一部の分析と計測を少ないメモリーアクセスで行うことができるため、実行時間がより短縮されます。
バイナリレベルの計測は、ソースコードが使用できない場合に役立ちます。ソースコードがある場合でもバイナリ計測を使用することがあります。ただしこの場合、アプリケーションが使用している共有ライブラリはコンパイルできません。discover ツールを使用したバイナリ計測では、バイナリだけでなく、開かれている共有ライブラリすべてを計測します。
ソースレベルで計測するには、特別なコンパイラオプションを付けてソースコードをコンパイルします。
-xinstrument=datarace |
このコンパイラオプションを付けてコンパイラで生成されたコードが、データの競合の検出用に計測されます。
-g コンパイラオプションも、アプリケーションバイナリの構築時に使用する必要があります。このオプションを付けると、スレッドアナライザでデータの競合を報告するときにソースコードおよび行番号情報を表示するための追加データを生成できます。
バイナリレベルで計測するには、discover ツールを使用する必要があります。バイナリが a.out という名前の場合、次のように実行することによって、計測済みのバイナリ a.outi を作成できます。
discover -i datarace -o a.outi a.out |
discover ツールでは、開かれている共有ライブラリを、それがプログラム内で静的にリンクされているか、dlopen() によって動的に開かれているかにかかわらず、すべて自動的に計測します。デフォルトで、ライブラリの計測済みコピーは、ディレクトリ $HOME/SUNW_Bit_Cache に書き込まれます。
有効な discover コマンド行オプションの一部を次に示します。詳細は、discover(1) のマニュアルページを参照してください。
計測済みバイナリを、指定したファイル名で出力する
指定したライブラリを計測しない
どのライブラリも計測しない
キャッシュディレクトリを dir に変更する
データの競合を検出するためにプログラムのバイナリコードを計測する場合、discover ツールでは、入力バイナリを次の条件を満たしてコンパイルする必要があります。
オペレーティングシステムのバージョンが、少なくとも Oracle Solaris 10 Update 5 または OpenSolaris version snv_70 である。
コンパイラが Oracle Solaris Studio 12 Update 1 以降のリリースである。
コンパイラ最適化フラグ (-xO1、-xO2、-xO3、-xO4、-xO5) のいずれかを使用している。
データの競合の報告時にスレッドアナライザがソースコードと行番号情報を表示できるように、-g コンパイラオプションも使用する必要がある。
また、バイナリがコンパイラオプション -xbinopt=prepare を付けてコンパイルされた場合は、SPARC ベースのシステムで実行中の、以前の Solaris バージョンでも discover ツールを使用できることがあります。このコンパイラオプションについては、cc(1)、CC(1)、または f95(1) のマニュアルページを参照してください。
データの競合の検出実験を作成するには、-r race フラグを付けて collect コマンドを使用して、アプリケーションを実行し、プロセスの実行中に実験データを収集します。-r race オプションを使用すると、競合を起こしたデータアクセスの対を収集データから知ることができます。
データの競合を検出する実験を検討するには、tha コマンドを使用します。このコマンドにより、スレッドアナライザのグラフィカルユーザーインタフェースが起動します。er_print コマンド行インタフェースも使用できます。
デッドロックの検出には、次の 2 つの手順が必要です。
デッドロック検出実験の作成
デッドロック実験結果の検討
デッドロック検出実験を作成するには、-r deadlock フラグを付けて collect コマンドを使用して、アプリケーションを実行し、プロセスの実行中に実験データを収集します。-r deadlock オプションを使用した場合、巡回チェーンを構成するロックの保持とロックの要求が収集データに含まれます。
デッドロックを検出する実験を検討するには、tha コマンドを使用します。このコマンドにより、スレッドアナライザのグラフィカルユーザーインタフェースが起動します。er_print コマンド行インタフェースも使用できます。
データの競合とデッドロックを同時に検出するには、「1.4.1 データの競合を検出するための使用モデル」で説明した 3 つの手順に従いデータの競合を検出し、-r race,deadlock フラグを付けた collect コマンドでアプリケーションを実行します。これで競合検出とデッドロック検出の両方のデータが実験結果に含まれます。
スレッドアナライザは tha コマンドで起動することができます。
スレッドアナライザは、マルチスレッドプログラムの解析向けに設計された パフォーマンスアナライザのインタフェースを採用しています。ただし、パフォーマンスアナライザの通常のタブの代わりに、「競合 (Races)」、「デッドロック (Deadlocks)」、「デュアルソース (Dual Source)」、「競合の詳細 (Race Details)」、「デッドロックの詳細 (Deadlock Details)」というタブが表示されます。パフォーマンスアナライザを使用してマルチスレッドプログラムの実験結果を調べる場合、データの競合とデッドロックのためのタブとともに、「関数 (Functions)」、「呼び出し元-呼び出し先(Callers-Callees)」、「逆アセンブリ (Disassembly)」など従来からパフォーマンスアナライザにあるタブが表示されます。