くれなゐの雑記

身の回りの人や自分が困っていたことを記事にしています

OpenFOAMでgdbを使ってデバッグっぽいことをしてみる

OpenFOAMでgdbを使いたい

Intro

Motivation

先日 OpenCAE勉強会でgdbが便利だという話だという話をしました。 詳細について教えてほしいということなのでブログの記事を書くことにしました。

この記事では,

  1. 概要
  2. デバッグオプションをいれたOpenFOAMのmake
  3. 実際にエラーの原因を探してみる
  4. 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流れの流速をU=100m/sとかにして計算を飛ばす

通常のエラーメッセージ
#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でエラーおちしてそうです

gdbを使ってみる

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];

とかかいてるのでゼロ除算とかで死んでるのかもしれないですね 見てみましょう

gdbを実際に使う

通常は, 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)

止まりました. snを押すと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 の発散する様子が見たいので, psiidisplayしてみましょう

  • 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