Sun Studio 12: dbx コマンドによるデバッグ

第 16 章 dbx を使用した Fortran のデバッグ

この章では、Fortran で使用されることが多いいくつかの dbx 機能を紹介します。dbx を使用して Fortran コードをデバッグするときの助けになる、dbx に対する要求の例も示してあります。

この章は次の各節から構成されています。

Fortran のデバッグ

次のアドバイスと概要は、Fortran プログラムをデバッグするときに役立ちます。dbx を使用した Fortran OpenMP コードのデバッグについては、「イベントとの対話」を参照してください。

カレントプロシージャとカレントファイル

デバッグセッション中、dbx は、1 つのプロシージャと 1 つのソースファイルをカレントとして定義します。ブレークポイントの設定要求と変数の出力または設定要求は、カレントの関数とファイルに関連付けて解釈されます。したがって、stop at 5 は、カレントファイルがどれであるかによって、3 つの異なるブレークポイントのうち 1 つを設定します。

大文字

プログラムのいずれかの識別子に大文字が含まれる場合、dbx はそれらを認識します。 いくつかの旧バージョンの場合のように、大文字/小文字を区別するコマンド、または区別しないコマンドを指定する必要はありません。

Fortran 95 と dbx は、大文字/小文字を区別するモードまたは区別しないモードのいずれかに統一する必要があります。

dbx のサンプルセッション

次の例では、サンプルプログラム my_program を使用します。

デバッグのための主プログラム a1.f


    PARAMETER ( n=2 )
    REAL twobytwo(2,2) / 4 *-1 /
    CALL mkidentity( twobytwo, n )
    PRINT *, determinant( twobytwo )
    END

デバッグのためのサブルーチン a2.f


    SUBROUTINE mkidentity ( array, m )
    REAL array(m,m)
    DO 90 i = 1, m
        DO 20 j = 1, m
            IF ( i .EQ. j ) THEN
            array(i,j) = 1.
            ELSE
            array(i,j) = 0.
            END IF
20        CONTINUE
90    CONTINUE
    RETURN
    END

デバッグのための関数 a3.f


    REAL FUNCTION determinant ( a )
    REAL a(2,2)
    determinant = a(1,1) * a(2,2) - a(1,2) / a(2,1)
    RETURN
    END

Proceduredbx のサンプルセッションの実行

  1. -g オプションでコンパイルとリンクをします。

    この処理は、まとめて 1 回または 2 回に分けて実行することができます。

    -g フラグ付きコンパイルとリンクを 1 度にまとめて行います。


     demo% f95 -o my_program -g a1.f a2.f a3.f
    

    コンパイルとリンクを分けて行います。


     demo% f95 -c -g a1.f a2.f a3.f
     demo% f95 -o my_program a1.o a2.o a3.o
    
  2. 実行可能ファイル my_program について dbx を起動します。


     demo% dbx my_program
     Reading symbolic information…
  3. stop in subnam と入力して、最初の実行可能文の前にブレークポイントを設定する。subnam は、サブルーチン、関数、ブロックデータサブプログラムを示します。

    main プログラム中の最初の実行可能文で停止します。


     (dbx) stop in MAIN
     (2) stop in MAIN

    通常 MAIN は大文字ですが、subnam は大文字でも小文字でもかまいません。

  4. run コマンドを入力して、dbx からプログラムを実行します。dbx の起動時に指定された実行可能ファイルの中で、プログラムが実行されます。


     (dbx) run
    実行中: my_program
    MAIN で停止しました 行位置 3 ファイル "a1.f"
         3         call mkidentity( twobytwo, n )

    ブレークポイントに到達すると、dbx はどこで停止したかを示すメッセージを表示します。 前述の例では、a1.f ファイルの行番号 3 で停止しています。

  5. print コマンドを使用して、値を出力します。

    n の値を出力します。


     (dbx) print n
     n = 2

    マトリックス twobytwo を出力します。


     (dbx) print twobytwo
     twobytwo =
        (1,1)       -1.0
        (2,1)       -1.0
        (1,2)       -1.0
        (2,2)       -1.0

    マトリックス array を出力します。


    (dbx) print array
    dbx: "array" が現在のスコープに定義されていません。
    (dbx)

    ここで array は定義されていないため、出力は失敗します (mkidentity 内でのみ有効)。

  6. next コマンドを使用して、次の行に実行を進めます。

    次の行に実行を進めます。


    (dbx) next
    MAIN で停止しました 行番号 4 ファイル "a1.f"
        4             print *, determinant( twobytwo )
    (dbx) print twobytwo
    twobytwo =
        (1,1)       1.0
        (2,1)       0.0
        (1,2)       0.0
        (2,2)       1.0
    (dbx) quit
    demo%

    next コマンドは現在のソース行を実行し、次のソース行で停止します。これは副プログラムの呼び出しを 1 つの文として数えます。

    next コマンドと step コマンドを比較します。step コマンドは、ソースの次の行または副プログラムの次のステップを実行します。通常、次の実行可能ソース文がサブルーチンまたは関数呼び出しの場合、各コマンドは次の処理を行います。

    • step コマンドは、副プログラムのソースの最初の文にブレークポイントを設定します。

    • next コマンドは、呼び出し元のプログラム中で、呼び出しのあとの最初の文にブレークポイントを設定します。

  7. quit コマンドを入力して、dbx を終了します。


    (dbx)quit        
    demo%

