* Reverse debugging (Pavel)

* CFLAGS cleanup (Paolo)
 * ASLR fix (Mark)
 * cpus.c refactoring (Claudio)
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAl98EB0UHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroOCsQf9G7EUAK1zcEOx20LtDdXFrk4tjsRp
 S83OGdihWe8SM+XiY9BfqsBbXdByqF+SitePOV3feGK0mOP5vtJIL7/2DLrtFTeF
 wOeARRA9ePVb7hcL5oXAQeE3bXrX8wq8Qtw9xAoHdw5JAEVmKIEJS6AL5Eu3M2Fh
 pvdBoV84pOm2/ARS3eRstRyW8gCC8rdLDlNsVDtCbYdNVq+VdkzR0l5Phc8JDx1M
 Qjdl1KpN6ZkuN8M6tnaQNTb9IUVu5c1tu5jdR6JdLUqAWp1wYZJ6r2jSatZWfLR3
 H+gzFsDoLPfCjZ3IhfZyvzF5leSZmdbFfzI0tHS1UJ/ZZYjutDvlPlbyYA==
 =Jys5
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging

* Reverse debugging (Pavel)
* CFLAGS cleanup (Paolo)
* ASLR fix (Mark)
* cpus.c refactoring (Claudio)

# gpg: Signature made Tue 06 Oct 2020 07:35:09 BST
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [full]
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* remotes/bonzini-gitlab/tags/for-upstream: (37 commits)
  tests/acceptance: add reverse debugging test
  replay: create temporary snapshot at debugger connection
  replay: describe reverse debugging in docs/replay.txt
  gdbstub: add reverse continue support in replay mode
  gdbstub: add reverse step support in replay mode
  replay: flush rr queue before loading the vmstate
  replay: implement replay-seek command
  replay: introduce breakpoint at the specified step
  replay: introduce info hmp/qmp command
  qapi: introduce replay.json for record/replay-related stuff
  migration: introduce icount field for snapshots
  qcow2: introduce icount field for snapshots
  replay: provide an accessor for rr filename
  replay: don't record interrupt poll
  configure: don't enable ASLR for --enable-debug Windows builds
  configure: consistently pass CFLAGS/CXXFLAGS/LDFLAGS to meson
  configure: do not clobber environment CFLAGS/CXXFLAGS/LDFLAGS
  dtc: Convert Makefile bits to meson bits
  slirp: Convert Makefile bits to meson bits
  accel/tcg: use current_machine as it is always set for softmmu
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-10-06 15:04:10 +01:00
commit f2687fdb75
125 changed files with 3960 additions and 2344 deletions

View File

@ -451,6 +451,7 @@ WHPX CPUs
M: Sunil Muthuswamy <sunilmut@microsoft.com>
S: Supported
F: target/i386/whpx-all.c
F: target/i386/whpx-cpus.c
F: target/i386/whp-dispatch.h
F: accel/stubs/whpx-stub.c
F: include/sysemu/whpx.h
@ -2321,6 +2322,8 @@ F: softmmu/vl.c
F: softmmu/main.c
F: softmmu/cpus.c
F: softmmu/cpu-throttle.c
F: softmmu/cpu-timers.c
F: softmmu/icount.c
F: qapi/run-state.json
Read, Copy, Update (RCU)
@ -2490,7 +2493,7 @@ M: Laurent Vivier <lvivier@redhat.com>
R: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
F: softmmu/qtest.c
F: accel/qtest.c
F: accel/qtest/
F: tests/qtest/
X: tests/qtest/bios-tables-test-allowed-diff.h
@ -2693,6 +2696,8 @@ F: include/sysemu/replay.h
F: docs/replay.txt
F: stubs/replay.c
F: tests/acceptance/replay_kernel.py
F: tests/acceptance/reverse_debugging.py
F: qapi/replay.json
IOVA Tree
M: Peter Xu <peterx@redhat.com>

View File

@ -142,33 +142,7 @@ SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet)
include $(SRC_PATH)/tests/Makefile.include
all: recurse-all
Makefile: $(addsuffix /all, $(SUBDIRS))
# LIBFDT_lib="": avoid breaking existing trees with objects requiring -fPIC
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_lib=""
DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS)
DTC_CPPFLAGS=-I$(SRC_PATH)/dtc/libfdt
.PHONY: dtc/all
dtc/all: .git-submodule-status dtc/libfdt
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt,)
dtc/%: .git-submodule-status
@mkdir -p $@
# Retain for a while so that incremental build across this patch
# does not raise an error for missing target "capstone/all", which
# comes from the saved SUBDIRS value.
.PHONY: capstone/all
capstone/all:
.PHONY: slirp/all
slirp/all: .git-submodule-status
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp \
BUILD_DIR="$(BUILD_DIR)/slirp" \
PKG_CONFIG="$(PKG_CONFIG)" \
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
Makefile:
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS)))

View File

@ -44,6 +44,9 @@
#include "qapi/qapi-types-common.h"
#include "qapi/qapi-visit-common.h"
#include "sysemu/reset.h"
#include "qemu/guest-random.h"
#include "sysemu/hw_accel.h"
#include "kvm-cpus.h"
#include "hw/boards.h"
@ -378,7 +381,7 @@ err:
return ret;
}
int kvm_destroy_vcpu(CPUState *cpu)
static int do_kvm_destroy_vcpu(CPUState *cpu)
{
KVMState *s = kvm_state;
long mmap_size;
@ -412,6 +415,14 @@ err:
return ret;
}
void kvm_destroy_vcpu(CPUState *cpu)
{
if (do_kvm_destroy_vcpu(cpu) < 0) {
error_report("kvm_destroy_vcpu failed");
exit(EXIT_FAILURE);
}
}
static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id)
{
struct KVMParkedVcpu *cpu;
@ -430,17 +441,18 @@ static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id)
return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id);
}
int kvm_init_vcpu(CPUState *cpu)
int kvm_init_vcpu(CPUState *cpu, Error **errp)
{
KVMState *s = kvm_state;
long mmap_size;
int ret;
DPRINTF("kvm_init_vcpu\n");
trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu));
if (ret < 0) {
DPRINTF("kvm_create_vcpu failed\n");
error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)",
kvm_arch_vcpu_id(cpu));
goto err;
}
@ -451,7 +463,8 @@ int kvm_init_vcpu(CPUState *cpu)
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
if (mmap_size < 0) {
ret = mmap_size;
DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n");
error_setg_errno(errp, -mmap_size,
"kvm_init_vcpu: KVM_GET_VCPU_MMAP_SIZE failed");
goto err;
}
@ -459,7 +472,9 @@ int kvm_init_vcpu(CPUState *cpu)
cpu->kvm_fd, 0);
if (cpu->kvm_run == MAP_FAILED) {
ret = -errno;
DPRINTF("mmap'ing vcpu state failed\n");
error_setg_errno(errp, ret,
"kvm_init_vcpu: mmap'ing vcpu state failed (%lu)",
kvm_arch_vcpu_id(cpu));
goto err;
}
@ -469,6 +484,11 @@ int kvm_init_vcpu(CPUState *cpu)
}
ret = kvm_arch_init_vcpu(cpu);
if (ret < 0) {
error_setg_errno(errp, -ret,
"kvm_init_vcpu: kvm_arch_init_vcpu failed (%lu)",
kvm_arch_vcpu_id(cpu));
}
err:
return ret;
}
@ -2232,6 +2252,7 @@ static int kvm_init(MachineState *ms)
assert(!ret);
}
cpus_register_accel(&kvm_cpus);
return 0;
err:

84
accel/kvm/kvm-cpus.c Normal file
View File

@ -0,0 +1,84 @@
/*
* QEMU KVM support
*
* Copyright IBM, Corp. 2008
* Red Hat, Inc. 2008
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
* Glauber Costa <gcosta@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "sysemu/kvm_int.h"
#include "sysemu/runstate.h"
#include "sysemu/cpus.h"
#include "qemu/guest-random.h"
#include "qapi/error.h"
#include "kvm-cpus.h"
static void *kvm_vcpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
int r;
rcu_register_thread();
qemu_mutex_lock_iothread();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
cpu->can_do_io = 1;
current_cpu = cpu;
r = kvm_init_vcpu(cpu, &error_fatal);
kvm_init_cpu_signals(cpu);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
if (cpu_can_run(cpu)) {
r = kvm_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
kvm_destroy_vcpu(cpu);
cpu_thread_signal_destroyed(cpu);
qemu_mutex_unlock_iothread();
rcu_unregister_thread();
return NULL;
}
static void kvm_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM",
cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
}
const CpusAccel kvm_cpus = {
.create_vcpu_thread = kvm_start_vcpu_thread,
.synchronize_post_reset = kvm_cpu_synchronize_post_reset,
.synchronize_post_init = kvm_cpu_synchronize_post_init,
.synchronize_state = kvm_cpu_synchronize_state,
.synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm,
};

24
accel/kvm/kvm-cpus.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Accelerator CPUS Interface
*
* Copyright 2020 SUSE LLC
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef KVM_CPUS_H
#define KVM_CPUS_H
#include "sysemu/cpus.h"
extern const CpusAccel kvm_cpus;
int kvm_init_vcpu(CPUState *cpu, Error **errp);
int kvm_cpu_exec(CPUState *cpu);
void kvm_destroy_vcpu(CPUState *cpu);
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
void kvm_cpu_synchronize_post_init(CPUState *cpu);
void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu);
#endif /* KVM_CPUS_H */

View File

@ -1,5 +1,8 @@
kvm_ss = ss.source_set()
kvm_ss.add(files('kvm-all.c'))
kvm_ss.add(files(
'kvm-all.c',
'kvm-cpus.c',
))
kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c'))
specific_ss.add_all(when: 'CONFIG_KVM', if_true: kvm_ss)

View File

@ -8,6 +8,7 @@ kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
kvm_irqchip_commit_routes(void) ""
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"

View File

@ -1,6 +1,6 @@
softmmu_ss.add(files('accel.c'))
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: files('qtest.c'))
subdir('qtest')
subdir('kvm')
subdir('tcg')
subdir('xen')

7
accel/qtest/meson.build Normal file
View File

@ -0,0 +1,7 @@
qtest_ss = ss.source_set()
qtest_ss.add(files(
'qtest.c',
'qtest-cpus.c',
))
specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: qtest_ss)

91
accel/qtest/qtest-cpus.c Normal file
View File

@ -0,0 +1,91 @@
/*
* QTest accelerator code
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/rcu.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "sysemu/accel.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "qemu/guest-random.h"
#include "qemu/main-loop.h"
#include "hw/core/cpu.h"
#include "qtest-cpus.h"
static void *qtest_cpu_thread_fn(void *arg)
{
#ifdef _WIN32
error_report("qtest is not supported under Windows");
exit(1);
#else
CPUState *cpu = arg;
sigset_t waitset;
int r;
rcu_register_thread();
qemu_mutex_lock_iothread();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
cpu->can_do_io = 1;
current_cpu = cpu;
sigemptyset(&waitset);
sigaddset(&waitset, SIG_IPI);
/* signal CPU creation */
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
qemu_mutex_unlock_iothread();
do {
int sig;
r = sigwait(&waitset, &sig);
} while (r == -1 && (errno == EAGAIN || errno == EINTR));
if (r == -1) {
perror("sigwait");
exit(1);
}
qemu_mutex_lock_iothread();
qemu_wait_io_event(cpu);
} while (!cpu->unplug);
qemu_mutex_unlock_iothread();
rcu_unregister_thread();
return NULL;
#endif
}
static void qtest_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY",
cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, qtest_cpu_thread_fn, cpu,
QEMU_THREAD_JOINABLE);
}
const CpusAccel qtest_cpus = {
.create_vcpu_thread = qtest_start_vcpu_thread,
.get_virtual_clock = qtest_get_virtual_clock,
};

17
accel/qtest/qtest-cpus.h Normal file
View File

@ -0,0 +1,17 @@
/*
* Accelerator CPUS Interface
*
* Copyright 2020 SUSE LLC
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef QTEST_CPUS_H
#define QTEST_CPUS_H
#include "sysemu/cpus.h"
extern const CpusAccel qtest_cpus;
#endif /* QTEST_CPUS_H */

View File

@ -12,6 +12,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/rcu.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
@ -19,14 +20,16 @@
#include "sysemu/accel.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "qemu/guest-random.h"
#include "qemu/main-loop.h"
#include "hw/core/cpu.h"
#include "qtest-cpus.h"
static int qtest_init_accel(MachineState *ms)
{
QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0,
&error_abort);
qemu_opt_set(opts, "shift", "0", &error_abort);
configure_icount(opts, &error_abort);
qemu_opts_del(opts);
cpus_register_accel(&qtest_cpus);
return 0;
}

View File

@ -21,13 +21,3 @@ int hax_sync_vcpus(void)
{
return 0;
}
int hax_init_vcpu(CPUState *cpu)
{
return -ENOSYS;
}
int hax_smp_cpu_exec(CPUState *cpu)
{
return -ENOSYS;
}

View File

