GDB -Debugging -Tutorial für Anfänger

GDB -Debugging -Tutorial für Anfänger

Möglicherweise sind Sie bereits mit Debugging -Bash -Skripten vertraut (siehe, wie Sie Bash -Skripte debuggen, wenn Sie noch nicht mit Debugging -Bash vertraut sind), und wie Sie C oder C debuggen++? Lass uns erforschen.

GDB ist ein langjähriges und umfassendes Linux-Debugging-Dienstprogramm, das viele Jahre dauern würde, um zu lernen, ob Sie das Tool gut wissen wollten. Selbst für Anfänger kann das Tool jedoch sehr leistungsfähig und nützlich sein, wenn es darum geht, C oder C zu debuggen++.

Wenn Sie beispielsweise ein QA -Ingenieur sind und ein C -Programm und binarisch debuggen möchten, arbeitet Ihr Team und es stürzt ab, können Sie GDB verwenden, um eine Backtrace (eine Stapelliste von Funktionen genannt - wie ein Baum - welches - welches - welche führte schließlich zum Absturz). Oder wenn Sie ein C- oder C ++ - Entwickler sind und gerade einen Fehler in Ihren Code eingeführt haben, können Sie GDB verwenden, um Variablen, Code und mehr zu debuggen! Lassen Sie uns eintauchen!

In diesem Tutorial lernen Sie:

  • So installieren und verwenden Sie das GDB -Dienstprogramm aus der Befehlszeile in Bash aus
  • So führen Sie das grundlegende GDB -Debuggen mit der GDB -Konsole und Eingabeaufforderung durch
  • Erfahren Sie mehr über die detaillierten Ausgabe, die GDB produziert
GDB -Debugging -Tutorial für Anfänger

Softwareanforderungen und Konventionen verwendet

Softwareanforderungen und Linux -Befehlszeilenkonventionen
Kategorie Anforderungen, Konventionen oder Softwareversion verwendet
System Linux-Verteilungsunabhängige
Software BASH- und GDB -Befehlszeilen, Linux -basiertes System
Andere Das GDB -Dienstprogramm kann mit den unten angegebenen Befehlen installiert werden
Konventionen # - Erfordert, dass Linux -Commands mit Root -Berechtigungen entweder direkt als Stammbenutzer oder mithilfe von verwendet werden sudo Befehl
$-erfordert, dass Linux-Commands als regulärer nicht privilegierter Benutzer ausgeführt werden

GDB und ein Testprogramm einrichten

Für diesen Artikel werden wir uns ein klein ansehen prüfen.C Programm in der C-Entwicklungssprache, die einen Division-by-Null-Fehler im Code einführt. Der Code ist ein bisschen länger als das, was im wirklichen Leben benötigt wird (einige Zeilen, und es wäre keine Funktionen erforderlich), aber dies wurde absichtlich durchgeführt, um hervorzuheben, wie Funktionsnamen beim Debuggen deutlich zu sehen sind.

Lassen Sie uns zunächst die Tools installieren, die wir verwenden müssen sudo apt installieren (oder sudo yum install Wenn Sie eine rote Hutbasis verwenden):

sudo apt installieren GDB Build-Escial GCC 

Der bau-esz Und GCC werden Ihnen helfen, das zu kompilieren prüfen.C C Programm auf Ihrem System.

Als nächstes definieren wir die prüfen.C Skript wie folgt (Sie können Folgendes in Ihren bevorzugten Editor kopieren und einfügen und die Datei als speichern prüfen.C):

int ecal_calc (int a, int b) int c; c = a/b; Rückkehr 0;  int calc () int a; int b; a = 13; B = 0; tatsächlich_calc (a, b); Rückkehr 0;  int main () Calc (); Rückkehr 0;  


Ein paar Notizen zu diesem Skript: Sie können das sehen, wenn die hauptsächlich Funktion wird gestartet (die hauptsächlich Die Funktion ist die immer Haupt- und erste Funktion, die beim Starten der kompilierten Binärdatei aufgerufen wird. Dies ist Teil des C -Standards.) Sie ruft sofort die Funktion auf berechnen, was wiederum anruft atual_calc Nach ein paar Variablen eingestellt A Und B Zu 13 Und 0 bzw.

