Wizard In The Market
システムトレードの魔術師

*

The art of debugging with GDB, DDD, and Eclipse の読書メモ

公開日: : cpp , , ,

GDBの勉強がしたくて、下の本を原著で読んでみました。10年前の本だけど全然現役でした。

和訳版はこちら。

目次

Chapter 1

1.3 Principle of Confirmation

この本のでよく出てくる重要なコンセプト。一つ一つ自分がそうだと思うことがそのとおりになっているかを確認する。もし、それがそのとおりになっていなければ、それは問題の解決する手がかりになる。

1.3.3 Other debugging principles

  • 小さく始めよ
  • トップダウンアプローチをしよう
    • 関数やモジュールベースでの問題の切り分けをしたほうが良いという試み
    • 関数の呼び出しでブレイクポイントを置くのが最初は良い。
  • segmentation fault にはデバッガで対処しよう
    • GDBを使えば、seg failt の場所でデバッガを停止させることができる。
    • core file が seg fault で作成されたら、そのエラーをGDBでレジュームできる。
  • 無限ループがあれば keyboard intruption で場所を特定できる
  • バイナリサーチしよう

1.4 Text-Based vs. GUI-Based Debugging Tools

CUIのデバッガとGUIのデバッガの比較を色々。DDD や Eclipse がバックエンドで GDB を使っているという話は知っておくべき。DDD の場合は GDB 用のコンソールウィンドウがあるので、GDB のコマンドをそのまま叩ける。

1.4.2 Compromises

GDB をそのまま使うのではなく、CUI でダッシュボードを持った TUI Mode というのが存在する。

上記のように-tuiオプション付きで実行すれば TUI モードで GDB が実行できる。他にも CGDB ってのがあるらしいけど、結構古い本なので知らなくて良さそう。

Github で今だったらどんなツールがあるんだろうと思って調べてみると、gdbguiというツールがあり、衝撃を受けた。ブラウザ上で操作できる TUI モードといった感じだが、もっとリッチな環境が整っている。これからの GDB のデバッグはこれで良いのでは。

1.5 Main Debugger Operations

デバッガの主な機能の説明。別に GDB に限ったことではないので、デバッグを日常的に使っている人なら多分もう知ってる。ただ、GDB のコマンドの説明があるので、それは知っておくべき。

Breakpoints

GDB では break コマンド

info breakで breakpoint の一覧が参照できる。

Single-stepping

step コマンド

Resume operation

continue

Temporary breakpoints

一回到達すると消えるブレイクポイント。コマンドは tbreak

他にもGDBは untilfinish コマンドなどがあるが、それは Chapter 2 で扱う。

1.5.2 Inspecting Variables

print コマンドで変数や構造体などの内容を確認できる。DDDとかなら変数をマウスオーバーするだけで表示される。

1.5.3 Issuing an “All Points Bulletin” for Changes to a Variable

Watchpoint の説明。今まで私はあまり使ってこなかったが、CPUのブレイクポイントでいうところのメモリブレイクポイント。この本での説明は、ブレイクポイントと変数の調査を組み合わせたもの。変数が変化したときに処理を止めてくれる。

Watchpoint はメモリブレイクポイントなので、グローバル変数などはメモリアドレスが不変なのでブレイクポイントが維持されるが、スコープから外れてしまったローカル変数は次回のメモリアドレスが変わってしまうのでブレイクポイントが削除されてしまう。なので、ローカル変数を watch したいときはこの点を留意すること。

1.5.4 Moving Up and Down the Call Stack

frame <num>, downup でコールスタックのフレームを移動できる。updown は使っていたが、frameコマンドは知らなかった。

frame コマンドは 0 がデバッグポイントの現在のフレーム。1だと一つ上のフレームということになる。

現在のスタックの情報がどうなっているかは backtrace コマンドで確認できる。

1.6 Online Help

help <command> で説明が見れる。

1.7 Introductory Debugging Session

デバッグの例。

gcc -g オプション

デバッグオプション。変数やコードの行の情報が入った symbol table を保存してくれる。調べてみると、-g3でデバックレベル3(最高のデバッグ情報が入った状態)のコンパイルオプションなどがある。GDB用のコンパイルオプションとして-ggdb3というのもあった。

TUI モードへの切り替え

GDB セッション中にctrx-X-A コマンドでTUIモードへの切り替えができる。

print した変数の index

print したら $ でIDが振られる。$1は初めの値。$2は2回めに調べた値。value historyで今まで調べたもののリストが表示できる。

condition コマンド