@ -1,30 +0,0 @@
/*
* QEMU HVF support
*
* Copyright 2017 Red Hat, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2 or later, as published by the Free Software Foundation,
* and may be copied, distributed, and modified under those terms.
*
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/hvf.h"
int hvf_init_vcpu(CPUState *cpu)
{
return -ENOSYS;
}
int hvf_vcpu_exec(CPUState *cpu)
{
return -ENOSYS;
}
void hvf_vcpu_destroy(CPUState *cpu)
{
}

View File

@ -32,16 +32,6 @@ bool kvm_readonly_mem_allowed;
bool kvm_ioeventfd_any_length_allowed;
bool kvm_msi_use_devid;
int kvm_destroy_vcpu(CPUState *cpu)
{
return -ENOSYS;
}
int kvm_init_vcpu(CPUState *cpu)
{
return -ENOSYS;
}
void kvm_flush_coalesced_mmio_buffer(void)
{
}
@ -50,19 +40,6 @@ void kvm_cpu_synchronize_state(CPUState *cpu)
{
}
void kvm_cpu_synchronize_post_reset(CPUState *cpu)
{
}
void kvm_cpu_synchronize_post_init(CPUState *cpu)
{
}
int kvm_cpu_exec(CPUState *cpu)
{
abort();
}
bool kvm_has_sync_mmu(void)
{
return false;

View File

@ -1,6 +1,4 @@
specific_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c'))
specific_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
specific_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
specific_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
specific_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
specific_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))

View File

@ -1,47 +0,0 @@
/*
* QEMU Windows Hypervisor Platform accelerator (WHPX) stub
*
* Copyright Microsoft Corp. 2017
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/whpx.h"
int whpx_init_vcpu(CPUState *cpu)
{
return -1;
}
int whpx_vcpu_exec(CPUState *cpu)
{
return -1;
}
void whpx_destroy_vcpu(CPUState *cpu)
{
}
void whpx_vcpu_kick(CPUState *cpu)
{
}
void whpx_cpu_synchronize_state(CPUState *cpu)
{
}
void whpx_cpu_synchronize_post_reset(CPUState *cpu)
{
}
void whpx_cpu_synchronize_post_init(CPUState *cpu)
{
}
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
}

View File

@ -19,6 +19,7 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/qemu-print.h"
#include "cpu.h"
#include "trace.h"
#include "disas/disas.h"
@ -36,6 +37,8 @@
#include "hw/i386/apic.h"
#endif
#include "sysemu/cpus.h"
#include "exec/cpu-all.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/replay.h"
/* -icount align implementation. */
@ -56,6 +59,9 @@ typedef struct SyncClocks {
#define MAX_DELAY_PRINT_RATE 2000000000LL
#define MAX_NB_PRINTS 100
static int64_t max_delay;
static int64_t max_advance;
static void align_clocks(SyncClocks *sc, CPUState *cpu)
{
int64_t cpu_icount;
@ -65,7 +71,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
}
cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
sc->diff_clk += icount_to_ns(sc->last_cpu_icount - cpu_icount);
sc->last_cpu_icount = cpu_icount;
if (sc->diff_clk > VM_CLOCK_ADVANCE) {
@ -98,7 +104,7 @@ static void print_delay(const SyncClocks *sc)
(-sc->diff_clk / (float)1000000000LL <
(threshold_delay - THRESHOLD_REDUCE))) {
threshold_delay = (-sc->diff_clk / 1000000000LL) + 1;
printf("Warning: The guest is now late by %.1f to %.1f seconds\n",
qemu_printf("Warning: The guest is now late by %.1f to %.1f seconds\n",
threshold_delay - 1,
threshold_delay);
nb_prints++;
@ -430,8 +436,7 @@ static inline bool cpu_handle_halt(CPUState *cpu)
{
if (cpu->halted) {
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
&& replay_interrupt()) {
if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
X86CPU *x86_cpu = X86_CPU(cpu);
qemu_mutex_lock_iothread();
apic_poll_irq(x86_cpu->apic_state);
@ -527,6 +532,20 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
return false;
}
/*
* CPU_INTERRUPT_POLL is a virtual event which gets converted into a
* "real" interrupt event later. It does not need to be recorded for
* replay purposes.
*/
static inline bool need_replay_interrupt(int interrupt_request)
{
#if defined(TARGET_I386)
return !(interrupt_request & CPU_INTERRUPT_POLL);
#else
return true;
#endif
}
static inline bool cpu_handle_interrupt(CPUState *cpu,
TranslationBlock **last_tb)
{
@ -588,7 +607,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
and via longjmp via cpu_loop_exit. */
else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
if (need_replay_interrupt(interrupt_request)) {
replay_interrupt();
}
/*
* After processing the interrupt, ensure an EXCP_DEBUG is
* raised when single-stepping so that GDB doesn't miss the
@ -615,7 +636,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
/* Finally, check if we need to exit to the main loop. */
if (unlikely(qatomic_read(&cpu->exit_request))
|| (use_icount
|| (icount_enabled()
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
qatomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) {
@ -656,10 +677,10 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
}
/* Instruction counter expired. */
assert(use_icount);
assert(icount_enabled());
#ifndef CONFIG_USER_ONLY
/* Ensure global icount has gone forward */
cpu_update_icount(cpu);
icount_update(cpu);
/* Refill decrementer and continue execution. */
insns_left = MIN(0xffff, cpu->icount_budget);
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
@ -759,3 +780,26 @@ int cpu_exec(CPUState *cpu)
return ret;
}
#ifndef CONFIG_USER_ONLY
void dump_drift_info(void)
{
if (!icount_enabled()) {
return;
}
qemu_printf("Host - Guest clock %"PRIi64" ms\n",
(cpu_get_clock() - icount_get()) / SCALE_MS);
if (icount_align_option) {
qemu_printf("Max guest delay %"PRIi64" ms\n",
-max_delay / SCALE_MS);
qemu_printf("Max guest advance %"PRIi64" ms\n",
max_advance / SCALE_MS);
} else {
qemu_printf("Max guest delay NA\n");
qemu_printf("Max guest advance NA\n");
}
}
#endif /* !CONFIG_USER_ONLY */

View File

@ -12,4 +12,4 @@ tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c'))
tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c'), libdl])
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files('tcg-all.c', 'cputlb.c'))
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files('tcg-all.c', 'cputlb.c', 'tcg-cpus.c'))

View File

@ -24,17 +24,15 @@
*/
#include "qemu/osdep.h"
#include "sysemu/accel.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "qom/object.h"
#include "cpu.h"
#include "sysemu/cpus.h"
#include "qemu/main-loop.h"
#include "sysemu/cpu-timers.h"
#include "tcg/tcg.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/boards.h"
#include "qapi/qapi-builtin-visit.h"
#include "tcg-cpus.h"
struct TCGState {
AccelState parent_obj;
@ -49,31 +47,6 @@ typedef struct TCGState TCGState;
DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE,
TYPE_TCG_ACCEL)
/* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask)
{
int old_mask;
g_assert(qemu_mutex_iothread_locked());
old_mask = cpu->interrupt_request;
cpu->interrupt_request |= mask;
/*
* If called from iothread context, wake the target cpu in
* case its halted.
*/
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
} else {
qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
if (use_icount &&
!cpu->can_do_io
&& (mask & ~old_mask) != 0) {
cpu_abort(cpu, "Raised interrupt while not in I/O function");
}
}
}
/*
* We default to false if we know other options have been enabled
* which are currently incompatible with MTTCG. Otherwise when each
@ -105,7 +78,7 @@ static bool check_tcg_memory_orders_compatible(void)
static bool default_mttcg_enabled(void)
{
if (use_icount || TCG_OVERSIZED_GUEST) {
if (icount_enabled() || TCG_OVERSIZED_GUEST) {
return false;
} else {
#ifdef TARGET_SUPPORTS_MTTCG
@ -123,13 +96,16 @@ static void tcg_accel_instance_init(Object *obj)
s->mttcg_enabled = default_mttcg_enabled();
}
bool mttcg_enabled;
static int tcg_init(MachineState *ms)
{
TCGState *s = TCG_STATE(current_accel());
tcg_exec_init(s->tb_size * 1024 * 1024);
cpu_interrupt_handler = tcg_handle_interrupt;
mttcg_enabled = s->mttcg_enabled;
cpus_register_accel(&tcg_cpus);
return 0;
}
@ -147,7 +123,7 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp)
if (strcmp(value, "multi") == 0) {
if (TCG_OVERSIZED_GUEST) {
error_setg(errp, "No MTTCG when guest word size > hosts");
} else if (use_icount) {
} else if (icount_enabled()) {
error_setg(errp, "No MTTCG when icount is enabled");
} else {
#ifndef TARGET_SUPPORTS_MTTCG

570
accel/tcg/tcg-cpus.c Normal file
View File

@ -0,0 +1,570 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (c) 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/tcg.h"
#include "sysemu/replay.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#include "exec/exec-all.h"
#include "hw/boards.h"
#include "tcg-cpus.h"
/* Kick all RR vCPUs */
static void qemu_cpu_kick_rr_cpus(void)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
cpu_exit(cpu);
};
}
static void tcg_kick_vcpu_thread(CPUState *cpu)
{
if (qemu_tcg_mttcg_enabled()) {
cpu_exit(cpu);
} else {
qemu_cpu_kick_rr_cpus();
}
}
/*
* TCG vCPU kick timer
*
* The kick timer is responsible for moving single threaded vCPU
* emulation on to the next vCPU. If more than one vCPU is running a
* timer event with force a cpu->exit so the next vCPU can get
* scheduled.
*
* The timer is removed if all vCPUs are idle and restarted again once
* idleness is complete.
*/
static QEMUTimer *tcg_kick_vcpu_timer;
static CPUState *tcg_current_rr_cpu;
#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10)
static inline int64_t qemu_tcg_next_kick(void)
{
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD;
}
/* Kick the currently round-robin scheduled vCPU to next */
static void qemu_cpu_kick_rr_next_cpu(void)
{
CPUState *cpu;
do {
cpu = qatomic_mb_read(&tcg_current_rr_cpu);
if (cpu) {
cpu_exit(cpu);
}
} while (cpu != qatomic_mb_read(&tcg_current_rr_cpu));
}
static void kick_tcg_thread(void *opaque)
{
timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
qemu_cpu_kick_rr_next_cpu();
}
static void start_tcg_kick_timer(void)
{
assert(!mttcg_enabled);
if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) {
tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
kick_tcg_thread, NULL);
}
if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) {
timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
}
}
static void stop_tcg_kick_timer(void)
{
assert(!mttcg_enabled);
if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) {
timer_del(tcg_kick_vcpu_timer);
}
}
static void qemu_tcg_destroy_vcpu(CPUState *cpu)
{
}
static void qemu_tcg_rr_wait_io_event(void)
{
CPUState *cpu;
while (all_cpu_threads_idle()) {
stop_tcg_kick_timer();
qemu_cond_wait_iothread(first_cpu->halt_cond);
}
start_tcg_kick_timer();
CPU_FOREACH(cpu) {
qemu_wait_io_event_common(cpu);
}
}
static int64_t tcg_get_icount_limit(void)
{
int64_t deadline;
if (replay_mode != REPLAY_MODE_PLAY) {
/*
* Include all the timers, because they may need an attention.
* Too long CPU execution may create unnecessary delay in UI.
*/
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
QEMU_TIMER_ATTR_ALL);
/* Check realtime timers, because they help with input processing */
deadline = qemu_soonest_timeout(deadline,
qemu_clock_deadline_ns_all(QEMU_CLOCK_REALTIME,
QEMU_TIMER_ATTR_ALL));
/*
* Maintain prior (possibly buggy) behaviour where if no deadline
* was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than
* INT32_MAX nanoseconds ahead, we still use INT32_MAX
* nanoseconds.
*/
if ((deadline < 0) || (deadline > INT32_MAX)) {
deadline = INT32_MAX;
}
return icount_round(deadline);
} else {
return replay_get_instructions();
}
}
static void notify_aio_contexts(void)
{
/* Wake up other AioContexts. */
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
}
static void handle_icount_deadline(void)
{
assert(qemu_in_vcpu_thread());
if (icount_enabled()) {
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
QEMU_TIMER_ATTR_ALL);
if (deadline == 0) {
notify_aio_contexts();
}
}
}
static void prepare_icount_for_run(CPUState *cpu)
{
if (icount_enabled()) {
int insns_left;
/*
* These should always be cleared by process_icount_data after
* each vCPU execution. However u16.high can be raised
* asynchronously by cpu_exit/cpu_interrupt/tcg_handle_interrupt
*/
g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0);
g_assert(cpu->icount_extra == 0);
cpu->icount_budget = tcg_get_icount_limit();
insns_left = MIN(0xffff, cpu->icount_budget);
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left;
replay_mutex_lock();
if (cpu->icount_budget == 0 && replay_has_checkpoint()) {
notify_aio_contexts();
}
}
}
static void process_icount_data(CPUState *cpu)
{
if (icount_enabled()) {
/* Account for executed instructions */
icount_update(cpu);
/* Reset the counters */
cpu_neg(cpu)->icount_decr.u16.low = 0;
cpu->icount_extra = 0;
cpu->icount_budget = 0;
replay_account_executed_instructions();
replay_mutex_unlock();
}
}
static int tcg_cpu_exec(CPUState *cpu)
{
int ret;
#ifdef CONFIG_PROFILER
int64_t ti;
#endif
assert(tcg_enabled());
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
cpu_exec_start(cpu);
ret = cpu_exec(cpu);
cpu_exec_end(cpu);
#ifdef CONFIG_PROFILER
qatomic_set(&tcg_ctx->prof.cpu_exec_time,
tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
#endif
return ret;
}
/*
* Destroy any remaining vCPUs which have been unplugged and have
* finished running
*/
static void deal_with_unplugged_cpus(void)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
if (cpu->unplug && !cpu_can_run(cpu)) {
qemu_tcg_destroy_vcpu(cpu);
cpu_thread_signal_destroyed(cpu);
break;
}
}
}
/*
* Single-threaded TCG
*
* In the single-threaded case each vCPU is simulated in turn. If
* there is more than a single vCPU we create a simple timer to kick
* the vCPU and ensure we don't get stuck in a tight loop in one vCPU.
* This is done explicitly rather than relying on side-effects
* elsewhere.
*/
static void *tcg_rr_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
assert(tcg_enabled());
rcu_register_thread();
tcg_register_thread();
qemu_mutex_lock_iothread();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
cpu->can_do_io = 1;
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
/* wait for initial kick-off after machine start */
while (first_cpu->stopped) {
qemu_cond_wait_iothread(first_cpu->halt_cond);
/* process any pending work */
CPU_FOREACH(cpu) {
current_cpu = cpu;
qemu_wait_io_event_common(cpu);
}
}
start_tcg_kick_timer();
cpu = first_cpu;
/* process any pending work */
cpu->exit_request = 1;
while (1) {
qemu_mutex_unlock_iothread();
replay_mutex_lock();
qemu_mutex_lock_iothread();
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
icount_account_warp_timer();
/*
* Run the timers here. This is much more efficient than
* waking up the I/O thread and waiting for completion.
*/
handle_icount_deadline();
replay_mutex_unlock();
if (!cpu) {
cpu = first_cpu;
}
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
qatomic_mb_set(&tcg_current_rr_cpu, cpu);
current_cpu = cpu;
qemu_clock_enable(QEMU_CLOCK_VIRTUAL,
(cpu->singlestep_enabled & SSTEP_NOTIMER) == 0);
if (cpu_can_run(cpu)) {
int r;
qemu_mutex_unlock_iothread();
prepare_icount_for_run(cpu);
r = tcg_cpu_exec(cpu);
process_icount_data(cpu);
qemu_mutex_lock_iothread();
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
break;
} else if (r == EXCP_ATOMIC) {
qemu_mutex_unlock_iothread();
cpu_exec_step_atomic(cpu);
qemu_mutex_lock_iothread();
break;
}
} else if (cpu->stop) {
if (cpu->unplug) {
cpu = CPU_NEXT(cpu);
}
break;
}
cpu = CPU_NEXT(cpu);
} /* while (cpu && !cpu->exit_request).. */
/* Does not need qatomic_mb_set because a spurious wakeup is okay. */
qatomic_set(&tcg_current_rr_cpu, NULL);
if (cpu && cpu->exit_request) {
qatomic_mb_set(&cpu->exit_request, 0);
}
if (icount_enabled() && all_cpu_threads_idle()) {
/*
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
* in the main_loop, wake it up in order to start the warp timer.
*/
qemu_notify_event();
}
qemu_tcg_rr_wait_io_event();
deal_with_unplugged_cpus();
}
rcu_unregister_thread();
return NULL;
}
/*
* Multi-threaded TCG
*
* In the multi-threaded case each vCPU has its own thread. The TLS
* variable current_cpu can be used deep in the code to find the
* current CPUState for a given thread.
*/
static void *tcg_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
assert(tcg_enabled());
g_assert(!icount_enabled());
rcu_register_thread();
tcg_register_thread();
qemu_mutex_lock_iothread();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
cpu->can_do_io = 1;
current_cpu = cpu;
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
/* process any pending work */
cpu->exit_request = 1;
do {
if (cpu_can_run(cpu)) {
int r;
qemu_mutex_unlock_iothread();
r = tcg_cpu_exec(cpu);
qemu_mutex_lock_iothread();
switch (r) {
case EXCP_DEBUG:
cpu_handle_guest_debug(cpu);
break;
case EXCP_HALTED:
/*
* during start-up the vCPU is reset and the thread is
* kicked several times. If we don't ensure we go back
* to sleep in the halted state we won't cleanly
* start-up when the vCPU is enabled.
*
* cpu->halted should ensure we sleep in wait_io_event
*/
g_assert(cpu->halted);
break;
case EXCP_ATOMIC:
qemu_mutex_unlock_iothread();
cpu_exec_step_atomic(cpu);
qemu_mutex_lock_iothread();
default:
/* Ignore everything else? */
break;
}
}
qatomic_mb_set(&cpu->exit_request, 0);
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
qemu_tcg_destroy_vcpu(cpu);
cpu_thread_signal_destroyed(cpu);
qemu_mutex_unlock_iothread();
rcu_unregister_thread();
return NULL;
}
static void tcg_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
static QemuCond *single_tcg_halt_cond;
static QemuThread *single_tcg_cpu_thread;
static int tcg_region_inited;
assert(tcg_enabled());
/*
* Initialize TCG regions--once. Now is a good time, because:
* (1) TCG's init context, prologue and target globals have been set up.
* (2) qemu_tcg_mttcg_enabled() works now (TCG init code runs before the
* -accel flag is processed, so the check doesn't work then).
*/
if (!tcg_region_inited) {
tcg_region_inited = 1;
tcg_region_init();
parallel_cpus = qemu_tcg_mttcg_enabled() && current_machine->smp.max_cpus > 1;
}
if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) {
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
if (qemu_tcg_mttcg_enabled()) {
/* create a thread per vCPU with TCG (MTTCG) */
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, tcg_cpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
} else {
/* share a single thread for all cpus with TCG */
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
qemu_thread_create(cpu->thread, thread_name,
tcg_rr_cpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
single_tcg_halt_cond = cpu->halt_cond;
single_tcg_cpu_thread = cpu->thread;
}
#ifdef _WIN32
cpu->hThread = qemu_thread_get_handle(cpu->thread);
#endif
} else {
/* For non-MTTCG cases we share the thread */
cpu->thread = single_tcg_cpu_thread;
cpu->halt_cond = single_tcg_halt_cond;
cpu->thread_id = first_cpu->thread_id;
cpu->can_do_io = 1;
cpu->created = true;
}
}
static int64_t tcg_get_virtual_clock(void)
{
if (icount_enabled()) {
return icount_get();
}
return cpu_get_clock();
}
static int64_t tcg_get_elapsed_ticks(void)
{
if (icount_enabled()) {
return icount_get();
}
return cpu_get_ticks();
}
/* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask)
{
int old_mask;
g_assert(qemu_mutex_iothread_locked());
old_mask = cpu->interrupt_request;
cpu->interrupt_request |= mask;
/*
* If called from iothread context, wake the target cpu in
* case its halted.
*/
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
} else {
qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
if (icount_enabled() &&
!cpu->can_do_io
&& (mask & ~old_mask) != 0) {
cpu_abort(cpu, "Raised interrupt while not in I/O function");
}
}
}
const CpusAccel tcg_cpus = {
.create_vcpu_thread = tcg_start_vcpu_thread,
.kick_vcpu_thread = tcg_kick_vcpu_thread,
.handle_interrupt = tcg_handle_interrupt,
.get_virtual_clock = tcg_get_virtual_clock,
.get_elapsed_ticks = tcg_get_elapsed_ticks,
};

17
accel/tcg/tcg-cpus.h Normal file
View File

@ -0,0 +1,17 @@
/*
* Accelerator CPUS Interface
*
* Copyright 2020 SUSE LLC
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef TCG_CPUS_H
#define TCG_CPUS_H
#include "sysemu/cpus.h"
extern const CpusAccel tcg_cpus;
#endif /* TCG_CPUS_H */

View File

