qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: fred.konrad@greensocs.com
To: qemu-devel@nongnu.org
Cc: peter.maydell@linaro.org, quintela@redhat.com,
	mark.burton@greensocs.com, dgilbert@redhat.com,
	amit.shah@redhat.com, pbonzini@redhat.com, vilanova@ac.upc.edu,
	fred.konrad@greensocs.com
Subject: [Qemu-devel] [RFC PATCH v4 09/13] introduce reverse execution mechanism.
Date: Wed, 25 Jun 2014 10:26:44 +0200	[thread overview]
Message-ID: <1403684808-23248-10-git-send-email-fred.konrad@greensocs.com> (raw)
In-Reply-To: <1403684808-23248-1-git-send-email-fred.konrad@greensocs.com>

From: KONRAD Frederic <fred.konrad@greensocs.com>

This introduces the basic reverse-execution mechanism.

Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
---
 Makefile.target             |   1 +
 cpus.c                      |   6 +
 include/reverse-execution.h |  41 ++++++
 reverse-execution.c         | 306 ++++++++++++++++++++++++++++++++++++++++++++
 vl.c                        |   7 +-
 5 files changed, 360 insertions(+), 1 deletion(-)
 create mode 100644 include/reverse-execution.h
 create mode 100644 reverse-execution.c

diff --git a/Makefile.target b/Makefile.target
index 6089d29..7cb5cae 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -120,6 +120,7 @@ endif #CONFIG_BSD_USER
 # System emulator target
 ifdef CONFIG_SOFTMMU
 obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
+obj-y += reverse-execution.o
 obj-y += qtest.o
 obj-y += hw/
 obj-$(CONFIG_FDT) += device_tree.o
diff --git a/cpus.c b/cpus.c
index 89c7a1a..e86045e 100644
--- a/cpus.c
+++ b/cpus.c
@@ -63,6 +63,8 @@
 
 #endif /* CONFIG_LINUX */
 
+#include "reverse-execution.h"
+
 static CPUState *next_cpu;
 
 bool cpu_is_stopped(CPUState *cpu)
@@ -593,7 +595,11 @@ static bool cpu_can_run(CPUState *cpu)
 
 static void cpu_handle_guest_debug(CPUState *cpu)
 {
+    if (cexe_is_continuing_backward()) {
+        cexe_step_done();
+    }
     gdb_set_stop_cpu(cpu);
+    cexe_stop_stepping_back_mode();
     qemu_system_debug_request();
     cpu->stopped = true;
 }