condition <break point index> でそのブレイクポイントの条件(Conditional breakpoint)を指定することができる。

break <b-index> if <condigion> でも可能。

同じ引数で run したいとき

一度引数を設定した上で run をしていれば、runを呼び直すだけで同一引数で実行してくれる。

デバッグ中は GDB を再起動する必要はない

GDB は再コンパイルしたかどうかの確認を run するときに常に行ってくれて、その場合はシンボルを再読込してくれる。また、セッションを保持しれいればブレイクポイントなども使いまわせるので、この本では基本的にGDBのセッションは終了しないまま進めることを勧めている。

1.8 Use of Startup Files

.gdbinitというファイルを作れば、GDB の立ち上げ時に読み込んでくれる。読み込まれるポイントは以下の通り。

  • Home Directory
    • 一般的な情報を保存
  • Current Directory
    • そのプロジェクト特有の情報(ブレイクポイントなど)を保存

gdb -command=initfile <program> のように -command オプションで startup file を指定できる。

Chapter 2 Stopping to take a look around

GDBの処理の基礎が載っている。このチャプターが一番勉強になった。

2.1 Mechanisms for Pause

Pause させるための3つの方法

  • Breakpoint
  • Watchpoint
  • Catchpoint

2.2 Overview of Breakpoints

Breakpoint は

  • コードのアドレス
  • ラインナンバー
  • 関数の入り口

に置くことができる。

2.3 Keeping Track of Breakpoints

2.3.1 Breakpoint Lists in GDB

info breakpoints でブレイクポイントのリストが取れる。

delete <b-index> <b-index> ... コマンドでブレイクポイントを削除できる。

2.4 Setting Breakpoints

2.4.1 Setting Breakpoints in GDB

ブレイクポイントの置き方。

  • break <function-name>
  • break <line-num>
  • break <filename>:<line-num>
  • break <filename>:<function-name>

現在のシンボルに存在しない関数ブレイクポイントを指定した場合、これから読み込むライブラリの関数にブレイクポイントを置くか確認プロンプトが表示される。 yes の場合、読み込まれた関数に自動でブレイクポイントを追加してくれるようになる。場合によっては、そのプロンプトが表示されないことがあるので、以下のコマンドでプロンプトの内容を自動で yes にできる。( default は auto )

他のブレイクポイントの引数

  • -<offset>
    • +4で指定の場所から4つ上
    • -4で指定の場所から4つ下にブレイクポイントをおく
  • *<address> で指定のメモリアドレスにブレイクポイント

他にも、ブレイクポイントを設定した位置と実際の部位レイクポイントが少しずれることがあることの説明があった。このあたりはCのデバッガを使ったことある人なら理解してるから言わなくても分かるはず。

他のブレイクポイントコマンド

  • hbreak Hardware assisted breakpoint
    • メモリの物理的な場所にブレイクポイントをおける。多分、バーチャルメモリアドレスじゃないという意味。
  • rbreak grep-style で関数にブレイクポイントをおける

デバッグの際は最適化オプションを無効化したほうがいい

最適化オプション(-O<Optimization level>など)はGCCが使っていない変数などを削除してしまう。デバッグ時は-O0にしよう。

Catchpoint

Catchpoint は throw や signals, folk, loading library のときをトリガーにできる。

2.5 Extended GDB Example

ソースファイルが複数にまたがったときのデバッグの例。

-g<num>オプション

-g3でデバッグレベル3でコンパイルできる。レベル3だと、マクロの情報もシンボルテーブルに格納してくれる。-gだけだとデフォルトの2が適応される。

デバッグ中の対象のファイル

stack の場所によって対象ファイルが変わる。break <line-num>とかの場合においてデバッグポイントが置かれるファイルの場所が変わるということ。listコマンドで対象ファイルを切り替えることができる。

2.6 Persistence of Breakpoints

GDB のデバッガをもう一度 run してもブレイクポイントが保持される。ただ、変更した場合もブレイクポイントの列番号は変わらない。

ブレイクポイントは .gdbinit ファイルでも設定できる。現在設定したブレイクポイントを保存する方法は書いてはなかった。

2.7 Deleting and Disabling Breakpoints

2.7.1 Deleting Breakpoints in GDB

  • delete <breakpoint-index>
    • 該当ブレイクポイントを削除
  • delete
    • 全消去
  • clear
    • 着いたばかりのブレイクポイントを削除
  • clear ....
    • breakと同じ方法でブレイクポイントを削除

2.7.2 Disabling Breakpoints in GDB

disable <breakpoint-id> コマンドでブレイクポイントを無効化できる。

