GNU/Linux >> LINUX-Kenntnisse >  >> Linux

5 Tipps für den GNU-Debugger

Der GNU Debugger (gdb) ist ein unschätzbares Werkzeug zum Untersuchen laufender Prozesse und zum Beheben von Problemen, während Sie Programme entwickeln.

Sie können Breakpoints an bestimmten Stellen setzen (nach Funktionsname, Zeilennummer usw.), diese Breakpoints aktivieren und deaktivieren, Variablenwerte anzeigen und ändern und alle Standardaufgaben ausführen, die Sie von einem Debugger erwarten würden. Aber es hat viele andere Funktionen, mit denen Sie vielleicht nicht experimentiert haben. Hier sind fünf, die Sie ausprobieren können.

Bedingte Haltepunkte

Das Setzen eines Breakpoints ist eines der ersten Dinge, die Sie mit dem GNU-Debugger lernen werden. Das Programm stoppt, wenn es einen Haltepunkt erreicht, und Sie können gdb-Befehle ausführen, um es zu untersuchen oder Variablen zu ändern, bevor Sie dem Programm erlauben, fortzufahren.

Sie wissen vielleicht, dass eine oft aufgerufene Funktion manchmal abstürzt, aber nur, wenn sie einen bestimmten Parameterwert erhält. Sie könnten am Anfang dieser Funktion einen Haltepunkt setzen und das Programm ausführen. Die Funktionsparameter werden jedes Mal angezeigt, wenn der Haltepunkt erreicht wird, und wenn der Parameterwert, der den Absturz auslöst, nicht angegeben wird, können Sie fortfahren, bis die Funktion erneut aufgerufen wird. Wenn der problematische Parameter einen Absturz auslöst, können Sie den Code schrittweise durchgehen, um zu sehen, was falsch ist.

(gdb) break sometimes_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) continue

Um dies wiederholbarer zu machen, könnten Sie zählen, wie oft die Funktion vor dem bestimmten Aufruf aufgerufen wird, an dem Sie interessiert sind, und einen Zähler auf diesen Haltepunkt setzen (z der Haltepunkt).

Aber wo Breakpoints wirklich leistungsfähig werden, ist ihre Fähigkeit, Ausdrücke zur Laufzeit auszuwerten, wodurch Sie diese Art von Tests automatisieren können. Geben Sie ein:bedingte Haltepunkte.

break [LOCATION] if CONDITION

