From patchwork Fri Mar 21 19:17:30 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: fred.konrad@greensocs.com X-Patchwork-Id: 332726 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 583DE2C0082 for ; Sat, 22 Mar 2014 06:18:50 +1100 (EST) Received: from localhost ([::1]:54343 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WR4xs-0001Bi-0N for incoming@patchwork.ozlabs.org; Fri, 21 Mar 2014 15:18:48 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:60854) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WR4x6-000134-JL for qemu-devel@nongnu.org; Fri, 21 Mar 2014 15:18:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WR4wx-0006h2-2T for qemu-devel@nongnu.org; Fri, 21 Mar 2014 15:18:00 -0400 Received: from greensocs.com ([178.33.234.66]:46141) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WR4ww-0006gG-Pf for qemu-devel@nongnu.org; Fri, 21 Mar 2014 15:17:51 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by greensocs.com (Postfix) with ESMTP id 3BE8927B11; Fri, 21 Mar 2014 20:17:50 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at Received: from greensocs.com ([127.0.0.1]) by localhost (greensocs.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id NS0BfIlfYDU3; Fri, 21 Mar 2014 20:17:50 +0100 (CET) Received: by greensocs.com (Postfix, from userid 998) id 1C91227A03; Fri, 21 Mar 2014 20:17:50 +0100 (CET) Received: from localhost.localdomain (lan31-11-83-155-143-136.fbx.proxad.net [83.155.143.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: fred.konrad@greensocs.com) by greensocs.com (Postfix) with ESMTPSA id C05A127A03; Fri, 21 Mar 2014 20:17:49 +0100 (CET) From: fred.konrad@greensocs.com To: qemu-devel@nongnu.org Date: Fri, 21 Mar 2014 20:17:30 +0100 Message-Id: <1395429454-24690-9-git-send-email-fred.konrad@greensocs.com> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1395429454-24690-1-git-send-email-fred.konrad@greensocs.com> References: <1395429454-24690-1-git-send-email-fred.konrad@greensocs.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 178.33.234.66 Cc: mark.burton@greensocs.com, fred.konrad@greensocs.com Subject: [Qemu-devel] [RFC PATCH 08/12] introduce reverse execution mechanism. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: KONRAD Frederic This introduces the basic reverse-execution mechanism. Signed-off-by: KONRAD Frederic --- Makefile.target | 1 + cpus.c | 6 + include/reverse-execution.h | 41 ++++++ reverse-execution.c | 326 ++++++++++++++++++++++++++++++++++++++++++++ vl.c | 7 +- 5 files changed, 380 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 ba12340..6720e0c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -110,6 +110,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 +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 007de34..20940bb 100644 --- a/cpus.c +++ b/cpus.c @@ -61,6 +61,8 @@ #endif /* CONFIG_LINUX */ +#include "reverse-execution.h" + static CPUState *next_cpu; bool cpu_is_stopped(CPUState *cpu) @@ -598,7 +600,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 + * + * 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 . + * + */ + +#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..44d1b80 --- /dev/null +++ b/reverse-execution.c @@ -0,0 +1,326 @@ +/* + * reverse execution. + * + * Copyright (C) 2014 : GreenSocs Ltd + * http://www.greensocs.com/ , email: info@greensocs.com + * + * Developed by : + * Frederic Konrad + * + * 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 . + * + */ + +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "migration/qemu-file.h" + +#include "reverse-execution.h" + +#define DEBUG_REV_EXEC + +#ifdef DEBUG_REV_EXEC +#define DPRINTF(fmt, ...) \ +do { printf("rexec: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +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); + + DPRINTF("*** snapshooting. ***\n"); + DPRINTF("actual time: %li\n", snap->time); + DPRINTF("file: %s\n", filename); + DPRINTF("*********************\n\n"); + + 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) +{ + DPRINTF("*** stopping now. ***\n"); + DPRINTF("current time: %li\n", 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) +{ + DPRINTF("stop stepping back.\n"); + 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); + DPRINTF("*** stepping back. ***\n"); + DPRINTF("current time: %li\n", qemu_clock_get_ns(QEMU_CLOCK_ICOUNT)); + DPRINTF("**********************\n\n"); + + file = qemu_fopen(filename, "rb"); + qemu_loadvm_state(file); + qemu_fclose(file); + + DPRINTF("*** vm reloaded. ***\n"); + DPRINTF("snapshot time: %li\n", snap->time); + DPRINTF("current time: %li\n", qemu_clock_get_ns(QEMU_CLOCK_ICOUNT)); + DPRINTF("stop time: %li\n", stop_time); + DPRINTF("******************\n\n"); + + /* + * 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) { + DPRINTF("*** stop continue backward. ***\n"); + 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 02bf8ec..c9e849b 100644 --- a/vl.c +++ b/vl.c @@ -118,6 +118,8 @@ int main(int argc, char **argv) #include "qapi/string-input-visitor.h" #include "qom/object_interfaces.h" +#include "reverse-execution.h" + #define DEFAULT_RAM_SIZE 128 #define MAX_VIRTIO_CONSOLES 1 @@ -1994,7 +1996,7 @@ void qemu_system_vmstop_request(RunState state) 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()) { @@ -2044,6 +2046,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();