enable <breakpoint-id>コマンドで有効化。

enable once <breakpoint-id>で一度だけ有効化できる。

2.8 More on Viewing Breakpoint Attributes

info breakpoints の省略形は i b

表示 説明
Num ブレイクポイントのインデックス
Type ブレイクポイントの種類
Disp keep: 次に到着しても変更はない
del: 次に到着したら削除される
dis:次に到着したら無効化される
Enb 有効・無効の状態
Address アドレス
What ブレイクポイントの場所

2.9 Resuming Execution

再開するコマンドは3種類

  • Single Stepping
    • step
    • next
  • continue
  • conditional stepping
    • 指定した条件を満たすまで実行する
    • finish
    • until

2.9.1.3 Resuming Program Execution with finish

finishコマンドの説明。関数を呼び出したところに戻るまで処理を全部する。

2.9.1.4 Resuming Program Execution with until

untilコマンドの説明。for や while 分などのループが終わるまで実行する。

想定した動きと違うとき

untilは機械語でのアドレスが現在のものより上になったら pause させるコマンド。ソースコード上ではないため、思ったとおりの動きをしないことがある。色々説明があるけど、untilで思ったところに行かなければuntilを呼び直せばいい。

disassembleコマンド

唖然ブルコマンドを確認できる。

gdb -sオプション

アゼンブリの確認ができる。実行はできない。

2.10 Conditional Breakpoints

比較演算子やビット演算し、算術演算、独自関数やライブラリのコマンドもGDBのconditionの指定で使える。ただし、リンクされていないライブラリの関数の呼び出しの戻り値はすべてint型と処理され、型のキャストをしてもそれは直らない。

どうしても使いたい場合は以下のようにする。

コンディションの削除

cond で何も条件を付けなければコンディションが削除される。

2.11 Breakpoint Command Lists

ブレイクポイントに自動で実行するコマンドを追加できる。

コマンド内ではprintfが使え、Cのprintfと同等の使い方ができる。

ブレイクポイントコマンドの書き方

コマンド用のコマンド

  • silent
    • ブレイクポイントがヒットしたときのブレイクポイントの説明を省略する。
  • continue
    • コマンドの一番最後に記入すれば、pause しない。

commands の define

defineコマンドで GDB で使うコマンドをマクロにできる。

defined command を使うためには以下のようにする。

  • マクロの引数は最大で10個まで。
  • show user ですべてのマクロのリストが見れる。

Eclipseにこの機能はなさそう。

2.12 Watchpoints

値が変えられたときに pause する watchpoint。

watch <name of variable>で設定。

上記のように複雑な watchpoint も可能

watchpoint は変数がどの frame からも参照されなくなれば自動で消される。

GDBはシングルスレッド内でしか watch できない。

GDB はメモリの場所の値が変わったときに pause する。

GDB の watchpoint の設定先

  1. Hardware watchpoint
    速い
  2. VM techniques watchpoint
    十分速い
  3. Software Watchpoint
    超遅い

上から順に可能なものが適応される。

watchpoint が有用な場合

  • ポインタからポインタへの偏光の場合
  • メモリポインタの参照先を間違えた場合

2.12.2 Expressions

watchコマンドの長い引数のこと。Expressions で使えるのは以下。

  • GDB convinience variables
  • Any in-scope variable from your program.
  • Any kind of string ,numerical, or character constant
  • Pre-processor macros
  • The conditionals, function calls, casts and operators defined by the language you’re using

プログラムがFortlan で書いてあったら Fortlanのコードもここで使える。

Chapter 3 Inspecting and setting variables

print以外にもっと使える方法をこのチャプタで説明する。

3.2.1 Inspection in GDB

最初の改善: 構造体を全部出す

print *val 構造体の結果を出力してくれる。

2つ目の改善: displayコマンドを使う

ブレイクポイントに到達するたびにprintしてくれる。displayされるのはスコープの中にいるときしか表示できない。

3つ目の改善: GDB commands を使う

if 文は括弧なしでendで終わるrubyと同じ仕様。 command 内で call を使って毎度、内部コマンドを実行させることもできる。

コマンドを確認したければ普通は再コンパイルが必要だが、この方法ならいらない。

コマンドを空で再定義すれば初期化できる。

3.2.4 Inspecting Dynamic Arrays

普通の配列なら print ですべての中身を確認することができるが、動的な配列だと初めの値しかとれない。

のような条件の場合、print xするとポインタのアドレスが、print *xするとその地点のxの値を参照できる。