(gdb) break sometimes_crashes if !f
Breakpoint 1 at 0x401132: file prog.c, line 5.
(gdb) run
[...]
Breakpoint 1, sometimes_crashes (f=0x0) at prog.c:5
5      fprintf(stderr,
(gdb)

Anstatt gdb jedes Mal fragen zu lassen, was zu tun ist, wenn die Funktion aufgerufen wird, ermöglicht Ihnen ein bedingter Haltepunkt, dass gdb nur dann an dieser Stelle stoppt, wenn ein bestimmter Ausdruck als wahr ausgewertet wird. Wenn die Ausführung die Position des bedingten Haltepunkts erreicht, der Ausdruck jedoch als falsch ausgewertet wird, wird die

Weitere Linux-Ressourcen

  • Spickzettel für Linux-Befehle
  • Spickzettel für fortgeschrittene Linux-Befehle
  • Kostenloser Online-Kurs:RHEL Technical Overview
  • Spickzettel für Linux-Netzwerke
  • SELinux-Spickzettel
  • Spickzettel für allgemeine Linux-Befehle
  • Was sind Linux-Container?
  • Unsere neuesten Linux-Artikel

Der Debugger lässt das Programm automatisch fortfahren, ohne den Benutzer zu fragen, was er tun soll.

Breakpoint-Befehle

Ein noch ausgeklügelteres Merkmal von Breakpoints im GNU Debugger ist die Möglichkeit, eine Antwort auf das Erreichen eines Breakpoints per Skript zu schreiben. Breakpoint-Befehle ermöglichen es Ihnen, eine Liste von GNU Debugger-Befehlen zu schreiben, die ausgeführt werden, wenn ein Breakpoint erreicht wird.

Wir können dies verwenden, um den Fehler zu umgehen, den wir bereits in den sometimes_crashes kennen Funktion und sorgen dafür, dass sie harmlos von dieser Funktion zurückkehrt, wenn sie einen Nullzeiger bereitstellt.

Wir können leise verwenden als erste Zeile, um mehr Kontrolle über die Ausgabe zu erhalten. Ohne dies wird der Stapelrahmen jedes Mal angezeigt, wenn der Haltepunkt erreicht wird, sogar bevor unsere Haltepunktbefehle ausgeführt werden.

(gdb) break sometimes_crashes
Breakpoint 1 at 0x401132: file prog.c, line 5.
(gdb) commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>silent
>if !f
 >frame
 >printf "Skipping call\n"
 >return 0
 >continue
 >end
>printf "Continuing\n"
>continue
>end
(gdb) run
Starting program: /home/twaugh/Documents/GDB/prog
warning: Loadable section ".note.gnu.property" outside of ELF segments
Continuing
Continuing
Continuing
#0  sometimes_crashes (f=0x0) at prog.c:5
5      fprintf(stderr,
Skipping call
[Inferior 1 (process 9373) exited normally]
(gdb)

Binären Speicher ausgeben

GNU Debugger hat eine eingebaute Unterstützung für die Untersuchung des Speichers mit dem x Befehl in verschiedenen Formaten, einschließlich oktal, hexadezimal usw. Aber ich sehe gerne zwei Formate nebeneinander:hexadezimale Bytes auf der linken Seite und ASCII-Zeichen, die durch dieselben Bytes auf der rechten Seite dargestellt werden.

Wenn ich den Inhalt einer Datei Byte für Byte anzeigen möchte, verwende ich häufig hexdump -C (Hexdump stammt aus dem Paket util-linux). Hier ist gdbs x Befehl, der hexadezimale Bytes anzeigt:

(gdb) x/33xb mydata
0x404040 <mydata>:    0x02    0x01    0x00    0x02    0x00    0x00    0x00    0x01
0x404048 <mydata+8>:    0x01    0x47    0x00    0x12    0x61    0x74    0x74    0x72
0x404050 <mydata+16>:    0x69    0x62    0x75    0x74    0x65    0x73    0x2d    0x63
0x404058 <mydata+24>:    0x68    0x61    0x72    0x73    0x65    0x75    0x00    0x05
0x404060 <mydata+32>:    0x00

Was wäre, wenn Sie gdb beibringen könnten, Speicher genau wie hexdump anzuzeigen? Sie können, und tatsächlich können Sie diese Methode für jedes Format verwenden, das Sie bevorzugen.

Durch Kombinieren der dump Befehl zum Speichern der Bytes in einer Datei, der Shell Befehl zum Ausführen von hexdump für die Datei und die define Befehl können wir unseren eigenen neuen Hexdump erstellen Befehl zur Verwendung von Hexdump zur Anzeige des Speicherinhalts.

(gdb) define hexdump
Type commands for definition of "hexdump".
End with a line saying just "end".
>dump binary memory /tmp/dump.bin $arg0 $arg0+$arg1
>shell hexdump -C /tmp/dump.bin
>end

Diese Befehle können sogar in ~/.gdbinit aufgenommen werden Datei, um den Hexdump-Befehl dauerhaft zu definieren. Hier ist es in Aktion:

(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                                                |.|
00000021

Inline-Disassemblierung

Manchmal möchten Sie mehr darüber erfahren, was zu einem Absturz geführt hat, und der Quellcode reicht nicht aus. Sie möchten sehen, was auf der Ebene der CPU-Anweisungen vor sich geht.

Die Zerlegung Mit dem Befehl können Sie die CPU-Anweisungen sehen, die eine Funktion implementieren. Aber manchmal kann die Ausgabe schwer zu verfolgen sein. Normalerweise möchte ich sehen, welche Anweisungen einem bestimmten Abschnitt des Quellcodes in der Funktion entsprechen. Verwenden Sie dazu /s Modifikator, um Quellcodezeilen in die Disassemblierung aufzunehmen.

(gdb) disassemble/s main
Dump of assembler code for function main:
prog.c:
11    {
   0x0000000000401158 <+0>:    push   %rbp
   0x0000000000401159 <+1>:    mov      %rsp,%rbp
   0x000000000040115c <+4>:    sub      $0x10,%rsp

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

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

Dies wird zusammen mit info registriert um die aktuellen Werte aller CPU-Register und Befehle wie stepi zu sehen eine Anweisung nach der anderen auszuführen, ermöglichen Ihnen ein viel detaillierteres Verständnis des Programms.

Umgekehrtes Debuggen

Manchmal wünscht man sich, man könnte die Zeit zurückdrehen. Stellen Sie sich vor, Sie haben einen Überwachungspunkt für eine Variable erreicht. Ein Watchpoint ist wie ein Haltepunkt, aber anstatt an einer Stelle im Programm gesetzt zu werden, wird er auf einen Ausdruck gesetzt (unter Verwendung der watch Befehl). Immer wenn sich der Wert des Ausdrucks ändert, stoppt die Ausführung und der Debugger übernimmt die Kontrolle.

Stellen Sie sich also vor, Sie haben diesen Überwachungspunkt erreicht und der von einer Variablen verwendete Speicher hat seinen Wert geändert. Dies kann durch etwas verursacht werden, das viel früher aufgetreten ist; Beispielsweise wurde der Speicher freigegeben und wird nun wiederverwendet. Aber wann und warum wurde es befreit?

Der GNU Debugger kann sogar dieses Problem lösen, weil Sie Ihr Programm rückwärts ausführen können!

Dies wird erreicht, indem der Zustand des Programms bei jedem Schritt sorgfältig aufgezeichnet wird, sodass zuvor aufgezeichnete Zustände wiederhergestellt werden können, wodurch die Illusion entsteht, dass die Zeit rückwärts fließt.

Um diese Statusaufzeichnung zu aktivieren, verwenden Sie das Ziel record-full Befehl. Dann können Sie unmöglich klingende Befehle verwenden, wie zum Beispiel:

  • Rückwärtsschritt , das zur vorherigen Quellzeile zurückspult
  • Rückwärts-Weiter , das zur vorherigen Quellzeile zurückspringt und Funktionsaufrufe rückwärts durchläuft
  • umgekehrtes Finish , das zu dem Punkt zurückspult, an dem die aktuelle Funktion aufgerufen werden sollte
  • Rückwärts-Weiter , das zum vorherigen Zustand im Programm zurückspringt, der (jetzt) ​​einen Haltepunkt (oder irgendetwas anderes, das zum Stoppen führt) auslösen würde

Hier ist ein Beispiel für Reverse-Debugging in Aktion:

(gdb) b main
Breakpoint 1 at 0x401160: file prog.c, line 12.
(gdb) r
Starting program: /home/twaugh/Documents/GDB/prog
[...]

Breakpoint 1, main () at prog.c:12
12      int n = 0;
(gdb) target record-full
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401154 in sometimes_crashes (f=0x0) at prog.c:7
7      return *f;
(gdb) reverse-finish
Run back to call of #0  0x0000000000401154 in sometimes_crashes (f=0x0)
        at prog.c:7
0x0000000000401190 in main () at prog.c:16
16      sometimes_crashes(0);

Dies sind nur einige nützliche Dinge, die der GNU Debugger tun kann. Es gibt noch viele weitere zu entdecken. Welches versteckte, wenig bekannte oder einfach nur erstaunliche Feature von gdb ist Ihr Favorit? Bitte teilen Sie es in den Kommentaren.


Linux
  1. Tipps und Tricks für curl und wget

  2. 5 erweiterte rsync-Tipps für Linux-Systemadministratoren

  3. Kurztipps für den OpenShift oc-Client

  4. Depends.exe für GNU/Linux

  5. Legen Sie Haltepunkte in C- oder C++-Code programmgesteuert für gdb unter Linux fest

Ein praktisches Tutorial zur Verwendung des GNU Project Debuggers

Tipps zur Verwendung des Top-Befehls unter Linux

Nützliche Meld-Tipps/Tricks für fortgeschrittene Benutzer

Tipps zur Verwendung von tmux

Tipps zur Verwendung des Bildschirms

GDB-Breakpoint-Beispiel für C – Breakpoints setzen, anzeigen, fortsetzen und löschen