diff --git a/include/reverse-execution.h b/include/reverse-execution.h
new file mode 100644
index 0000000..bf42003
--- /dev/null
+++ b/include/reverse-execution.h
@@ -0,0 +1,41 @@
+/*
+ *  reverse execution.
+ *
+ *  Copyright (C) 2014 : GreenSocs Ltd
+ *      http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ *  Developed by :
+ *  Frederic Konrad   <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef REVERSE_EXECUTION
+#define REVERSE_EXECUTION
+
+void cexe_setup(void);
+void cexe_step_backward(CPUState *cpu, uint64_t steps);
+void cexe_stop_stepping_back_mode(void);
+void cexe_continue_backward(CPUState *cpu);
+int cexe_is_continuing_backward(void);
+void cexe_next_reverse_continue_step(void);
+void cexe_stop_reverse_continue(void);
+void cexe_step_done(void);
+bool cexe_is_step_done(void);
+bool cexe_is_enabled(void);
+void cexe_cleanup(void);
+bool cexe_dbg_requested(void);
+
+#endif /* REVERSE_EXECUTION */
diff --git a/reverse-execution.c b/reverse-execution.c
new file mode 100644
index 0000000..9bf2ee8
--- /dev/null
+++ b/reverse-execution.c
@@ -0,0 +1,306 @@
+/*
+ *  reverse execution.
+ *
+ *  Copyright (C) 2014 : GreenSocs Ltd
+ *      http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ *  Developed by :
+ *  Frederic Konrad   <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "migration/qemu-file.h"
+
+#include "reverse-execution.h"
+
+#include "trace.h"  /* needed for trace event prototype */
+
+typedef struct snapshot_entry {
+    uint32_t id;
+    int64_t time;
+    QLIST_ENTRY(snapshot_entry) next;
+} snapshot_entry;
+
+static QLIST_HEAD(, snapshot_entry) snapshot = QLIST_HEAD_INITIALIZER(snapshot);
+
+QEMUTimer *snap_timer;
+QEMUTimer *stop_timer;
+
+struct cexe_state {
+    int stepping_back;
+    int continue_backward_mode;
+    int singlestep_was_enabled;
+    bool step_done;
+    bool stop_requested;
+};
+
+static bool cexe_enabled;
+struct cexe_state cexe_state;
+
+static snapshot_entry *new_snapshot(void)
+{
+    snapshot_entry *snap = NULL;
+    snap = g_malloc(sizeof(snapshot_entry));
+    assert(snap);
+
+    if (QLIST_FIRST(&snapshot) != NULL) {
+        snap->id = QLIST_FIRST(&snapshot)->id + 1;
+    } else {
+        snap->id = 0;
+    }
+
+    QLIST_INSERT_HEAD(&snapshot, snap, next);
+    return snap;
+}
+
+/*
+ * Timer callback called when a snapshot must be done.
+ */
+static void snap_callback(void *opaque)
+{
+    QEMUFile *file = NULL;
+    int saved_vm_running;
+    snapshot_entry *snap = NULL;
+    CPUArchState *cpu = NULL;
+    char filename[20];
+
+    cpu = qemu_get_cpu(0)->env_ptr;
+    assert(cpu != NULL);
+
+    if (!cexe_state.stepping_back) {
+        snap = new_snapshot();
+
+        saved_vm_running = runstate_is_running();
+        vm_stop(RUN_STATE_SAVE_VM);
+        snap->time = qemu_clock_get_ns(QEMU_CLOCK_ICOUNT);
+        sprintf(filename, ".save%04u", snap->id);
+
+        trace_snap_callback(snap->time, filename);
+
+        file = qemu_fopen(filename, "wb");
+        qemu_savevm_state(file);
+        qemu_fclose(file);
+
+        if (saved_vm_running) {
+            vm_start();
+        }
+        timer_mod_ns(snap_timer, snap->time + 100000000);
+    }
+}
+
+/*
+ * Timer callback called when the VM have to stop.
+ */
+static void stop_callback(void *opaque)
+{
+    trace_stop_callback(qemu_clock_get_ns(QEMU_CLOCK_ICOUNT));
+    cexe_state.stop_requested = true;
+}
+
+void cexe_setup(void)
+{
+    snap_timer = timer_new_ns(QEMU_CLOCK_ICOUNT, snap_callback, NULL);
+    stop_timer = timer_new_ns(QEMU_CLOCK_ICOUNT, stop_callback, NULL);
+
+    timer_mod_ns(snap_timer, qemu_clock_get_ns(QEMU_CLOCK_ICOUNT));
+    cexe_enabled = true;
+    cexe_state.stepping_back = 0;
+    cexe_state.continue_backward_mode = 0;
+    cexe_state.stop_requested = false;
+}
+
+void cexe_stop_stepping_back_mode(void)
+{
+    if (cexe_state.stepping_back) {
+        singlestep = cexe_state.singlestep_was_enabled;
+        cexe_state.stepping_back = 0;
+    }
+    cexe_state.stop_requested = false;
+}
+
+static void cexe_start_stepping_back_mode(CPUState *cpu)
+{
+    assert(!cexe_state.stepping_back);
+    /*
+     * Flushing tb.
+     * FIXME: might not be necessary with counter.
+     */
+    tb_flush(cpu->env_ptr);
+
+    /*
+     * Single step to the right PC.
+     */
+    cexe_state.singlestep_was_enabled = singlestep;
+    singlestep = 1;
+
+    cexe_state.stepping_back = 1;
+}
+
+/**
+ * \func cexe_step_backward
+ * \param cpu GDBStub's cpu.
+ * \param steps Number of steps to step back.
+ * \brief Steps backward: "reverse-step" in GDB.
+ *
+ */
+void cexe_step_backward(CPUState *cpu, uint64_t steps)
+{
+    QEMUFile *file = NULL;
+    char filename[20];
+    snapshot_entry *snap = QLIST_FIRST(&snapshot);
+
+    int64_t stop_time = qemu_clock_get_ns(QEMU_CLOCK_ICOUNT)
+                      - cpu_icount_to_ns(steps);
+
+    /*
+     * FIXME: Remove the file?
+     */
+    while ((stop_time > 0) && ((snap = QLIST_FIRST(&snapshot)) != NULL)
+                           && (snap->time >= stop_time)) {
+        /*
+         * Remove the snapshot from the list and mod the snapshot timer to its
+         * time. This will cause the snapshot to be taken at the same value in
+         * case of a forward execution.
+         */
+        QLIST_REMOVE(snap, next);
+        timer_mod_ns(snap_timer, snap->time);
+        g_free(snap);
+    }
+
+    if ((stop_time <= 0) || (snap == NULL)) {
+        /*
+         * This happens when an instruction behind the first snapshot is asked.
+         * Just trigger a debug event so it won't move.
+         */
+        cexe_state.stop_requested = true;
+        vm_start();
+        return;
+    }
+
+    sprintf(filename, ".save%04u", snap->id);
+
+    /*
+     * Load the previous state.
+     */
+    vm_stop(RUN_STATE_RESTORE_VM);
+
+    trace_cexe_step_backward(qemu_clock_get_ns(QEMU_CLOCK_ICOUNT), stop_time);
+
+    file = qemu_fopen(filename, "rb");
+    qemu_loadvm_state(file);
+    qemu_fclose(file);
+
+    /*
+     * Mod the timer so it will stop at the exact instruction.
+     */
+    timer_mod_ns(stop_timer, stop_time);
+
+    cexe_start_stepping_back_mode(cpu);
+    /*
+     * Restart the vm.
+     */
+    vm_start();
+}
+
+/**
+ * \func cexe_continue_backward
+ * \brief Continue execution backward.
+ * \param cpu GDB's stub cpu.
+ *
+ */
+void cexe_continue_backward(CPUState *cpu)
+{
+    cexe_state.continue_backward_mode = 1;
+    cexe_state.step_done = false;
+    cexe_step_backward(cpu, 1);
+}
+
+/**
+ * \func cexe_is_continuing_backward
+ * \brief Check if we are continuing backward.
+ * \return Return true if we are continuing backward.
+ *
+ */
+int cexe_is_continuing_backward(void)
+{
+    return cexe_state.continue_backward_mode;
+}
+
+void cexe_next_reverse_continue_step(void)
+{
+    CPUState *cpu = qemu_get_cpu(0);
+
+    assert(cpu != NULL);
+    cexe_state.step_done = false;
+
+    /*
+     * FIXME:
+     *         - Stop at breakpoint in reverse order.
+     *         - The reverse execution speed is not constant as the snapshot
+     *           replay is not constant.
+     */
+    cexe_step_backward(cpu, 10000000);
+}
+
+void cexe_stop_reverse_continue(void)
+{
+    if (cexe_state.continue_backward_mode) {
+        trace_cexe_stop_reverse_continue();
+        cexe_state.continue_backward_mode = false;
+        cexe_state.step_done = false;
+        cexe_stop_stepping_back_mode();
+    }
+}
+
+void cexe_step_done(void)
+{
+    cexe_state.step_done = true;
+}
+
+bool cexe_is_step_done(void)
+{
+    return cexe_state.step_done;
+}
+
+bool cexe_is_enabled(void)
+{
+    return cexe_enabled;
+}
+
+void cexe_cleanup(void)
+{
+    snapshot_entry *snap = QLIST_FIRST(&snapshot);
+
+    /*
+     * FIXME: Remove the file?
+     */
+    while ((snap = QLIST_FIRST(&snapshot)) != NULL) {
+        /*
+         * Remove the snapshot from the list and mod the snapshot timer to its
+         * time. This will cause the snapshot to be taken at the same value in
+         * case of a forward execution.
+         */
+        QLIST_REMOVE(snap, next);
+        g_free(snap);
+    }
+}
+
+bool cexe_dbg_requested(void)
+{
+    return cexe_state.stop_requested;
+}
diff --git a/vl.c b/vl.c
index a1686ef..d0352e3 100644
--- a/vl.c
+++ b/vl.c
@@ -119,6 +119,8 @@ int main(int argc, char **argv)
 #include "qom/object_interfaces.h"
 #include "qapi-event.h"
 