そのような場合の書き方は以下。

*pointer@number_of_elementでサイズを指定できる。

3.2.5 What about C++?

C++の場合の説明。クラスの場合は少しむずかしくなるという話。そのあたりはIDA使っているときにとても苦労したので、すでに理解していた。

ptypeコマンド

クラスや構造体の評価の出力ができる。

3.2.6 Monitoring Local Variables

info localsコマンドでローカル変数のリストを出力できる。

3.2.7 Examining Memory Directly

examineコマンドでメモリのアドレスを調べることができる。アゼンブリ言語で使う。

3.2.8 Advanced Options for Print and Display

print
コマンド 説明
p/x y yのhexでの出力を確認。
p/c y yのcharを確認
p/s y yを文字列として確認
p/f y y をfloatとして確認
display
コマンド 説明
disable display \<display-id> displayを無効化
enable display \<display-id> display を有効化
undisp \<display-id> displayを削除

3.3 Setting Variables from Within GDB/DDD/Eclipse

setコマンドで変数の値を設定できる。

みたいにすると、配列の値の設定もできる。

info argsコマンドでコマンドライン引数の確認ができる。

3.4 GDB’s Own Variables

3.4.1 Making Use of the Value History

print historyから値を出力できる。

$マークだけだと、最後に実行した変数になる。

3.4.2 Convinience Variables

GDB用の変数を保存できる。その場合は先頭に$マークを付ける。

Chapter 4 When a program crashes

メモリマネジメントの話。Seg fault でると大変だよねー。

4.1 Background Material: Memory Management

4.1.1 Why Does a Program Crash?

Segmentation Fault (Linux) とか general protection fault とか。

4.1.2 Program Layout in Memory

メモリの構成。図参照。

‘/proc//maps’ にメモリファイルが確保されている。catとかでheapstackの内容を確認できる。

4.1.3 The Notion of Pages

メモリのPageについて。

resident memory

物理メモリ上に展開された一定サイズの page のこと。ある resident でないプログラムのページが必要とされたとき、ハードウェアからOSにその管理が移行される。必要とされる page が保存され、それが新しい page の秋がなければ、他のプログラムの page が置き換えられる。置き換えられたpageはディスク上に保存されて、管理がプログラムに戻される。これらを管理するためにOSは それぞれのプロセスごとに Page Table を持っている。

プロセステーブルの入り口の情報
  • page がどこにあるか
  • R/W のパーミッション

page の一部だけが確保されることはない。このことが、異常なメモリアクセスがあったとしても常に segmentation fault を引き起こすわけではないという理由の説明になる。

4.1.4 Details on the Role of the Page Table

ページテーブルの説明。ファイルアクセスのパーミッションと同じように seg fault もアクセスパーミッションである。そのパーミッションがプロセスの Page Table で管理されている。

ハードウェアはプログラム上のバーチャルメモリを実メモリアドレスに置き換える。そのときにpage table で R/W パーミッションの確認をする。

パーミッションを持っていなければ、ハードウェアが内部で中断する。(seg fault)

どの種類のメモリアクセスのバグでも seg fault になる。

x[i]*(x+i)のアライアス。

call variableはCではコンパイル時にエラーがでるので seg fault にはならないが、 variable がポインタだと関数ポインタだと認識してコンパイルが通ってしまい、結果として seg fault になる。

4.1.5 A Slight Memory-Access Bug Migh Not Cause a Seg Fault

Page は余分に確保されるので page 内では seg fault が出ないという話。

gcc -fmudflapオプション

このオプションをつければ、インデックス関係のバグもコンパイル時に検出してくれる。

4.1.6 Seg Fault and Unix Signals

Unix Signal は4通りの方法で渡される。

  1. ハードウェアから
  2. OSから
  3. 他のプログラムから
  4. 自分のプロセスから
man 7 signal Linuxコマンド

Unix Signalの全リストを確認できる。

Signal handler には上書きできないものもあるが、多くのものが上書きできる。

カスタムシグナルハンドラを使うと、デバッガを正常に動かさなくすることがある。

4.1.7 Other Types of Exception

SIGFPE とか bus error とか。

SIGFPE Floatingpoint Exceptions

Floatのオーバーフローや 0 devision で起こる。でも、これの場合は付加情報でFPEが渡されて、Floatのオーバーフローの場合は無視され、0 devision の場合は処理が落ちるように標準で設計されている。

4.2 Core Files

4.2.1 How Core Files Are Created

Seg fault などが起こると、core ファイルがカレントディレクトリに作成される。そのファイルがあるとGDBなどのデバッガでプロセスの死んだところから検証できる。

