dbx コマンドによるデバッグ

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

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

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

Fortran のデバッグ

次のアドバイスと概要は、Fortran プログラムをデバッグするときに役立ちます。

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

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

大文字 (FORTRAN 77 の場合のみ)

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

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

ソースに LAST という変数がある場合、dbx では、print LAST コマンドおよび print last コマンドはいずれも要求どおりに動作します。

ソースに LAST という変数と last という変数がある場合、dbx では、print LAST コマンドは動作しますが、print last コマンドは動作しません。FORTRAN 77dbx はいずれも、LASTlast を要求どおりに区別します。


注 -

dbxenv case insensitive 環境属性を設定しても、dbx ではファイル名またはディレクトリ名について、大文字/小文字を常に区別します。


最適化プログラム

最適化プログラムをデバッグするには、次のことを行ってください。

デバッグの主プログラム

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 

dbx のサンプルセッション

以下の例では、上記のサンプルプログラム my_program を使用しています。

  1. -g フラグを付けてコンパイルとリンクを行います。次のように 、1 度にまたは 2 度に分けて実行できます。

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


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

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


     demo% f77 -c -g a1.f a2.f a3.f 
     demo% f77 -o my_program a1.o a2.o a3.o

  2. 実行可能ファイル my_program について dbx を起動します。


    demo% dbx my_program
    my_program の読み込み中
    ld.so.1 の読み込み中
    libF77.so.4 の読み込み中
    libM77.so.2 の読み込み中
    libsunmath.so.1 の読み込み中
    libm.so.1 の読み込み中
    libc.so.1 の読み込み中
    libdl.so.1 の読み込み中
    libc_psr.so.1 の読み込み中

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

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


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

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

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

    dbx 内でプログラムを実行します。


    (dbx) run
    実行中: my_program
    (プロセス id 23395)
    mb.so.1 の読み込み中
    wcwidth.so.1 の読み込み中
    MAIN で停止しました 行番号 3  ファイル "a1.f"
        3           call mkidentity( twobytwo, n )
    (dbx) 

    ブレークポイントに到達すると、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" は スコープ`my_program`a1.f`MAIN` に定義されていません
    dbx: 詳しくは 'help scope' をご覧ください
    (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) 

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

    nextstep コマンドを比較します。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% f77 -g -silent WhereSEGV.f
demo% ./a.out

*** TERMINATING  ./a.out
*** Received signal 11 SIGSEGV
Segmentation fault (core dumped)
demo% dbx a.out
a.out の読み込み中
rtld /usr/lib/ld.so.1 の読み込み中
libF77.so.3 の読み込み中
libsunmath.so.1 の読み込み中
libm.so.1 の読み込み中
libc.so.1 の読み込み中
libdl.so.1 の読み込み中
(dbx) run
実行中: a.out 
(プロセス id 2273)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
シグナル SEGV (フォルトのアドレスにマッピングしていません) 関数 MAIN 行番号 4  ファイル "WhereSEGV.f"
    4                   a(j) = (i * 10)
(dbx) 

例外の検出

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

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

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


demo%  cat wh.f
      call joe(r,s)
      print *, r/s
      end
      subroutine joe(r,s)
      r=12.
      s=0.
      return
      end
demo% f77 -g -o wh -ftrap=%all wh.f
wh.f:
 MAIN:
        joe:
demo% dbx wh
wh の読み込み中
ld.so.1 の読み込み中
 .....
(dbx) catch FPE
(dbx) run
実行中: wh
(プロセス id 23464)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
シグナル FPE (ゼロによる浮動小数点徐算) 関数 MAIN 行番号 2  ファイル "wh.f"
    2         print *, r/s
(dbx) 

呼び出しのトレース

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

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

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

実行が停止した時点から呼び出しシーケンスを表示します。


demo% f77 -silent -g ShowTrace.f   (注 1)
demo% a.out
*** TERMINATING a.out
*** Received signal 11 (SIGSEGV)   (注 2)
Segmentation Fault (core dumped)
demo% dbx a.out
a.out のシンボル情報を読み込んでいます。
...
(dbx) run
実行中: a.out
(process id 1089)
シグナル SEGV (フォルトのアドレスにマッピングしていません) 関数 calcb 行番号 23  ファイル "ShowTrace.f"  (注 3)
   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"  (注 4)
  [3] MAIN() 行番号 3 ファイル "ShowTrace.f"
