Oracle Solaris Studio 12.2: dbx コマンドによるデバッグ

第 13 章 OpenMP プログラムのデバッグ

OpenMP アプリケーションプログラミングインタフェース (API) は、共用メモリーマルチプロセッサアーキテクチャー用に複数のコンピュータベンダーと共同で開発された並列プログラミングモデルです。Fortran、C++ および C の OpenMP プログラムを dbx を使用してデバッグするためのサポートは、dbx の汎用マルチスレッドデバッグ機能に基づいています。スレッドおよび LWP 上で動作するすべての dbx コマンドは OpenMP デバッグに使用できます。dbx は、OpenMP デバッグでの非同期スレッド制御はサポートしていません。

この章の内容は次のとおりです。

Oracle Solaris Studio Fortran 95 および C コンパイラによって実装される指示、実行時ライブラリルーチン、および OpenMP Version 2.0 アプリケーションプログラムインタフェースの環境変数については、『OpenMP API ユーザーズガイド』を参照してください。

コンパイラによる OpenMP コードの変換

OpenMP デバッグの詳細については、OpenMP コードがコンパイラによってどのように変換されるかを理解することが役立ちます。次に Fortran の例を示します。


1    program example
2        integer i, n
3        parameter (n = 1000000)
4        real sum, a(n)
5    
6        do i = 1, n
7        a(i) = i*i
8        end do
9    
10        sum = 0
11    
12    !$OMP PARALLEL DO DEFAULT(PRIVATE), SHARED(a, sum)
13    
14        do i = 1, n
15        sum = sum + a(i)
16        end do
17    
18    !$OMP END PARALLEL DO
19    
20        print*, sum
21        end program example

行 12 潤オ 18 のコードは並列領域です。f95 コンパイラは、コードのこのセクションを、OpenMP 実行時ライブラリから呼び出されるアウトラインサブルーチンに変換します。このアウトラインサブルーチンには、内部で生成された名前が付きます。この場合は _$d1A12.MAIN_ です。次に f95 コンパイラは、OpenMP 実行時ライブラリへの呼び出しによって並列領域用にコードを置換して、アウトラインサブルーチンを引数の 1 つとして渡します。OpenMP 実行時ライブラリはすべてのスレッド関連実行を処理し、アウトラインサブルーチンを並列で実行するスレーブスレッドをディスパッチします。C コンパイラも同様に動作します。

OpenMP プログラムをデバッグするときには、アウトラインサブルーチンは dbx によって別の関数として扱われますが、内部生成された名前を使用して関数内のブレークポイントを明示的に設定することはできません。

OpenMP コードで利用可能な dbx の機能

dbx には、マルチスレッドプログラムをデバッグする通常の機能に加え、OpenMP プログラムのデバッグを行う機能もあります。

並列領域へのシングルステップ

dbx は、並列領域にシングルステップ実行できます。並列領域は OpenMP の実行時ライブラリから始まり呼び出されるため、実際には一回のシングルステップの中で、この目的で作成されたスレッドが実行時ライブラリを幾重にも呼び出しを行うことになります。並列領域にシングルステップ実行すると、最初にブレークポイントに到達したスレッドによってプログラムが停止します。このスレッドは、ステップを開始したマスターステップではなく、スレーブスレッドになります。

たとえば、Fortran コードを 「コンパイラによる OpenMP コードの変換」で参照し、マスタースレッド t@1 が行 10 にあると想定します。行 12 に対してシングルステップを実行すると、スレーブスレッド t@2t@3、および t@4 が生成され、実行時ライブラリ呼び出しを実行します。スレッド t@3 が最初にブレークポイントに到達し、プログラムの実行が停止します。このように、スレッド t@1 によって開始されたシングルステップ実行は、次のスレッドで終了します。t@3 この動作は、シングルステップ実行後も通常は以前と同じスレッドにいる普通のステップ実行とは異なります。

変数と式の出力

dbx は、shared、private、および thread-private 変数をすべて出力します。並列領域外で thread private 変数を出力しようとすると、マスタースレッドのコピーが出力されます。whatis コマンドは、並列構文内の shared 変数と private 変数のデータ共有属性を出力します。thread-private 変数については、これらの変数が並列構文内にあるかないかにかかわらず、データ共有属性を出力します。次に例を示します。


(dbx) whatis p_a
# OpenMP first and last private variable
int p_a;

print -s expression コマンドは、式に private または thread private 変数が含まれている場合に、現在の OpenMP の並列領域の各スレッドの式 expression の値を出力します。次に例を示します。


(dbx) print -s p_a
thread t@3: p_a = 3
thread t@4: p_a = 3

