Wie gehe ich zur vorherigen Zeile in GDB?


75

Ist es in gdb möglich, zu einer Zeile vor der aktuell ausgeführten Zeile zu wechseln? z.B:


void my_fun( somePtrType** arr,int start,int end)
{
 // arr is an array of pointers to somePtrType
  //line a
 ... some assignments
 swap(&arr[ind1] , &arr[ind2] ) ;
 //line b (current line )
}

Ich bin derzeit in Zeile b und kann die arrWerte dort überprüfen , aber ich möchte zu Zeile a zurückkehren und den Inhalt von arrzu diesem Zeitpunkt untersuchen.

Ich denke, es ist möglicherweise nicht möglich, weil ein Debugger einen Code in Zeitlupe ausführen kann, ihn aber nicht rückwärts ausführen kann.
Noch mehr Einblicke ..

Antworten:


114

Ja! Mit der neuen Version 7.0 gdb können Sie genau das tun!

Der Befehl wäre " reverse-step" oder " reverse-next".

Sie können gdb-7.0 von ftp.gnu.org:/pub/gnu/gdb erhalten

Wenn Sie auf den Fehler Target child does not support this command.stoßen, versuchen Sie, ihn target recordzu Beginn der Ausführung nach dem Start hinzuzufügen run.

Bearbeiten: Da GDB 7.6 target recordveraltet ist, verwenden Sie target record-fullstattdessen.


6
Der Befehl existiert, aber hier "das Zielkind unterstützt ihn nicht" = (sowieso abgestimmt .;)
freitass

@ MichaelSnyder Wo ist mit gdb 6.x?
SIFE

