GNU/Linux >> Linux の 問題 >  >> Linux

GNUデバッガーの5つのヒント

GNU Debugger(gdb)は、実行中のプロセスを検査し、プログラムの開発中に問題を修正するための非常に貴重なツールです。

特定の場所(関数名、行番号など)にブレークポイントを設定し、それらのブレークポイントを有効または無効にし、変数値を表示および変更し、デバッガーに期待されるすべての標準的なことを実行できます。しかし、それはあなたが実験しなかったかもしれない他の多くの機能を持っています。ここにあなたが試すべき5つがあります。

条件付きブレークポイント

ブレークポイントの設定は、GNUデバッガーを使用して行うことを最初に学ぶことの1つです。プログラムはブレークポイントに達すると停止します。プログラムを続行する前に、gdbコマンドを実行してプログラムを検査したり、変数を変更したりできます。

たとえば、頻繁に呼び出される関数がクラッシュすることがありますが、それは特定のパラメータ値を取得した場合に限られます。その関数の開始時にブレークポイントを設定して、プログラムを実行できます。関数パラメーターは、ブレークポイントに到達するたびに表示されます。クラッシュをトリガーするパラメーター値が指定されていない場合は、関数が再度呼び出されるまで続行できます。面倒なパラメータがクラッシュを引き起こした場合、コードをステップスルーして何が問題なのかを確認できます。