セグメント不正のデバッグ

プログラムでセグメント不正 (SIGSEGV) が発生するのは、プログラムが使用可能なメモリー範囲外のメモリーアドレスを参照したことを示します。

セグメント不正の主な原因を次に示します。

dbx により問題を見つける方法

問題のあるソース行を見つけるには、dbx を使用してセグメント例外が発生したソースコード行を検出します。

プログラムを使ってセグメント例外を生成します。


demo% cat WhereSEGV.f
    INTEGER a(5)
    j = 2000000
    DO 9 i = 1,5
        a(j) = (i * 10)
9    CONTINUE
    PRINT *, a
    END
demo%

dbx を使用してセグメント例外が発生した行番号を検出します。


demo% f95 -g -silent WhereSEGV.f
demo% a.out
セグメント例外
demo% dbx a.out
a.out のシンボル情報を読んでいます
シグナル SEGV でプログラムが停止しました (セグメント侵害)
(dbx) run
実行中: a.out
シグナル SEGV (障害アドレスにマッピングがありません)
ファイル "WhereSEGV.f" の行番号 4 の MAIN で
    4                   a(j) = (i * 10)
(dbx)

例外の検出

プログラムが例外を受け取る原因は数多く考えられます。問題を見つける方法の 1 つとして、ソースプログラムで例外が発生した行番号を検出して調べる方法があります。

-ftrap=common によってコンパイルすると、すべての例外に対してトラップが強制的に行われます。

例外が発生した箇所を検索します。


demo% cat wh.f
                 call joe(r, s)
                 print *, r/s
                 end
                 subroutine joe(r,s)
                 r = 12.
                 s = 0.
                 return
                 end
demo% f95 -g -o wh -ftrap=common wh.f
demo% dbx wh
wh の記号情報を読み込み中
(dbx) catch FPE
(dbx) run
実行中: wh
(プロセス id 17970)
シグナル FPE (ゼロによる浮動小数点除算) 関数 MAIN 行番号 2 ファイル "wh.f"
   2                     print *, r/s
(dbx)

呼び出しのトレース

プログラムがコアダンプで終了したため、終了するまでの呼び出しシーケンスが必要な場合があるとします。このシーケンスをスタックトレースといいます。

where コマンドは、プログラムフローの実行が停止した位置、およびどのようにその位置に達したかを表示します。これを呼び出し先ルーチンのスタックトレースといいます。

ShowTrace.f は、呼び出しシーケンスでコアダンプを数レベル深くする、つまりスタックトレースを示すために考えられたプログラムです。