26
Ich bekomme Target multi-thread does not support this command.:(
Derek 12 會 功夫

Jetzt verstehe ich: Typ start... dann Typ target record-full. Abkürzung ist rn.
user2023370

Ist recorddas gleiche wie target record-full? Es scheint für mich zu funktionieren und es ist der erste Befehl, der unter sourceware.org/gdb/current/onlinedocs/gdb/…
user2023370


9

Mozilla rr

https://github.com/mozilla/rr

Der in GDB integrierte Datensatz und die Wiedergabe weisen schwerwiegende Einschränkungen auf, z. B. keine Unterstützung für AVX-Anweisungen: Das Reverse-Debugging von GDB schlägt mit "Prozessdatensatz unterstützt Anweisung 0xf0d an Adresse nicht fehl" fehl.

Oberseite von rr:

  • derzeit viel zuverlässiger. Ich habe es relativ lange Läufe von mehreren komplexen Software getestet.
  • bietet auch eine GDB-Schnittstelle mit gdbserver-Protokoll, was es zu einem großartigen Ersatz macht
  • kleiner Leistungsabfall für die meisten Programme, ich habe es selbst nicht bemerkt, ohne Messungen durchzuführen
  • Die generierten Spuren sind auf der Festplatte klein, da nur sehr wenige nicht deterministische Ereignisse aufgezeichnet werden. Bisher musste ich mir keine Gedanken über ihre Größe machen

Das folgende Beispiel zeigt einige seiner Funktionen, vor allem die reverse-next, reverse-stepund reverse-continueBefehle.

Unter Ubuntu 18.04 installieren:

sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
# Overcome "rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is 3."
echo 'kernel.perf_event_paranoid=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Testprogramm:

reverse.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int f() {
    int i;
    i = 0;
    i = 1;
    i = 2;
    return i;
}

int main(void) {
    int i;

    i = 0;
    i = 1;
    i = 2;

    /* Local call. */
    f();

    printf("i = %d\n", i);

    /* Is randomness completely removed?
     * Recently fixed: https://github.com/mozilla/rr/issues/2088 */
    i = time(NULL);
    printf("time(NULL) = %d\n", i);

    return EXIT_SUCCESS;
}

kompilieren und ausführen:

gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay

Jetzt befinden Sie sich in einer GDB-Sitzung und können das Debuggen ordnungsgemäß rückgängig machen:

(rr) break main
Breakpoint 1 at 0x55da250e96b0: file a.c, line 16.
(rr) continue
Continuing.

Breakpoint 1, main () at a.c:16
16          i = 0;
(rr) next
17          i = 1;
(rr) print i
$1 = 0
(rr) next
18          i = 2;
(rr) print i
$2 = 1
(rr) reverse-next
17          i = 1;
(rr) print i
$3 = 0
(rr) next
18          i = 2;
(rr) print i
$4 = 1
(rr) next
21          f();
(rr) step
f () at a.c:7
7           i = 0;
(rr) reverse-step
main () at a.c:21
21          f();
(rr) next
23          printf("i = %d\n", i);
(rr) next
i = 2
27          i = time(NULL);
(rr) reverse-next
23          printf("i = %d\n", i);
(rr) next
i = 2
27          i = time(NULL);
(rr) next
28          printf("time(NULL) = %d\n", i);
(rr) print i
$5 = 1509245372
(rr) reverse-next
27          i = time(NULL);
(rr) next
28          printf("time(NULL) = %d\n", i);
(rr) print i
$6 = 1509245372
(rr) reverse-continue
Continuing.

Breakpoint 1, main () at a.c:16
16          i = 0;

rr erreicht dies, indem das Programm zunächst so ausgeführt wird, dass aufgezeichnet wird, was bei jedem einzelnen nicht deterministischen Ereignis passiert ist, z. B. bei einem Thread-Wechsel.

Während des zweiten Wiederholungslaufs wird diese überraschend kleine Ablaufverfolgungsdatei verwendet, um genau das zu rekonstruieren, was beim ursprünglichen nicht deterministischen Lauf passiert ist, jedoch auf deterministische Weise, entweder vorwärts oder rückwärts.

rr wurde ursprünglich von Mozilla entwickelt, um ihnen zu helfen, Timing-Fehler zu reproduzieren, die bei ihren nächtlichen Tests am nächsten Tag auftraten. Der Aspekt des umgekehrten Debuggens ist jedoch auch von grundlegender Bedeutung, wenn Sie einen Fehler haben, der nur Stunden innerhalb der Ausführung auftritt, da Sie häufig einen Schritt zurücktreten möchten, um zu untersuchen, welcher vorherige Status zu dem späteren Fehler geführt hat.

Die schwerwiegendsten Einschränkungen von rr sind meiner Meinung nach:

UndoDB ist eine kommerzielle Alternative zu rr: https://undo.io Beide basieren auf Trace / Replay, aber ich bin mir nicht sicher, wie sie sich in Bezug auf Funktionen und Leistung vergleichen lassen.


5

Kurze Antwort : Nein.

Um dieses Problem zu umgehen, lesen Sie unten.

Obwohl es in Zeile b nicht möglich ist, den Wert in Zeile a zu bestimmen, ist es möglich, den Wert von arr an a und b und anderen Stellen zu protokollieren, indem nur ein Haltepunkt getroffen wird.

  • Verwenden Sie den Befehl "display" ( Anzeigen des Variablennamens, wobei Variablenname durch arr, * arr, ** arr ersetzt werden soll, je nachdem, wonach Sie suchen), damit der Inhalt des Variablennamens bei Erreichen eines Haltepunkts ausgegeben wird der Bildschirm. Beachten Sie, dass Sie der Anzeigeliste hinzufügen können, wenn sich der Variablenname im Gültigkeitsbereich befindet, sodass Sie möglicherweise auf Ihren ersten Haltepunkt warten müssen.
  • Erstellen Sie Haltepunkte an verschiedenen Stellen des Codes, an denen Sie den Wert von Variablenname protokollieren möchten. Ein solcher Haltepunkt wäre in Zeile a.
  • Verwenden Sie für jeden Haltepunkt den Befehl ( Befehl breakpoint_number ) und weisen Sie Ihren Haltepunkt an, die Ausführung des Programms nicht anzuhalten. Der Befehl, den Sie verwenden müssen, wird fortgesetzt, gefolgt von end . Siehe Beispiel unten.

(gdb) Befehl 1

Geben Sie Befehle ein, wenn Haltepunkt 1 erreicht wird, einen pro Zeile. Beenden Sie mit einer Zeile, die nur "Ende" sagt.

fortsetzen

Ende

  • Setzen Sie einen Haltepunkt in Zeile b.

Wenn nun alle anderen Protokollierungs-Haltepunkte erreicht sind, wird der Wert von arr auf dem Bildschirm angezeigt, aber der Haltepunkt wartet nicht auf Benutzerinteraktion und fährt automatisch fort. Wenn Sie in Zeile b einen Haltepunkt erreichen, sehen Sie die vergangenen Werte von arr, die in gdb selbst protokolliert würden.

Abhängig von der Situation können Sie auch viele nützliche Informationen sichern (und anzeigen). Beispielsweise möchten Sie möglicherweise auch einen Schleifenzähler (z. B. i) ausgeben, wenn die obige Funktion in einer Schleife 10000 Mal aufgerufen wird. Das hängt wirklich davon ab, was Sie erreichen wollen.


1
Dies ist eine sehr nützliche Technik, auch wenn echtes Reverse-Debugging möglich ist! Das umgekehrte Debuggen hat Nachteile: Es ist langsam, wird nicht überall unterstützt und die Anzahl der gespeicherten Datensätze ist begrenzt. Bitte bearbeiten Sie Ihre Antwort, um die neue Entwicklung zu berücksichtigen.
misiu_mp

3

Wenn Ihr Programm kurz ist, ist der übliche Trick:

  1. Platzieren Sie einen neuen Haltepunkt in der vorherigen Zeile
    • Feuer r, um das Debug neu zu starten

GDB wurde dafür gemacht!


1

Nicht gdb, aber Sie können mit einem Debugger namens qira einfach in die Geschichte zurückkehren. Sie können die Aufwärts- und Abwärtspfeile verwenden, um hin und her zu gehen. Außerdem wird hervorgehoben, welche Register geändert wurden.

Geben Sie hier die Bildbeschreibung ein



0

Wenn sich Ihr Setup-Code für arr direkt über "Zeile a" befindet (ein sehr häufiges Szenario), können Sie dies folgendermaßen tun:

tbreak myfilename.c:123 (Zeile 123 ist der Beginn des Setup-Codes für arr) dann

jump 123

Der "tbreak" verhindert, dass gdb das Programm nach dem Sprung fortsetzt (fortsetzt).

Dann können Sie den Setup-Code durchgehen oder einfach einen Haltepunkt auf "Zeile a" setzen und fortfahren


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.