コアファイルに保存されるデータ

  • スタックの中身
  • CPUレジスタの中身
  • 静的に確保された変数

など。

file core Linux コマンドで、どのプログラムが core ファイルを作ったかを調べることができる。

coreファイルがマルチプロセス、スレッドの場合は最後にプロセス番号が入る。

core という名前の付け方は/proc/sys/kernel/のファイルをいじれば変えられるらしい。

4.2.2 Your Shell May Suppress the Creation of a Core File

ulimit -c <core file size> で規制されたファイルサイズ以上のcoreファイルは破棄される。

ulimit -c unlimited とすれば、その制限を無制限にできる。

coreファイルが作成されなければulimitを疑う

core file が有用なとき
  • 長時間実行のときだけ起こる
  • ランダムな要素が関係している
  • 他のユーザからcore dumpしたファイルをもらう

4.3 Extended Example

実際にC++の文字列のクラスを作ってそれをデバッグする例を紹介している。

4.3.2 Don’t Leave GDB During a Debugging Session

何度も言うようにGDBをデバッグ中は再起動するなということ。

Chapter 5

いろんな複雑なデバッグの例を紹介している。気になった点だけまとめる。

thread はプロセスとよく似ているが、生成にかかる時間とメモリ使用量がすくない。Lightweighted Processと呼ばれたりする。ランタイム環境によってはプロセスだったりする。

プロセスとスレッドの大きな違いは、スレッドでは main thread のグローバル変数が各スレッドで共有される。

ps axHLinux コマンド。すべてのスレッドとプロセスをリスト化する。

gdb <program-name> <process-no>でプロセスにGDBをアタッチする。

OpenMP

並列プログラミングライブラリ。Cythonが対応しているらしい。Windowsでも動く模様。

このあたりで大体のGDBの説明は終わっている。このあとのチャプターでは個人的にはSwigのPython拡張をデバッグするところがとても有用だった。ほかは今の所いらない。

余談

今回の読書では始めて DPT-RP1 を使ってみましたがとても快適でした。細かい文字の注釈を入れるという今までのタブレットなどのタッチペンではなかなかできなかったことも問題なくできる書き心地は素晴らしいです。常にクリップボードの紙に書いているような感覚で場所も気にせずどこでも読書、メモができます。気が向けば感想ブログ書きたいです。

GDB を使えるようになれば、いろんな言語でデバッグできるようになるので強くなれます。読んでよかった。

にほんブログ村 為替ブログへ 
Fx-Kirin

About Fx-Kirin

2009年10月にFXを開始、翌年2010年5月から脱サラをしてFX業界に専念。 2012年10月頃から本格的に勝ち始め、一月で資産を倍にする、2年半月間負けなし等、安定した収支で2013年11月に生涯FX収支が1億を超える。 投資スタイルはシステムトレード。プログラミングの知識がほぼない状態から、独学で自分がしたいと思うことであればほぼ実現することが可能なレベルまで成長。好きな言語はRuby, Python。必要となればC++からVBA、Pascal等なんでも行う。MT4/MT5のプログラミングも得意。 2011年にはFXで稼いだ資金をもとにシンガポールに移住し、留学も兼ねて起業をチャレンジするほど、ビジネスを興すことに熱意がある。国内の業者を主に使い始めたことから、2012年に帰国。零細株式会社経営中。

Adsense

  • このエントリーをはてなブックマークに追加
  • Pocket
  • 67 follow us in feedly

関連記事

no image

cppでツリー構造を実現する

http://serennz.sakura.ne.jp/sb/log/eid120.htmlかしこい・・・

記事を読む

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Adsense

Ubuntu で仮想ディスプレイを使う

雑多な備忘録ですが、せっかくなので残しておきます。 Ubuntu 18.04 の resolv.c

PYPIへの登録を10秒でできるようになる方法

pip 使ってますよね Pythonを使っている人であれば、pip installでライブラリ

The art of debugging with GDB, DDD, and Eclipse の読書メモ

GDBの勉強がしたくて、下の本を原著で読んでみました。10年前の本だけど全然現役でした。

JupyterでボタンからJavascriptを実行して追加のアウトプットをさせない方法

Javascriptを実行するとアウトプットセルの行が増える これがとても面倒だった。上の

まだBokehで消耗してるの?これからはPandas-Bokehを使おうぜ

タイトルはあまり気にしないでください。全然、Bokehで消耗する価値があるとは思っています。ただ

→もっと見る

PAGE TOP ↑