Ausführung unseres Skripts und Konfigurieren von Kern -Dumps

Lassen Sie uns nun dieses Skript mithilfe verwenden GCC und das gleiche ausführen:

$ gcc -ggdb Test.C -O -Test.aus $ ./prüfen.Out Floating Point Exception (Core Dumped) 

Der -GGDB Option zu GCC wird sicherstellen, dass unsere Debugging -Sitzung mit GDB freundlich sein wird. Es fügt dem GDB -spezifischen Debugging -Informationen zum hinzu prüfen.aus binär. Wir nennen diese Ausgabe -Binärdatei mit der Option zu GCC, Und als Eingabe haben wir unser Skript prüfen.C.

Wenn wir das Skript ausführen, erhalten wir sofort eine kryptische Nachricht Schwebende Punktausnahme (Core Dumped). Der Teil, den wir für den Moment interessieren, ist der Kern abgeladen Nachricht. Wenn Sie diese Nachricht nicht sehen (oder wenn Sie die Nachricht sehen, aber die Kerndatei nicht finden können), können Sie wie folgt ein besseres Kerndumping einrichten:

Wenn ! Grep -Qi 'Kernel.core_pattern ' /etc /sysctl.conf; Dann sudo sh -c 'echo "Kernel.core_pattern = core.%P.%u.%S.%e.%t ">> /etc /sysctl.Conf 'sudo sysctl -p fi ulimit -c unbegrenzt 

