Wie funktioniert das Reverse Debugging?


82

GDB hat eine neue Version herausgebracht, die Reverse Debug unterstützt (siehe http://www.gnu.org/software/gdb/news/reversible.html ). Ich habe mich gefragt, wie das funktioniert.

Damit das Reverse-Debug funktioniert, müssen Sie für jeden Schritt den gesamten Maschinenstatus einschließlich des Speichers speichern. Dies würde die Leistung unglaublich langsam machen, ganz zu schweigen von der Verwendung von viel Speicher. Wie werden diese Probleme gelöst?


4
Ich kann mir vorstellen, dass Sie mit der Speicherung von Staatsdeltas und nicht mit dem gesamten Staat auskommen könnten, aber es scheint immer noch, dass dies kostspielig sein könnte.
Spender


Das Speichern von Deltas kann in der Tat sehr gut funktionieren und ist für eine effiziente reversible Komplettsystemlösung wirklich notwendig.
Jakobengblom2

Antworten:


130

Ich bin ein GDB-Betreuer und einer der Autoren des neuen Reverse-Debugging. Ich würde gerne darüber sprechen, wie es funktioniert. Wie mehrere Personen spekuliert haben, müssen Sie genügend Maschinenzustand speichern, um ihn später wiederherstellen zu können. Es gibt eine Reihe von Schemata, von denen eines darin besteht, einfach die Register oder Speicherstellen zu speichern, die von jedem Maschinenbefehl geändert werden. Um diesen Befehl rückgängig zu machen, setzen Sie einfach die Daten in diesen Registern oder Speicherstellen zurück.

Ja, es ist teuer, aber moderne CPUs sind so schnell, dass Sie es nicht wirklich bemerken, wenn Sie ohnehin interaktiv sind (Stepping oder Breakpoints).


4
Ermöglicht das umgekehrte Debuggen jedoch nur das Zurücksetzen nextund stepEingeben von Befehlen oder das Rückgängigmachen einer beliebigen Anzahl von Anweisungen? Wenn ich beispielsweise einen Haltepunkt für eine Anweisung festlege und sie bis dahin laufen lasse, kann ich dann zur vorherigen Anweisung zurückkehren, obwohl ich sie übersprungen habe?
Nathan Fellman

10
> Mit dem umgekehrten Debugging können Sie jedoch nur die von Ihnen eingegebenen Befehle "next" und "step" zurücksetzen oder eine beliebige Anzahl von Anweisungen rückgängig machen. Sie können eine beliebige Anzahl von Anweisungen rückgängig machen. Sie sind nicht darauf beschränkt, zum Beispiel nur an den Punkten anzuhalten, an denen Sie angehalten haben, als Sie vorwärts gingen. Sie können einen neuen Haltepunkt festlegen und rückwärts dorthin ausführen.> Wenn ich beispielsweise einen Haltepunkt für eine Anweisung festlege und ihn bis dahin ausführen lasse, kann ich dann zur vorherigen Anweisung zurückkehren, obwohl ich ihn übersprungen habe. Ja Solange Sie hat den Aufnahmemodus aktiviert, bevor Sie zum Haltepunkt gelaufen sind
Michael Snyder

3
Entschuldigung für den unformatierten Text, ich weiß nicht, was damit los ist.
Michael Snyder

10
Ich mache mir Sorgen, dass das Reverse-Debugging die Zeit rückgängig machen und uns dazu bringen kann, in die 60er oder 70er Jahre zurückzukehren. Ich möchte keine Schlaghosen tragen und meine Haare wieder lang wachsen lassen.
der Blechmann

3
Und die Systemaufrufe, die den Status im Betriebssystem ändern? Funktioniert das einfach nicht richtig? Was ist, wenn ein undurchsichtiger Griff geändert wird?
Adrian

12

Beachten Sie, dass Sie die Verwendung von Simulatoren, virtuellen Maschinen und Hardware-Rekordern zur Implementierung der umgekehrten Ausführung nicht vergessen dürfen.

Eine andere Lösung zur Implementierung besteht darin, die Ausführung auf physischer Hardware zu verfolgen, wie dies GreenHills und Lauterbach in ihren hardwarebasierten Debuggern tun. Basierend auf dieser festen Spur der Aktion jeder Anweisung können Sie dann zu einem beliebigen Punkt in der Spur wechseln, indem Sie die Auswirkungen jeder Anweisung nacheinander entfernen. Beachten Sie, dass dies voraussetzt, dass Sie alle Dinge verfolgen können, die sich auf den im Debugger sichtbaren Status auswirken.

Eine andere Möglichkeit ist die Verwendung einer Checkpoint + Neuausführungsmethode, die von VmWare Workstation 6.5 und Virtutech Simics 3.0 (und höher) verwendet wird und anscheinend mit Visual Studio 2010 geliefert wird. Hier verwenden Sie eine virtuelle Maschine oder einen Simulator um eine Indirektionsebene für die Ausführung eines Systems zu erhalten. Sie speichern regelmäßig den gesamten Status auf der Festplatte oder im Speicher und verlassen sich dann darauf, dass der Simulator genau denselben Programmpfad deterministisch erneut ausführen kann.

Vereinfacht funktioniert es so: Angenommen, Sie befinden sich zum Zeitpunkt T in der Ausführung eines Systems. Um zur Zeit T-1 zu gelangen, nehmen Sie einen Kontrollpunkt vom Punkt t <T auf und führen dann (Tt-1) Zyklen aus, um einen Zyklus vor Ihrer Position zu beenden. Dies kann sehr gut funktionieren und gilt auch für Workloads, die Festplatten-E / A ausführen, aus Code auf Kernelebene bestehen und Gerätetreiberarbeiten ausführen. Der Schlüssel ist ein Simulator, der das gesamte Zielsystem mit all seinen Prozessoren, Geräten, Speichern und E / A enthält. Weitere Informationen finden Sie in der GDB-Mailingliste und in der folgenden Diskussion auf der GDB-Mailingliste. Ich selbst benutze diesen Ansatz ziemlich regelmäßig, um kniffligen Code zu debuggen, insbesondere in Gerätetreibern und frühen Betriebssystemstarts.

Eine weitere Informationsquelle ist a Virtutech-Whitepaper zum Thema Checkpointing (das ich in vollständiger Offenlegung verfasst habe).


Weitere Informationen zu Reverse-Debugging-Techniken finden Sie unter jakob.engbloms.se/archives/1547 und den beiden folgenden Blog-Posts.
Jakobengblom2

Wie wäre es mit der Möglichkeit, "Speicherpunkte zu setzen", anstatt Rückwärtsschritte zu implementieren? Sie debuggen also, und irgendwann können Sie den aktuellen Schritt als "Speicherpunkt" auswählen. Später können Sie zu diesem Speicherpunkt zurückspringen und erneut vorwärts gehen und gegebenenfalls Ihre Variablen bearbeiten. So ähnlich wie "Snapshots" für VMs oder "Wiederherstellungspunkte" für Betriebssysteme.
Rolf

9

Während einer EclipseCon-Sitzung haben wir auch gefragt, wie dies mit dem Chronon Debugger für Java gemacht wird. Dass man nicht erlauben Sie tatsächlich einen Schritt zurück, kann aber eine aufgezeichnete Programmausführung so wiedergeben , dass es fühlt sich an wie Reverse Debugging. (Der Hauptunterschied besteht darin, dass Sie das laufende Programm im Chronon-Debugger nicht ändern können, während Sie dies in den meisten anderen Java-Debuggern tun können.)

Wenn ich es richtig verstanden habe, manipuliert es den Bytecode des laufenden Programms so, dass jede Änderung eines internen Status des Programms aufgezeichnet wird. Externe Zustände müssen nicht zusätzlich aufgezeichnet werden. Wenn sie Ihr Programm in irgendeiner Weise beeinflussen, müssen Sie eine interne Variable haben, die diesem externen Status entspricht (und daher ist diese interne Variable ausreichend).

Während der Wiedergabezeit können sie dann grundsätzlich jeden Status des laufenden Programms aus den aufgezeichneten Statusänderungen neu erstellen.

Interessanterweise sind die Zustandsänderungen viel kleiner als man auf den ersten Blick erwarten würde. Wenn Sie also eine bedingte "if" -Anweisung haben, würden Sie denken, dass Sie mindestens ein Bit benötigen, um aufzuzeichnen, ob das Programm die then- oder die else-Anweisung verwendet hat. In vielen Fällen können Sie sogar dies vermeiden, beispielsweise in dem Fall, dass diese verschiedenen Zweige einen Rückgabewert enthalten. Dann reicht es aus, nur den Rückgabewert aufzuzeichnen (der ohnehin benötigt würde) und die Entscheidung über den ausgeführten Zweig aus dem Rückgabewert selbst neu zu berechnen .


8

Obwohl diese Frage alt ist, sind die meisten Antworten auch und als bleibt ein interessantes Thema, ich poste eine Antwort für 2015. Kapitel 1 und 2 meiner MSc-Arbeit, Kombination von Reverse-Debugging und Live-Programmierung in Richtung visuelles Denken in der Computerprogrammierung , behandeln einige der historischen Ansätze für das Reverse-Debugging (insbesondere im Hinblick auf den Snapshot- (oder Checkpoint-) und Wiederholungsansatz) und erklärt den Unterschied zum allwissenden Debuggen:

Der Computer, der das Programm bis zu einem bestimmten Zeitpunkt vorwärts ausgeführt hat, sollte uns wirklich Informationen darüber liefern können. Eine solche Verbesserung ist möglich und findet sich in sogenannten allwissenden Debuggern. Sie werden normalerweise als Reverse-Debugger klassifiziert, obwohl sie genauer als "Verlaufsprotokollierungs" -Debugger bezeichnet werden können, da sie lediglich Informationen während der Ausführung aufzeichnen, um sie später anzuzeigen oder abzufragen, anstatt es dem Programmierer zu ermöglichen, in einem ausführenden Programm tatsächlich zeitlich zurückzutreten . "Allwissend" ergibt sich aus der Tatsache, dass der gesamte aufgezeichnete Statusverlauf des Programms dem Debugger nach der Ausführung zur Verfügung steht. Es ist dann nicht erforderlich, das Programm erneut auszuführen, und es ist keine manuelle Code-Instrumentierung erforderlich.

Das softwarebasierte allwissende Debuggen begann mit dem EXDAMS-System von 1969, das als "Debug-Time-History-Playback" bezeichnet wurde. Der GNU-Debugger GDB unterstützt seit 2009 das allwissende Debuggen mit seiner Funktion "Prozessaufzeichnung und Wiedergabe". TotalView, UndoDB und Chronon scheinen die derzeit besten allwissenden Debugger zu sein, sind jedoch kommerzielle Systeme. TOD scheint für Java die beste Open-Source-Alternative zu sein, die partielle deterministische Wiedergabe sowie teilweise Trace-Erfassung und eine verteilte Datenbank verwendet, um die Aufzeichnung der großen Informationsmengen zu ermöglichen.

Es gibt auch Debugger, die nicht nur die Navigation einer Aufzeichnung ermöglichen, sondern tatsächlich in der Ausführungszeit einen Schritt zurücktreten können. Sie können genauer als Back-in-Time-, Time-Travel-, bidirektionale oder Reverse-Debugger beschrieben werden.

Das erste derartige System war der COPE-Prototyp von 1981 ...


4

Mozilla rrist eine robustere Alternative zum Reverse-Debugging von GDB

https://github.com/mozilla/rr

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

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

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 Trace-Datei verwendet, um genau 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.

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:

#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;

Wenn Sie komplexe Software debuggen, werden Sie wahrscheinlich bis zu einem Absturzpunkt laufen und dann in einen tiefen Rahmen fallen. Vergessen Sie in diesem Fall nicht, dass Sie reverse-nextbei höheren Frames zuerst Folgendes tun müssen:

reverse-finish

Bis zu diesem Rahmen upreicht es nicht aus , nur das Übliche zu tun .

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.


Weißt du, wie ich das mit ddd machen kann? Danke
Spraff

@spraff Ich bin nicht sicher, aber wahrscheinlich. Versuchen Sie zunächst, ddd mit gdbserver zu verbinden. Wenn das funktioniert, sollte es auch mit rr funktionieren.
Ciro Santilli 法轮功 冠状 病 六四 事件 13

1
@spraff jedoch nicht ddd verwenden, gdb-Dashboard verwenden ;-) stackoverflow.com/questions/10115540/gdb-split-view-with-code/… Dies wird definitiv funktionieren, da es sich nur um reguläres GDB handelt.
Ciro Santilli 法轮功 冠状 病 六四 事件 13