@ -57,6 +57,7 @@
#include "qemu/main-loop.h"
#include "exec/log.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/tcg.h"
/* #define DEBUG_TB_INVALIDATE */
@ -369,7 +370,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
found:
if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) {
assert(use_icount);
assert(icount_enabled());
/* Reset the cycle counter to the start of the block
and shift if to the number of actually executed instructions */
cpu_neg(cpu)->icount_decr.u16.low += num_insns - i;

View File

@ -17,6 +17,7 @@
#include "exec/log.h"
#include "exec/translator.h"
#include "exec/plugin-gen.h"
#include "sysemu/replay.h"
/* Pairs with tcg_clear_temp_count.
To be called by #TranslatorOps.{translate_insn,tb_stop} if

View File

@ -230,6 +230,8 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
info->date_nsec = sn_tab[i].date_nsec;
info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000;
info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000;
info->icount = sn_tab[i].icount;
info->has_icount = sn_tab[i].icount != -1ULL;
info_list = g_new0(SnapshotInfoList, 1);
info_list->value = info;
@ -694,14 +696,15 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
{
char date_buf[128], clock_buf[128];
char icount_buf[128] = {0};
struct tm tm;
time_t ti;
int64_t secs;
char *sizing = NULL;
if (!sn) {
qemu_printf("%-10s%-20s%11s%20s%15s",
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
qemu_printf("%-10s%-18s%7s%20s%13s%11s",
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT");
} else {
ti = sn->date_sec;
localtime_r(&ti, &tm);
@ -715,11 +718,16 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
(int)(secs % 60),
(int)((sn->vm_clock_nsec / 1000000) % 1000));
sizing = size_to_str(sn->vm_state_size);
qemu_printf("%-10s%-20s%11s%20s%15s",
if (sn->icount != -1ULL) {
snprintf(icount_buf, sizeof(icount_buf),
"%"PRId64, sn->icount);
}
qemu_printf("%-9s %-17s %7s%20s%13s%11s",
sn->id_str, sn->name,
sizing,
date_buf,
clock_buf);
clock_buf,
icount_buf);
}
g_free(sizing);
}
@ -881,6 +889,8 @@ void bdrv_image_info_dump(ImageInfo *info)
.date_nsec = elem->value->date_nsec,
.vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL +
elem->value->vm_clock_nsec,
.icount = elem->value->has_icount ?
elem->value->icount : -1ULL,
};
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);

View File

@ -164,6 +164,12 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair,
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
}
if (sn->extra_data_size >= endof(QCowSnapshotExtraData, icount)) {
sn->icount = be64_to_cpu(extra.icount);
} else {
sn->icount = -1ULL;
}
if (sn->extra_data_size > sizeof(extra)) {
uint64_t extra_data_end;
size_t unknown_extra_data_size;
@ -333,6 +339,7 @@ int qcow2_write_snapshots(BlockDriverState *bs)
memset(&extra, 0, sizeof(extra));
extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size);
extra.disk_size = cpu_to_be64(sn->disk_size);
extra.icount = cpu_to_be64(sn->icount);
id_str_size = strlen(sn->id_str);
name_size = strlen(sn->name);
@ -656,6 +663,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
sn->date_sec = sn_info->date_sec;
sn->date_nsec = sn_info->date_nsec;
sn->vm_clock_nsec = sn_info->vm_clock_nsec;
sn->icount = sn_info->icount;
sn->extra_data_size = sizeof(QCowSnapshotExtraData);
/* Allocate the L1 table of the snapshot and copy the current one there. */
@ -1000,6 +1008,7 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
sn_info->date_sec = sn->date_sec;
sn_info->date_nsec = sn->date_nsec;
sn_info->vm_clock_nsec = sn->vm_clock_nsec;
sn_info->icount = sn->icount;
}
*psn_tab = sn_tab;
return s->nb_snapshots;

View File

@ -206,6 +206,7 @@ typedef struct QEMU_PACKED QCowSnapshotHeader {
typedef struct QEMU_PACKED QCowSnapshotExtraData {
uint64_t vm_state_size_large;
uint64_t disk_size;
uint64_t icount;
} QCowSnapshotExtraData;
@ -219,6 +220,8 @@ typedef struct QCowSnapshot {
uint32_t date_sec;
uint32_t date_nsec;
uint64_t vm_clock_nsec;
/* icount value for the moment when snapshot was taken */
uint64_t icount;
/* Size of all extra data, including QCowSnapshotExtraData if available */
uint32_t extra_data_size;
/* Data beyond QCowSnapshotExtraData, if any */

View File

@ -59,6 +59,7 @@
#include "sysemu/arch_init.h"
#include "sysemu/qtest.h"
#include "sysemu/runstate.h"
#include "sysemu/replay.h"
#include "qemu/cutils.h"
#include "qemu/help_option.h"
#include "qemu/main-loop.h"
@ -1190,6 +1191,10 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
info->vm_state_size = sn.vm_state_size;
info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
if (sn.icount != -1ULL) {
info->icount = sn.icount;
info->has_icount = true;
}
return info;
@ -1350,6 +1355,11 @@ static void internal_snapshot_prepare(BlkActionState *common,
sn->date_sec = tv.tv_sec;
sn->date_nsec = tv.tv_usec * 1000;
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (replay_mode != REPLAY_MODE_NONE) {
sn->icount = replay_get_current_icount();
} else {
sn->icount = -1ULL;
}
ret1 = bdrv_snapshot_create(bs, sn);
if (ret1 < 0) {

225
configure vendored
View File

@ -155,7 +155,7 @@ update_cxxflags() {
# options which some versions of GCC's C++ compiler complain about
# because they only make sense for C programs.
QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS"
CXXFLAGS=$(echo "$CFLAGS" | sed s/-std=gnu99/-std=gnu++11/)
CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu99/-std=gnu++11/)
for arg in $QEMU_CFLAGS; do
case $arg in
-Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\
@ -170,13 +170,14 @@ update_cxxflags() {
compile_object() {
local_cflags="$1"
do_cc $CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC
do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC
}
compile_prog() {
local_cflags="$1"
local_ldflags="$2"
do_cc $CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $QEMU_LDFLAGS $local_ldflags
do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC \
$LDFLAGS $CONFIGURE_LDFLAGS $QEMU_LDFLAGS $local_ldflags
}
# symbolically link $1 to $2. Portable version of "ln -sf".
@ -296,7 +297,7 @@ brlapi=""
curl=""
curses=""
docs=""
fdt=""
fdt="auto"
netmap="no"
sdl="auto"
sdl_image="auto"
@ -349,7 +350,7 @@ modules="no"
module_upgrades="no"
prefix="/usr/local"
qemu_suffix="qemu"
slirp=""
slirp="auto"
oss_lib=""
bsd="no"
linux="no"
@ -537,7 +538,10 @@ QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS"
QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS"
QEMU_INCLUDES="-iquote . -iquote ${source_path} -iquote ${source_path}/accel/tcg -iquote ${source_path}/include"
QEMU_INCLUDES="$QEMU_INCLUDES -iquote ${source_path}/disas/libvixl"
CFLAGS="-std=gnu99 -Wall"
# Flags that are needed during configure but later taken care of by Meson
CONFIGURE_CFLAGS="-std=gnu99 -Wall"
CONFIGURE_LDFLAGS=
check_define() {
@ -851,7 +855,7 @@ if test "$mingw32" = "yes" ; then
EXESUF=".exe"
HOST_DSOSUF=".dll"
# MinGW needs -mthreads for TLS and macro _MT.
CFLAGS="-mthreads $CFLAGS"
CONFIGURE_CFLAGS="-mthreads $CONFIGURE_CFLAGS"
write_c_skeleton;
prefix="/qemu"
qemu_suffix=""
@ -1058,9 +1062,9 @@ for opt do
;;
--enable-vnc-png) vnc_png="enabled"
;;
--disable-slirp) slirp="no"
--disable-slirp) slirp="disabled"
;;
--enable-slirp=git) slirp="git"
--enable-slirp=git) slirp="internal"
;;
--enable-slirp=system) slirp="system"
;;
@ -1181,9 +1185,13 @@ for opt do
;;
--enable-curl) curl="yes"
;;
--disable-fdt) fdt="no"
--disable-fdt) fdt="disabled"
;;
--enable-fdt) fdt="yes"
--enable-fdt) fdt="enabled"
;;
--enable-fdt=git) fdt="internal"
;;
--enable-fdt=system) fdt="system"
;;
--disable-linux-aio) linux_aio="no"
;;
@ -2105,7 +2113,7 @@ fi
if test "$static" = "yes"; then
if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then
CFLAGS="-fPIE -DPIE $CFLAGS"
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
QEMU_LDFLAGS="-static-pie $QEMU_LDFLAGS"
pie="yes"
elif test "$pie" = "yes"; then
@ -2115,11 +2123,11 @@ if test "$static" = "yes"; then
pie="no"
fi
elif test "$pie" = "no"; then
CFLAGS="$CFLAGS_NOPIE $CFLAGS"
LDFLAGS="$LDFLAGS_NOPIE $LDFLAGS"
CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS"
elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then
CFLAGS="-fPIE -DPIE $CFLAGS"
LDFLAGS="-pie $LDFLAGS"
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS"
pie="yes"
elif test "$pie" = "yes"; then
error_exit "PIE not available due to missing toolchain support"
@ -3663,7 +3671,7 @@ EOF
if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
if cc_has_warning_flag "-Wno-unknown-attributes"; then
glib_cflags="-Wno-unknown-attributes $glib_cflags"
CFLAGS="-Wno-unknown-attributes $CFLAGS"
CONFIGURE_CFLAGS="-Wno-unknown-attributes $CONFIGURE_CFLAGS"
fi
fi
@ -3683,7 +3691,7 @@ EOF
if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
if cc_has_warning_flag "-Wno-unused-function"; then
glib_cflags="$glib_cflags -Wno-unused-function"
CFLAGS="$CFLAGS -Wno-unused-function"
CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -Wno-unused-function"
fi
fi
@ -3941,67 +3949,15 @@ fi
##########################################
# fdt probe
# fdt support is mandatory for at least some target architectures,
# so insist on it if we're building those system emulators.
fdt_required=no
for target in $target_list; do
case $target in
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu|rx-softmmu)
fdt_required=yes
;;
esac
done
if test "$fdt_required" = "yes"; then
if test "$fdt" = "no"; then
error_exit "fdt disabled but some requested targets require it." \
"You can turn off fdt only if you also disable all the system emulation" \
"targets which need it (by specifying a cut down --target-list)."
fi
fdt=yes
elif test "$fdt" != "yes" ; then
fdt=no
fi
# fdt is only required when building softmmu targets
if test -z "$fdt" -a "$softmmu" != "yes" ; then
fdt="no"
fi
if test "$fdt" != "no" ; then
fdt_libs="-lfdt"
# explicitly check for libfdt_env.h as it is missing in some stable installs
# and test for required functions to make sure we are on a version >= 1.4.2
cat > $TMPC << EOF
#include <libfdt.h>
#include <libfdt_env.h>
int main(void) { fdt_check_full(NULL, 0); return 0; }
EOF
if compile_prog "" "$fdt_libs" ; then
# system DTC is good - use it
fdt=system
else
# have GIT checkout, so activate dtc submodule
if test -e "${source_path}/.git" ; then
case "$fdt" in
auto | enabled | internal)
# Simpler to always update submodule, even if not needed.
if test -e "${source_path}/.git" && test $git_update = 'yes' ; then
git_submodules="${git_submodules} dtc"
fi
if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then
fdt=git
mkdir -p dtc
fdt_cflags="-I${source_path}/dtc/libfdt"
fdt_ldflags="-Ldtc/libfdt"
fdt_libs="$fdt_libs"
elif test "$fdt" = "yes" ; then
# Not a git build & no libfdt found, prompt for system install
error_exit "DTC (libfdt) version >= 1.4.2 not present." \
"Please install the DTC (libfdt) devel package"
else
# don't have and don't want
fdt_libs=
fdt=no
fi
fi
fi
;;
esac
##########################################
# opengl probe (for sdl2, gtk, milkymist-tmu2)
@ -5806,56 +5762,12 @@ fi
##########################################
# check for slirp
# slirp is only required when building softmmu targets
if test -z "$slirp" -a "$softmmu" != "yes" ; then
slirp="no"
fi
case "$slirp" in
"" | yes)
if $pkg_config slirp; then
slirp=system
elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then
slirp=git
elif test -e "${source_path}/slirp/Makefile" ; then
slirp=internal
elif test -z "$slirp" ; then
slirp=no
else
feature_not_found "slirp" "Install slirp devel or git submodule"
fi
;;
system)
if ! $pkg_config slirp; then
feature_not_found "slirp" "Install slirp devel"
fi
;;
esac
case "$slirp" in
git | internal)
if test "$slirp" = git; then
auto | enabled | internal)
# Simpler to always update submodule, even if not needed.
if test -e "${source_path}/.git" && test $git_update = 'yes' ; then
git_submodules="${git_submodules} slirp"
fi
mkdir -p slirp
slirp_cflags="-I${source_path}/slirp/src -Islirp/src"
slirp_libs="-Lslirp -lslirp"
if test "$mingw32" = "yes" ; then
slirp_libs="$slirp_libs -lws2_32 -liphlpapi"
fi
;;
system)
slirp_version=$($pkg_config --modversion slirp 2>/dev/null)
slirp_cflags=$($pkg_config --cflags slirp 2>/dev/null)
slirp_libs=$($pkg_config --libs slirp 2>/dev/null)
;;
no)
;;
*)
error_exit "Unknown state for slirp: $slirp"
;;
esac
@ -5906,13 +5818,6 @@ elif test "$fortify_source" = "yes" ; then
QEMU_CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $QEMU_CFLAGS"
debug=no
fi
if test "$debug_info" = "yes"; then
CFLAGS="-g $CFLAGS"
LDFLAGS="-g $LDFLAGS"
fi
if test "$debug" = "no"; then
CFLAGS="-O2 $CFLAGS"
fi
case "$ARCH" in
alpha)
@ -5977,7 +5882,14 @@ fi
# Use ASLR, no-SEH and DEP if available
if test "$mingw32" = "yes" ; then
for flag in --dynamicbase --no-seh --nxcompat; do
flags="--no-seh --nxcompat"
# Disable ASLR for debug builds to allow debugging with gdb
if test "$debug" = "no" ; then
flags="--dynamicbase $flags"
fi
for flag in $flags; do
if ld_has $flag ; then
QEMU_LDFLAGS="-Wl,$flag $QEMU_LDFLAGS"
fi
@ -6135,7 +6047,7 @@ EOF
update_cxxflags
if do_cxx $CXXFLAGS $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then
if do_cxx $CXXFLAGS $CONFIGURE_CXXFLAGS $QEMU_CXXFLAGS -o $TMPE $TMPCXX $TMPO $QEMU_LDFLAGS; then
# C++ compiler $cxx works ok with C compiler $cc
:
else
@ -6151,9 +6063,6 @@ fi
if test $git_update = 'yes' ; then
(cd "${source_path}" && GIT="$git" "./scripts/git-submodule.sh" update "$git_submodules")
fi
if test "$fdt" = "git" ; then
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
fi
config_host_mak="config-host.mak"
@ -6256,16 +6165,7 @@ fi
if test "$guest_agent" = "yes" ; then
echo "CONFIG_GUEST_AGENT=y" >> $config_host_mak
fi
if test "$slirp" != "no"; then
echo "CONFIG_SLIRP=y" >> $config_host_mak
echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak
echo "SLIRP_CFLAGS=$slirp_cflags" >> $config_host_mak
echo "SLIRP_LIBS=$slirp_libs" >> $config_host_mak
fi
subdirs=
if [ "$slirp" = "git" -o "$slirp" = "internal" ]; then
subdirs="$subdirs slirp"
fi
if test "$vde" = "yes" ; then
echo "CONFIG_VDE=y" >> $config_host_mak
echo "VDE_LIBS=$vde_libs" >> $config_host_mak
@ -6591,11 +6491,6 @@ fi
if test "$preadv" = "yes" ; then
echo "CONFIG_PREADV=y" >> $config_host_mak
fi
if test "$fdt" != "no" ; then
echo "CONFIG_FDT=y" >> $config_host_mak
echo "FDT_CFLAGS=$fdt_cflags" >> $config_host_mak
echo "FDT_LIBS=$fdt_ldflags $fdt_libs" >> $config_host_mak
fi
if test "$membarrier" = "yes" ; then
echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
fi
@ -7062,7 +6957,6 @@ echo "RANLIB=$ranlib" >> $config_host_mak
echo "NM=$nm" >> $config_host_mak
echo "PKG_CONFIG=$pkg_config_exe" >> $config_host_mak
echo "WINDRES=$windres" >> $config_host_mak
echo "CFLAGS=$CFLAGS" >> $config_host_mak
echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak
echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak
@ -7135,14 +7029,10 @@ for target in $target_list; do
esac
done
if [ "$fdt" = "git" ]; then
subdirs="$subdirs dtc"
fi
echo "CONFIG_QEMU_INTERP_PREFIX=$interp_prefix" | sed 's/%M/@0@/' >> $config_host_mak
if test "$default_targets" = "yes"; then
echo "CONFIG_DEFAULT_TARGETS=y" >> $config_host_mak
fi
echo "SUBDIRS=$subdirs" >> $config_host_mak
if test "$numa" = "yes"; then
echo "CONFIG_NUMA=y" >> $config_host_mak
@ -7247,24 +7137,29 @@ echo "export PYTHON='$python'" >> "$iotests_common_env"
if test "$skip_meson" = no; then
cross="config-meson.cross.new"
meson_quote() {
echo "['$(echo $* | sed "s/ /','/g")']"
echo "'$(echo $* | sed "s/ /','/g")'"
}
echo "# Automatically generated by configure - do not modify" > $cross
echo "[properties]" >> $cross
test -z "$cxx" && echo "link_language = 'c'" >> $cross
echo "[built-in options]" >> $cross
echo "c_args = [${CFLAGS:+$(meson_quote $CFLAGS)}]" >> $cross
echo "cpp_args = [${CXXFLAGS:+$(meson_quote $CXXFLAGS)}]" >> $cross
echo "c_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross
echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross
echo "[binaries]" >> $cross
echo "c = $(meson_quote $cc)" >> $cross
test -n "$cxx" && echo "cpp = $(meson_quote $cxx)" >> $cross
echo "ar = $(meson_quote $ar)" >> $cross
echo "nm = $(meson_quote $nm)" >> $cross
echo "pkgconfig = $(meson_quote $pkg_config_exe)" >> $cross
echo "ranlib = $(meson_quote $ranlib)" >> $cross
echo "c = [$(meson_quote $cc)]" >> $cross
test -n "$cxx" && echo "cpp = [$(meson_quote $cxx)]" >> $cross
echo "ar = [$(meson_quote $ar)]" >> $cross
echo "nm = [$(meson_quote $nm)]" >> $cross
echo "pkgconfig = [$(meson_quote $pkg_config_exe)]" >> $cross
echo "ranlib = [$(meson_quote $ranlib)]" >> $cross
if has $sdl2_config; then
echo "sdl2-config = $(meson_quote $sdl2_config)" >> $cross
echo "sdl2-config = [$(meson_quote $sdl2_config)]" >> $cross
fi
echo "strip = $(meson_quote $strip)" >> $cross
echo "windres = $(meson_quote $windres)" >> $cross
echo "strip = [$(meson_quote $strip)]" >> $cross
echo "windres = [$(meson_quote $windres)]" >> $cross
if test -n "$cross_prefix"; then
cross_arg="--cross-file config-meson.cross"
echo "[host_machine]" >> $cross
@ -7321,7 +7216,7 @@ NINJA=${ninja:-$PWD/ninjatool} $meson setup \
-Dcocoa=$cocoa -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \
-Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \
-Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f \
-Dcapstone=$capstone \
-Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt \
$cross_arg \
"$PWD" "$source_path"

