OpenFOAMでgdbを使いたい
Intro
Motivation
先日 OpenCAE勉強会でgdbが便利だという話だという話をしました。
詳細について教えてほしいということなのでブログの記事を書くことにしました。
この記事では,
- 概要
- デバッグオプションをいれたOpenFOAMのmake
- 実際にエラーの原因を探してみる
- gdbOF(WIP まだ描いてないよ)
の4つを紹介していきます
結構長いですが, 冷静に見ると内容薄いのでささっと読めると思います(出力結果おおいし)
Target
- OpenFOAMのcavity流れのtutorialが回せるくらいの人
- gdbが何かわからない人
- OpenFOAMでgdbを使えることがしらなかった人
環境
ubutnu16.04, OpenFOAM-2.4.0
どんなことができるの
OpenFOAMで適当なものを実行してみる.
デバッグをしたいので, なにか計算が落ちるケースがほしい.
今回は OpenFOAM/OpenFOAM-2.4.0/tutorials/incompressible/icoFoam/cavity
のcavity流れの流速をとかにして計算を飛ばす
通常のエラーメッセージ
#0 Foam::error::printStack(Foam::Ostream&) at ??:?
#1 Foam::sigFpe::sigHandler(int) at ??:?
#2 ? in "/lib/x86_64-linux-gnu/libc.so.6"
#3 Foam::symGaussSeidelSmoother::smooth(Foam::word const&, Foam::Field<double>&, Foam::lduMatrix const&, Foam::Field<double> const&, Foam::FieldField<Foam::Field, double> const&, Foam::UPtrList<Foam::lduInterfaceField const> const&, unsigned char, int) at ??:?
#4 Foam::symGaussSeidelSmoother::smooth(Foam::Field<double>&, Foam::Field<double> const&, unsigned char, int) const at ??:?
#5 Foam::smoothSolver::solve(Foam::Field<double>&, Foam::Field<double> const&, unsigned char) const at ??:?
#6 ? at ??:?
#7 ? at ??:?
#8 ? at ??:?
#9 ? at ??:?
#10 __libc_start_main in "/lib/x86_64-linux-gnu/libc.so.6"
#11 ? at ??:?
#0 Foam::error::printStack(Foam::Ostream&) at ~/OpenFOAM/OpenFOAM-2.4.0/src/OSspecific/POSIX/printStack.C:219
#1 Foam::sigFpe::sigHandler(int) at ~/OpenFOAM/OpenFOAM-2.4.0/src/OSspecific/POSIX/signals/sigFpe.C:108
#2 ? in "/lib/x86_64-linux-gnu/libc.so.6"
#3 Foam::symGaussSeidelSmoother::smooth(Foam::word const&, Foam::Field<double>&, Foam::lduMatrix const&, Foam::Field<double> const&, Foam::FieldField<Foam::Field, double> const&, Foam::UPtrList<Foam::lduInterfaceField const> const&, unsigned char, int) at ~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
#4 Foam::symGaussSeidelSmoother::smooth(Foam::Field<double>&, Foam::Field<double> const&, unsigned char, int) const at ~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:237
#5 Foam::smoothSolver::solve(Foam::Field<double>&, Foam::Field<double> const&, unsigned char) const at ~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/solvers/smoothSolver/smoothSolver.C:169良いかと思います
#6 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrixSolve.C:193 (discriminator 9)
#7 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrixSolve.C:82
#8 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrixSolve.C:331
#9 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrix.C:1390
#10 ? at ~/OpenFOAM/OpenFOAM-2.4.0/applications/solvers/incompressible/icoFoam/icoFoam.C:63 (discriminator 7)
#11 __libc_start_main in "/lib/x86_64-linux-gnu/libc.so.6"
#12 ? at ??:?
パット見で両者に違いがわかります.
- 通常のエラーメッセージ: どこでエラーが起きてるのかよくわからない. ギリギリメソッドはわかるけどどのファイル名かはわからない.
- デバッグオプション込: どこのファイルで呼ばれたのかわかる. 通常のエラーメッセージでは
#6
くらいから完全に?になってるが, デバッグオプションでは, icoFoam.C
まで遡ることができる.
しかし, デバッグオプション込では実行速度が遅くなる欠点があります. 体感実行時間2倍は硬いです.
なので基本的にはデバッグオプション無しで実行し, エラーが起きた時にデバッグオプションをつけてコンパイル, 実行が良いかと思います.
また, gdb
を使えば
- プログラムを途中で止めてその状態で変数等確認
- 1行ずつ実行
- backtrace(今いる関数はどこから呼ばれたのかなどを表示)
などができます.
使ってみる
Debugオプションを入れた状態でのmake
OpenFoam-x.x.x/etc/bashrc
の
#- Optimised, debug, profiling:
# WM_COMPILE_OPTION = Opt | Debug | Prof
export WM_COMPILE_OPTION=Opt
を(Debug
という文字はここにしかでてこないので, Debug
という文字列で検索したらここがすぐ出てくると思います.)
#- Optimised, debug, profiling:
# WM_COMPILE_OPTION = Opt | Debug | Prof
export WM_COMPILE_OPTION=Debug
に変更します.(Opt->Debug)
この状態で, bashrcを読みなおします
bashrcの読みなおしは,
source etc/bashrc
などで行います.
そのあと
cd ~/OpenFOAM/OpenFOAM-x.x.x
./Allwmake
などで再コンパイルするとDebug用実行ファイルが生成されます.
例えば, OpenFOAM-2.4.0/src/finiteVolume/Make
などのファイルを確認すると linux64GccDPDebug
みたいなDebugっぽい感じのディレクトリが生成されます.
OptとDebugでフォルダがわかれているので, bashrcのオプションをoptに戻すとまた全部コンパイルせずに済みますね.
その後適当に実行時エラーを吐くようになコードを書いたり, ケースを作ったりしたら先ほど述べたようなエラーメッセージが出ます.
DebugとOptを切り替える
bashrc
をDebugにしたり, Optにしたり, 変更があった場合はmakeをすればOKです.
エラー落ちさせてみる
コードをいじってもいいですが, めんどくさいのでエラー落ちするようなケースを作ります.
cp ~/OpenFOAM/OpenFOAM-2.4.0/tutorials/incompressible/icoFoam/cavity ~/Desktop # 一応避難
cd ~/Desktop/cavity
流速を100m/sなどにして計算を発散させる
以下のようなメッセージが出る
#0 Foam::error::printStack(Foam::Ostream&) at ~/OpenFOAM/OpenFOAM-2.4.0/src/OSspecific/POSIX/printStack.C:219
#1 Foam::sigFpe::sigHandler(int) at ~/OpenFOAM/OpenFOAM-2.4.0/src/OSspecific/POSIX/signals/sigFpe.C:108
#2 ? in "/lib/x86_64-linux-gnu/libc.so.6"
#3 Foam::symGaussSeidelSmoother::smooth(Foam::word const&, Foam::Field<double>&, Foam::lduMatrix const&, Foam::Field<double> const&, Foam::FieldField<Foam::Field, double> const&, Foam::UPtrList<Foam::lduInterfaceField const> const&, unsigned char, int) at ~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
#4 Foam::symGaussSeidelSmoother::smooth(Foam::Field<double>&, Foam::Field<double> const&, unsigned char, int) const at ~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:237
#5 Foam::smoothSolver::solve(Foam::Field<double>&, Foam::Field<double> const&, unsigned char) const at ~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/solvers/smoothSolver/smoothSolver.C:169良いかと思います
#6 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrixSolve.C:193 (discriminator 9)
#7 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrixSolve.C:82
#8 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrixSolve.C:331
#9 ? at ~/OpenFOAM/OpenFOAM-2.4.0/src/finiteVolume/lnInclude/fvMatrix.C:1390
#10 ? at ~/OpenFOAM/OpenFOAM-2.4.0/applications/solvers/incompressible/icoFoam/icoFoam.C:63 (discriminator 7)
#11 __libc_start_main in "/lib/x86_64-linux-gnu/libc.so.6"
#12 ? at ??:?
sigFpeより上はエラーをお知らせするだけなので, #3
でエラーおちしてそうです
macだとlldbになります. 私はlldbは使ったことがないのでよくわかりませんが, 多分同様にやればOKです.
gdbのコマンド一覧
説明がめんどくさいのでこちらの記事に任せます.
gdb の使い方・デバッグ方法まとめ
エラー落ちの原因を探る
当然流速が早すぎるからです ソースコードではどこで落ちているのでしょうか
エラーメッセージを見てみると,
~/OpenFOAM/OpenFOAM-2.4.0/src/OpenFOAM/matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C
の167行目
Foam::symGaussSeidelSmoother::smooth(...)
メソッド内で死んでいるっぽいです.
ソースコードはこれみたいですね
github.com
167行目は
psii /= diagPtr[celli];
とかかいてるのでゼロ除算とかで死んでるのかもしれないですね 見てみましょう
通常は, icoFoam
と打つところですが, 今回は少しコマンドを増やして
gdb icoFoam
と実行します.
(gdb)
みたいなのが出るので, run
と打ち込んでやりましょう
(gdb) run
出力結果
Starting program: /home/kurenaif/OpenFOAM/OpenFOAM-2.4.0/platforms/linux64GccDPDebug/bin/icoFoam
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
(gdb) /*---------------------------------------------------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: 2.4.0 |
| \\ / A nd | Web: www.OpenFOAM.org |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
Build : 2.4.0-bf7c62d394a7
Exec : /home/kurenaif/OpenFOAM/OpenFOAM-2.4.0/platforms/linux64GccDPDebug/bin/icoFoam
Date : Jan 29 2017
Time : 01:55:16
Host : "kurenaif-server"
PID : 22737
Case : /home/kurenaif/Desktop/cavity
nProcs : 1
sigFpe : Enabling floating point exception trapping (FOAM_SIGFPE).
fileModificationChecking : Monitoring run-time modified files using timeStampMaster
allowSystemOperations : Allowing user-supplied system call operations
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Create time
Create mesh for time = 0
Reading transportProperties
Reading field p
Reading field U
Reading/calculating face flux field phi
Starting time loop
Time = 0.005
Courant Number mean: 0 max: 0
smoothSolver: Solving for Ux, Initial residual = 1, Final residual = 8.90511e-06, No Iterations 19
smoothSolver: Solving for Uy, Initial residual = 0, Final residual = 0, No Iterations 0
DICPCG: Solving for p, Initial residual = 1, Final residual = 7.55423e-07, No Iterations 35
time step continuity errors : sum local = 5.03808e-07, global = -4.99749e-18, cumulative = -4.99749e-18
DICPCG: Solving for p, Initial residual = 0.523588, Final residual = 9.72371e-07, No Iterations 34
time step continuity errors : sum local = 1.07766e-06, global = -2.2097e-17, cumulative = -2.70945e-17
ExecutionTime = 0.02 s ClockTime = 0 s
Time = 0.01
Courant Number mean: 9.76803 max: 58.5723
Program received signal SIGFPE, Arithmetic exception.
0x00007ffff59e5d14 in Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
167行目でArithmetic exception
ですね ゼロ除算だとかオーバーフローだとかででるエラーメッセージです.
例外をはいただけでまだプログラムは終了してませんこのまま 変数を覗いてみましょう. p
コマンドで変数の中身を見ることができます.
(gdb) p psii
出力結果
$1 = 1.7457973770058812e+305
あ、やっぱり発散してますね除算する値はどうでしょうか
(gdb) p diagPtr[celli]
value has been optimized out
困りました。 見れません “このエラーは通常コンパイラの最適化によって変数が無いよ” みたいなエラーです 元をたどりましょう 85行目にこんなのがありました
register const scalar* const __restrict__ diagPtr = matrix_.diag().begin();
どうやらdiagPtrはmatrix_.diag().begin()
と同じ意味みたいです. 同じ意味なのでコンパイラが省略しちゃったんですね
ではdiagPtrを見る代わりに, matrix_.diag().begin()
で確認してみましょう
(gdb) p matrix_.diag().begin()[celli]
$2 = 0.00055000001416059699
無事見れましたね どうやら0除算ではなかったみたいです. ただの値の発散ですね
次に値が発散していく様子を観察してみましょう いちどgdbを閉じて (Ctrl+d) 再度起動します
gdb icoFoam
とりあえず問題の symGaussSeidelSmoother.Cの167行目 で止めてみましょう
途中のやつはy
でOKです
(gdb) b symGaussSeidelSmoother.C:167
No source file named symGaussSeidelSmoother.C.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (symGaussSeidelSmoother.C:167) pending.
これでrunをしてみます
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
(gdb)
止まりました. s
やn
を押すと1行次に進みたいと思います.
- 関数があったときに, 関数の中身に飛んでいくほうが
s
- 関数があってもそれを1行とみなして中身をスルーするのが
n
です。
gdbは, 前のコマンドと同じ入力をするときに, 何も入力せずにEnterを押すと繰り返しができます.
n
を押してみて, Enterを連打してみましょう
167 psii /= diagPtr[celli];
(gdb) n
170 for (register label facei=fStart; facei<fEnd; facei++)
(gdb)
172 bPrimePtr[uPtr[facei]] -= lowerPtr[facei]*psii;
(gdb)
170 for (register label facei=fStart; facei<fEnd; facei++)
(gdb)
172 bPrimePtr[uPtr[facei]] -= lowerPtr[facei]*psii;
(gdb)
170 for (register label facei=fStart; facei<fEnd; facei++)
(gdb)
175 psiPtr[celli] = psii;
(gdb)
151 for (register label celli=0; celli<nCells; celli++)
(gdb)
154 fStart = fEnd;
(gdb)
155 fEnd = ownStartPtr[celli + 1];
forループが観測できますね psii
の発散する様子が見たいので, psii
をdisplay
してみましょう
display
: 値を監視して変更等があったときに, その都度printしてくれる
(gdb) display psii
1: psii = 0
はい 監視対象に入りました
n
コマンドではforに捕まった時に面倒です. breakpoint 1
まで飛ばしてしまってよいでしょう
そういうときはc
を使います
(gdb) c
Continuing.
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
1: psii = 0
はい breakpointまで飛びました. ついでに監視対象のpsii
も見れますね
続けて見ていきましょう Enter
連打してc
を何度も回してみます
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
1: psii = 0
(gdb)
Continuing.
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
1: psii = 0
(gdb)
Continuing.
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
1: psii = 0
(gdb)
Continuing.
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
1: psii = 0
(gdb)
Continuing.
Breakpoint 1, Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:167
167 psii /= diagPtr[celli];
1: psii = 0
うーんなかなか発散してくれませんね こういう時はウォッチポイントを使います.
psii
が1以上になった時に止まるようにしてみましょう
breakpointで止まるようになるのが邪魔なので消します.
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) watch psii >= 1
Watchpoint 2: psii >= 1
消えました. ついでにwatchpointも増えました continue
してみましょう
(gdb) c
Continuing.
Watchpoint 2: psii >= 1
Old value = false
New value = true
Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:170
170 for (register label facei=fStart; facei<fEnd; facei++)
1: psii = 30.769230769230489
おっ psii
が30になりました これは発散しそうです
では, 次はpsii
が変化した時に止まるようにしてみましょう
(gdb) watch psii
Watchpoint 3: psii
(gdb) c
Continuing.
Watchpoint 3: psii
Old value = 30.769230769230489
New value = 0.023076923076922929
Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:161
161 for (register label facei=fStart; facei<fEnd; facei++)
1: psii = 0.023076923076922929
(gdb)
Continuing.
Watchpoint 3: psii
Old value = 0.023076923076922929
New value = 41.958041958041726
Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:170
170 for (register label facei=fStart; facei<fEnd; facei++)
1: psii = 41.958041958041726
(gdb)
Continuing.
### Watchpoint 3: psii
Old value = 41.958041958041726
New value = 0.024195804195803933
Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:161
161 for (register label facei=fStart; facei<fEnd; facei++)
1: psii = 0.024195804195803933
(gdb)
Continuing.
Watchpoint 3: psii
Old value = 0.024195804195803933
New value = 43.9923712650982
Foam::symGaussSeidelSmoother::smooth (fieldName_=..., psi=..., matrix_=..., source=..., interfaceBouCoeffs_=..., interfaces_=..., cmpt=0 '\000', nSweeps=1) at matrices/lduMatrix/smoothers/symGaussSeidel/symGaussSeidelSmoother.C:170
170 for (register label facei=fStart; facei<fEnd; facei++)
1: psii = 43.9923712650982
徐々に発散してますね 今回はこれくらいにしておきましょう
backtrace
書き忘れました こんな感じで関数の呼び出し元を見てくれます
(gdb) bt
#0 0x00007ffff5ed496d in Foam::symGaussSeidelSmoother::smooth(Foam::word const&, Foam::Field<double>&, Foam::lduMatrix const&, Foam::Field<double> const&, Foam::FieldField<Foam::Field, double> const&, Foam::UPtrList<Foam::lduInterfaceField const> const&, unsigned char, int) ()
from /home/kurenaif/OpenFOAM/OpenFOAM-2.4.0/platforms/linux64GccDPOpt/lib/libOpenFOAM.so
#1 0x00007ffff5ed4e88 in Foam::symGaussSeidelSmoother::smooth(Foam::Field<double>&, Foam::Field<double> const&, unsigned char, int) const ()
from /home/kurenaif/OpenFOAM/OpenFOAM-2.4.0/platforms/linux64GccDPOpt/lib/libOpenFOAM.so
#2 0x00007ffff5ecc07a in Foam::smoothSolver::solve(Foam::Field<double>&, Foam::Field<double> const&, unsigned char) const ()
from /home/kurenaif/OpenFOAM/OpenFOAM-2.4.0/platforms/linux64GccDPOpt/lib/libOpenFOAM.so
#3 0x0000000000437d9f in Foam::fvMatrix<Foam::Vector<double> >::solveSegregated(Foam::dictionary const&) ()
#4 0x0000000000457bc9 in Foam::fvMatrix<Foam::Vector<double> >::solve(Foam::dictionary const&) ()
#5 0x0000000000457e24 in Foam::fvMatrix<Foam::Vector<double> >::solve() ()
#6 0x0000000000419e59 in main ()
まとめ
今回はエラーを起こしてそのエラーの発生箇所の特定, 及び原因の一部を見てみました.
お役に立てれば幸いです
References
gdb の使い方・デバッグ方法まとめ
(pp.44~)
www.slideshare.net