Hallo und willkommen.
In dieser Vorlesung möchte ich ein wenig über den Aufrufstapel sprechen, was er genau ist und wie
Sie ihn lesen können, um Ihr Programm weiter zu debuggen.
Eine Stapelverfolgung, die im Wesentlichen dem Aufrufstapel entspricht, wird immer dann erstellt, wenn Ihre Anwendung aufgrund eines
schwerwiegenden Fehlers abstürzt.
Der Begriff Stack wird verwendet, weil er sich auf eine Datenstruktur bezieht, die in der Programmierung üblich ist.
Stack ist eine Datenstruktur, die als LIFO (last in, first out) bekannt ist, und auf diese Weise werden die Daten gespeichert.
Das letzte Datenelement, das hinzugefügt wird, ist das erste, das Sie wieder abrufen.
Man kann sich das so vorstellen, als ob man einen Stapel Pfannkuchen isst, der erste Pfannkuchen liegt unten und
wird hinzugefügt.
Der letzte Pfannkuchen liegt ganz oben.
Wenn man sie essen will, isst man die letzte, die hinzugefügt wurde.
Ein Stack-Trace ist also sehr nützlich, wenn Ihr Programm abstürzt, weil er Informationen enthält, die Ihnen bei der Fehlersuche
helfen.
Ein Stack-Trace zeigt also eine Liste der Funktionsaufrufe, die zu dem Fehler geführt haben.
Wenn Sie in einem Programm Ihre Hauptfunktion aufrufen, ist das der Einstiegspunkt.
Die Hauptfunktion wird andere Funktionen aufrufen.
Diese Funktionen werden andere Funktionen aufrufen, und all diese Funktionen, die implementiert oder aufgerufen
werden, werden auf den Stapel gelegt.
Wenn ein Programm abstürzt, zeigt der Aufrufstapel die zuletzt aufgerufene Funktion und die
spezifische Codezeile an, die den Fehler verursacht hat.
Der Callstack enthält also eine Menge guter Informationen.
Sie enthält den Dateinamen und die Zeilennummern des Codes, der die Ausnahme oder den schwerwiegenden
Fehler verursacht.
Der oberste Teil des Stapels enthält den letzten Aufruf, der den Fehler verursacht hat, und es kann verschachtelte Aufrufe geben.
Das untere Ende des Stapels enthält den ersten Aufruf, der die Kette der Aufrufe zum Aufrufen des Fehlers gestartet hat.
Wenn Sie also normalerweise in C in der Hauptroutine sind, rufen Sie main auf, main ruft eine andere Funktion auf, die wiederum eine
andere Funktion aufruft und so weiter und so fort.
Und diese letzte Zeile ist sehr detailliert.
Es ist sehr, sehr hilfreich, wenn App für App abstürzt.
Wenn also ein C-Programm abstürzt, kann es einen Segmentierungsfehler erzeugen, und das sagt nicht
viel aus.
Es bedeutet nur, dass etwas Schlimmes passiert ist.
Dabei wird auch eine Kerndatei erzeugt.
Sie können die Kerndatei analysieren, die den Aufrufstapel enthält, der Ihnen die genaue Codezeile verrät,
die das Problem für Ihren Absturz verursacht hat.
Sie müssen also den Aufruf in Ihrer Anwendung finden, der den Absturz verursacht.
Und der Aufrufstapel ist wahrscheinlich der beste Weg, dies zu tun.
Und Sie müssen sich diese Kerndatei nicht immer ansehen.
Sie können den Aufrufstapel auch mit GDB und einem Debugger analysieren.
Und ich möchte Ihnen zeigen, wie das aussieht.
Ein Programmierer kann auch jederzeit den Stack-Trace ausgeben.
Wenn also ein Fehler auftritt oder Sie erkennen, dass ein schwerwiegender Fehler auftreten könnte, können Sie den Aufrufstapel
ausgeben, um zu sehen, was vor sich geht.
Wenn Sie nicht wissen, wann der Fehler auftritt, können Sie trotzdem den Aufrufstapel
ausgeben, um die aktuellen Spuren zu sehen, die zu einem bestimmten Aufruf geführt haben.
Bei der Verwendung von GDB sieht der Aufrufstapel in etwa so aus.
Sie können auf den Aufrufstapel über das Menü zugreifen, wenn Sie den Debugger in GDB ausführen,
und ich werde Ihnen zeigen, wie man das macht, aber so sieht er aus, und Sie sehen hier eine Nummer, eine
Adresse und dann die Funktion.
Dieser spezielle Aufrufstapel hier hat viele verschiedene Aufrufe, also muss er viele verschiedene Verschachtelungen und viele verschiedene
Funktionsaufrufe durchgeführt haben.
Aber ganz unten im Stack, in Zeile 22, steht GTK main.
Das bedeutet die.
Das bedeutet, dass er wahrscheinlich die Hauptfunktion ausgeführt hat.
Und in Zeile 21 sieht es so aus, als ob sie ausgeführt wurde.
Vielleicht sagt eine Anweisung, dass die Hauptschleife läuft.
Und dann sieht es so aus, als würde eine Funktion namens G main context iterate aufgerufen.
Und innerhalb dieser Funktion gab es immer dann, wenn es einen weiteren Aufruf einer IAG-Hauptkontextversandfunktion
gab, einige.
Es gab also eine Iterate-Funktion, dann eine Dispatch-Funktion, und dann bedeuten die Fragezeichen in Zeile
18, dass es keine Debugging-Informationen gibt.
Sie weiß nicht, was dort passiert ist.
Wenn Sie Ihr Programm erstellen, können Sie es entweder für die Veröffentlichung oder mit Debugging-Informationen
erstellen.
Wenn Sie es mit Debugging-Informationen erstellen, enthält es beim Kompilieren zusätzliche Informationen, so dass Sie
beim Ausführen die Aufrufstapel betrachten können.
Wenn Sie es freigeben, bauen Sie etwas für die Freigabe.
Die ausführbare Datei ist viel kleiner und enthält diese Informationen möglicherweise nicht.
Sie kann mehr Fragezeichen enthalten.
Aber es wird Ihnen auch sagen, ob Sie irgendwelche Bibliotheken oder zusätzliche Programme aufgerufen haben, es wird diese aufrufen und Ihnen
den Aufrufstapel von dort mitteilen.
Aber auch hier zeigt sich, dass die verschiedenen verschachtelten Ereignisse für den Aufruf ganz
oben wichtig sind, da sie die Ursache des Absturzes anzeigen.
Es sieht also so aus, als ob in diesem speziellen Aufrufstapel die innere Aktualisierung ein Problem verursacht hat.
Und Sie können sehen, dass es von Zeile 22 bis Zeile Null viele verschiedene Aufrufe von unterschiedlichem Code
gibt.
Aber der eventuelle Programmabsturz in Zeile Null ruft Stapel auf.
Sie können sich auch Aufrufstapel ansehen, während das Programm läuft, um Informationen zu erhalten, die Sie zurückverfolgen
können.
Wenn Sie also einen Haltepunkt innerhalb einer Funktion setzen, debuggen Sie, Sie führen den Debugger darauf aus.
Der Debugger wird an diesem Haltepunkt anhalten.
Sie können dann den Aufrufstapel aufrufen, der Ihnen sagt, wie Sie zu dieser Codezeile gekommen sind.
Diese Funktion wurde vor dieser Funktion aufgerufen.
Und auch hier wird in der Regel mit der Hauptfunktion begonnen.
Sie können auch zu verschiedenen Frames wechseln, wenn Sie mit dem Stack und dem Konzept vertraut sind und wissen, wie
er sich im Speicher befindet, können Sie sich verschiedene Frames ansehen, aber dies ist ein sehr nützliches Werkzeug, um hauptsächlich
Abstürze in C zu finden, und es ist sehr einfach zu lesen.
Wenn Sie nur auf den oberen Teil schauen und nicht viel über den Aufrufstapel wissen, können Sie
auf den oberen Teil schauen und sagen: Oh, das ist eine Quelle, das ist eine Codezeile, die den Absturz verursacht hat.
Hier ist ein schönes Beispiel, das ich im Internet gefunden habe.
Excma.
Edu.
Dies veranschaulicht Beispiele für Funktionsaufrufstapel.
Wenn wir uns also ein Beispiel im Code ansehen, werden wir sehen, dass wir nur einen einfachen Code haben.
Die Berechnungen sind nicht wirklich wichtig.
Wir rufen im Grunde nur einige Funktionen auf.
Wir haben also diese mogrify-Funktion und diese wahrhaft halbe Funktion, und main wird eine mogrify-Funktion aufrufen
und dann wahrhaft halbe und dann mogrify. Wir können also den Aufrufstapel veranschaulichen
und wie er bei diesen Aufrufen aussieht, indem wir im Grunde nur einen einfachen Haltepunkt in main setzen und dann
den Aufrufstapel betrachten, während wir fortschreiten und Aufrufe machen.
Auch hier werden wir uns also mit Haltepunkten und ähnlichen Informationen vertraut machen.
Aber ich möchte den Aufrufstapel nur kurz demonstrieren, und wir werden später ein ausführlicheres Beispiel
für den Aufrufstapel geben.
Hier setze ich also einen Haltepunkt.
Ich kann fortfahren und dies kompilieren und den Debugger starten, und wir werden den Haltepunkt erreichen.
Sie werden feststellen, dass Sie das gesamte Debugger-Menü hier rechts unten sehen können, und
Sie sehen den Aufrufstapel.
Lassen Sie uns das ein bisschen größer machen.
Aber genau dann, wenn es in Zeile 14 aufhört, hat es nur einige Zuweisungsanweisungen und Sie werden sehen, dass
der Aufrufstapel main anzeigt.
Wenn wir darüber hinweggehen und zur nächsten Zeile gehen, können wir sehen, dass es nur bis Zeile 15 geht,
bevor es Mogrify aufruft, richtig?
Sie sehen also, dass der Aufrufstapel bei Main bleibt.
Wenn wir jetzt reingehen, sehen wir, dass der Aufrufstapel Mogrify anzeigt.
Wir befinden uns also innerhalb dieser Funktion.
Darüber können wir hinweggehen.
Es wird also die erste Zeile ausgeführt und dann wird temp zurückgegeben.
Es bleibt immer noch in dieser Funktion und kehrt dann zurück, wo es in Zeile 16 den Druck F ausführt, Sie sehen, wie sich der
Aufrufstapel ändert.
Dann gehen wir zu Zeile 18, rufen Stack Stills in Main auf und treten in die wirklich halbe Funktion ein.
Und jetzt sehen Sie, wie sich der Aufrufstapel ändert, der zeigt, dass die Hälfte der Funktion an der Spitze immer die
Funktion ist, die zuletzt aufgerufen wurde.
Noch einmal, wir gehen rüber und sehen, dass wir immer noch in der Hälfte sind.
Wir gehen zurück zu Main, wir drucken etwas in Zeile 19 aus, wir gehen zu Zeile 21.
Wir springen wieder in die modify-Funktion, die Sie wiederum auf dem Aufrufstapel sehen. Wir springen weiter, wir springen weiter und schließlich
drucken wir das Endergebnis in Zeile 23 aus und kehren aus dem Programm zurück.
Aber der Aufrufstapel zeigt uns wiederum die Reihenfolge der Funktionen, die von einer Funktion zur nächsten aufgerufen
werden.
Wenn Sie also verschachtelte Funktionen haben, kann es immer komplizierter werden.
Der Vorteil des Aufrufstapels besteht darin, dass Sie bei einem Absturz einer Codezeile, z. B. in Zeile vier, einen Aufrufstapel
haben, der ganz oben enthält, wo die Zeile abgestürzt ist.
Sie können also sehen, indem Sie den Aufrufstapel durchgehen, welche Codezeile als letzte aufgerufen
wurde, und das wird Ihnen sagen, welche Codezeile abgestürzt ist.
Aber Sie werden dieses Werkzeug verwenden wollen, wenn Ihr Programm abstürzt.
Ich danke Ihnen.