View File

@ -2,3 +2,4 @@ TARGET_ARCH=aarch64
TARGET_BASE_ARCH=arm
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml
TARGET_NEED_FDT=y

View File

@ -1,3 +1,4 @@
TARGET_ARCH=arm
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml
TARGET_NEED_FDT=y

View File

@ -1,3 +1,4 @@
TARGET_ARCH=microblaze
TARGET_WORDS_BIGENDIAN=y
TARGET_SUPPORTS_MTTCG=y
TARGET_NEED_FDT=y

View File

@ -1,2 +1,3 @@
TARGET_ARCH=microblaze
TARGET_SUPPORTS_MTTCG=y
TARGET_NEED_FDT=y

View File

@ -1,3 +1,4 @@
TARGET_ARCH=mips64
TARGET_BASE_ARCH=mips
TARGET_ALIGNED_ONLY=y
TARGET_NEED_FDT=y

View File

@ -1,3 +1,4 @@
TARGET_ARCH=ppc
TARGET_WORDS_BIGENDIAN=y
TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml
TARGET_NEED_FDT=y

View File

@ -3,3 +3,4 @@ TARGET_BASE_ARCH=ppc
TARGET_WORDS_BIGENDIAN=y
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml
TARGET_NEED_FDT=y

View File

@ -2,3 +2,4 @@ TARGET_ARCH=riscv32
TARGET_BASE_ARCH=riscv
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-csr.xml gdb-xml/riscv-32bit-virtual.xml
TARGET_NEED_FDT=y

View File

@ -2,3 +2,4 @@ TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv
TARGET_SUPPORTS_MTTCG=y
TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-csr.xml gdb-xml/riscv-64bit-virtual.xml
TARGET_NEED_FDT=y

View File

@ -1,2 +1,3 @@
TARGET_ARCH=rx
TARGET_XML_FILES= gdb-xml/rx-core.xml
TARGET_NEED_FDT=y

View File

@ -13,7 +13,7 @@
#include "trace/trace-root.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "qemu/range.h"
/* #define DEBUG_IOMMU */
@ -151,7 +151,7 @@ static void dma_blk_cb(void *opaque, int ret)
* from several sectors. This code splits all SGs into several
* groups. SGs in every group do not overlap.
*/
if (mem && use_icount && dbs->dir == DMA_DIRECTION_FROM_DEVICE) {
if (mem && icount_enabled() && dbs->dir == DMA_DIRECTION_FROM_DEVICE) {
int i;
for (i = 0 ; i < dbs->iov.niov ; ++i) {
if (ranges_overlap((intptr_t)dbs->iov.iov[i].iov_base,

View File

@ -707,6 +707,11 @@ Snapshot table entry:
Byte 48 - 55: Virtual disk size of the snapshot in bytes
Byte 56 - 63: icount value which corresponds to
the record/replay instruction count
when the snapshot was taken. Set to -1
if icount was disabled
Version 3 images must include extra data at least up to
byte 55.

View File

@ -184,11 +184,11 @@ is then incremented (which is called "warping" the virtual clock) as
soon as the timer fires or the CPUs need to go out of the idle state.
Two functions are used for this purpose; because these actions change
virtual machine state and must be deterministic, each of them creates a
checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so
starts accounting real time to virtual clock. qemu_account_warp_timer
checkpoint. icount_start_warp_timer checks if the CPUs are idle and if so
starts accounting real time to virtual clock. icount_account_warp_timer
is called when the CPUs get an interrupt or when the warp timer fires,
and it warps the virtual clock by the amount of real time that has passed
since qemu_start_warp_timer.
since icount_start_warp_timer.
Bottom halves
-------------
@ -265,6 +265,16 @@ of the original disk image, use overlay files linked to the original images.
Therefore all new snapshots (including the starting one) will be saved in
overlays and the original image remains unchanged.
When you need to use snapshots with diskless virtual machine,
it must be started with 'orphan' qcow2 image. This image will be used
for storing VM snapshots. Here is the example of the command line for this:
qemu-system-i386 -icount shift=3,rr=replay,rrfile=record.bin,rrsnapshot=init \
-net none -drive file=empty.qcow2,if=none,id=rr
empty.qcow2 drive does not connected to any virtual block device and used
for VM snapshots only.
Network devices
---------------
@ -294,6 +304,42 @@ for recording and replaying must contain identical number of ports in record
and replay modes, but their backends may differ.
E.g., '-serial stdio' in record mode, and '-serial null' in replay mode.
Reverse debugging
-----------------
Reverse debugging allows "executing" the program in reverse direction.
GDB remote protocol supports "reverse step" and "reverse continue"
commands. The first one steps single instruction backwards in time,
and the second one finds the last breakpoint in the past.
Recorded executions may be used to enable reverse debugging. QEMU can't
execute the code in backwards direction, but can load a snapshot and
replay forward to find the desired position or breakpoint.
The following GDB commands are supported:
- reverse-stepi (or rsi) - step one instruction backwards
- reverse-continue (or rc) - find last breakpoint in the past
Reverse step loads the nearest snapshot and replays the execution until
the required instruction is met.
Reverse continue may include several passes of examining the execution
between the snapshots. Each of the passes include the following steps:
1. loading the snapshot
2. replaying to examine the breakpoints
3. if breakpoint or watchpoint was met
- loading the snaphot again
- replaying to the required breakpoint
4. else
- proceeding to the p.1 with the earlier snapshot
Therefore usage of the reverse debugging requires at least one snapshot
created in advance. This can be done by omitting 'snapshot' option
for the block drives and adding 'rrsnapshot' for both record and replay
command lines.
See the "Snapshotting" section to learn more about running record/replay
and creating the snapshot in these modes.
Replay log format
-----------------

12
exec.c
View File

@ -102,10 +102,6 @@ uintptr_t qemu_host_page_size;
intptr_t qemu_host_page_mask;
#if !defined(CONFIG_USER_ONLY)
/* 0 = Do not count executed instructions.
1 = Precise instruction counting.
2 = Adaptive rate instruction counting. */
int use_icount;
typedef struct PhysPageEntry PhysPageEntry;
@ -2752,6 +2748,14 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len,
QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
if (watchpoint_address_matches(wp, addr, len)
&& (wp->flags & flags)) {
if (replay_running_debug()) {
/*
* Don't process the watchpoints when we are
* in a reverse debugging operation.
*/
replay_breakpoint();
return;
}
if (flags == BP_MEM_READ) {
wp->flags |= BP_WATCHPOINT_HIT_READ;
} else {

View File

@ -51,6 +51,7 @@
#include "sysemu/runstate.h"
#include "hw/semihosting/semihost.h"
#include "exec/exec-all.h"
#include "sysemu/replay.h"
#ifdef CONFIG_USER_ONLY
#define GDB_ATTACHED "0"
@ -375,6 +376,20 @@ typedef struct GDBState {
*/
static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
/* Retrieves flags for single step mode. */
static int get_sstep_flags(void)
{
/*
* In replay mode all events written into the log should be replayed.
* That is why NOIRQ flag is removed in this mode.
*/
if (replay_mode != REPLAY_MODE_NONE) {
return SSTEP_ENABLE;
} else {
return sstep_flags;
}
}
static GDBState gdbserver_state;
static void init_gdbserver_state(void)
@ -501,7 +516,7 @@ static int gdb_continue_partial(char *newstates)
break; /* nothing to do here */
case 's':
trace_gdbstub_op_stepping(cpu->cpu_index);
cpu_single_step(cpu, sstep_flags);
cpu_single_step(cpu, get_sstep_flags());
cpu_resume(cpu);
flag = 1;
break;
@ -1874,10 +1889,38 @@ static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx)
gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull);
}
cpu_single_step(gdbserver_state.c_cpu, sstep_flags);
cpu_single_step(gdbserver_state.c_cpu, get_sstep_flags());
gdb_continue();
}
static void handle_backward(GdbCmdContext *gdb_ctx, void *user_ctx)
{
if (replay_mode != REPLAY_MODE_PLAY) {
put_packet("E22");
}
if (gdb_ctx->num_params == 1) {
switch (gdb_ctx->params[0].opcode) {
case 's':
if (replay_reverse_step()) {
gdb_continue();
} else {
put_packet("E14");
}
return;
case 'c':
if (replay_reverse_continue()) {
gdb_continue();
} else {
put_packet("E14");
}
return;
}
}
/* Default invalid command */
put_packet("");
}
static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx)
{
put_packet("vCont;c;C;s;S");
@ -2124,6 +2167,11 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
}
if (replay_mode == REPLAY_MODE_PLAY) {
g_string_append(gdbserver_state.str_buf,
";ReverseStep+;ReverseContinue+");
}
if (gdb_ctx->num_params &&
strstr(gdb_ctx->params[0].data, "multiprocess+")) {
gdbserver_state.multiprocess = true;
@ -2460,6 +2508,17 @@ static int gdb_handle_packet(const char *line_buf)
cmd_parser = &step_cmd_desc;
}
break;
case 'b':
{
static const GdbCmdParseEntry backward_cmd_desc = {
.handler = handle_backward,
.cmd = "b",
.cmd_startswith = 1,
.schema = "o0"
};
cmd_parser = &backward_cmd_desc;
}
break;
case 'F':
{
static const GdbCmdParseEntry file_io_cmd_desc = {
@ -3262,6 +3321,7 @@ static void gdb_chr_event(void *opaque, QEMUChrEvent event)
s->g_cpu = s->c_cpu;
vm_stop(RUN_STATE_PAUSED);
replay_gdb_attached();
gdb_has_xml = false;
break;
default:

View File

@ -881,4 +881,15 @@ SRST
Show SEV information.
ERST
{
.name = "replay",
.args_type = "",
.params = "",
.help = "show record/replay information",
.cmd = hmp_info_replay,
},
SRST
``info replay``
Display the record/replay information: mode and the current icount.
ERST

View File

@ -1804,6 +1804,56 @@ SRST
Set QOM property *property* of object at location *path* to value *value*
ERST
{
.name = "replay_break",
.args_type = "icount:i",
.params = "icount",
.help = "set breakpoint at the specified instruction count",
.cmd = hmp_replay_break,
},
SRST
``replay_break`` *icount*
Set replay breakpoint at instruction count *icount*.
Execution stops when the specified instruction is reached.
There can be at most one breakpoint. When breakpoint is set, any prior
one is removed. The breakpoint may be set only in replay mode and only
"in the future", i.e. at instruction counts greater than the current one.
The current instruction count can be observed with ``info replay``.
ERST
{
.name = "replay_delete_break",
.args_type = "",
.params = "",
.help = "remove replay breakpoint",
.cmd = hmp_replay_delete_break,
},
SRST
``replay_delete_break``
Remove replay breakpoint which was previously set with ``replay_break``.
The command is ignored when there are no replay breakpoints.
ERST
{
.name = "replay_seek",
.args_type = "icount:i",
.params = "icount",
.help = "replay execution to the specified instruction count",
.cmd = hmp_replay_seek,
},
SRST
``replay_seek`` *icount*
Automatically proceed to the instruction count *icount*, when
replaying the execution. The command automatically loads nearest
snapshot and replays the execution to find the desired instruction.
When there is no preceding snapshot or the execution is not replayed,
then the command fails.
*icount* for the reference may be observed with ``info replay`` command.
ERST
{
.name = "info",
.args_type = "item:s?",

View File

@ -33,8 +33,7 @@
#include "hw/qdev-properties.h"
#include "trace/trace-root.h"
#include "qemu/plugin.h"
CPUInterruptHandler cpu_interrupt_handler;
#include "sysemu/hw_accel.h"
CPUState *cpu_by_arch_id(int64_t id)
{
@ -393,17 +392,6 @@ static vaddr cpu_adjust_watchpoint_address(CPUState *cpu, vaddr addr, int len)
return addr;
}
static void generic_handle_interrupt(CPUState *cpu, int mask)
{
cpu->interrupt_request |= mask;
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
}
}
CPUInterruptHandler cpu_interrupt_handler = generic_handle_interrupt;
static void cpu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);

View File

@ -7,11 +7,11 @@
*/
#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "hw/ptimer.h"
#include "migration/vmstate.h"
#include "qemu/host-utils.h"
#include "sysemu/replay.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/qtest.h"
#include "block/aio.h"
#include "sysemu/cpus.h"
@ -134,7 +134,8 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust)
* on the current generation of host machines.
*/
if (s->enabled == 1 && (delta * period < 10000) && !use_icount) {
if (s->enabled == 1 && (delta * period < 10000) &&
!icount_enabled() && !qtest_enabled()) {
period = 10000 / delta;
period_frac = 0;
}
@ -217,7 +218,8 @@ uint64_t ptimer_get_count(ptimer_state *s)
uint32_t period_frac = s->period_frac;
uint64_t period = s->period;
if (!oneshot && (s->delta * period < 10000) && !use_icount) {
if (!oneshot && (s->delta * period < 10000) &&
!icount_enabled() && !qtest_enabled()) {
period = 10000 / s->delta;
period_frac = 0;
}

View File

@ -34,6 +34,7 @@
#include "sysemu/numa.h"
#include "sysemu/replay.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpu-timers.h"
#include "trace.h"
#include "hw/i386/x86.h"
@ -521,7 +522,7 @@ static long get_file_size(FILE *f)
/* TSC handling */
uint64_t cpu_get_tsc(CPUX86State *env)
{
return cpu_get_ticks();
return cpus_get_elapsed_ticks();
}
/* IRQ handling */

View File

@ -42,6 +42,7 @@ typedef struct QEMUSnapshotInfo {
uint32_t date_sec; /* UTC date of the snapshot */
uint32_t date_nsec;
uint64_t vm_clock_nsec; /* VM clock relative to boot */
uint64_t icount; /* record/replay step */
} QEMUSnapshotInfo;
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,

View File

@ -407,8 +407,12 @@ static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr)
return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK);
}
#ifdef CONFIG_TCG
void dump_drift_info(void);
void dump_exec_info(void);
void dump_opcount_info(void);
#endif /* CONFIG_TCG */
#endif /* !CONFIG_USER_ONLY */
/* Returns: 0 on success, -1 on error */

View File

@ -25,7 +25,7 @@
#ifdef CONFIG_TCG
#include "exec/cpu_ldst.h"
#endif
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
/* allow to see translation results - the slowdown should be negligible, so we leave it */
#define DEBUG_DISAS
@ -497,7 +497,7 @@ static inline uint32_t tb_cflags(const TranslationBlock *tb)
static inline uint32_t curr_cflags(void)
{
return (parallel_cpus ? CF_PARALLEL : 0)
| (use_icount ? CF_USE_ICOUNT : 0);
| (icount_enabled() ? CF_USE_ICOUNT : 0);
}
/* TranslationBlock invalidate API */

View File

@ -844,12 +844,6 @@ bool cpu_exists(int64_t id);
*/
CPUState *cpu_by_arch_id(int64_t id);
#ifndef CONFIG_USER_ONLY
typedef void (*CPUInterruptHandler)(CPUState *, int);
extern CPUInterruptHandler cpu_interrupt_handler;
/**
* cpu_interrupt:
* @cpu: The CPU to set an interrupt on.
@ -857,17 +851,9 @@ extern CPUInterruptHandler cpu_interrupt_handler;
*
* Invokes the interrupt handler.
*/
static inline void cpu_interrupt(CPUState *cpu, int mask)
{
cpu_interrupt_handler(cpu, mask);
}
#else /* USER_ONLY */
void cpu_interrupt(CPUState *cpu, int mask);
#endif /* USER_ONLY */
#ifdef NEED_CPU_H
#ifdef CONFIG_SOFTMMU

View File

@ -129,5 +129,9 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict);
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
void hmp_info_sev(Monitor *mon, const QDict *qdict);
void hmp_info_replay(Monitor *mon, const QDict *qdict);
void hmp_replay_break(Monitor *mon, const QDict *qdict);
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict);
void hmp_replay_seek(Monitor *mon, const QDict *qdict);
#endif

View File