逆順での注意:
demo% f77 -silent -g ShowTrace.f
demo% a.out
MAIN が calc を呼び出し、calc が calcb を呼び出します。
*** TERMINATING a.out
*** Received signal 11 (SIGSEGV)
Segmentation Fault (core dumped)
quil 174% dbx a.out
23 行目で実行が停止します。
a.out のシンボル情報を読んでいます
...
(dbx) run
calcb が calc の 9 行目呼び出されました。
実行中: a.out
(プロセス id 1089)
calc が MAIN の 3 行目で呼び出されました。
シグナル SEGV (フォルトのアドレスにマッピングしていません) 関数
calcb 行番号 23 ファイル "ShowTrace.f"
   23                   v(j) = (i * 10)
(dbx) where -V
=>[1] calcb(v = ARRAY , m = 2) 行番号 23 ファイル "ShowTrace.f"
[2] calc(a = ARRAY , m = 2, d = 0) 行番号 9 ファイル "ShowTrace.f"
[3] MAIN() 行番号 3 ファイル "ShowTrace.f"
(dbx)
実行が停止した時点から呼び出しシーケンスを表示します。

配列の操作

dbx が配列を認識し、配列を出力します。


demo% dbx a.out
シンボル情報を読んでいます…
(dbx) list 1,25
    1           DIMENSION IARR(4,4)
    2           DO 90 I = 1,4
    3                   DO 20 J = 1,4
    4                           IARR(I,J) = (I*10) + J
    5   20              CONTINUE
    6   90      CONTINUE
    7           END
(dbx)  stop at 7
(1) stop at "Arraysdbx.f":7
(dbx) run
実行中: a.out
MAIN で停止しました 行番号 7 ファイル "Arraysdbx.f"
    7           END
(dbx) print IARR
iarr =
    (1,1) 11
    (2,1) 21
    (3,1) 31
    (4,1) 41
    (1,2) 12
    (2,2) 22
    (3,2) 32
    (4,2) 42
    (1,3) 13
    (2,3) 23
    (3,3) 33
    (4,3) 43
    (1,4) 14
    (2,4) 24
    (3,4) 34
    (4,4) 44
(dbx) print IARR(2,3)
    iarr(2, 3) = 23  - 配列の要素を指定することができます
(dbx) quit

Fortran の配列のスライスについては、「Fortran のための配列断面化構文」を参照してください。

Fortran 95 割り当て可能配列

次の例は、dbx で割り当て済み配列を処理する方法を示しています。


demo% f95 -g Alloc.f95
  demo% dbx a.out
  (dbx) list 1,99
      1   PROGRAM TestAllocate
      2   INTEGER n, status
      3   INTEGER, ALLOCATABLE :: buffer(:)
      4           PRINT *, 'Size?'
      5           READ *, n
      6           ALLOCATE( buffer(n), STAT=status )
      7           IF ( status /= 0 ) STOP 'cannot allocate buffer'
      8           buffer(n) = n
      9           PRINT *, buffer(n)
     10           DEALLOCATE( buffer, STAT=status)
     11   END
(dbx) stop at 6
 (2) stop at "alloc.f95":6
 (dbx) stop at 9
 (3) stop at "alloc.f95":9
 (dbx) run
実行中: a.out
 (プロセス id 10749)
  Size?
 1000
行番号 6 ではサイズは未知
 MAIN で停止しました 行番号 6 ファイル"alloc.f95"
     6           ALLOCATE( buffer(n), STAT=status )
 (dbx) whatis buffer
 integer*4 , allocatable::buffer(:)
 (dbx) next
 続き
 MAIN で停止しました 行番号 7 ファイル "alloc.f95"
     7           IF ( status /= 0 ) STOP 'cannot allocate buffer'
 (dbx) whatis buffer
 integer*4 buffer(1:1000)
行番号 9 ではサイズは既知
 (dbx) cont