+#include "reverse-execution.h"
+
 #define DEFAULT_RAM_SIZE 128
 
 #define MAX_VIRTIO_CONSOLES 1
@@ -1950,7 +1952,7 @@ void qemu_system_debug_request(void)
 static bool main_loop_should_exit(void)
 {
     RunState r;
-    if (qemu_debug_requested()) {
+    if (qemu_debug_requested() && !cexe_is_continuing_backward()) {
         vm_stop(RUN_STATE_DEBUG);
     }
     if (qemu_suspend_requested()) {
@@ -2000,6 +2002,9 @@ static void main_loop(void)
     int64_t ti;
 #endif
     do {
+        if (cexe_is_continuing_backward() && cexe_is_step_done()) {
+            cexe_next_reverse_continue_step();
+        }
         nonblocking = !kvm_enabled() && !xen_enabled() && last_io > 0;
 #ifdef CONFIG_PROFILER
         ti = profile_getclock();
-- 
1.9.0

  parent reply	other threads:[~2014-06-25  8:27 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-06-25  8:26 [Qemu-devel] [RFC PATCH v4 00/13] Reverse execution fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 01/13] icount: put icount variables into TimerState fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 02/13] migration: migrate icount fields fred.konrad
2014-06-25 12:17   ` Juan Quintela
2014-06-25 15:17     ` Frederic Konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 03/13] migration: make qemu_savevm_state public fred.konrad
2014-06-25 12:18   ` Juan Quintela
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 04/13] icount: introduce icount timer fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 05/13] icount: check for icount clock deadline when cpu loop exits fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 06/13] icount: make icount extra computed on icount clock as well fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 07/13] timer: add cpu_icount_to_ns function fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 08/13] trace-events: add reverse-execution events fred.konrad
2014-06-25  8:26 ` fred.konrad [this message]
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 10/13] gdbstub: allow reverse execution in gdb stub fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 11/13] cpu-exec: trigger a debug request when rexec stops fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 12/13] cexe: synchronize icount on the next event fred.konrad
2014-06-25  8:26 ` [Qemu-devel] [RFC PATCH v4 13/13] cexe: allow to enable reverse execution fred.konrad

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1403684808-23248-10-git-send-email-fred.konrad@greensocs.com \
    --to=fred.konrad@greensocs.com \
    --cc=amit.shah@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=mark.burton@greensocs.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@redhat.com \
    --cc=vilanova@ac.upc.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).