@ -166,8 +166,8 @@ bool qemu_clock_expired(QEMUClockType type);
*
* Determine whether a clock should be used for deadline
* calculations. Some clocks, for instance vm_clock with
* use_icount set, do not count in nanoseconds. Such clocks
* are not used for deadline calculations, and are presumed
* icount_enabled() set, do not count in nanoseconds.
* Such clocks are not used for deadline calculations, and are presumed
* to interrupt any poll using qemu_notify/aio_notify
* etc.
*
@ -224,13 +224,6 @@ void qemu_clock_notify(QEMUClockType type);
*/
void qemu_clock_enable(QEMUClockType type, bool enabled);
/**
* qemu_start_warp_timer:
*
* Starts a timer for virtual clock update
*/
void qemu_start_warp_timer(void);
/**
* qemu_clock_run_timers:
* @type: clock on which to operate
@ -791,12 +784,6 @@ static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2)
*/
void init_clocks(QEMUTimerListNotifyCB *notify_cb);
int64_t cpu_get_ticks(void);
/* Caller must hold BQL */
void cpu_enable_ticks(void);
/* Caller must hold BQL */
void cpu_disable_ticks(void);
static inline int64_t get_max_clock_jump(void)
{
/* This should be small enough to prevent excessive interrupts from being
@ -850,13 +837,6 @@ static inline int64_t get_clock(void)
}
#endif
/* icount */
int64_t cpu_get_icount_raw(void);
int64_t cpu_get_icount(void);
int64_t cpu_get_clock(void);
int64_t cpu_icount_to_ns(int64_t icount);
void cpu_update_icount(CPUState *cpu);
/*******************************************/
/* host CPU ticks (if available) */

View File

@ -0,0 +1,90 @@
/*
* CPU timers state API
*
* Copyright 2020 SUSE LLC
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef SYSEMU_CPU_TIMERS_H
#define SYSEMU_CPU_TIMERS_H
#include "qemu/timer.h"
/* init the whole cpu timers API, including icount, ticks, and cpu_throttle */
void cpu_timers_init(void);
/* icount - Instruction Counter API */
/*
* icount enablement state:
*
* 0 = Disabled - Do not count executed instructions.
* 1 = Enabled - Fixed conversion of insn to ns via "shift" option
* 2 = Enabled - Runtime adaptive algorithm to compute shift
*/
#ifdef CONFIG_TCG
extern int use_icount;
#define icount_enabled() (use_icount)
#else
#define icount_enabled() 0
#endif
/*
* Update the icount with the executed instructions. Called by
* cpus-tcg vCPU thread so the main-loop can see time has moved forward.
*/
void icount_update(CPUState *cpu);
/* get raw icount value */
int64_t icount_get_raw(void);
/* return the virtual CPU time in ns, based on the instruction counter. */
int64_t icount_get(void);
/*
* convert an instruction counter value to ns, based on the icount shift.
* This shift is set as a fixed value with the icount "shift" option
* (precise mode), or it is constantly approximated and corrected at
* runtime in adaptive mode.
*/
int64_t icount_to_ns(int64_t icount);
/* configure the icount options, including "shift" */
void icount_configure(QemuOpts *opts, Error **errp);
/* used by tcg vcpu thread to calc icount budget */
int64_t icount_round(int64_t count);
/* if the CPUs are idle, start accounting real time to virtual clock. */
void icount_start_warp_timer(void);
void icount_account_warp_timer(void);
/*
* CPU Ticks and Clock
*/
/* Caller must hold BQL */
void cpu_enable_ticks(void);
/* Caller must hold BQL */
void cpu_disable_ticks(void);
/*
* return the time elapsed in VM between vm_start and vm_stop.
* cpu_get_ticks() uses units of the host CPU cycle counter.
*/
int64_t cpu_get_ticks(void);
/*
* Returns the monotonic time elapsed in VM, i.e.,
* the time between vm_start and vm_stop
*/
int64_t cpu_get_clock(void);
void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
int64_t cpus_get_virtual_clock(void);
int64_t cpus_get_elapsed_ticks(void);
#endif /* SYSEMU_CPU_TIMERS_H */

View File

@ -4,33 +4,61 @@
#include "qemu/timer.h"
/* cpus.c */
/* CPU execution threads */
typedef struct CpusAccel {
void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY */
void (*kick_vcpu_thread)(CPUState *cpu);
void (*synchronize_post_reset)(CPUState *cpu);
void (*synchronize_post_init)(CPUState *cpu);
void (*synchronize_state)(CPUState *cpu);
void (*synchronize_pre_loadvm)(CPUState *cpu);
void (*handle_interrupt)(CPUState *cpu, int mask);
int64_t (*get_virtual_clock)(void);
int64_t (*get_elapsed_ticks)(void);
} CpusAccel;
/* register accel-specific cpus interface implementation */
void cpus_register_accel(const CpusAccel *i);
/* interface available for cpus accelerator threads */
/* For temporary buffers for forming a name */
#define VCPU_THREAD_NAME_SIZE 16
void cpus_kick_thread(CPUState *cpu);
bool cpu_work_list_empty(CPUState *cpu);
bool cpu_thread_is_idle(CPUState *cpu);
bool all_cpu_threads_idle(void);
bool cpu_can_run(CPUState *cpu);
void qemu_wait_io_event_common(CPUState *cpu);
void qemu_wait_io_event(CPUState *cpu);
void cpu_thread_signal_created(CPUState *cpu);
void cpu_thread_signal_destroyed(CPUState *cpu);
void cpu_handle_guest_debug(CPUState *cpu);
/* end interface for cpus accelerator threads */
bool qemu_in_vcpu_thread(void);
void qemu_init_cpu_loop(void);
void resume_all_vcpus(void);
void pause_all_vcpus(void);
void cpu_stop_current(void);
void cpu_ticks_init(void);
void configure_icount(QemuOpts *opts, Error **errp);
extern int use_icount;
extern int icount_align_option;
/* drift information for info jit command */
extern int64_t max_delay;
extern int64_t max_advance;
void dump_drift_info(void);
/* Unblock cpu */
void qemu_cpu_kick_self(void);
void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
void cpu_synchronize_all_states(void);
void cpu_synchronize_all_post_reset(void);
void cpu_synchronize_all_post_init(void);
void cpu_synchronize_all_pre_loadvm(void);
void qtest_clock_warp(int64_t dest);
#ifndef CONFIG_USER_ONLY
/* vl.c */
/* *-user doesn't have configurable SMP topology */

View File

@ -22,29 +22,12 @@
#ifndef QEMU_HAX_H
#define QEMU_HAX_H
int hax_sync_vcpus(void);
int hax_init_vcpu(CPUState *cpu);
int hax_smp_cpu_exec(CPUState *cpu);
int hax_populate_ram(uint64_t va, uint64_t size);
void hax_cpu_synchronize_state(CPUState *cpu);
void hax_cpu_synchronize_post_reset(CPUState *cpu);
void hax_cpu_synchronize_post_init(CPUState *cpu);
void hax_cpu_synchronize_pre_loadvm(CPUState *cpu);
#ifdef CONFIG_HAX
int hax_enabled(void);
#include "qemu/bitops.h"
#include "exec/memory.h"
int hax_vcpu_destroy(CPUState *cpu);
void hax_raise_event(CPUState *cpu);
void hax_reset_vcpu_state(void *opaque);
#include "target/i386/hax-interface.h"
#include "target/i386/hax-i386.h"
#else /* CONFIG_HAX */
#define hax_enabled() (0)

View File

@ -26,14 +26,6 @@ extern bool hvf_allowed;
#define hvf_get_supported_cpuid(func, idx, reg) 0
#endif /* !CONFIG_HVF */
int hvf_init_vcpu(CPUState *);
int hvf_vcpu_exec(CPUState *);
void hvf_cpu_synchronize_state(CPUState *);
void hvf_cpu_synchronize_post_reset(CPUState *);
void hvf_cpu_synchronize_post_init(CPUState *);
void hvf_cpu_synchronize_pre_loadvm(CPUState *);
void hvf_vcpu_destroy(CPUState *);
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
typedef struct HVFState HVFState;

View File

@ -1,5 +1,5 @@
/*
* QEMU Hardware accelertors support
* QEMU Hardware accelerators support
*
* Copyright 2016 Google, Inc.
*
@ -17,68 +17,9 @@
#include "sysemu/hvf.h"
#include "sysemu/whpx.h"
static inline void cpu_synchronize_state(CPUState *cpu)
{
if (kvm_enabled()) {
kvm_cpu_synchronize_state(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_state(cpu);
}
if (hvf_enabled()) {
hvf_cpu_synchronize_state(cpu);
}
if (whpx_enabled()) {
whpx_cpu_synchronize_state(cpu);
}
}
static inline void cpu_synchronize_post_reset(CPUState *cpu)
{
if (kvm_enabled()) {
kvm_cpu_synchronize_post_reset(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_post_reset(cpu);
}
if (hvf_enabled()) {
hvf_cpu_synchronize_post_reset(cpu);
}
if (whpx_enabled()) {
whpx_cpu_synchronize_post_reset(cpu);
}
}
static inline void cpu_synchronize_post_init(CPUState *cpu)
{
if (kvm_enabled()) {
kvm_cpu_synchronize_post_init(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_post_init(cpu);
}
if (hvf_enabled()) {
hvf_cpu_synchronize_post_init(cpu);
}
if (whpx_enabled()) {
whpx_cpu_synchronize_post_init(cpu);
}
}
static inline void cpu_synchronize_pre_loadvm(CPUState *cpu)
{
if (kvm_enabled()) {
kvm_cpu_synchronize_pre_loadvm(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_pre_loadvm(cpu);
}
if (hvf_enabled()) {
hvf_cpu_synchronize_pre_loadvm(cpu);
}
if (whpx_enabled()) {
whpx_cpu_synchronize_pre_loadvm(cpu);
}
}
void cpu_synchronize_state(CPUState *cpu);
void cpu_synchronize_post_reset(CPUState *cpu);
void cpu_synchronize_post_init(CPUState *cpu);
void cpu_synchronize_pre_loadvm(CPUState *cpu);
#endif /* QEMU_HW_ACCEL_H */

View File

@ -223,10 +223,6 @@ int kvm_has_many_ioeventfds(void);
int kvm_has_gsi_routing(void);
int kvm_has_intx_set_mask(void);
int kvm_init_vcpu(CPUState *cpu);
int kvm_cpu_exec(CPUState *cpu);
int kvm_destroy_vcpu(CPUState *cpu);
/**
* kvm_arm_supports_user_irq
*
@ -486,9 +482,6 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram_addr,
#endif /* NEED_CPU_H */
void kvm_cpu_synchronize_state(CPUState *cpu);
void kvm_cpu_synchronize_post_reset(CPUState *cpu);
void kvm_cpu_synchronize_post_init(CPUState *cpu);
void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu);
void kvm_init_cpu_signals(CPUState *cpu);

View File

@ -30,4 +30,6 @@ void qtest_server_set_send_handler(void (*send)(void *, const char *),
void *opaque);
void qtest_server_inproc_recv(void *opaque, const char *buf);
int64_t qtest_get_virtual_clock(void);
#endif

View File

@ -14,6 +14,7 @@
#include "qapi/qapi-types-misc.h"
#include "qapi/qapi-types-run-state.h"
#include "qapi/qapi-types-replay.h"
#include "qapi/qapi-types-ui.h"
#include "block/aio.h"
@ -72,6 +73,29 @@ void replay_start(void);
void replay_finish(void);
/*! Adds replay blocker with the specified error description */
void replay_add_blocker(Error *reason);
/* Returns name of the replay log file */
const char *replay_get_filename(void);
/*
* Start making one step in backward direction.
* Used by gdbstub for backwards debugging.
* Returns true on success.
*/
bool replay_reverse_step(void);
/*
* Start searching the last breakpoint/watchpoint.
* Used by gdbstub for backwards debugging.
* Returns true if the process successfully started.
*/
bool replay_reverse_continue(void);
/*
* Returns true if replay module is processing
* reverse_continue or reverse_step request
*/
bool replay_running_debug(void);
/* Called in reverse debugging mode to collect breakpoint information */
void replay_breakpoint(void);
/* Called when gdb is attached to gdbstub */
void replay_gdb_attached(void);
/* Processing the instructions */
@ -109,12 +133,12 @@ int64_t replay_read_clock(ReplayClockKind kind);
#define REPLAY_CLOCK(clock, value) \
(replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
: replay_mode == REPLAY_MODE_RECORD \
? replay_save_clock((clock), (value), cpu_get_icount_raw()) \
? replay_save_clock((clock), (value), icount_get_raw()) \
: (value))
#define REPLAY_CLOCK_LOCKED(clock, value) \
(replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
: replay_mode == REPLAY_MODE_RECORD \
? replay_save_clock((clock), (value), cpu_get_icount_raw_locked()) \
? replay_save_clock((clock), (value), icount_get_raw_locked()) \
: (value))
/* Processing data from random generators */
@ -146,6 +170,8 @@ void replay_disable_events(void);
void replay_enable_events(void);
/*! Returns true when saving events is enabled */
bool replay_events_enabled(void);
/* Flushes events queue */
void replay_flush_events(void);
/*! Adds bottom half event to the queue */
void replay_bh_schedule_event(QEMUBH *bh);
/* Adds oneshot bottom half event to the queue */

View File

@ -13,18 +13,6 @@
#ifndef QEMU_WHPX_H
#define QEMU_WHPX_H
int whpx_init_vcpu(CPUState *cpu);
int whpx_vcpu_exec(CPUState *cpu);
void whpx_destroy_vcpu(CPUState *cpu);
void whpx_vcpu_kick(CPUState *cpu);
void whpx_cpu_synchronize_state(CPUState *cpu);
void whpx_cpu_synchronize_post_reset(CPUState *cpu);
void whpx_cpu_synchronize_post_init(CPUState *cpu);
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu);
#ifdef CONFIG_WHPX
int whpx_enabled(void);
@ -35,11 +23,4 @@ int whpx_enabled(void);
#endif /* CONFIG_WHPX */
/* state subset only touched by the VCPU itself during runtime */
#define WHPX_SET_RUNTIME_STATE 1
/* state subset modified during VCPU reset */
#define WHPX_SET_RESET_STATE 2
/* full state set, modified during initialization or on vmload */
#define WHPX_SET_FULL_STATE 3
#endif /* QEMU_WHPX_H */

View File