3

Nathan Fellman schrieb:

Können Sie mit dem umgekehrten Debugging nur die von Ihnen eingegebenen Befehle next und step zurücksetzen oder eine beliebige Anzahl von Anweisungen rückgängig machen?

Sie können eine beliebige Anzahl von Anweisungen rückgängig machen. Sie sind beispielsweise nicht darauf beschränkt, nur an den Punkten anzuhalten, an denen Sie angehalten haben, als Sie vorwärts gingen. Sie können einen neuen Haltepunkt festlegen und rückwärts dorthin laufen.

Wenn ich beispielsweise einen Haltepunkt für eine Anweisung festlege und sie bis dahin laufen lasse, kann ich dann zur vorherigen Anweisung zurückkehren, obwohl ich sie übersprungen habe?

Ja. Solange Sie den Aufnahmemodus aktiviert haben, bevor Sie zum Haltepunkt gelaufen sind.


2
Ein entscheidender Teil jeder umgekehrten Lösung ist, dass Sie sie irgendwann einschalten und nur bis zu diesem Punkt rückgängig machen können. Es gibt keine Magie, die eine Maschine in umgekehrter Richtung laufen lassen und herausfinden kann, was zuvor passiert ist, ohne irgendeine Aufzeichnung darüber, was passiert ist.
Jakobengblom2