式に private 変数または thread private 変数が含まれない場合は、1 つの値だけが出力されます。

領域およびスレッド情報の出力

dbx は、現在の並列領域、または指定された並列領域に関する説明を出力できます。これには、親領域、並列領域 ID、チームのサイズ (スレッド数)、プログラムの場所 (プログラムのカウンタアドレス) が含まれます。次に例を示します。


(dbx) omp_pr
parallel region 127283434369843201
    team size = 4
    source location = test.c:103
    parent = 127283430568755201

また、現在の並列領域または指定された並列領域から、そのルートに至るまで、パス上のすべての並列領域の説明も出力できます。次に例を示します。


(dbx) omp_pr -ancestors
parallel region 127283434369843201
    team size = 4
    source location = test.c:103
    parent = 127283430568755201

    parallel region 127283430568755201
        team size = 4
        source location = test.c:95
        parent = <no parent>

さらに、並列領域ツリー全体も出力できます。次に例を示します。


(dbx) omp_pr -tree
parallel region 127283430568755201
    team size = 4
    source location = test.c:95
    parent = <no parent>

    parallel region 127283434369843201
        team size = 4
        source location = test.c:103
        parent = 127283430568755201

詳細については、omp_pr コマンド」を参照してください。

dbx は、現在のタスク領域、または指定されたタスク領域に関する説明を出力できます。これには、タスク領域 ID、状態 (生成済み、実行中、または待機中)、実行中のスレッド、プログラムの場所 (プログラムのカウンタアドレス)、未完了の子、親が含まれます。次に例を示します。


(dbx) omp_tr
task region 65540
    type = implicit
    state = executing
    executing thread = t@4
    source location == test.c:46
    unfinished children = 0
    parent = <no parent>

また、現在のタスク領域または指定されたタスク領域から、そのルートに至るまで、パス上のすべてのタスク領域の説明も出力できます。


(dbx) omp_tr -ancestors
task region 196611
    type = implicit
    state = executing
    executing thread = t@3
    source location - test.c:103
    unfinished children = 0
    parent = 131075

    task region 131075
        type = implicit
        state = executing
        executing thread = t@3
        unfinished children = 0
        parent = <no parent>

さらに、タスク領域ツリー全体も出力できます。次に例を示します。


(dbx) omp_tr -tree
task region 10
    type = implicit
    state = executing
    executing thread = t@10
    source location = test.c:103
    unfinished children = 0
    parent = <no parent>
task region 7
    type = implicit
    state = executing
    executing thread = t@7
    source location = test.c:103
    unfinished children = 0
    parent = <no parent>
task region 6
    type implicit
    state = executing
    executing thread = t@6
    source location = test.c:103
    unfinished children = 0
    parent = <o parent>
task region 196609
    type = implicit
    state = executing
    executing thread = t@1
    source location = test.c:95
    unfinished children = 0
    parent = <no parent>

    task region 262145
        type = implicit
        state = executing
        executing thread = t@1
        source location = test.c:103
        unfinished children - 0
        parent = 196609
 

詳細については、omp_tr コマンド」を参照してください。

dbx は、現在のループに関する説明を出力できます。これには、スケジューリングの型 (静的、動的、ガイド付き、自動、または実行時)、番号付きまたは番号なし、範囲、ステップ数または刻み幅、および繰り返し回数が含まれます。次に例を示します。


(dbx) omp_loop
    ordered loop: no
    lower bound: 0
    upper bound: 3
    step: 1
    chunk: 1
    schedule type: static
    source location: test.c:49

詳細については、omp_loop コマンド」を参照してください。

dbx は、現在のチーム、または指定された並列領域のチームのすべてのスレッドを出力できます。次に例を示します。


(dbx) omp_team
team members:
    0: t@1 state = in implicit barrier, task region = 262145
    1: t@6 state = in implicit barrier, task region = 6
    2: t@7 state = working, task region = 7
    3: t@10 state = in implicit barrier, task region = 10

詳細については、omp_team コマンド」を参照してください。

OpenMP コードをデバッグするとき、thread -info は、現在のスレッドまたは指定のスレッドに関する通常の情報に加え、OpenMP スレッド ID、並列領域 ID、タスク領域 ID、および OpenMP スレッドの状態も出力します。詳細については、thread コマンド」を参照してください。

並列領域の実行の直列化

dbx 現在のスレッド、または現在のチームのすべてのスレッドで、次に検出された並列領域の実行を直列化します。詳細については、omp_serialize コマンド」を参照してください。

スタックトレースの使用

並列領域で実行が停止されると、アウトラインサブルーチンを含んだスタックトレースがwhere コマンドによって表示されます。