@ -300,11 +300,6 @@ else
xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'),
method: 'pkg-config', static: enable_static)
endif
slirp = not_found
if config_host.has_key('CONFIG_SLIRP')
slirp = declare_dependency(compile_args: config_host['SLIRP_CFLAGS'].split(),
link_args: config_host['SLIRP_LIBS'].split())
endif
vde = not_found
if config_host.has_key('CONFIG_VDE')
vde = declare_dependency(link_args: config_host['VDE_LIBS'].split())
@ -536,11 +531,6 @@ if get_option('vnc').enabled()
compile_args: '-DSTRUCT_IOVEC_DEFINED')
endif
endif
fdt = not_found
if 'CONFIG_FDT' in config_host
fdt = declare_dependency(compile_args: config_host['FDT_CFLAGS'].split(),
link_args: config_host['FDT_LIBS'].split())
endif
snappy = not_found
if 'CONFIG_SNAPPY' in config_host
snappy = declare_dependency(link_args: config_host['SNAPPY_LIBS'].split())
@ -728,6 +718,7 @@ ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ]
default_targets = 'CONFIG_DEFAULT_TARGETS' in config_host
actual_target_dirs = []
fdt_required = []
foreach target : target_dirs
config_target = { 'TARGET_NAME': target.split('-')[0] }
if target.endswith('linux-user')
@ -779,6 +770,10 @@ foreach target : target_dirs
config_target += keyval.load('default-configs/targets' / target + '.mak')
config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' }
if 'TARGET_NEED_FDT' in config_target
fdt_required += target
endif
# Add default keys
if 'TARGET_BASE_ARCH' not in config_target
config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']}
@ -978,7 +973,135 @@ if capstone_opt == 'internal'
capstone = declare_dependency(link_with: libcapstone,
include_directories: 'capstone/include/capstone')
endif
slirp = not_found
slirp_opt = 'disabled'
if have_system
slirp_opt = get_option('slirp')
if slirp_opt in ['enabled', 'auto', 'system']
have_internal = fs.exists(meson.current_source_dir() / 'slirp/meson.build')
slirp = dependency('slirp', static: enable_static,
method: 'pkg-config',
required: slirp_opt == 'system' or
slirp_opt == 'enabled' and not have_internal)
if slirp.found()
slirp_opt = 'system'
elif have_internal
slirp_opt = 'internal'
else
slirp_opt = 'disabled'
endif
endif
if slirp_opt == 'internal'
slirp_deps = []
if targetos == 'windows'
slirp_deps = cc.find_library('iphlpapi')
endif
slirp_conf = configuration_data()
slirp_conf.set('SLIRP_MAJOR_VERSION', meson.project_version().split('.')[0])
slirp_conf.set('SLIRP_MINOR_VERSION', meson.project_version().split('.')[1])
slirp_conf.set('SLIRP_MICRO_VERSION', meson.project_version().split('.')[2])
slirp_conf.set_quoted('SLIRP_VERSION_STRING', meson.project_version())
slirp_cargs = ['-DG_LOG_DOMAIN="Slirp"']
slirp_files = [
'slirp/src/arp_table.c',
'slirp/src/bootp.c',
'slirp/src/cksum.c',
'slirp/src/dhcpv6.c',
'slirp/src/dnssearch.c',
'slirp/src/if.c',
'slirp/src/ip6_icmp.c',
'slirp/src/ip6_input.c',
'slirp/src/ip6_output.c',
'slirp/src/ip_icmp.c',
'slirp/src/ip_input.c',
'slirp/src/ip_output.c',
'slirp/src/mbuf.c',
'slirp/src/misc.c',
'slirp/src/ncsi.c',
'slirp/src/ndp_table.c',
'slirp/src/sbuf.c',
'slirp/src/slirp.c',
'slirp/src/socket.c',
'slirp/src/state.c',
'slirp/src/stream.c',
'slirp/src/tcp_input.c',
'slirp/src/tcp_output.c',
'slirp/src/tcp_subr.c',
'slirp/src/tcp_timer.c',
'slirp/src/tftp.c',
'slirp/src/udp.c',
'slirp/src/udp6.c',
'slirp/src/util.c',
'slirp/src/version.c',
'slirp/src/vmstate.c',
]
configure_file(
input : 'slirp/src/libslirp-version.h.in',
output : 'libslirp-version.h',
configuration: slirp_conf)
slirp_inc = include_directories('slirp', 'slirp/src')
libslirp = static_library('slirp',
sources: slirp_files,
c_args: slirp_cargs,
include_directories: slirp_inc)
slirp = declare_dependency(link_with: libslirp,
dependencies: slirp_deps,
include_directories: slirp_inc)
endif
endif
fdt = not_found
fdt_opt = get_option('fdt')
if have_system
if fdt_opt in ['enabled', 'auto', 'system']
have_internal = fs.exists(meson.current_source_dir() / 'dtc/libfdt/Makefile.libfdt')
fdt = cc.find_library('fdt', static: enable_static,
required: fdt_opt == 'system' or
fdt_opt == 'enabled' and not have_internal)
if fdt.found() and cc.links('''
#include <libfdt.h>
#include <libfdt_env.h>
int main(void) { fdt_check_full(NULL, 0); return 0; }''',
dependencies: fdt)
fdt_opt = 'system'
elif have_internal
fdt_opt = 'internal'
else
fdt_opt = 'disabled'
endif
endif
if fdt_opt == 'internal'
fdt_files = files(
'dtc/libfdt/fdt.c',
'dtc/libfdt/fdt_ro.c',
'dtc/libfdt/fdt_wip.c',
'dtc/libfdt/fdt_sw.c',
'dtc/libfdt/fdt_rw.c',
'dtc/libfdt/fdt_strerror.c',
'dtc/libfdt/fdt_empty_tree.c',
'dtc/libfdt/fdt_addresses.c',
'dtc/libfdt/fdt_overlay.c',
'dtc/libfdt/fdt_check.c',
)
fdt_inc = include_directories('dtc/libfdt')
libfdt = static_library('fdt',
sources: fdt_files,
include_directories: fdt_inc)
fdt = declare_dependency(link_with: libfdt,
include_directories: fdt_inc)
endif
endif
if not fdt.found() and fdt_required.length() > 0
error('fdt not available but required by targets ' + ', '.join(fdt_required))
endif
config_host_data.set('CONFIG_CAPSTONE', capstone.found())
config_host_data.set('CONFIG_FDT', fdt.found())
config_host_data.set('CONFIG_SLIRP', slirp.found())
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
@ -1247,7 +1370,7 @@ softmmu_ss.add(files(
softmmu_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
softmmu_ss.add(when: 'CONFIG_SECCOMP', if_true: [files('qemu-seccomp.c'), seccomp])
softmmu_ss.add(when: ['CONFIG_FDT', fdt], if_true: [files('device_tree.c')])
softmmu_ss.add(when: fdt, if_true: files('device_tree.c'))
common_ss.add(files('cpus-common.c'))
@ -1648,7 +1771,18 @@ if targetos == 'darwin'
summary_info += {'Objective-C compiler': meson.get_compiler('objc').cmd_array()[0]}
endif
summary_info += {'ARFLAGS': config_host['ARFLAGS']}
summary_info += {'CFLAGS': config_host['CFLAGS']}
summary_info += {'CFLAGS': ' '.join(get_option('c_args')
+ ['-O' + get_option('optimization')]
+ (get_option('debug') ? ['-g'] : []))}
if link_language == 'cpp'
summary_info += {'CXXFLAGS': ' '.join(get_option('cpp_args')
+ ['-O' + get_option('optimization')]
+ (get_option('debug') ? ['-g'] : []))}
endif
link_args = get_option(link_language + '_link_args')
if link_args.length() > 0
summary_info += {'LDFLAGS': ' '.join(link_args)}
endif
summary_info += {'QEMU_CFLAGS': config_host['QEMU_CFLAGS']}
summary_info += {'QEMU_LDFLAGS': config_host['QEMU_LDFLAGS']}
summary_info += {'make': config_host['MAKE']}
@ -1656,8 +1790,8 @@ summary_info += {'python': '@0@ (version: @1@)'.format(python.full_pa
summary_info += {'sphinx-build': config_host['SPHINX_BUILD']}
summary_info += {'genisoimage': config_host['GENISOIMAGE']}
# TODO: add back version
summary_info += {'slirp support': config_host.has_key('CONFIG_SLIRP')}
if config_host.has_key('CONFIG_SLIRP')
summary_info += {'slirp support': slirp_opt == 'disabled' ? false : slirp_opt}
if slirp_opt != 'disabled'
summary_info += {'smbd': config_host['CONFIG_SMBD_COMMAND']}
endif
summary_info += {'module support': config_host.has_key('CONFIG_MODULES')}
@ -1741,7 +1875,7 @@ endif
summary_info += {'malloc trim support': has_malloc_trim}
summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')}
summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')}
summary_info += {'fdt support': config_host.has_key('CONFIG_FDT')}
summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt}
summary_info += {'membarrier': config_host.has_key('CONFIG_MEMBARRIER')}
summary_info += {'preadv support': config_host.has_key('CONFIG_PREADV')}
summary_info += {'fdatasync': config_host.has_key('CONFIG_FDATASYNC')}

View File

@ -52,3 +52,9 @@ option('xkbcommon', type : 'feature', value : 'auto',
option('capstone', type: 'combo', value: 'auto',
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
description: 'Whether and how to find the capstone library')
option('slirp', type: 'combo', value: 'auto',
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
description: 'Whether and how to find the slirp library')
option('fdt', type: 'combo', value: 'auto',
choices: ['disabled', 'enabled', 'auto', 'system', 'internal'],
description: 'Whether and how to find the libfdt library')

View File

@ -2723,6 +2723,11 @@ int save_snapshot(const char *name, Error **errp)
sn->date_sec = tv.tv_sec;
sn->date_nsec = tv.tv_usec * 1000;
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
if (replay_mode != REPLAY_MODE_NONE) {
sn->icount = replay_get_current_icount();
} else {
sn->icount = -1ULL;
}
if (name) {
ret = bdrv_snapshot_find(bs, old_sn, name);
@ -2876,12 +2881,6 @@ int load_snapshot(const char *name, Error **errp)
AioContext *aio_context;
MigrationIncomingState *mis = migration_incoming_get_current();
if (!replay_can_snapshot()) {
error_setg(errp, "Record/replay does not allow loading snapshot "
"right now. Try once more later.");
return -EINVAL;
}
if (!bdrv_all_can_snapshot(&bs)) {
error_setg(errp,
"Device '%s' is writable but does not support snapshots",
@ -2915,6 +2914,12 @@ int load_snapshot(const char *name, Error **errp)
return -EINVAL;
}
/*
* Flush the record/replay queue. Now the VM state is going
* to change. Therefore we don't need to preserve its consistency
*/
replay_flush_events();
/* Flush all IO requests so they don't interfere with the new state. */
bdrv_drain_all_begin();

View File

@ -18,7 +18,7 @@ softmmu_ss.add(files(
))
softmmu_ss.add(when: 'CONFIG_L2TPV3', if_true: files('l2tpv3.c'))
softmmu_ss.add(when: ['CONFIG_SLIRP', slirp], if_true: files('slirp.c'))
softmmu_ss.add(when: slirp, if_true: files('slirp.c'))
softmmu_ss.add(when: ['CONFIG_VDE', vde], if_true: files('vde.c'))
softmmu_ss.add(when: 'CONFIG_NETMAP', if_true: files('netmap.c'))
vhost_user_ss = ss.source_set()

View File

@ -27,13 +27,19 @@
#
# @vm-clock-nsec: fractional part in nano seconds to be used with vm-clock-sec
#
# @icount: Current instruction count. Appears when execution record/replay
# is enabled. Used for "time-traveling" to match the moment
# in the recorded execution with the snapshots. This counter may
# be obtained through @query-replay command (since 5.2)
#
# Since: 1.3
#
##
{ 'struct': 'SnapshotInfo',
'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int',
'date-sec': 'int', 'date-nsec': 'int',
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int',
'*icount': 'int' } }
##
# @ImageInfoSpecificQCow2EncryptionBase:
@ -5363,7 +5369,8 @@
# "date-sec": 1000012,
# "date-nsec": 10,
# "vm-clock-sec": 100,
# "vm-clock-nsec": 20
# "vm-clock-nsec": 20,
# "icount": 220414
# }
# }
#

View File

@ -39,6 +39,7 @@ qapi_all_modules = [
'pci',
'qom',
'rdma',
'replay',
'rocker',
'run-state',
'sockets',

View File

@ -757,24 +757,6 @@
'returns': ['CommandLineOptionInfo'],
'allow-preconfig': true }
##
# @ReplayMode:
#
# Mode of the replay subsystem.
#
# @none: normal execution mode. Replay or record are not enabled.
#
# @record: record mode. All non-deterministic data is written into the
# replay log.
#
# @play: replay mode. Non-deterministic data required for system execution
# is read from the log.
#
# Since: 2.5
##
{ 'enum': 'ReplayMode',
'data': [ 'none', 'record', 'play' ] }
##
# @xen-load-devices-state:
#

View File

@ -85,6 +85,7 @@
{ 'include': 'qdev.json' }
{ 'include': 'machine.json' }
{ 'include': 'machine-target.json' }
{ 'include': 'replay.json' }
{ 'include': 'misc.json' }
{ 'include': 'misc-target.json' }
{ 'include': 'audio.json' }

121
qapi/replay.json Normal file
View File

@ -0,0 +1,121 @@
# -*- Mode: Python -*-
#
##
# = Record/replay
##
{ 'include': 'common.json' }
##
# @ReplayMode:
#
# Mode of the replay subsystem.
#
# @none: normal execution mode. Replay or record are not enabled.
#
# @record: record mode. All non-deterministic data is written into the
# replay log.
#
# @play: replay mode. Non-deterministic data required for system execution
# is read from the log.
#
# Since: 2.5
##
{ 'enum': 'ReplayMode',
'data': [ 'none', 'record', 'play' ] }
##
# @ReplayInfo:
#
# Record/replay information.
#
# @mode: current mode.
#
# @filename: name of the record/replay log file.
# It is present only in record or replay modes, when the log
# is recorded or replayed.
#
# @icount: current number of executed instructions.
#
# Since: 5.2
#
##
{ 'struct': 'ReplayInfo',
'data': { 'mode': 'ReplayMode', '*filename': 'str', 'icount': 'int' } }
##
# @query-replay:
#
# Retrieve the record/replay information.
# It includes current instruction count which may be used for
# @replay-break and @replay-seek commands.
#
# Returns: record/replay information.
#
# Since: 5.2
#
# Example:
#
# -> { "execute": "query-replay" }
# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } }
#
##
{ 'command': 'query-replay',
'returns': 'ReplayInfo' }
##
# @replay-break:
#
# Set replay breakpoint at instruction count @icount.
# Execution stops when the specified instruction is reached.
# There can be at most one breakpoint. When breakpoint is set, any prior
# one is removed. The breakpoint may be set only in replay mode and only
# "in the future", i.e. at instruction counts greater than the current one.
# The current instruction count can be observed with @query-replay.
#
# @icount: instruction count to stop at
#
# Since: 5.2
#
# Example:
#
# -> { "execute": "replay-break", "data": { "icount": 220414 } }
#
##
{ 'command': 'replay-break', 'data': { 'icount': 'int' } }
##
# @replay-delete-break:
#
# Remove replay breakpoint which was set with @replay-break.
# The command is ignored when there are no replay breakpoints.
#
# Since: 5.2
#
# Example:
#
# -> { "execute": "replay-delete-break" }
#
##
{ 'command': 'replay-delete-break' }
##
# @replay-seek:
#
# Automatically proceed to the instruction count @icount, when
# replaying the execution. The command automatically loads nearest
# snapshot and replays the execution to find the desired instruction.
# When there is no preceding snapshot or the execution is not replayed,
# then the command fails.
# icount for the reference may be obtained with @query-replay command.
#
# @icount: target instruction count
#
# Since: 5.2
#
# Example:
#
# -> { "execute": "replay-seek", "data": { "icount": 220414 } }
##
{ 'command': 'replay-seek', 'data': { 'icount': 'int' } }

View File

@ -9,4 +9,5 @@ softmmu_ss.add(files(
'replay-net.c',
'replay-audio.c',
'replay-random.c',
'replay-debugging.c',
))

334
replay/replay-debugging.c Normal file
View File

@ -0,0 +1,334 @@
/*
* replay-debugging.c
*
* Copyright (c) 2010-2020 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "replay-internal.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
#include "qapi/qapi-commands-replay.h"
#include "qapi/qmp/qdict.h"
#include "qemu/timer.h"
#include "block/snapshot.h"
#include "migration/snapshot.h"
static bool replay_is_debugging;
static int64_t replay_last_breakpoint;
static int64_t replay_last_snapshot;
bool replay_running_debug(void)
{
return replay_is_debugging;
}
void hmp_info_replay(Monitor *mon, const QDict *qdict)
{
if (replay_mode == REPLAY_MODE_NONE) {
monitor_printf(mon, "Record/replay is not active\n");
} else {
monitor_printf(mon,
"%s execution '%s': instruction count = %"PRId64"\n",
replay_mode == REPLAY_MODE_RECORD ? "Recording" : "Replaying",
replay_get_filename(), replay_get_current_icount());
}
}
ReplayInfo *qmp_query_replay(Error **errp)
{
ReplayInfo *retval = g_new0(ReplayInfo, 1);
retval->mode = replay_mode;
if (replay_get_filename()) {
retval->filename = g_strdup(replay_get_filename());
retval->has_filename = true;
}
retval->icount = replay_get_current_icount();
return retval;
}
static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque)
{
assert(replay_mode == REPLAY_MODE_PLAY);
assert(replay_mutex_locked());
assert(replay_break_icount >= replay_get_current_icount());
assert(callback);
replay_break_icount = icount;
if (replay_break_timer) {
timer_del(replay_break_timer);
}
replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME,
callback, opaque);
}
static void replay_delete_break(void)
{
assert(replay_mode == REPLAY_MODE_PLAY);
assert(replay_mutex_locked());
if (replay_break_timer) {
timer_del(replay_break_timer);
timer_free(replay_break_timer);
replay_break_timer = NULL;
}
replay_break_icount = -1ULL;
}
static void replay_stop_vm(void *opaque)
{
vm_stop(RUN_STATE_PAUSED);
replay_delete_break();
}
void qmp_replay_break(int64_t icount, Error **errp)
{
if (replay_mode == REPLAY_MODE_PLAY) {
if (icount >= replay_get_current_icount()) {
replay_break(icount, replay_stop_vm, NULL);
} else {
error_setg(errp,
"cannot set breakpoint at the instruction in the past");
}
} else {
error_setg(errp, "setting the breakpoint is allowed only in play mode");
}
}
void hmp_replay_break(Monitor *mon, const QDict *qdict)
{
int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
Error *err = NULL;
qmp_replay_break(icount, &err);
if (err) {
error_report_err(err);
return;
}
}
void qmp_replay_delete_break(Error **errp)
{
if (replay_mode == REPLAY_MODE_PLAY) {
replay_delete_break();
} else {
error_setg(errp, "replay breakpoints are allowed only in play mode");
}
}
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_replay_delete_break(&err);
if (err) {
error_report_err(err);
return;
}
}
static char *replay_find_nearest_snapshot(int64_t icount,
int64_t *snapshot_icount)
{
BlockDriverState *bs;
QEMUSnapshotInfo *sn_tab;
QEMUSnapshotInfo *nearest = NULL;
char *ret = NULL;
int nb_sns, i;
AioContext *aio_context;
*snapshot_icount = -1;
bs = bdrv_all_find_vmstate_bs();
if (!bs) {
goto fail;
}
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
aio_context_release(aio_context);
for (i = 0; i < nb_sns; i++) {
if (bdrv_all_find_snapshot(sn_tab[i].name, &bs) == 0) {
if (sn_tab[i].icount != -1ULL
&& sn_tab[i].icount <= icount
&& (!nearest || nearest->icount < sn_tab[i].icount)) {
nearest = &sn_tab[i];
}
}
}
if (nearest) {
ret = g_strdup(nearest->name);
*snapshot_icount = nearest->icount;
}
g_free(sn_tab);
fail:
return ret;
}
static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp)
{
char *snapshot = NULL;
int64_t snapshot_icount;
if (replay_mode != REPLAY_MODE_PLAY) {
error_setg(errp, "replay must be enabled to seek");
return;
}
snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount);
if (snapshot) {
if (icount < replay_get_current_icount()
|| replay_get_current_icount() < snapshot_icount) {
vm_stop(RUN_STATE_RESTORE_VM);
load_snapshot(snapshot, errp);
}
g_free(snapshot);
}
if (replay_get_current_icount() <= icount) {
replay_break(icount, callback, NULL);
vm_start();
} else {
error_setg(errp, "cannot seek to the specified instruction count");
}
}
void qmp_replay_seek(int64_t icount, Error **errp)
{
replay_seek(icount, replay_stop_vm, errp);
}
void hmp_replay_seek(Monitor *mon, const QDict *qdict)
{
int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
Error *err = NULL;
qmp_replay_seek(icount, &err);
if (err) {
error_report_err(err);
return;
}
}
static void replay_stop_vm_debug(void *opaque)
{
replay_is_debugging = false;
vm_stop(RUN_STATE_DEBUG);
replay_delete_break();
}
bool replay_reverse_step(void)
{
Error *err = NULL;
assert(replay_mode == REPLAY_MODE_PLAY);
if (replay_get_current_icount() != 0) {
replay_seek(replay_get_current_icount() - 1,
replay_stop_vm_debug, &err);
if (err) {
error_free(err);
return false;
}
replay_is_debugging = true;
return true;
}
return false;
}
static void replay_continue_end(void)
{
replay_is_debugging = false;
vm_stop(RUN_STATE_DEBUG);
replay_delete_break();
}
static void replay_continue_stop(void *opaque)
{
Error *err = NULL;
if (replay_last_breakpoint != -1LL) {
replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err);
if (err) {
error_free(err);
replay_continue_end();
}
return;
}
/*
* No breakpoints since the last snapshot.
* Find previous snapshot and try again.
*/
if (replay_last_snapshot != 0) {
replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err);
if (err) {
error_free(err);
replay_continue_end();
}
replay_last_snapshot = replay_get_current_icount();
return;
} else {
/* Seek to the very first step */
replay_seek(0, replay_stop_vm_debug, &err);
if (err) {
error_free(err);
replay_continue_end();
}
return;
}
replay_continue_end();
}
bool replay_reverse_continue(void)
{
Error *err = NULL;
assert(replay_mode == REPLAY_MODE_PLAY);
if (replay_get_current_icount() != 0) {
replay_seek(replay_get_current_icount() - 1,
replay_continue_stop, &err);
if (err) {
error_free(err);
return false;
}
replay_last_breakpoint = -1LL;
replay_is_debugging = true;
replay_last_snapshot = replay_get_current_icount();
return true;
}
return false;
}
void replay_breakpoint(void)
{
assert(replay_mode == REPLAY_MODE_PLAY);
replay_last_breakpoint = replay_get_current_icount();
}
void replay_gdb_attached(void)
{
/*
* Create VM snapshot on temporary overlay to allow reverse
* debugging even if snapshots were not enabled.
*/
if (replay_mode == REPLAY_MODE_PLAY
&& !replay_snapshot) {
if (save_snapshot("start_debugging", NULL) != 0) {
/* Can't create the snapshot. Continue conventional debugging. */
}
}
}

View File

@ -77,6 +77,10 @@ bool replay_has_events(void)
void replay_flush_events(void)
{
if (replay_mode == REPLAY_MODE_NONE) {
return;
}
g_assert(replay_mutex_locked());
while (!QTAILQ_EMPTY(&events_list)) {

View File

@ -94,6 +94,10 @@ extern ReplayState replay_state;
/* File for replay writing */
extern FILE *replay_file;
/* Instruction count of the replay breakpoint */
extern uint64_t replay_break_icount;
/* Timer for the replay breakpoint callback */
extern QEMUTimer *replay_break_timer;
void replay_put_byte(uint8_t byte);
void replay_put_event(uint8_t event);
@ -145,8 +149,6 @@ void replay_read_next_clock(unsigned int kind);
void replay_init_events(void);
/*! Clears internal data structures for events handling */
void replay_finish_events(void);
/*! Flushes events queue */
void replay_flush_events(void);
/*! Returns true if there are any unsaved events in the queue */
bool replay_has_events(void);
/*! Saves events from queue into the file */

View File

@ -11,10 +11,10 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "replay-internal.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "sysemu/cpus.h"
@ -34,6 +34,10 @@ static char *replay_filename;
ReplayState replay_state;
static GSList *replay_blockers;
/* Replay breakpoints */
uint64_t replay_break_icount = -1ULL;
QEMUTimer *replay_break_timer;
bool replay_next_event_is(int event)
{
bool res = false;
@ -64,7 +68,7 @@ bool replay_next_event_is(int event)
uint64_t replay_get_current_icount(void)
{
return cpu_get_icount_raw();
return icount_get_raw();
}
int replay_get_instructions(void)
@ -73,6 +77,13 @@ int replay_get_instructions(void)
replay_mutex_lock();
if (replay_next_event_is(EVENT_INSTRUCTION)) {
res = replay_state.instruction_count;
if (replay_break_icount != -1LL) {
uint64_t current = replay_get_current_icount();
assert(replay_break_icount >= current);
if (current + res > replay_break_icount) {
res = replay_break_icount - current;
}
}
}
replay_mutex_unlock();
return res;
@ -99,6 +110,12 @@ void replay_account_executed_instructions(void)
will be read from the log. */
qemu_notify_event();
}
/* Execution reached the break step */
if (replay_break_icount == replay_state.current_icount) {
/* Cannot make callback directly from the vCPU thread */
timer_mod_ns(replay_break_timer,
qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
}
}
}
}
@ -345,7 +362,7 @@ void replay_start(void)
error_reportf_err(replay_blockers->data, "Record/replay: ");
exit(1);
}
if (!use_icount) {
if (!icount_enabled()) {
error_report("Please enable icount to use record/replay");
exit(1);
}
@ -399,3 +416,8 @@ void replay_add_blocker(Error *reason)
{
replay_blockers = g_slist_prepend(replay_blockers, reason);
}
const char *replay_get_filename(void)
{
return replay_filename;
}

279
softmmu/cpu-timers.c Normal file
View File

@ -0,0 +1,279 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/cutils.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "exec/exec-all.h"
#include "sysemu/cpus.h"
#include "sysemu/qtest.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "qemu/seqlock.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "hw/core/cpu.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/cpu-throttle.h"
#include "timers-state.h"
/* clock and ticks */
static int64_t cpu_get_ticks_locked(void)
{
int64_t ticks = timers_state.cpu_ticks_offset;
if (timers_state.cpu_ticks_enabled) {
ticks += cpu_get_host_ticks();
}
if (timers_state.cpu_ticks_prev > ticks) {
/* Non increasing ticks may happen if the host uses software suspend. */
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
ticks = timers_state.cpu_ticks_prev;
}
timers_state.cpu_ticks_prev = ticks;
return ticks;
}
/*
* return the time elapsed in VM between vm_start and vm_stop.
* cpu_get_ticks() uses units of the host CPU cycle counter.
*/
int64_t cpu_get_ticks(void)
{
int64_t ticks;
qemu_spin_lock(&timers_state.vm_clock_lock);
ticks = cpu_get_ticks_locked();
qemu_spin_unlock(&timers_state.vm_clock_lock);
return ticks;
}
int64_t cpu_get_clock_locked(void)
{
int64_t time;
time = timers_state.cpu_clock_offset;
if (timers_state.cpu_ticks_enabled) {
time += get_clock();
}
return time;
}
/*
* Return the monotonic time elapsed in VM, i.e.,
* the time between vm_start and vm_stop
*/
int64_t cpu_get_clock(void)
{
int64_t ti;
unsigned start;
do {
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
ti = cpu_get_clock_locked();
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
return ti;
}
/*
* enable cpu_get_ticks()
* Caller must hold BQL which serves as mutex for vm_clock_seqlock.
*/
void cpu_enable_ticks(void)
{
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
if (!timers_state.cpu_ticks_enabled) {
timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
timers_state.cpu_clock_offset -= get_clock();
timers_state.cpu_ticks_enabled = 1;
}
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
}
/*
* disable cpu_get_ticks() : the clock is stopped. You must not call
* cpu_get_ticks() after that.
* Caller must hold BQL which serves as mutex for vm_clock_seqlock.
*/
void cpu_disable_ticks(void)
{
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
if (timers_state.cpu_ticks_enabled) {
timers_state.cpu_ticks_offset += cpu_get_host_ticks();
timers_state.cpu_clock_offset = cpu_get_clock_locked();
timers_state.cpu_ticks_enabled = 0;
}
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
}
static bool icount_state_needed(void *opaque)
{
return icount_enabled();
}
static bool warp_timer_state_needed(void *opaque)
{
TimersState *s = opaque;
return s->icount_warp_timer != NULL;
}
static bool adjust_timers_state_needed(void *opaque)
{
TimersState *s = opaque;
return s->icount_rt_timer != NULL;
}
static bool icount_shift_state_needed(void *opaque)
{
return icount_enabled() == 2;
}
/*
* Subsection for warp timer migration is optional, because may not be created
*/
static const VMStateDescription icount_vmstate_warp_timer = {
.name = "timer/icount/warp_timer",
.version_id = 1,
.minimum_version_id = 1,
.needed = warp_timer_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT64(vm_clock_warp_start, TimersState),
VMSTATE_TIMER_PTR(icount_warp_timer, TimersState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription icount_vmstate_adjust_timers = {
.name = "timer/icount/timers",
.version_id = 1,
.minimum_version_id = 1,
.needed = adjust_timers_state_needed,
.fields = (VMStateField[]) {
VMSTATE_TIMER_PTR(icount_rt_timer, TimersState),
VMSTATE_TIMER_PTR(icount_vm_timer, TimersState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription icount_vmstate_shift = {
.name = "timer/icount/shift",
.version_id = 1,
.minimum_version_id = 1,
.needed = icount_shift_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT16(icount_time_shift, TimersState),
VMSTATE_END_OF_LIST()
}
};
/*
* This is a subsection for icount migration.
*/
static const VMStateDescription icount_vmstate_timers = {
.name = "timer/icount",
.version_id = 1,
.minimum_version_id = 1,
.needed = icount_state_needed,
.fields = (VMStateField[]) {
VMSTATE_INT64(qemu_icount_bias, TimersState),
VMSTATE_INT64(qemu_icount, TimersState),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
&icount_vmstate_warp_timer,
&icount_vmstate_adjust_timers,
&icount_vmstate_shift,
NULL
}
};
static const VMStateDescription vmstate_timers = {
.name = "timer",
.version_id = 2,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_INT64(cpu_ticks_offset, TimersState),
VMSTATE_UNUSED(8),
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
&icount_vmstate_timers,
NULL
}
};
static void do_nothing(CPUState *cpu, run_on_cpu_data unused)
{
}
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
{
if (!icount_enabled() || type != QEMU_CLOCK_VIRTUAL) {
qemu_notify_event();
return;
}
if (qemu_in_vcpu_thread()) {
/*
* A CPU is currently running; kick it back out to the
* tcg_cpu_exec() loop so it will recalculate its
* icount deadline immediately.
*/
qemu_cpu_kick(current_cpu);
} else if (first_cpu) {
/*
* qemu_cpu_kick is not enough to kick a halted CPU out of
* qemu_tcg_wait_io_event. async_run_on_cpu, instead,
* causes cpu_thread_is_idle to return false. This way,
* handle_icount_deadline can run.
* If we have no CPUs at all for some reason, we don't
* need to do anything.
*/
async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL);
}
}
TimersState timers_state;
/* initialize timers state and the cpu throttle for convenience */
void cpu_timers_init(void)
{
seqlock_init(&timers_state.vm_clock_seqlock);
qemu_spin_init(&timers_state.vm_clock_lock);
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
cpu_throttle_init();
}

File diff suppressed because it is too large Load Diff

492
softmmu/icount.c Normal file
View File

@ -0,0 +1,492 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/cutils.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "exec/exec-all.h"
#include "sysemu/cpus.h"
#include "sysemu/qtest.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "qemu/seqlock.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "hw/core/cpu.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/cpu-throttle.h"
#include "timers-state.h"
/*
* ICOUNT: Instruction Counter
*
* this module is split off from cpu-timers because the icount part
* is TCG-specific, and does not need to be built for other accels.
*/
static bool icount_sleep = true;
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
#define MAX_ICOUNT_SHIFT 10
/*
* 0 = Do not count executed instructions.
* 1 = Fixed conversion of insn to ns via "shift" option
* 2 = Runtime adaptive algorithm to compute shift
*/
int use_icount;
static void icount_enable_precise(void)
{
use_icount = 1;
}
static void icount_enable_adaptive(void)
{
use_icount = 2;
}
/*
* The current number of executed instructions is based on what we
* originally budgeted minus the current state of the decrementing
* icount counters in extra/u16.low.
*/
static int64_t icount_get_executed(CPUState *cpu)
{
return (cpu->icount_budget -
(cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra));
}
/*
* Update the global shared timer_state.qemu_icount to take into
* account executed instructions. This is done by the TCG vCPU
* thread so the main-loop can see time has moved forward.
*/
static void icount_update_locked(CPUState *cpu)
{
int64_t executed = icount_get_executed(cpu);
cpu->icount_budget -= executed;
qatomic_set_i64(&timers_state.qemu_icount,
timers_state.qemu_icount + executed);
}
/*
* Update the global shared timer_state.qemu_icount to take into
* account executed instructions. This is done by the TCG vCPU
* thread so the main-loop can see time has moved forward.
*/
void icount_update(CPUState *cpu)
{
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
icount_update_locked(cpu);
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
}
static int64_t icount_get_raw_locked(void)
{
CPUState *cpu = current_cpu;
if (cpu && cpu->running) {
if (!cpu->can_do_io) {
error_report("Bad icount read");
exit(1);
}
/* Take into account what has run */
icount_update_locked(cpu);
}
/* The read is protected by the seqlock, but needs atomic64 to avoid UB */
return qatomic_read_i64(&timers_state.qemu_icount);
}
static int64_t icount_get_locked(void)
{
int64_t icount = icount_get_raw_locked();
return qatomic_read_i64(&timers_state.qemu_icount_bias) +
icount_to_ns(icount);
}
int64_t icount_get_raw(void)
{
int64_t icount;
unsigned start;
do {
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
icount = icount_get_raw_locked();
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
return icount;
}
/* Return the virtual CPU time, based on the instruction counter. */
int64_t icount_get(void)
{
int64_t icount;
unsigned start;
do {
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
icount = icount_get_locked();
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
return icount;
}
int64_t icount_to_ns(int64_t icount)
{
return icount << qatomic_read(&timers_state.icount_time_shift);
}
/*
* Correlation between real and virtual time is always going to be
* fairly approximate, so ignore small variation.
* When the guest is idle real and virtual time will be aligned in
* the IO wait loop.
*/
#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10)
static void icount_adjust(void)
{
int64_t cur_time;
int64_t cur_icount;
int64_t delta;
/* Protected by TimersState mutex. */
static int64_t last_delta;
/* If the VM is not running, then do nothing. */
if (!runstate_is_running()) {
return;
}
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
cur_time = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
cpu_get_clock_locked());
cur_icount = icount_get_locked();
delta = cur_icount - cur_time;
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
if (delta > 0
&& last_delta + ICOUNT_WOBBLE < delta * 2
&& timers_state.icount_time_shift > 0) {
/* The guest is getting too far ahead. Slow time down. */
qatomic_set(&timers_state.icount_time_shift,
timers_state.icount_time_shift - 1);
}
if (delta < 0
&& last_delta - ICOUNT_WOBBLE > delta * 2
&& timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
/* The guest is getting too far behind. Speed time up. */
qatomic_set(&timers_state.icount_time_shift,
timers_state.icount_time_shift + 1);
}
last_delta = delta;
qatomic_set_i64(&timers_state.qemu_icount_bias,
cur_icount - (timers_state.qemu_icount
<< timers_state.icount_time_shift));
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
}
static void icount_adjust_rt(void *opaque)
{
timer_mod(timers_state.icount_rt_timer,
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
icount_adjust();
}
static void icount_adjust_vm(void *opaque)
{
timer_mod(timers_state.icount_vm_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
NANOSECONDS_PER_SECOND / 10);
icount_adjust();
}
int64_t icount_round(int64_t count)
{
int shift = qatomic_read(&timers_state.icount_time_shift);
return (count + (1 << shift) - 1) >> shift;
}
static void icount_warp_rt(void)
{
unsigned seq;
int64_t warp_start;
/*
* The icount_warp_timer is rescheduled soon after vm_clock_warp_start
* changes from -1 to another value, so the race here is okay.
*/
do {
seq = seqlock_read_begin(&timers_state.vm_clock_seqlock);
warp_start = timers_state.vm_clock_warp_start;
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq));
if (warp_start == -1) {
return;
}
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
if (runstate_is_running()) {
int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
cpu_get_clock_locked());
int64_t warp_delta;
warp_delta = clock - timers_state.vm_clock_warp_start;
if (icount_enabled() == 2) {
/*
* In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too
* far ahead of real time.
*/
int64_t cur_icount = icount_get_locked();
int64_t delta = clock - cur_icount;
warp_delta = MIN(warp_delta, delta);
}
qatomic_set_i64(&timers_state.qemu_icount_bias,
timers_state.qemu_icount_bias + warp_delta);
}
timers_state.vm_clock_warp_start = -1;
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
}
}
static void icount_timer_cb(void *opaque)
{
/*
* No need for a checkpoint because the timer already synchronizes
* with CHECKPOINT_CLOCK_VIRTUAL_RT.
*/
icount_warp_rt();
}
void icount_start_warp_timer(void)
{
int64_t clock;
int64_t deadline;
assert(icount_enabled());
/*
* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
* do not fire, so computing the deadline does not make sense.
*/
if (!runstate_is_running()) {
return;
}
if (replay_mode != REPLAY_MODE_PLAY) {
if (!all_cpu_threads_idle()) {
return;
}
if (qtest_enabled()) {
/* When testing, qtest commands advance icount. */
return;
}
replay_checkpoint(CHECKPOINT_CLOCK_WARP_START);
} else {
/* warp clock deterministically in record/replay mode */
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
/*
* vCPU is sleeping and warp can't be started.
* It is probably a race condition: notification sent
* to vCPU was processed in advance and vCPU went to sleep.
* Therefore we have to wake it up for doing someting.
*/
if (replay_has_checkpoint()) {
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
}
return;
}
}
/* We want to use the earliest deadline from ALL vm_clocks */
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
~QEMU_TIMER_ATTR_EXTERNAL);
if (deadline < 0) {
static bool notified;
if (!icount_sleep && !notified) {
warn_report("icount sleep disabled and no active timers");
notified = true;
}
return;
}
if (deadline > 0) {
/*
* Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to
* sleep. Otherwise, the CPU might be waiting for a future timer
* interrupt to wake it up, but the interrupt never comes because
* the vCPU isn't running any insns and thus doesn't advance the
* QEMU_CLOCK_VIRTUAL.
*/
if (!icount_sleep) {
/*
* We never let VCPUs sleep in no sleep icount mode.
* If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance
* to the next QEMU_CLOCK_VIRTUAL event and notify it.
* It is useful when we want a deterministic execution time,
* isolated from host latencies.
*/
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
qatomic_set_i64(&timers_state.qemu_icount_bias,
timers_state.qemu_icount_bias + deadline);
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
} else {
/*
* We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some
* "real" time, (related to the time left until the next event) has
* passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this.
* This avoids that the warps are visible externally; for example,
* you will not be sending network packets continuously instead of
* every 100ms.
*/
seqlock_write_lock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
if (timers_state.vm_clock_warp_start == -1
|| timers_state.vm_clock_warp_start > clock) {
timers_state.vm_clock_warp_start = clock;
}
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
&timers_state.vm_clock_lock);
timer_mod_anticipate(timers_state.icount_warp_timer,
clock + deadline);
}
} else if (deadline == 0) {
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
}
}
void icount_account_warp_timer(void)
{
if (!icount_enabled() || !icount_sleep) {
return;
}
/*
* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
* do not fire, so computing the deadline does not make sense.
*/
if (!runstate_is_running()) {
return;
}
/* warp clock deterministically in record/replay mode */
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
return;
}
timer_del(timers_state.icount_warp_timer);
icount_warp_rt();
}
void icount_configure(QemuOpts *opts, Error **errp)
{
const char *option = qemu_opt_get(opts, "shift");
bool sleep = qemu_opt_get_bool(opts, "sleep", true);
bool align = qemu_opt_get_bool(opts, "align", false);
long time_shift = -1;
if (!option) {
if (qemu_opt_get(opts, "align") != NULL) {
error_setg(errp, "Please specify shift option when using align");
}
return;
}
if (align && !sleep) {
error_setg(errp, "align=on and sleep=off are incompatible");
return;
}
if (strcmp(option, "auto") != 0) {
if (qemu_strtol(option, NULL, 0, &time_shift) < 0
|| time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) {
error_setg(errp, "icount: Invalid shift value");
return;
}
} else if (icount_align_option) {
error_setg(errp, "shift=auto and align=on are incompatible");
return;
} else if (!icount_sleep) {
error_setg(errp, "shift=auto and sleep=off are incompatible");
return;
}
icount_sleep = sleep;
if (icount_sleep) {
timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
icount_timer_cb, NULL);
}
icount_align_option = align;
if (time_shift >= 0) {
timers_state.icount_time_shift = time_shift;
icount_enable_precise();
return;
}
icount_enable_adaptive();
/*
* 125MIPS seems a reasonable initial guess at the guest speed.
* It will be corrected fairly quickly anyway.
*/
timers_state.icount_time_shift = 3;
/*
* Have both realtime and virtual time triggers for speed adjustment.
* The realtime trigger catches emulated time passing too slowly,
* the virtual time trigger catches emulated time passing too fast.
* Realtime triggers occur even when idle, so use them less frequently
* than VM triggers.
*/
timers_state.vm_clock_warp_start = -1;
timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT,
icount_adjust_rt, NULL);
timer_mod(timers_state.icount_rt_timer,
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
icount_adjust_vm, NULL);
timer_mod(timers_state.icount_vm_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
NANOSECONDS_PER_SECOND / 10);
}