(dbx)

注 1: 順序が逆になっていることに注意してください。

注 2: MAINcalc を呼び出し、calccalcb を呼び出しています。

注 3: 23 行目で実行が停止しました。

注 4: calcMAIN の 3 行目で呼び出さ れました。

配列の操作

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

Arraysdbx.f


demo% dbx a.out
a.out の読み込み中
ld.so.1 の読み込み中
 ......
(dbx) list
    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
(2) stop at "Arraysdbx.f":7
(dbx) run
実行中: a.out
(プロセス id 23495)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
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 90 割り当て可能配列

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


demo% f90 -g Alloc.f90
demo% dbx a.out
a.out の読み込み中
ld.so.1 の読み込み中
  ......   (注 1)
(dbx) list 1,$
    1   PROGRAM TestAlocate
    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.f90":6
(dbx) stop at 9
(3) stop at "Alloc.f90":9
(dbx) run
実行中: a.out
(プロセス id 23512)
 Size?
1000
MAIN で停止しました 行番号 6  ファイル "Alloc.f90"  (注 2)
    6           ALLOCATE (buffer(n), STAT=status)
(dbx) whatis buffer
INTEGER*4 , allocatable::buffer(:)
(dbx) next
MAIN で停止しました 行番号 7  ファイル "Alloc.f90"
    7           IF (status /= 0) STOP `cannot allocate buffer'
(dbx) whatis buffer
INTEGER*4  buffer(1:1000)
(dbx) cont
MAIN で停止しました 行番号 9  ファイル "Alloc.f90"  (注 3)
    9           PRINT *,buffer(n)
(dbx) print n
n = 1000
(dbx) print buffer(n)
buffer(n) = 1000   (注 4)

注 1: Alloc.f90

注 2: 行番号 6 ではサイズは未知

注 3: 行番号 9 ではサイズは未知

注 4: buffer (1000)

配列のスライス

Fortran 配列スライスの構文は次のとおりです。


print arr-exp(first-exp
:last-exp:stride-exp
)

表 17-1 配列スライスの変数

変数 

説明 

デフォルト 

arr-exp

1 つの配列型に評価される式 

 

first-exp

出力される最初の要素 

配列の下限 

last-exp

出力される最後の要素 

配列の上限 

stride-exp

刻み幅 

行と列を指定します。


demo% f77 -g -silent ShoSli.f
demo% dbx a.out
a.out の読み込み中
ld.so.1 の読み込み中
  ......
(dbx) list 1,12
    1                integer*4  A(3,4), col, row
    2                do row = 1,3
    3                   do col = 1,4
    4                        A(row,col) = (row*10) + col
    5                   end do
    6                end do
    7                do row = 1, 3
    8                write(*,'(4I3)') (A(row,col),col=1,4)
    9                end do
   10                end
(dbx) stop at 7
(2) stop at "ShoSli.f":7
(dbx) run
実行中: a.out
(プロセス id 23521)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
MAIN で停止しました 行番号 7  ファイル "ShoSli.f"
    7                           do row = 1, 3
(dbx)

行 3 を出力


(dbx) print a(3:3,1:4)
a(3:3, 1:4) = 
       (3,1)   31 
       (3,2)   32 
       (3,3)   33 
       (3,4)   34
(dbx)

列 4 を出力


(dbx) print a(1:3,4:4)
a(3:3, 1:4) = 
       (1,4)   14 
       (2,4)   24 
       (3,4)   34
(dbx)

組み込み関数

dbx は、Fortran の組み込み関数を認識します。

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


(dbx) list 1,$
    1         INTEGER i
    2         i = -2
    3         END
(dbx) stop in MAIN
(2) stop in MAIN
(dbx) run
実行中: a.out
(プロセス id 23536)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
MAIN で停止しました 行番号 2  ファイル "ShowIntrinsic.f"
    2         i = -2
(dbx) whatis abs
Generic intrinsic function: "abs"
(dbx) print i
i = 0
(dbx) step
MAIN で停止しました 行番号 3  ファイル "ShowIntrinsic.f"
    3         END
(dbx) print i
i = -2
(dbx) print abs(i)
abs(i) = 2
(dbx) quit
demo%

複合式

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

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


demo% cat ShowComplex.f
      COMPLEX z
      z = (2.0, 3.0)
      END
demo% f77 -g -silent ShowComplex.f
demo% dbx a.out
a.out の読み込み中
ld.so.1 の読み込み中
  ......
(dbx) stop in MAIN
(2) stop in MAIN
(dbx) run
実行中: a.out
(プロセス id 23567)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
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 

論理演算子

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

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


demo% cat ShowLogical1.f
      LOGICAL a, b, y, z
      a = .true.
      b = .false.
      y = .true.
      z = .false.
      END
demo% f77 -g -silent ShowLogical1.f
demo% dbx a.out
a.out の読み込み中
ld.so.1 の読み込み中
  ......
(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 "ShowLogical1.f":5
(dbx) run
実行中: a.out
(プロセス id 23587)
mb.so.1 の読み込み中
wcwidth.so.1 の読み込み中
MAIN で停止しました 行番号 5  ファイル "ShowLogical1.f"
    5         z = .false.
(dbx) whatis y
logical*4  y
(dbx) print a .or. y
a.OR.y = true
(dbx) print z
z = false
(dbx) quit
demo%

Fortran 90 構造型の表示

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


demo% f90 -g DebStruct.f90
demo% dbx a.out
a.out の読み込み中
ld.so.1 の読み込み中
  ......
(dbx) list 1,$
    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 = "Coffe  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 "DebStruct.f90":17
(dbx) run
実行中: a.out
(プロセス id 23677)
MAIN で停止しました 行番号 17  ファイル "DebStruct.f90"
   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
 Coffe  Cup
MAIN で停止しました 行番号 18  ファイル "DebStruct.f90"
   18   END
(dbx) print prod1
prod1 = (
    id    = 82
    name  = "Coffe  Cup      "
    model = "XL      "
    cost  = 24.0
    price = 104.0
)

Fortran 90 構造型へのポインタ

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

DebStruc.f90


demo% f90 -o debstr -g DebStruct.f90
demo% dbx debstr
debstr の読み込み中
ld.so.1 の読み込み中
 ......
(dbx) list 1,$    (注 1)
     1 Program DebStruPtr  ! Debug a Structure & pointer
    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), TARGET :: prod1, prod2   (注 2)
   11       TYPE(product), POINTER :: curr, prior   (注 3)
   12
   13       curr => prod2   (注 4)
   14       prior => prod1  (注 5)
   15       prior%id = 82   (注 6)
   16       prior%name = "Coffe  Cup"
   17       prior%model = "XL"
   18       prior%cost = 24.0
   19       prior%price = 104.0
   20       curr = prior    (注 7)
   21       WRITE (*, *) curr%name, " ", prior%name  (注 8)
   22   END PROGRAM DebStruPtr
   23
(dbx) stop at 21
(2) stop at "DebStruct.f90":21
(dbx) run
実行中: debstr
(dbx) run
実行中: debstr
(プロセス id 23726)
MAIN で停止しました 行番号 21  ファイル "DebStruct.f90"
   21       WRITE (*, *) curr%name, " ", prior%name
(dbx) print prod1
prod1 = (
    id    = 82
    name  = "Coffe  Cup      "
    model = "XL      "
    cost  = 24.0
    price = 104.0
)

注 1: 構造型を宣言します。

注 2: prod1 および prod2 ターゲットを宣言します。(#10)

注 3: curr および prior ポインタを宣言します。(#11)

注 4: currprod2 を指すようにします。(#13)

注 5: priorprod1 を指すようにします。(#14

注 6: prior を初期化します。(#15

注 7: currprior に設定します。(#20)

注 8: curr および prior から名前を印刷します。(#21)

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

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


(dbx) whatis prod1  (注 1)
product  prod1
(dbx) whatis -t product   (注 2)
type product
    INTEGER*4 id
    character*16 name
    character*8 model
    REAL*4 cost
    REAL*4 price
end type product

注 1: 変数について尋ねます。

注 2: 型 (-t) について尋ねます。

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


(dbx) print prior  (注 1)
prior = (
    id    = 82
    name  = "Coffe  Cup      "
    model = "XL      "
    cost  = 24.0
    price = 104.0
)

注 1: dbx は、アドレスであるポインタの内容を表示します。このアドレスは、実行のたびに異 なる場合があります。