Hier stellen wir zunächst sicher, dass es kein Linux -Kernel -Kernmuster gibt (Kernel.core_pattern) Einstellung noch in /etc/sysctl.Conf (Die Konfigurationsdatei zum Einstellen von Systemvariablen auf Ubuntu und anderen Betriebssystemen) und - bereitgestellt kein vorhandenes Kernmuster - fügen Sie ein praktisches Kerndateiname -Muster hinzu (Muster des Kerndateinamens (Muster des Kerndateins (Kern.%P.%u.%S.%e.%T) zur gleichen Datei.

Der sysctl -p Befehl (als Wurzel ausgeführt werden, daher die sudo) Als nächstes stellt die Datei sofort sicher, ohne einen Neustart zu benötigen. Weitere Informationen zum Kernmuster finden Sie im Kernmuster Benennung von Kerndarmdateien Abschnitt, auf den durch die Verwendung der zugegriffen werden kann Mann Kern Befehl.

Endlich, das Ulimit -c unbegrenzt Der Befehl legt einfach die Kerndateigröße maximal auf unbegrenzt Für diese Sitzung. Diese Einstellung ist nicht persistent über Neustarts. Um es dauerhaft zu machen, können Sie:

sudo bash -c "Katze < /etc/security/limits.conf * soft core unlimited * hard core unlimited EOF 

Das wird hinzufügen * Soft Core Unlimited Und * Hard Core Unlimited Zu /etc/Sicherheit/Grenzen.Conf, Stellen Sie sicher, dass es keine Grenzen für Kern -Müllkopien gibt.

Wenn Sie jetzt erneut ausführen prüfen.aus Datei Sie sollten die sehen Kern abgeladen Nachricht und Sie sollten in der Lage sein, eine Kerndatei (mit dem angegebenen Kernmuster) zu sehen, wie folgt:

$ ls Core.1341870.1000.8.prüfen.aus.1598867712 Test.c Test.aus 

Lassen Sie uns als nächstes die Metadaten der Kerndatei untersuchen:

$ file core.1341870.1000.8.prüfen.aus.1598867712 Kern.1341870.1000.8.prüfen.aus.1598867712: ELF 64-Bit-LSB-Kerndatei, x86-64, Version 1 (SYSV), SVR4-Stil, aus './prüfen.Out ', Real UID: 1000, effektive UID: 1000, Real GID: 1000, Effektives GID: 1000, execfn:'./prüfen.out ', Plattform:' x86_64 ' 

Wir sehen. Wir können auch aus dem Dateinamen sehen (.8.) dass es ein Signal 8 war, das das Programm beendete. Signal 8 ist SIGFPE, eine schwimmende Punktausnahme. GDB wird uns später zeigen, dass dies eine arithmetische Ausnahme ist.

Verwenden Sie GDB, um den Kern -Dump zu analysieren

Öffnen wir die Kerndatei mit GDB und nehmen für eine Sekunde an, dass wir nicht wissen, was passiert ist (wenn Sie ein erfahrener Entwickler sind, haben Sie möglicherweise bereits den tatsächlichen Fehler in der Quelle gesehen!):

$ gdb ./prüfen.aus ./Kern.1341870.1000.8.prüfen.aus.1598867712 GNU GDB (Ubuntu 9.1-0ubuntu1) 9.1 Copyright (C) 2020 Free Software Foundation, Inc. Lizenz GPLV3+: GNU GPL Version 3 oder später ist diese kostenlose Software: Sie können es kostenlos ändern und neu verteilt. Es gibt keine Garantie, sofern dies gesetzlich zulässig ist. Geben Sie "Copying" und "Garantie" für Details ein. Dieses GDB wurde als "x86_64-linux-gnu" konfiguriert. Geben Sie "Konfiguration anzeigen" für Konfigurationsdetails ein. Für Anweisungen zur Fehlerberichterstattung finden Sie unter: . Finden Sie das GDB -Handbuch und andere Dokumentationsressourcen online unter: . Für Hilfe, geben Sie "Hilfe" ein. Geben Sie "APROPOS Word" ein, um nach Befehlen zu suchen, die sich mit "Wort" beziehen ... Symbole von Symbolen aus ./prüfen.Aus… [neuer LWP 1341870] CORE wurde von 'erzeugt' erzeugt './prüfen.aus'. Programm beendet mit Signal SIGFPE, arithmetische Ausnahme. #0 0x000056468844813b in real_calc (a = 13, b = 0) beim Test.C: 3 3 C = A/B; (GDB) 


Wie Sie sehen können, haben wir in der ersten Zeile angerufen GDB mit als erste Option unsere binäre und als zweite Option die Kerndatei. Denken Sie einfach daran Binär und Kern. Als nächstes sehen wir GDB initialisieren und werden einige Informationen präsentiert.

Wenn Sie a sehen WARNUNG: Unerwartete Größe des Abschnitts.Reg-Xstate/1341870 'in der Kerndatei.'oder eine ähnliche Nachricht, Sie können sie vorerst ignorieren.

Wir sehen, dass der Kern -Müllkippe von erzeugt wurde von prüfen.aus und wurde gesagt, dass das Signal eine sigfpe -arithmetische Ausnahme war. Großartig; Wir wissen bereits, dass etwas mit unserer Mathematik und vielleicht nicht mit unserem Code nicht stimmt!

Als nächstes sehen wir den Rahmen (bitte denken Sie an a rahmen wie ein Verfahren im Code vorerst), auf dem das Programm beendet wurde: Frame #0. GDB fügt dieser tatsächlich_calc, Was unsere variablen Werte waren und sogar in einer Zeile (3) von welcher Datei (prüfen.C) Das Problem ist geschehen.

Als nächstes sehen wir die Codezeile (Zeile 3) Diesmal mit dem tatsächlichen Code (c = a/b;) aus dieser Linie enthalten. Schließlich wird uns eine GDB -Eingabeaufforderung präsentiert.

Das Problem ist inzwischen wahrscheinlich sehr klar; Wir machten c = a/b, oder mit Variablen ausgefüllt c = 13/0 0. Aber der Mensch kann sich nicht durch Null teilen, und ein Computer kann daher auch nicht nicht. Wie niemand einem Computer sagte, wie er sich durch Null teilen sollte, trat eine Ausnahme auf, eine arithmetische Ausnahme, eine schwebende Punktausnahme / einen Fehler.

Backcraceing

Mal sehen, was wir mit GDB sonst noch entdecken können. Schauen wir uns einige grundlegende Befehle an. Die Faust ist diejenige, die Sie am häufigsten verwenden, am häufigsten: Bt:

(GDB) BT #0 0x000056468844813b in eURITY_CALC (A = 13, B = 0) Beim Test.C: 3 #1 0x0000564688448171 in Calc () bei Test.C: 12 #2 0x000056468844818a in Main () bei Test.C: 17 

Dieser Befehl ist eine Abkürzung für Backtrace und gibt uns im Grunde eine Spur des aktuellen Zustands (Vorgehensweise nach dem aufgerufenen Verfahren) des Programms. Denken Sie darüber nach wie eine umgekehrte Reihenfolge der Dinge, die passiert sind. rahmen #0 (Der erste Frame) ist die letzte Funktion, die vom Programm ausgeführt wurde, wenn es abstürzt, und des Rahmens #2 war der allererste Frame, der als das Programm gestartet wurde.

Wir können so analysieren, was passiert ist: Das Programm begann und hauptsächlich() wurde automatisch aufgerufen. Nächste, hauptsächlich() genannt calc () (Und wir können dies im obigen Quellcode bestätigen) und schließlich calc () genannt tatsächlich_calc Und da sind die Dinge schief gelaufen.

Schön, wir können jede Zeile sehen, in der etwas passiert ist. Zum Beispiel die tatsächlich_calc () Die Funktion wurde aus Zeile 12 in aufgerufen prüfen.C. Beachten Sie, dass es nicht ist calc () was aus Zeile 12 genannt wurde, aber eher tatsächlich_calc () was Sinn macht; prüfen.C wurde bis zur Ausführung in Zeile 12 ausführend calc () Funktion ist besorgt, da hier die calc () Funktion aufgerufen tatsächlich_calc ().

Power -Benutzer -Tipp: Wenn Sie mehrere Threads verwenden, können Sie den Befehl verwenden Faden anwenden Sie alle BT an Um einen Backtrac für alle Threads zu erhalten, die beim Absturz des Programms ausgeführt wurden!

Rahmenprüfung

Wenn wir wollen, können wir jeden Frame, den übereinstimmenden Quellcode (falls er verfügbar ist) und jede variable variable Schritt für Schritt inspizieren:

(GDB) F 2 #2 0x000055FA2323318a in Main () bei Test.C: 17 17 Calc (); (GDB) LIST 12 TRISE_CALC (A, B); 13 Rückkehr 0; 14 15 16 int main () 17 Calc (); 18 Rückkehr 0; 19 (GDB) P a kein Symbol "a" im aktuellen Kontext. 

Hier springen wir in den Rahmen 2, indem wir die verwenden f 2 Befehl. F ist eine kurze Hand für die rahmen Befehl. Als nächstes listen wir den Quellcode mit der Verwendung des Liste Befehl und schließlich versuchen zu drucken (mit dem mit der P Shorthand -Befehl) Der Wert der A Variable, die fehlschlägt, wie an diesem Punkt A wurde zu diesem Zeitpunkt im Code noch nicht definiert; Beachten Sie, dass wir in der Funktion in Zeile 17 arbeiten hauptsächlich(), und den tatsächlichen Kontext, den es innerhalb der Grenzen dieser Funktion/in diesen Rahmen existierte.

Es ist zu beachten.

Hier sehen wir sofort auch eine Gotcha; Wenn der Quellcode anders ist, kann der Code, aus dem die Binärdatei zusammengestellt wurde, leicht irregeführt werden. Der Ausgang kann nicht anwendbare / geänderte Quelle angezeigt werden. GDB tut nicht Überprüfen Sie, ob eine Quellcode -Revisionsübereinstimmung vorliegt! Es ist daher von größter Bedeutung, dass Sie genau dieselbe Quellcode -Revision wie diejenige verwenden, aus der Ihr Binärer zusammengestellt wurde.

Eine Alternative besteht darin, den Quellcode überhaupt nicht zu verwenden und einfach eine bestimmte Situation in einer bestimmten Funktion zu debuggen, wobei eine neuere Überarbeitung des Quellcode verwendet wird. Dies geschieht häufig für fortgeschrittene Entwickler und Debugger, die wahrscheinlich nicht zu viele Hinweise darauf benötigen, wo sich das Problem in einer bestimmten Funktion befindet, und mit bereitgestellten variablen Werten.

Lassen Sie uns als nächstes Rahmen 1 untersuchen:

(GDB) F 1 #1 0x000055FA23233171 in Calc () bei Test.C: 12 12 TRUCE_CALC (A, B); (GDB) Liste 7 int Calc () 8 int a; 9 int B; 10 a = 13; 11 b = 0; 12 TRUCE_CALC (A, B); 13 Rückkehr 0; 14 15 16 int main ()  

Hier können wir wieder viele Informationen sehen, die von GDB ausgegeben werden, was dem Entwickler beim Debuggen des jeweiligen Problems erleichtert wird. Da sind wir jetzt dabei berechnen (in Zeile 12), und wir haben die Variablen bereits initialisiert und anschließend festgelegt A Und B Zu 13 Und 0 Wir können jetzt ihre Werte drucken:

(GDB) P A $ 1 = 13 (GDB) P B $ 2 = 0 (GDB) P C NO SYMBER. (GDB) P A/B -Division von Null 


Beachten Sie das, wenn wir versuchen, den Wert von zu drucken C, Es scheitert immer noch wieder als wieder C ist bis zu diesem Punkt nicht definiert (Entwickler können in diesem Zusammenhang über "in diesem Zusammenhang" sprechen).

Schließlich schauen wir uns den Rahmen an #0, Unser Absturzrahmen:

(GDB) F 0 #0 0x000055FA2323313b in real_calc (a = 13, b = 0) beim Test.C: 3 3 C = A/B; (GDB) P A $ 3 = 13 (GDB) P B $ 4 = 0 (GDB) P C $ 5 = 22010 

Alle selbstbewusst, mit Ausnahme des Wertes für den von gemeldeten Wert C. Beachten Sie, dass wir die Variable definiert hatten C, hatte ihm aber noch keinen Anfangswert gegeben. Als solche C ist wirklich undefiniert (und es wurde nicht durch die Gleichung gefüllt c = a/b Doch als dieser fehlgeschlagen ist) und der resultierende Wert wurde wahrscheinlich aus einem Adressraum gelesen, zu dem die Variable C wurde zugewiesen (und dieser Speicherplatz wurde noch nicht initialisiert/gelöscht).

Abschluss

Großartig. Wir konnten einen Kern -Müllkippe für ein C -Programm debuggen und in der Zwischenzeit die Grundlagen des GDB -Debuggens beugten. Wenn Sie ein QA -Ingenieur oder ein Junior -Entwickler sind und alles in diesem Tutorial gut verstanden und gelernt haben, sind Sie den meisten QA -Ingenieuren und möglicherweise anderen Entwicklern in Ihrer Umgebung schon ziemlich weit voraus.

Und wenn Sie das nächste Mal Star Trek und Captain Janeway oder Captain Picard sehen möchten, werden Sie sicher ein breiteres Lächeln machen. Genießen Sie es, Ihren nächsten Kern zu debuggen, und hinterlassen Sie uns unten einen Kommentar mit Ihren Debugging -Abenteuern.

Verwandte Linux -Tutorials:

  • Eine Einführung in Linux -Automatisierung, Tools und Techniken
  • Dinge zu installieren auf Ubuntu 20.04
  • Mastering -Bash -Skriptschleifen beherrschen
  • Dinge zu tun nach der Installation Ubuntu 20.04 fokale Fossa Linux
  • Mint 20: Besser als Ubuntu und Microsoft Windows?
  • Ubuntu 20.04 Leitfaden
  • Dinge zu installieren auf Ubuntu 22.04
  • Verschachtelte Schleifen in Bash -Skripten
  • Wie man Kali Linux und Windows 10 Dual -Boot -Start hat
  • Hung Linux System? Wie man zur Befehlszeile entkommt und…