View File

@ -1,4 +1,4 @@
specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files(
'arch_init.c',
'balloon.c',
'cpus.c',
@ -7,4 +7,10 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
'memory.c',
'memory_mapping.c',
'qtest.c',
'vl.c'))
'vl.c',
'cpu-timers.c',
)])
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files(
'icount.c'
)])

View File

@ -21,7 +21,7 @@
#include "exec/memory.h"
#include "hw/irq.h"
#include "sysemu/accel.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/error-report.h"
@ -273,6 +273,38 @@ static void qtest_irq_handler(void *opaque, int n, int level)
}
}
static int64_t qtest_clock_counter;
int64_t qtest_get_virtual_clock(void)
{
return qatomic_read_i64(&qtest_clock_counter);
}
static void qtest_set_virtual_clock(int64_t count)
{
qatomic_set_i64(&qtest_clock_counter, count);
}
static void qtest_clock_warp(int64_t dest)
{
int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
AioContext *aio_context;
assert(qtest_enabled());
aio_context = qemu_get_aio_context();
while (clock < dest) {
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
QEMU_TIMER_ATTR_ALL);
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
qtest_set_virtual_clock(qtest_get_virtual_clock() + warp);
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
}
static void qtest_process_command(CharBackend *chr, gchar **words)
{
const gchar *command;

69
softmmu/timers-state.h Normal file
View File

@ -0,0 +1,69 @@
/*
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef TIMERS_STATE_H
#define TIMERS_STATE_H
/* timers state, for sharing between icount and cpu-timers */
typedef struct TimersState {
/* Protected by BQL. */
int64_t cpu_ticks_prev;
int64_t cpu_ticks_offset;
/*
* Protect fields that can be respectively read outside the
* BQL, and written from multiple threads.
*/
QemuSeqLock vm_clock_seqlock;
QemuSpin vm_clock_lock;
int16_t cpu_ticks_enabled;
/* Conversion factor from emulated instructions to virtual clock ticks. */
int16_t icount_time_shift;
/* Compensate for varying guest execution speed. */
int64_t qemu_icount_bias;
int64_t vm_clock_warp_start;
int64_t cpu_clock_offset;
/* Only written by TCG thread */
int64_t qemu_icount;
/* for adjusting icount */
QEMUTimer *icount_rt_timer;
QEMUTimer *icount_vm_timer;
QEMUTimer *icount_warp_timer;
} TimersState;
extern TimersState timers_state;
/*
* icount needs this internal from cpu-timers when adjusting the icount shift.
*/
int64_t cpu_get_clock_locked(void);
#endif /* TIMERS_STATE_H */

View File

@ -74,6 +74,7 @@
#include "hw/audio/soundhw.h"
#include "audio/audio.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "migration/colo.h"
#include "migration/postcopy-ram.h"
#include "sysemu/kvm.h"
@ -2694,7 +2695,7 @@ static void user_register_global_props(void)
static int do_configure_icount(void *opaque, QemuOpts *opts, Error **errp)
{
configure_icount(opts, errp);
icount_configure(opts, errp);
return 0;
}
@ -2804,7 +2805,7 @@ static void configure_accelerators(const char *progname)
error_report("falling back to %s", ac->name);
}
if (use_icount && !(tcg_enabled() || qtest_enabled())) {
if (icount_enabled() && !tcg_enabled()) {
error_report("-icount is not allowed with hardware virtualization");
exit(1);
}
@ -4254,7 +4255,8 @@ void qemu_init(int argc, char **argv, char **envp)
semihosting_arg_fallback(kernel_filename, kernel_cmdline);
}
cpu_ticks_init();
/* initialize cpu timers and VCPU throttle modules */
cpu_timers_init();
if (default_net) {
QemuOptsList *net = qemu_find_opts("net");

View File

@ -1,7 +0,0 @@
#include "qemu/osdep.h"
#include "qemu/timer.h"
void qemu_start_warp_timer(void)
{
}

View File

@ -1,5 +1,6 @@
#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
int64_t cpu_get_clock(void)
{

View File

@ -1,16 +0,0 @@
#include "qemu/osdep.h"
#include "qemu/timer.h"
#include "sysemu/cpus.h"
#include "qemu/main-loop.h"
int use_icount;
int64_t cpu_get_icount(void)
{
abort();
}
int64_t cpu_get_icount_raw(void)
{
abort();
}

View File

@ -0,0 +1,9 @@
#include "qemu/osdep.h"
#include "sysemu/hw_accel.h"
void cpu_synchronize_state(CPUState *cpu)
{
}
void cpu_synchronize_post_init(CPUState *cpu)
{
}

View File

@ -0,0 +1,8 @@
#include "qemu/osdep.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
int64_t cpus_get_virtual_clock(void)
{
return cpu_get_clock();
}

45
stubs/icount.c Normal file
View File

@ -0,0 +1,45 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "sysemu/cpu-timers.h"
/* icount - Instruction Counter API */
int use_icount;
void icount_update(CPUState *cpu)
{
abort();
}
void icount_configure(QemuOpts *opts, Error **errp)
{
/* signal error */
error_setg(errp, "cannot configure icount, TCG support not available");
}
int64_t icount_get_raw(void)
{
abort();
return 0;
}
int64_t icount_get(void)
{
abort();
return 0;
}
int64_t icount_to_ns(int64_t icount)
{
abort();
return 0;
}
int64_t icount_round(int64_t count)
{
abort();
return 0;
}
void icount_start_warp_timer(void)
{
abort();
}
void icount_account_warp_timer(void)
{
abort();
}

View File

@ -3,10 +3,11 @@ stub_ss.add(files('bdrv-next-monitor-owned.c'))
stub_ss.add(files('blk-commit-all.c'))
stub_ss.add(files('blockdev-close-all-bdrv-states.c'))
stub_ss.add(files('change-state-handler.c'))
stub_ss.add(files('clock-warp.c'))
stub_ss.add(files('cmos.c'))
stub_ss.add(files('cpu-get-clock.c'))
stub_ss.add(files('cpu-get-icount.c'))
stub_ss.add(files('cpus-get-virtual-clock.c'))
stub_ss.add(files('qemu-timer-notify-cb.c'))
stub_ss.add(files('icount.c'))
stub_ss.add(files('dump.c'))
stub_ss.add(files('error-printf.c'))
stub_ss.add(files('fdset.c'))
@ -44,6 +45,7 @@ stub_ss.add(files('vmgenid.c'))
stub_ss.add(files('vmstate.c'))
stub_ss.add(files('vm-stop.c'))
stub_ss.add(files('win32-kbd-hook.c'))
stub_ss.add(files('cpu-synchronize-state.c'))
if have_system
stub_ss.add(files('semihost.c'))
stub_ss.add(files('xen-hw-stub.c'))

View File

@ -1,5 +1,5 @@
#include "qemu/osdep.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "qemu/main-loop.h"
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)

View File

@ -18,3 +18,8 @@ bool qtest_driver(void)
{
return false;
}
int64_t qtest_get_virtual_clock(void)
{
return 0;
}

View File

@ -88,3 +88,18 @@ int replay_read_random(void *buf, size_t len)
{
return 0;
}
uint64_t replay_get_current_icount(void)
{
return 0;
}
bool replay_reverse_step(void)
{
return false;
}
bool replay_reverse_continue(void)
{
return false;
}

View File

@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "disas/disas.h"
#include "qemu/host-utils.h"
#include "exec/exec-all.h"
@ -1329,7 +1330,7 @@ static DisasJumpType gen_mfpr(DisasContext *ctx, TCGv va, int regno)
case 249: /* VMTIME */
helper = gen_helper_get_vmtime;
do_helper:
if (use_icount) {
if (icount_enabled()) {
gen_io_start();
helper(va);
return DISAS_PC_STALE;

View File

@ -24,6 +24,7 @@
#include "hw/irq.h"
#include "hw/semihosting/semihost.h"
#include "sysemu/cpus.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/kvm.h"
#include "sysemu/tcg.h"
#include "qemu/range.h"
@ -1206,17 +1207,17 @@ static int64_t cycles_ns_per(uint64_t cycles)
static bool instructions_supported(CPUARMState *env)
{
return use_icount == 1 /* Precise instruction counting */;
return icount_enabled() == 1; /* Precise instruction counting */
}
static uint64_t instructions_get_count(CPUARMState *env)
{
return (uint64_t)cpu_get_icount_raw();
return (uint64_t)icount_get_raw();
}
static int64_t instructions_ns_per(uint64_t icount)
{
return cpu_icount_to_ns((int64_t)icount);
return icount_to_ns((int64_t)icount);
}
#endif

View File

@ -28,13 +28,13 @@
#include "exec/address-spaces.h"
#include "qemu-common.h"
#include "hax-i386.h"
#include "sysemu/accel.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
#include "qemu/main-loop.h"
#include "hw/boards.h"
#include "hax-cpus.h"
#define DEBUG_HAX 0
#define DPRINTF(fmt, ...) \
@ -296,15 +296,6 @@ int hax_vm_destroy(struct hax_vm *vm)
return 0;
}
static void hax_handle_interrupt(CPUState *cpu, int mask)
{
cpu->interrupt_request |= mask;
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
}
}
static int hax_init(ram_addr_t ram_size, int max_cpus)
{
struct hax_state *hax = NULL;
@ -349,7 +340,6 @@ static int hax_init(ram_addr_t ram_size, int max_cpus)
qversion.cur_version = hax_cur_version;
qversion.min_version = hax_min_version;
hax_notify_qemu_version(hax->vm->fd, &qversion);
cpu_interrupt_handler = hax_handle_interrupt;
return ret;
error:
@ -374,6 +364,9 @@ static int hax_accel_init(MachineState *ms)
!ret ? "working" : "not working",
!ret ? "fast virt" : "emulation");
}
if (ret == 0) {
cpus_register_accel(&hax_cpus);
}
return ret;
}