(gdb)break some_crashes 
Breakpoint 1 at 0x40110e:file prog.c、line 5
(gdb)run
[...]
Breakpoint 1、sometimes_crashes (f =0x7fffffffd1bc)at prog.c:5
5 fprintf(stderr、
(gdb)continue
Breakpoint 1、sometimes_crashes(f =0x7fffffffd1bc)at prog.c:5
5 fprintf(stderr、
(gdb)続行

これをより再現性のあるものにするために、関心のある特定の呼び出しの前に関数が呼び出された回数をカウントし、そのブレークポイントにカウンターを設定できます(たとえば、「30を続行」して、到達した次の29回を無視するようにします。ブレークポイント)。

しかし、ブレークポイントが非常に強力になるのは、実行時に式を評価する機能です。これにより、この種のテストを自動化できます。次のように入力します:条件付きブレークポイント。

 break [LOCATION] if CONDITION 

(gdb)break some_crashes if!f
Breakpoint 1 at 0x401132:file prog.c、line 5
(gdb)run
[...]
ブレークポイント1、prog.c:5で時々_crashes(f =0x0)
5 fprintf(stderr、
(gdb)

関数が呼び出されるたびにgdbに何をするかを尋ねさせる代わりに、条件付きブレークポイントを使用すると、特定の式がtrueと評価された場合にのみ、gdbをその場所で停止させることができます。実行が条件付きブレークポイントの場所に到達したが、式がfalseと評価された場合、

その他のLinuxリソース

  • Linuxコマンドのチートシート
  • 高度なLinuxコマンドのチートシート
  • 無料のオンラインコース:RHELの技術概要
  • Linuxネットワーキングのチートシート
  • SELinuxチートシート
  • Linuxの一般的なコマンドのチートシート
  • Linuxコンテナとは何ですか?
  • 最新のLinux記事

デバッガーは、ユーザーに何をすべきかを尋ねることなく、プログラムを自動的に続行させます。

ブレークポイントコマンド

GNUデバッガーのブレークポイントのさらに洗練された機能は、ブレークポイントに到達したときの応答をスクリプト化する機能です。ブレークポイントコマンドを使用すると、ブレークポイントに到達するたびに実行するGNUデバッガコマンドのリストを作成できます。

これを使用して、 sometimes_crashesですでに知っているバグを回避できます。 関数を作成し、nullポインターを提供するときに、その関数から無害に戻るようにします。

サイレントを使用できます 出力をより細かく制御するための最初の行として。これがないと、ブレークポイントコマンドが実行される前であっても、ブレークポイントに到達するたびにスタックフレームが表示されます。

(gdb)break somesome_crashes 
0x401132でのブレークポイント1:ファイルprog.c、5行目
(gdb)コマンド1
ブレークポイント1のコマンドを1行に1つずつ入力します。
「終了」とだけの行で終了します。
>サイレント
>if!f
> frame
> printf "Skipping call \ n"
> return 0
> continue
> end
> printf "Continuing \ n"
> continue
> end
(gdb)run
プログラムの開始:/ home / twaugh / Documents / GDB / prog
警告:ELFセグメント外のロード可能なセクション「.note.gnu.property」
続行
続行
続行
#0 sometimes_crashes(f =0x0)at prog.c:5
5 fprintf(stderr、
Skipping call
[Inferior 1(process 9373)exited normal]
(gdb)
バイナリメモリをダンプ

GNU Debuggerには、 xを使用してメモリを検査するためのサポートが組み込まれています。 8進数、16進数など、さまざまな形式のコマンド。ただし、左側に16進バイト、右側に同じバイトで表されるASCII文字の2つの形式を並べて表示するのが好きです。

ファイルの内容をバイト単位で表示したい場合は、 hexdump -Cをよく使用します。 (hexdumpはutil-linuxパッケージから取得されます)。これがgdbのx 16進バイトを表示するコマンド:

(GDB)X / 33xb MYDATA 
0x404040 :0×02は0x01 0x00の0x02の0x00から0x00には0x00 0x01の
0x404048 は0x01 0x47 0x00に0x12をの0x61 0x74 0x74 0x72
0x404050 :0x69の0x62 0x75 0x74 0x65 0x73 0x2dは0x63
0x404058 :0x68の0x61 0x72 0x73 0x65 0x75 0x00から0x05の
0x404060 :0x00の

hexdumpと同じようにメモリを表示するようにgdbに教えることができたらどうでしょうか?この方法は、好きな形式で使用できます。実際、この方法を使用できます。

ダンプを組み合わせることにより バイトをファイルに保存するコマンド、シェル ファイルに対してhexdumpを実行するコマンド、および定義 コマンドを使用すると、独自の新しい hexdumpを作成できます hexdumpを使用してメモリの内容を表示するコマンド。

(gdb)define hexdump
「hexdump」を定義するためのコマンドを入力します。
「end」とだけ言う行で終了します。
>dump binary memory /tmp/dump.bin $ arg0 $ arg0 + $ arg1
> shell hexdump -C /tmp/dump.bin
> end

これらのコマンドは、〜/ .gdbinitに含めることもできます hexdumpコマンドを永続的に定義するファイル。ここで動作しています:

(gdb)hexdump mydata sizeof(mydata)
00000000 02 01 00 02 00 00 00 01 01 47 00 12 61 74 74 72 | ......... G..attr |
00000010 69 62 75 74 65 73 2d 63 68 61 72 73 65 75 00 05 | ibutes-charseu .. |
00000020 00 pre br /> 21 | インライン分解

クラッシュに至るまでに何が起こったのかをもっと知りたい場合がありますが、ソースコードが十分ではありません。 CPU命令レベルで何が起こっているかを確認したい。

分解 コマンドを使用すると、関数を実装するCPU命令を確認できます。ただし、出力を追跡するのが難しい場合があります。通常、関数内のソースコードの特定のセクションに対応する命令を確認したいと思います。これを実現するには、 / sを使用します 逆アセンブリにソースコード行を含める修飾子。

(gdb)disassemble / s main 
関数mainのアセンブラコードのダンプ:
prog.c:
11 {
0x0000000000401158 <+0>:push%rbp
0x0000000000401159 <+1>:mov%rsp、%rbp
0x000000000040115c <+4>:sub $ 0x10、%rsp

12 int n =0;
<+8>:movl $ 0x0、-0x4(%rbp)

13時々_crashes(&n);
0x0000000000401167 <+15>:lea -0x4(%rbp)、%rax
0x000000000040116b <+19>:mov%rax、%rdi
0x000000000040116e <+22>:callq 0x401126
[... snipped ...]

これと情報レジスタ stepiなどのすべてのCPUレジスタとコマンドの現在の値を確認するには 一度に1つの命令を実行することで、プログラムをより詳細に理解できるようになります。

リバースデバッグ

時間を戻せたらいいのにと思うこともあります。変数のウォッチポイントに到達したと想像してください。ウォッチポイントはブレークポイントのようなものですが、プログラム内のある場所に設定されるのではなく、式に設定されます(ウォッチを使用) 指図)。式の値が変更されるたびに実行が停止し、デバッガーが制御を引き継ぎます。

したがって、このウォッチポイントに到達し、変数によって使用されるメモリの値が変更されたと想像してください。これは、はるかに以前に発生した何かが原因であることが判明する可能性があります。たとえば、メモリが解放され、現在再利用されています。しかし、いつ、なぜそれが解放されたのですか?

プログラムを逆に実行できるため、GNUデバッガーでもこの問題を解決できます!

これは、各ステップでプログラムの状態を注意深く記録することで実現され、以前に記録された状態を復元できるため、時間が逆流しているように見えます。

この状態の記録を有効にするには、 target record-fullを使用します 指図。次に、次のような不可能なサウンドのコマンドを使用できます。

  • 逆ステップ 、前のソース行に巻き戻します
  • リバースネクスト 、前のソース行に巻き戻され、関数呼び出しを逆方向にステップオーバーします
  • リバースフィニッシュ 、現在の関数が呼び出されようとした時点まで巻き戻します
  • 逆-続行 、これは、ブレークポイント(またはブレークポイントを停止させるその他の何か)を(現在)トリガーするプログラムの前の状態に巻き戻します

実行中の逆デバッグの例を次に示します。

(gdb)b main 
0x401160のブレークポイント1:ファイルprog.c、12行目
(gdb)r
開始プログラム:/ home / twaugh / Documents / GDB / prog
[...]

Breakpoint 1、main()at prog.c:12
12 int n =0;
(gdb)target record-full
(gdb)c
続行します。

プログラムは、信号SIGSEGV、セグメンテーション違反を受信しました。
0x0000000000401154、prog.c:7で時々_crashes(f =0x0) /> 7 return * f;
(gdb)reverse-finish
run back to call of#0 0x0000000000401154 in some_crashes(f =0x0)
at prog.c:7
0x0000000000401190 in main()at prog.c:16
16時々_crashes(0);

これらは、GNUデバッガーが実行できるほんの一握りの便利なことです。発見することはもっとたくさんあります。 gdbの隠された、あまり知られていない、または単なる驚くべき機能のどれがあなたのお気に入りですか?コメントで共有してください。


Linux
  1. Linuxで印刷するための3つのヒント

  2. GNUScreenを使用するためのLinuxのヒント

  3. Linuxコマンドラインに関する8つのヒント

  1. curlとwgetのヒントとコツ

  2. 信頼性の高いLinuxシステム自動化のための8つのヒント

  3. システム管理者向けの3つの確かな自己レビューのヒント

  1. GNU/Linux 用の Depends.exe

  2. Linux 上の gdb 用にプログラムで C または C++ コードにブレークポイントを設定する

  3. GNU ポートのミリ秒を timespec に変換する