MAIN で停止しました 行番号 9 ファイル "alloc.f95"
     9           PRINT *, buffer(n)
 (dbx) print n
バッファー (1000) に 1000 を格納
n = 1000
 (dbx) print buffer(n)
 buffer(n) = 1000

組み込み関数

dbx は、Fortran の組み込み関数 (SPARC プラットフォームおよび x86 プラットフォームのみ) を認識します。

dbx での組み込み関数を示します。


demo% cat ShowIntrinsic.f
    INTEGER i
    i = -2
    END
(dbx) stop in MAIN
(2) stop in MAIN
(dbx) run
実行中: shi
(プロセス id 18019)
MAIN で停止しました 行番号 2 ファイル "shi.f"
    2              i = -2
(dbx) whatis abs
Generic intrinsic function: "abs"
(dbx) print i
i = 0
(dbx) step
MAIN で停止しました 行番号 3 ファイル "shi.f"
    3              end
(dbx) print i
i = -2
(dbx) print abs(1)
abs(i) = 2
(dbx)

複合式

dbx は、Fortran 複合式も認識します。

dbx での複合式を示します。


demo% cat ShowComplex.f
   COMPLEX z
    z = ( 2.0, 3.0 )
    END
demo% f95 -g ShowComplex.f
demo% dbx a.out
(dbx) stop in MAIN
(dbx) run
実行中: a.out
(プロセス id 10953)
MAIN で停止しました 行番号 2 ファイル "ShowComplex.f"
    2       z = ( 2.0, 3.0 )
(dbx) whatis z
complex*8  z
(dbx) print z
z = (0.0,0.0)
(dbx) next
MAIN で停止しました 行番号 3 ファイル "ShowComplex.f"
    3       END
(dbx) print z
z = (2.0,3.0)
(dbx) print z+(1.0,1.0)
z+(1,1) = (3.0,4.0)
(dbx) quit
demo%

間隔式の表示

dbx で間隔式を表示するには、次のように入力します。


demo% cat ShowInterval.f95
   INTERVAL v
   v = [ 37.1, 38.6 ]
   END
demo% f95 -g -xia ShowInterval.f95
demo% dbx a.out
(dbx) stop in MAIN
(2) stop in MAIN
(dbx) run
実行中: a.out
(プロセス id 5217)
MAIN で停止しました 行番号 2 ファイル "ShowInterval.f95"
    2      v = [ 37.1, 38.6 ]
(dbx) whatis v
INTERVAL*16  v
(dbx) print v
v = [0.0,0.0]
(dbx) next
MAIN で停止しました 行番号 3 ファイル "ShowInterval.f95"
    3      END
(dbx) print v
v = [37.1,38.6]
(dbx) print v+[0.99,1.01]
v+[0.99,1.01] = [38.09,39.61]
(dbx) quit
demo%

注 –

間隔式は、SPARC プラットフォームで実行するよう、Solaris x86 SSE/SSE2 Pentium 4 互換プラットフォームでは -xarch={sse|sse2}、x64 プラットフォームでは -xarch=amd64 を付けてコンパイルされたプログラムに対してのみサポートされます。


論理演算子

dbx は、Fortran の論理演算子を配置し、出力することができます。

dbx での論理演算子を示します。


demo% cat ShowLogical.f
        LOGICAL a, b, y, z
        a = .true.
        b = .false.
        y = .true.
        z = .false.
        END
demo% f95 -g ShowLogical.f
demo% dbx a.out
(dbx) list 1,9
    1           LOGICAL a, b, y, z
    2           a = .true.
    3           b = .false.
    4           y = .true.
    5           z = .false.
    6           END
(dbx) stop at 5
(2) stop at "ShowLogical.f":5
(dbx) run
実行中: a.out
(プロセス id 15394)
MAIN で停止しました 行番号 5 ファイル "ShowLogical.f"
    5           z = .false.