(dbx) where
current thread: t@4
=>[1] _$d1E48.main(), line 52 in "test.c"
  [2] _$p1I46.main(), line 48 in "test.c"

--- frames from parent thread ---
current thread: t@1
  [7] main(argc = 1, argv = 0xffffffff7fffec98), line 46 in "test.c"

スタックの上位フレームはアウトライン関数のフレームです。コードが略述されているにもかかわらず、ソース行番号は依然として 15 にマップされます。

並列領域で実行が停止されたときに、関連フレームがアクティブ状態である場合、スレーブスレッドの where コマンドはマスタースレッドのスタックトレースを出力します。マスタースレッドの where コマンドは完全トレースバックを行います。

まず omp_team コマンドを実行して現在のチームのすべてのスレッドをリストし、次にマスタースレッド (OpenMP スレッド ID が 0 のスレッド) に切り替え、そのスレッドからスタックトレースを取得することによって、実行が、スレーブスレッドでブレークポイントにどのように到達したかを判断することもできます。

dump コマンドの使用

並列領域で実行が停止すると、dump コマンドによって private 変数の複数のコピーが出力されます。次の例では、dump c コマンドが変数 i の 2 つのコピーを出力します。


[t@1 l@1]: dump
i = 1
sum = 0.0
a = ARRAY
i = 1000001

変数 i の 2 つのコピーが出力されるのは、アウトラインルーチンがホストルーチンのネストされた関数として実装され、private 変数がアウトラインルーチンの局所変数として実装されます。dump コマンドがスコープ内のすべての変数を出力するため、ホストルーチン内の i およびアウトラインルーチン内の i の両方が表示されます。

イベントの使用

dbx は、OpenMP コードで stopwhen、および trace コマンドとともに使用できるイベントを提供します。これらのコマンドとともにイベントを使用する方法については、「イベント指定の設定」を参照してください。

同期イベント

omp_barrier [type] [state]

バリアーに入るスレッドのイベントを追跡します。

type は次のいずれかです。

  • 明示的なバリアーを追跡する explicit

  • 暗黙的なバリアーを追跡する implicit

type を指定しなければ、明示的なバリアーだけが追跡されます。

state は次のいずれかです。

  • いずれかのスレッドがバリアーに入ったときにイベントをレポートする enter

  • いずれかのスレッドがバリアーを出たときにイベントをレポートする exit

  • すべてのスレッドがバリアーに入ったときにイベントをレポートする all_entered

state を指定しない場合のデフォルトは all_entered です。

enter または exit を指定するときにスレッド ID を含めると、そのスレッドのみ追跡を行えます。

omp_taskwait [state]

taskwait に入るスレッドのイベントを追跡します。

state は次のいずれかです。

  • スレッドが taskwait に入ったときにイベントをレポートする enter

  • すべての子タスクが完了したときにイベントをレポートする exit

state を指定しない場合のデフォルトは exit です。

omp_ordered [state]

番号付き領域に入るスレッドのイベントを追跡します。

state は次のいずれかです。

  • 番号付き領域が開始したときにイベントをレポートする begin

  • スレッドが番号付き領域に入ったときにイベントをレポートする enter

  • スレッドが番号付き領域を出たときにイベントをレポートする exit

state を指定しない場合のデフォルトは enter です。

omp_critical

クリティカルリージョンに入るスレッドのイベントを追跡します。

omp_atomic [state]

微細領域に入るスレッドのイベントを追跡します。

state は次のいずれかです。

  • 微細領域が開始したときにイベントをレポートする begin

  • スレッドが微細領域を出たときにイベントをレポートする exit

state を指定しない場合のデフォルトは begin です。

omp_flush

フラッシュを実行するスレッドのイベントを追跡します。

そのほかのイベント

omp_task [state]

タスクの作成と終了を追跡します。

state は次のいずれかです。

  • タスクが作成されてから、実行が開始する直前にイベントをレポートする create

  • タスクが実行を開始したときにイベントをレポートする start

  • タスクの実行が完了し、終了処理が実行されるときにイベントをレポートする finish

state を指定しない場合のデフォルトは start です。

omp_master

マスター領域に入るマスタースレッドのイベントを追跡します。

omp_single

単一領域に入るスレッドのイベントを追跡します。

OpenMP コードの実行シーケンス

OpenMP プログラム内の並列領域の内部にシングルステップするときの実行シーケンスは、ソースコードシーケンスとは同じではありません。シーケンスが異なるのは、並列領域内のコードが通常はコンパイラによって変換され再配置されるためです。OpenMP コード内でのシングルステップは、オプティマイザがコードを移動する最適化コード内でのシングルステップと似ています。