84
target/i386/hax-cpus.c Normal file
View File

@ -0,0 +1,84 @@
/*
* QEMU HAX support
*
* Copyright IBM, Corp. 2008
* Red Hat, Inc. 2008
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
* Glauber Costa <gcosta@redhat.com>
*
* Copyright (c) 2011 Intel Corporation
* Written by:
* Jiang Yunhong<yunhong.jiang@intel.com>
* Xin Xiaohui<xiaohui.xin@intel.com>
* Zhang Xiantao<xiantao.zhang@intel.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "sysemu/runstate.h"
#include "sysemu/cpus.h"
#include "qemu/guest-random.h"
#include "hax-cpus.h"
static void *hax_cpu_thread_fn(void *arg)
{
CPUState *cpu = arg;
int r;
rcu_register_thread();
qemu_mutex_lock_iothread();
qemu_thread_get_self(cpu->thread);
cpu->thread_id = qemu_get_thread_id();
hax_init_vcpu(cpu);
cpu_thread_signal_created(cpu);
qemu_guest_random_seed_thread_part2(cpu->random_seed);
do {
if (cpu_can_run(cpu)) {
r = hax_smp_cpu_exec(cpu);
if (r == EXCP_DEBUG) {
cpu_handle_guest_debug(cpu);
}
}
qemu_wait_io_event(cpu);
} while (!cpu->unplug || cpu_can_run(cpu));
rcu_unregister_thread();
return NULL;
}
static void hax_start_vcpu_thread(CPUState *cpu)
{
char thread_name[VCPU_THREAD_NAME_SIZE];
cpu->thread = g_malloc0(sizeof(QemuThread));
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(cpu->halt_cond);
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX",
cpu->cpu_index);
qemu_thread_create(cpu->thread, thread_name, hax_cpu_thread_fn,
cpu, QEMU_THREAD_JOINABLE);
#ifdef _WIN32
cpu->hThread = qemu_thread_get_handle(cpu->thread);
#endif
}
const CpusAccel hax_cpus = {
.create_vcpu_thread = hax_start_vcpu_thread,
.kick_vcpu_thread = hax_kick_vcpu_thread,
.synchronize_post_reset = hax_cpu_synchronize_post_reset,
.synchronize_post_init = hax_cpu_synchronize_post_init,
.synchronize_state = hax_cpu_synchronize_state,
.synchronize_pre_loadvm = hax_cpu_synchronize_pre_loadvm,
};

33
target/i386/hax-cpus.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Accelerator CPUS Interface
*
* Copyright 2020 SUSE LLC
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef HAX_CPUS_H
#define HAX_CPUS_H
#include "sysemu/cpus.h"
extern const CpusAccel hax_cpus;
#include "hax-interface.h"
#include "hax-i386.h"
int hax_init_vcpu(CPUState *cpu);
int hax_smp_cpu_exec(CPUState *cpu);
int hax_populate_ram(uint64_t va, uint64_t size);
void hax_cpu_synchronize_state(CPUState *cpu);
void hax_cpu_synchronize_post_reset(CPUState *cpu);
void hax_cpu_synchronize_post_init(CPUState *cpu);
void hax_cpu_synchronize_pre_loadvm(CPUState *cpu);
int hax_vcpu_destroy(CPUState *cpu);
void hax_raise_event(CPUState *cpu);
void hax_reset_vcpu_state(void *opaque);
#endif /* HAX_CPUS_H */

Some files were not shown because too many files have changed in this diff Show More