(dbx) whatis y
logical*4 y
(dbx) print a .or. y
a.OR.y = true
(dbx) assign z = a .or. y
(dbx) print z
z = true
(dbx) quit
demo%

Fortran 95 構造型の表示

構造体、Fortran 95 構造型を dbx で表示できます。


demo% f95 -g DebStruc.f95
demo% dbx a.out
(dbx) list 1,99
    1   PROGRAM Struct ! Debug a Structure
    2      TYPE product
    3         INTEGER        id
    4         CHARACTER*16   name
    5         CHARACTER*8    model
    6         REAL           cost
    7 REAL price
    8      END TYPE product
    9
   10      TYPE(product) :: prod1
   11
   12      prod1%id = 82
   13      prod1%name = "Coffee Cup"
   14      prod1%model = "XL"
   15      prod1%cost = 24.0
   16      prod1%price = 104.0
   17      WRITE ( *, * ) prod1%name
   18   END
(dbx) stop at 17
(2) stop at "Struct.f95":17
(dbx) run
実行中: a.out
(プロセス id 12326)
MAIN で停止しました 行番号 17 ファイル "DebStruct.f95"
   17      WRITE ( *, * ) prod1%name
(dbx) whatis prod1
product prod1
(dbx) whatis -t product
type product
    integer*4 id
    character*16 name
    character*8 model
    real*4 cost
    real*4 price
end type product
(dbx) n
(dbx) print prod1
    prod1 = (
    id    = 82
    name = 'Coffee Cup'
    model = 'XL'
    cost = 24.0
    price = 104.0
)

Fortran 95 構造型へのポインタ

構造体、Fortran 95 構造型およびポインタを dbx で表示できます。


demo% f95 -o debstr -g DebStruc.f95
 demo% dbx debstr
 (dbx) stop in main
 (2) stop in main
 (dbx) list 1,99
     1   PROGRAM DebStruPtr! Debug structures & pointers
構造型を宣言します。
     2      TYPE product
     3         INTEGER        id
     4         CHARACTER*16   name
     5         CHARACTER*8    model
     6         REAL           cost
     7         REAL           price
     8      END TYPE product
     9
prod1 および prod2 ターゲットを宣言します。

    10      TYPE(product), TARGET :: prod1, prod2
curr および prior ポインタを宣言します。
    11      TYPE(product), POINTER :: curr, prior
    12
curr が prod2 を指すようにします。
    13      curr => prod2
prior が prod1 を指すようにします。
    14      prior => prod1
prior を初期化します。
    15      prior%id = 82
    16      prior%name = "Coffee Cup"
    17      prior%model = "XL"
    18      prior%cost = 24.0
    19      prior%price = 104.0
curr を prior に設定します。
    20      curr = prior
curr および prior から名前を出力します。
    21      WRITE ( *, * ) curr%name, " ", prior%name
    22   END PROGRAM DebStruPtr
 (dbx) stop at 21
 (1) stop at "DebStruc.f95":21
 (dbx) run
実行中: debstr
(プロセス id 10972)
MAIN で停止しました 行番号 21 ファイル "DebStruct.f95"
   21      WRITE ( *, * ) curr%name, " ", prior%name
(dbx) print prod1
 prod1 = (
    id = 82
    name = "Coffee Cup"
    model = "XL"
    cost = 24.0
    price = 104.0
)

前述において dbx は、構造型のすべての要素を表示します。

構造体を使用して、Fortran 95 構造型の項目について照会できます。


変数について尋ねます。
(dbx) whatis prod1
 product prod1
型 (-t) について尋ねます。
 (dbx) whatis -t product
 type product
    integer*4 id
    character*16 name
    character*8 model
    real cost
    real price
 end type product

ポインタを出力するには、次のようにします。


dbx は、アドレスであるポインタの内容を表示します。このアドレスは、実行のたび
に異なる場合があります。
(dbx) print prior
 prior = (
     id    = 82
     name = 'Coffee Cup'
     model = 'XL'
     cost = 24.0
     price = 104.0
 )