2

So funktioniert ein anderer Reverse-Debugger namens ODB. Extrakt:

Omniscient Debugging ist die Idee, "Zeitstempel" an jedem "Punkt von Interesse" (Festlegen eines Werts, Ausführen eines Methodenaufrufs, Auslösen / Abfangen einer Ausnahme) in einem Programm zu sammeln und dem Programmierer dann zu ermöglichen, diese Zeitstempel zum Erkunden des Zeitstempels zu verwenden Geschichte dieses Programmlaufs.

Die ODB ... fügt beim Laden Code in die Klassen des Programms ein, und wenn das Programm ausgeführt wird, werden die Ereignisse aufgezeichnet.

Ich vermute, dass die GDB auf die gleiche Art und Weise funktioniert.


Würde dies Anweisungen im Code erfordern, um dem Compiler und Debugger mitzuteilen, wo sich diese interessanten Punkte befinden?
Nathan Fellman

Nein. Unter www.LambdaCS.com/debugger/debugger.html finden Sie eine Java Web Start-Demo, die Ihnen zeigt, wie es funktioniert. Es sieht aus wie ein normales Programm. Das ist sowieso ODB, weiß nichts über GDB. Es ist aber sehr cool :)
Demoncodemonkey

Beachten Sie, dass die GDB-Lösung das Zielprogramm in keiner Weise ändert. Wenn Sie ein Programm instrumentieren müssen, um es zu debuggen, besteht eine gute Chance, dass das Problem aufgrund von Zeitunterschieden und anderen Störungen verschwindet. Alle kommerziellen Revexec-Tools basieren auf einer Form von externen Aufzeichnungen, die den Code des Programms selbst nicht ändern.
Jakobengblom2

@ jakobengblom2: Ich denke, Sie legen zu viel Wert auf den Unterschied zwischen dem Ändern des Ziels, indem Sie in seinen Speicher schreiben, die Ausführung emulieren oder einfach Hardware-Haltepunkte hinzufügen. Sie alle ändern das Timing. Tatsächlich ändert die Zielinstrumentierung wahrscheinlich das Timing am wenigsten.
Ben Voigt

2

Reverse Debugging bedeutet, dass Sie das Programm rückwärts ausführen können. Dies ist sehr nützlich, um die Ursache eines Problems zu ermitteln.

Sie müssen nicht für jeden Schritt den vollständigen Maschinenstatus speichern, sondern nur die Änderungen. Es ist wahrscheinlich immer noch ziemlich teuer.


Ich verstehe, aber Sie müssen immer noch die Ausführung bei jeder Änderung unterbrechen, um die Änderungen zu speichern.
Nathan Fellman

Ja, das ist richtig, aber Maschinen sind jetzt ziemlich schnell, und in menschlicher Hinsicht glaube ich nicht, dass die Verlangsamung unerträglich ist. Es ist vergleichbar mit Valgrind, vielleicht nicht so langsam wie Valgrind.
Michael Snyder
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.