mirror of https://github.com/xemu-project/xemu.git
* prepare to expand usage of test venv
* fix CPUID when passing through host cache information * a20 fix * SGX fix * generate per-target modinfo * replay cleanups and simplifications * "make modules" target -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmKeCV0UHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroNVWQgArVaF9Q/RWd1jJ2EUoWhy/GKsrdBs CHmSzrYzF/0cqD4OtrjTRyUP72Kywf4/aP4GYtLsjSYRUyriqS4nm5yEkDsME+pH 0adATIumtGHBPjVJyPUux3H1cbPUmsxbE03ZIWUK65M/IM2m46hmPDTWVx6ZSXMN VnSQdTUeZdxY26D8zfLp+oufqmfgaPT19zURcMZlvWFJ830t+1spfZwH0RGjDlfZ ffNVMPcco1qreNFggNIV9sKtoLb8A9PxpFJPV9OtBONA5UGxZ7xhjjZY9jCI4Bxa xAd60nXeoAwiOP7LE2bEwXbPku3xgy2e0LNXhqblMGNGOjdlRlwfL/FhFw== =C9N3 -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * prepare to expand usage of test venv * fix CPUID when passing through host cache information * a20 fix * SGX fix * generate per-target modinfo * replay cleanups and simplifications * "make modules" target # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmKeCV0UHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroNVWQgArVaF9Q/RWd1jJ2EUoWhy/GKsrdBs # CHmSzrYzF/0cqD4OtrjTRyUP72Kywf4/aP4GYtLsjSYRUyriqS4nm5yEkDsME+pH # 0adATIumtGHBPjVJyPUux3H1cbPUmsxbE03ZIWUK65M/IM2m46hmPDTWVx6ZSXMN # VnSQdTUeZdxY26D8zfLp+oufqmfgaPT19zURcMZlvWFJ830t+1spfZwH0RGjDlfZ # ffNVMPcco1qreNFggNIV9sKtoLb8A9PxpFJPV9OtBONA5UGxZ7xhjjZY9jCI4Bxa # xAd60nXeoAwiOP7LE2bEwXbPku3xgy2e0LNXhqblMGNGOjdlRlwfL/FhFw== # =C9N3 # -----END PGP SIGNATURE----- # gpg: Signature made Mon 06 Jun 2022 07:04:13 AM PDT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [undefined] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # 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 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (29 commits) meson: qga: do not use deprecated meson.build_root() configure: remove reference to removed option regenerate meson-buildoptions.sh tests: run 'device-crash-test' from tests/venv tests: add python3-venv to debian10.docker tests: use tests/venv to run basevm.py-based scripts tests: install "qemu" namespace package into venv tests: add quiet-venv-pip macro tests: silence pip upgrade warnings during venv creation tests: use python3 as the python executable name tests: add "TESTS_PYTHON" variable to Makefile python: update for mypy 0.950 x86: cpu: fixup number of addressable IDs for logical processors sharing cache x86: cpu: make sure number of addressable IDs for processor cores meets the spec tests/Makefile.include: Fix 'make check-help' output tests/avocado: add replay Linux test for Aarch64 machines tests/avocado: add replay Linux tests for virtio machine tests/avocado: update replay_linux test docs: move replay docs to docs/system/replay.rst docs: convert docs/devel/replay page to rst ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
57c9363c45
|
@ -110,7 +110,8 @@ crash-test-debian:
|
|||
IMAGE: debian-amd64
|
||||
script:
|
||||
- cd build
|
||||
- scripts/device-crash-test -q ./qemu-system-i386
|
||||
- make check-venv
|
||||
- tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-i386
|
||||
|
||||
build-system-fedora:
|
||||
extends: .native_build_job_template
|
||||
|
@ -155,8 +156,9 @@ crash-test-fedora:
|
|||
IMAGE: fedora
|
||||
script:
|
||||
- cd build
|
||||
- scripts/device-crash-test -q ./qemu-system-ppc
|
||||
- scripts/device-crash-test -q ./qemu-system-riscv32
|
||||
- make check-venv
|
||||
- tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-ppc
|
||||
- tests/venv/bin/python3 scripts/device-crash-test -q ./qemu-system-riscv32
|
||||
|
||||
build-system-centos:
|
||||
extends: .native_build_job_template
|
||||
|
|
|
@ -84,8 +84,7 @@ void icount_handle_deadline(void)
|
|||
* Don't interrupt cpu thread, when these events are waiting
|
||||
* (i.e., there is no checkpoint)
|
||||
*/
|
||||
if (deadline == 0
|
||||
&& (replay_mode != REPLAY_MODE_PLAY || replay_has_checkpoint())) {
|
||||
if (deadline == 0) {
|
||||
icount_notify_aio_contexts();
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +108,7 @@ void icount_prepare_for_run(CPUState *cpu)
|
|||
|
||||
replay_mutex_lock();
|
||||
|
||||
if (cpu->icount_budget == 0 && replay_has_checkpoint()) {
|
||||
if (cpu->icount_budget == 0) {
|
||||
icount_notify_aio_contexts();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1035,7 +1035,6 @@ Advanced options (experts only):
|
|||
--with-git-submodules=ignore do not update or check git submodules (default if no .git dir)
|
||||
--static enable static build [$static]
|
||||
--bindir=PATH install binaries in PATH
|
||||
--efi-aarch64=PATH PATH of efi file to use for aarch64 VMs.
|
||||
--with-suffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir/docdir [$qemu_suffix]
|
||||
--without-default-features default all --enable-* options to "disabled"
|
||||
--without-default-devices do not include any device that is not needed to
|
||||
|
|
|
@ -13,3 +13,4 @@ are only implementing things for HW accelerated hypervisors.
|
|||
multi-thread-tcg
|
||||
tcg-icount
|
||||
tcg-plugins
|
||||
replay
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
..
|
||||
Copyright (c) 2022, ISP RAS
|
||||
Written by Pavel Dovgalyuk and Alex Bennée
|
||||
|
||||
=======================
|
||||
Execution Record/Replay
|
||||
=======================
|
||||
|
||||
Core concepts
|
||||
=============
|
||||
|
||||
Record/replay functions are used for the deterministic replay of qemu
|
||||
execution. Execution recording writes a non-deterministic events log, which
|
||||
can be later used for replaying the execution anywhere and for unlimited
|
||||
number of times. Execution replaying reads the log and replays all
|
||||
non-deterministic events including external input, hardware clocks,
|
||||
and interrupts.
|
||||
|
||||
Several parts of QEMU include function calls to make event log recording
|
||||
and replaying.
|
||||
Devices' models that have non-deterministic input from external devices were
|
||||
changed to write every external event into the execution log immediately.
|
||||
E.g. network packets are written into the log when they arrive into the virtual
|
||||
network adapter.
|
||||
|
||||
All non-deterministic events are coming from these devices. But to
|
||||
replay them we need to know at which moments they occur. We specify
|
||||
these moments by counting the number of instructions executed between
|
||||
every pair of consecutive events.
|
||||
|
||||
Academic papers with description of deterministic replay implementation:
|
||||
|
||||
* `Deterministic Replay of System's Execution with Multi-target QEMU Simulator for Dynamic Analysis and Reverse Debugging <https://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html>`_
|
||||
* `Don't panic: reverse debugging of kernel drivers <https://dl.acm.org/citation.cfm?id=2786805.2803179>`_
|
||||
|
||||
Modifications of qemu include:
|
||||
|
||||
* wrappers for clock and time functions to save their return values in the log
|
||||
* saving different asynchronous events (e.g. system shutdown) into the log
|
||||
* synchronization of the bottom halves execution
|
||||
* synchronization of the threads from thread pool
|
||||
* recording/replaying user input (mouse, keyboard, and microphone)
|
||||
* adding internal checkpoints for cpu and io synchronization
|
||||
* network filter for recording and replaying the packets
|
||||
* block driver for making block layer deterministic
|
||||
* serial port input record and replay
|
||||
* recording of random numbers obtained from the external sources
|
||||
|
||||
Instruction counting
|
||||
--------------------
|
||||
|
||||
QEMU should work in icount mode to use record/replay feature. icount was
|
||||
designed to allow deterministic execution in absence of external inputs
|
||||
of the virtual machine. We also use icount to control the occurrence of the
|
||||
non-deterministic events. The number of instructions elapsed from the last event
|
||||
is written to the log while recording the execution. In replay mode we
|
||||
can predict when to inject that event using the instruction counter.
|
||||
|
||||
Locking and thread synchronisation
|
||||
----------------------------------
|
||||
|
||||
Previously the synchronisation of the main thread and the vCPU thread
|
||||
was ensured by the holding of the BQL. However the trend has been to
|
||||
reduce the time the BQL was held across the system including under TCG
|
||||
system emulation. As it is important that batches of events are kept
|
||||
in sequence (e.g. expiring timers and checkpoints in the main thread
|
||||
while instruction checkpoints are written by the vCPU thread) we need
|
||||
another lock to keep things in lock-step. This role is now handled by
|
||||
the replay_mutex_lock. It used to be held only for each event being
|
||||
written but now it is held for a whole execution period. This results
|
||||
in a deterministic ping-pong between the two main threads.
|
||||
|
||||
As the BQL is now a finer grained lock than the replay_lock it is almost
|
||||
certainly a bug, and a source of deadlocks, to take the
|
||||
replay_mutex_lock while the BQL is held. This is enforced by an assert.
|
||||
While the unlocks are usually in the reverse order, this is not
|
||||
necessary; you can drop the replay_lock while holding the BQL, without
|
||||
doing a more complicated unlock_iothread/replay_unlock/lock_iothread
|
||||
sequence.
|
||||
|
||||
Checkpoints
|
||||
-----------
|
||||
|
||||
Replaying the execution of virtual machine is bound by sources of
|
||||
non-determinism. These are inputs from clock and peripheral devices,
|
||||
and QEMU thread scheduling. Thread scheduling affect on processing events
|
||||
from timers, asynchronous input-output, and bottom halves.
|
||||
|
||||
Invocations of timers are coupled with clock reads and changing the state
|
||||
of the virtual machine. Reads produce non-deterministic data taken from
|
||||
host clock. And VM state changes should preserve their order. Their relative
|
||||
order in replay mode must replicate the order of callbacks in record mode.
|
||||
To preserve this order we use checkpoints. When a specific clock is processed
|
||||
in record mode we save to the log special "checkpoint" event.
|
||||
Checkpoints here do not refer to virtual machine snapshots. They are just
|
||||
record/replay events used for synchronization.
|
||||
|
||||
QEMU in replay mode will try to invoke timers processing in random moment
|
||||
of time. That's why we do not process a group of timers until the checkpoint
|
||||
event will be read from the log. Such an event allows synchronizing CPU
|
||||
execution and timer events.
|
||||
|
||||
Two other checkpoints govern the "warping" of the virtual clock.
|
||||
While the virtual machine is idle, the virtual clock increments at
|
||||
1 ns per *real time* nanosecond. This is done by setting up a timer
|
||||
(called the warp timer) on the virtual real time clock, so that the
|
||||
timer fires at the next deadline of the virtual clock; the virtual clock
|
||||
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. ``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 ``icount_start_warp_timer``.
|
||||
|
||||
Virtual devices
|
||||
===============
|
||||
|
||||
Record/replay mechanism, that could be enabled through icount mode, expects
|
||||
the virtual devices to satisfy the following requirement:
|
||||
everything that affects
|
||||
the guest state during execution in icount mode should be deterministic.
|
||||
|
||||
Timers
|
||||
------
|
||||
|
||||
Timers are used to execute callbacks from different subsystems of QEMU
|
||||
at the specified moments of time. There are several kinds of timers:
|
||||
|
||||
* Real time clock. Based on host time and used only for callbacks that
|
||||
do not change the virtual machine state. For this reason real time
|
||||
clock and timers does not affect deterministic replay at all.
|
||||
* Virtual clock. These timers run only during the emulation. In icount
|
||||
mode virtual clock value is calculated using executed instructions counter.
|
||||
That is why it is completely deterministic and does not have to be recorded.
|
||||
* Host clock. This clock is used by device models that simulate real time
|
||||
sources (e.g. real time clock chip). Host clock is the one of the sources
|
||||
of non-determinism. Host clock read operations should be logged to
|
||||
make the execution deterministic.
|
||||
* Virtual real time clock. This clock is similar to real time clock but
|
||||
it is used only for increasing virtual clock while virtual machine is
|
||||
sleeping. Due to its nature it is also non-deterministic as the host clock
|
||||
and has to be logged too.
|
||||
|
||||
All virtual devices should use virtual clock for timers that change the guest
|
||||
state. Virtual clock is deterministic, therefore such timers are deterministic
|
||||
too.
|
||||
|
||||
Virtual devices can also use realtime clock for the events that do not change
|
||||
the guest state directly. When the clock ticking should depend on VM execution
|
||||
speed, use virtual clock with EXTERNAL attribute. It is not deterministic,
|
||||
but its speed depends on the guest execution. This clock is used by
|
||||
the virtual devices (e.g., slirp routing device) that lie outside the
|
||||
replayed guest.
|
||||
|
||||
Block devices
|
||||
-------------
|
||||
|
||||
Block devices record/replay module (``blkreplay``) intercepts calls of
|
||||
bdrv coroutine functions at the top of block drivers stack.
|
||||
|
||||
All block completion operations are added to the queue in the coroutines.
|
||||
When the queue is flushed the information about processed requests
|
||||
is recorded to the log. In replay phase the queue is matched with
|
||||
events read from the log. Therefore block devices requests are processed
|
||||
deterministically.
|
||||
|
||||
Bottom halves
|
||||
-------------
|
||||
|
||||
Bottom half callbacks, that affect the guest state, should be invoked through
|
||||
``replay_bh_schedule_event`` or ``replay_bh_schedule_oneshot_event`` functions.
|
||||
Their invocations are saved in record mode and synchronized with the existing
|
||||
log in replay mode.
|
||||
|
||||
Disk I/O events are completely deterministic in our model, because
|
||||
in both record and replay modes we start virtual machine from the same
|
||||
disk state. But callbacks that virtual disk controller uses for reading and
|
||||
writing the disk may occur at different moments of time in record and replay
|
||||
modes.
|
||||
|
||||
Reading and writing requests are created by CPU thread of QEMU. Later these
|
||||
requests proceed to block layer which creates "bottom halves". Bottom
|
||||
halves consist of callback and its parameters. They are processed when
|
||||
main loop locks the global mutex. These locks are not synchronized with
|
||||
replaying process because main loop also processes the events that do not
|
||||
affect the virtual machine state (like user interaction with monitor).
|
||||
|
||||
That is why we had to implement saving and replaying bottom halves callbacks
|
||||
synchronously to the CPU execution. When the callback is about to execute
|
||||
it is added to the queue in the replay module. This queue is written to the
|
||||
log when its callbacks are executed. In replay mode callbacks are not processed
|
||||
until the corresponding event is read from the events log file.
|
||||
|
||||
Sometimes the block layer uses asynchronous callbacks for its internal purposes
|
||||
(like reading or writing VM snapshots or disk image cluster tables). In this
|
||||
case bottom halves are not marked as "replayable" and do not saved
|
||||
into the log.
|
||||
|
||||
Saving/restoring the VM state
|
||||
-----------------------------
|
||||
|
||||
All fields in the device state structure (including virtual timers)
|
||||
should be restored by loadvm to the same values they had before savevm.
|
||||
|
||||
Avoid accessing other devices' state, because the order of saving/restoring
|
||||
is not defined. It means that you should not call functions like
|
||||
``update_irq`` in ``post_load`` callback. Save everything explicitly to avoid
|
||||
the dependencies that may make restoring the VM state non-deterministic.
|
||||
|
||||
Stopping the VM
|
||||
---------------
|
||||
|
||||
Stopping the guest should not interfere with its state (with the exception
|
||||
of the network connections, that could be broken by the remote timeouts).
|
||||
VM can be stopped at any moment of replay by the user. Restarting the VM
|
||||
after that stop should not break the replay by the unneeded guest state change.
|
||||
|
||||
Replay log format
|
||||
=================
|
||||
|
||||
Record/replay log consists of the header and the sequence of execution
|
||||
events. The header includes 4-byte replay version id and 8-byte reserved
|
||||
field. Version is updated every time replay log format changes to prevent
|
||||
using replay log created by another build of qemu.
|
||||
|
||||
The sequence of the events describes virtual machine state changes.
|
||||
It includes all non-deterministic inputs of VM, synchronization marks and
|
||||
instruction counts used to correctly inject inputs at replay.
|
||||
|
||||
Synchronization marks (checkpoints) are used for synchronizing qemu threads
|
||||
that perform operations with virtual hardware. These operations may change
|
||||
system's state (e.g., change some register or generate interrupt) and
|
||||
therefore should execute synchronously with CPU thread.
|
||||
|
||||
Every event in the log includes 1-byte event id and optional arguments.
|
||||
When argument is an array, it is stored as 4-byte array length
|
||||
and corresponding number of bytes with data.
|
||||
Here is the list of events that are written into the log:
|
||||
|
||||
- EVENT_INSTRUCTION. Instructions executed since last event. Followed by:
|
||||
|
||||
- 4-byte number of executed instructions.
|
||||
|
||||
- EVENT_INTERRUPT. Used to synchronize interrupt processing.
|
||||
- EVENT_EXCEPTION. Used to synchronize exception handling.
|
||||
- EVENT_ASYNC. This is a group of events. When such an event is generated,
|
||||
it is stored in the queue and processed in icount_account_warp_timer().
|
||||
Every such event has it's own id from the following list:
|
||||
|
||||
- REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes
|
||||
callbacks that affect virtual machine state, but normally called
|
||||
asynchronously. Followed by:
|
||||
|
||||
- 8-byte operation id.
|
||||
|
||||
- REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains
|
||||
parameters of keyboard and mouse input operations
|
||||
(key press/release, mouse pointer movement). Followed by:
|
||||
|
||||
- 9-16 bytes depending of input event.
|
||||
|
||||
- REPLAY_ASYNC_EVENT_INPUT_SYNC. Internal input synchronization event.
|
||||
- REPLAY_ASYNC_EVENT_CHAR_READ. Character (e.g., serial port) device input
|
||||
initiated by the sender. Followed by:
|
||||
|
||||
- 1-byte character device id.
|
||||
- Array with bytes were read.
|
||||
|
||||
- REPLAY_ASYNC_EVENT_BLOCK. Block device operation. Used to synchronize
|
||||
operations with disk and flash drives with CPU. Followed by:
|
||||
|
||||
- 8-byte operation id.
|
||||
|
||||
- REPLAY_ASYNC_EVENT_NET. Incoming network packet. Followed by:
|
||||
|
||||
- 1-byte network adapter id.
|
||||
- 4-byte packet flags.
|
||||
- Array with packet bytes.
|
||||
|
||||
- EVENT_SHUTDOWN. Occurs when user sends shutdown event to qemu,
|
||||
e.g., by closing the window.
|
||||
- EVENT_CHAR_WRITE. Used to synchronize character output operations. Followed by:
|
||||
|
||||
- 4-byte output function return value.
|
||||
- 4-byte offset in the output array.
|
||||
|
||||
- EVENT_CHAR_READ_ALL. Used to synchronize character input operations,
|
||||
initiated by qemu. Followed by:
|
||||
|
||||
- Array with bytes that were read.
|
||||
|
||||
- EVENT_CHAR_READ_ALL_ERROR. Unsuccessful character input operation,
|
||||
initiated by qemu. Followed by:
|
||||
|
||||
- 4-byte error code.
|
||||
|
||||
- EVENT_CLOCK + clock_id. Group of events for host clock read operations. Followed by:
|
||||
|
||||
- 8-byte clock value.
|
||||
|
||||
- EVENT_CHECKPOINT + checkpoint_id. Checkpoint for synchronization of
|
||||
CPU, internal threads, and asynchronous input events.
|
||||
- EVENT_END. Last event in the log.
|
|
@ -1,46 +0,0 @@
|
|||
Record/replay mechanism, that could be enabled through icount mode, expects
|
||||
the virtual devices to satisfy the following requirements.
|
||||
|
||||
The main idea behind this document is that everything that affects
|
||||
the guest state during execution in icount mode should be deterministic.
|
||||
|
||||
Timers
|
||||
======
|
||||
|
||||
All virtual devices should use virtual clock for timers that change the guest
|
||||
state. Virtual clock is deterministic, therefore such timers are deterministic
|
||||
too.
|
||||
|
||||
Virtual devices can also use realtime clock for the events that do not change
|
||||
the guest state directly. When the clock ticking should depend on VM execution
|
||||
speed, use virtual clock with EXTERNAL attribute. It is not deterministic,
|
||||
but its speed depends on the guest execution. This clock is used by
|
||||
the virtual devices (e.g., slirp routing device) that lie outside the
|
||||
replayed guest.
|
||||
|
||||
Bottom halves
|
||||
=============
|
||||
|
||||
Bottom half callbacks, that affect the guest state, should be invoked through
|
||||
replay_bh_schedule_event or replay_bh_schedule_oneshot_event functions.
|
||||
Their invocations are saved in record mode and synchronized with the existing
|
||||
log in replay mode.
|
||||
|
||||
Saving/restoring the VM state
|
||||
=============================
|
||||
|
||||
All fields in the device state structure (including virtual timers)
|
||||
should be restored by loadvm to the same values they had before savevm.
|
||||
|
||||
Avoid accessing other devices' state, because the order of saving/restoring
|
||||
is not defined. It means that you should not call functions like
|
||||
'update_irq' in post_load callback. Save everything explicitly to avoid
|
||||
the dependencies that may make restoring the VM state non-deterministic.
|
||||
|
||||
Stopping the VM
|
||||
===============
|
||||
|
||||
Stopping the guest should not interfere with its state (with the exception
|
||||
of the network connections, that could be broken by the remote timeouts).
|
||||
VM can be stopped at any moment of replay by the user. Restarting the VM
|
||||
after that stop should not break the replay by the unneeded guest state change.
|
410
docs/replay.txt
410
docs/replay.txt
|
@ -1,410 +0,0 @@
|
|||
Copyright (c) 2010-2015 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.
|
||||
|
||||
Record/replay
|
||||
-------------
|
||||
|
||||
Record/replay functions are used for the deterministic replay of qemu execution.
|
||||
Execution recording writes a non-deterministic events log, which can be later
|
||||
used for replaying the execution anywhere and for unlimited number of times.
|
||||
It also supports checkpointing for faster rewind to the specific replay moment.
|
||||
Execution replaying reads the log and replays all non-deterministic events
|
||||
including external input, hardware clocks, and interrupts.
|
||||
|
||||
Deterministic replay has the following features:
|
||||
* Deterministically replays whole system execution and all contents of
|
||||
the memory, state of the hardware devices, clocks, and screen of the VM.
|
||||
* Writes execution log into the file for later replaying for multiple times
|
||||
on different machines.
|
||||
* Supports i386, x86_64, and Arm hardware platforms.
|
||||
* Performs deterministic replay of all operations with keyboard and mouse
|
||||
input devices.
|
||||
|
||||
Usage of the record/replay:
|
||||
* First, record the execution with the following command line:
|
||||
qemu-system-i386 \
|
||||
-icount shift=7,rr=record,rrfile=replay.bin \
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct \
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
|
||||
-device ide-hd,drive=img-blkreplay \
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
* After recording, you can replay it by using another command line:
|
||||
qemu-system-i386 \
|
||||
-icount shift=7,rr=replay,rrfile=replay.bin \
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct \
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
|
||||
-device ide-hd,drive=img-blkreplay \
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
The only difference with recording is changing the rr option
|
||||
from record to replay.
|
||||
* Block device images are not actually changed in the recording mode,
|
||||
because all of the changes are written to the temporary overlay file.
|
||||
This behavior is enabled by using blkreplay driver. It should be used
|
||||
for every enabled block device, as described in 'Block devices' section.
|
||||
* '-net none' option should be specified when network is not used,
|
||||
because QEMU adds network card by default. When network is needed,
|
||||
it should be configured explicitly with replay filter, as described
|
||||
in 'Network devices' section.
|
||||
* Interaction with audio devices and serial ports are recorded and replayed
|
||||
automatically when such devices are enabled.
|
||||
|
||||
Academic papers with description of deterministic replay implementation:
|
||||
http://www.computer.org/csdl/proceedings/csmr/2012/4666/00/4666a553-abs.html
|
||||
http://dl.acm.org/citation.cfm?id=2786805.2803179
|
||||
|
||||
Modifications of qemu include:
|
||||
* wrappers for clock and time functions to save their return values in the log
|
||||
* saving different asynchronous events (e.g. system shutdown) into the log
|
||||
* synchronization of the bottom halves execution
|
||||
* synchronization of the threads from thread pool
|
||||
* recording/replaying user input (mouse, keyboard, and microphone)
|
||||
* adding internal checkpoints for cpu and io synchronization
|
||||
* network filter for recording and replaying the packets
|
||||
* block driver for making block layer deterministic
|
||||
* serial port input record and replay
|
||||
* recording of random numbers obtained from the external sources
|
||||
|
||||
Locking and thread synchronisation
|
||||
----------------------------------
|
||||
|
||||
Previously the synchronisation of the main thread and the vCPU thread
|
||||
was ensured by the holding of the BQL. However the trend has been to
|
||||
reduce the time the BQL was held across the system including under TCG
|
||||
system emulation. As it is important that batches of events are kept
|
||||
in sequence (e.g. expiring timers and checkpoints in the main thread
|
||||
while instruction checkpoints are written by the vCPU thread) we need
|
||||
another lock to keep things in lock-step. This role is now handled by
|
||||
the replay_mutex_lock. It used to be held only for each event being
|
||||
written but now it is held for a whole execution period. This results
|
||||
in a deterministic ping-pong between the two main threads.
|
||||
|
||||
As the BQL is now a finer grained lock than the replay_lock it is almost
|
||||
certainly a bug, and a source of deadlocks, to take the
|
||||
replay_mutex_lock while the BQL is held. This is enforced by an assert.
|
||||
While the unlocks are usually in the reverse order, this is not
|
||||
necessary; you can drop the replay_lock while holding the BQL, without
|
||||
doing a more complicated unlock_iothread/replay_unlock/lock_iothread
|
||||
sequence.
|
||||
|
||||
Non-deterministic events
|
||||
------------------------
|
||||
|
||||
Our record/replay system is based on saving and replaying non-deterministic
|
||||
events (e.g. keyboard input) and simulating deterministic ones (e.g. reading
|
||||
from HDD or memory of the VM). Saving only non-deterministic events makes
|
||||
log file smaller and simulation faster.
|
||||
|
||||
The following non-deterministic data from peripheral devices is saved into
|
||||
the log: mouse and keyboard input, network packets, audio controller input,
|
||||
serial port input, and hardware clocks (they are non-deterministic
|
||||
too, because their values are taken from the host machine). Inputs from
|
||||
simulated hardware, memory of VM, software interrupts, and execution of
|
||||
instructions are not saved into the log, because they are deterministic and
|
||||
can be replayed by simulating the behavior of virtual machine starting from
|
||||
initial state.
|
||||
|
||||
We had to solve three tasks to implement deterministic replay: recording
|
||||
non-deterministic events, replaying non-deterministic events, and checking
|
||||
that there is no divergence between record and replay modes.
|
||||
|
||||
We changed several parts of QEMU to make event log recording and replaying.
|
||||
Devices' models that have non-deterministic input from external devices were
|
||||
changed to write every external event into the execution log immediately.
|
||||
E.g. network packets are written into the log when they arrive into the virtual
|
||||
network adapter.
|
||||
|
||||
All non-deterministic events are coming from these devices. But to
|
||||
replay them we need to know at which moments they occur. We specify
|
||||
these moments by counting the number of instructions executed between
|
||||
every pair of consecutive events.
|
||||
|
||||
Instruction counting
|
||||
--------------------
|
||||
|
||||
QEMU should work in icount mode to use record/replay feature. icount was
|
||||
designed to allow deterministic execution in absence of external inputs
|
||||
of the virtual machine. We also use icount to control the occurrence of the
|
||||
non-deterministic events. The number of instructions elapsed from the last event
|
||||
is written to the log while recording the execution. In replay mode we
|
||||
can predict when to inject that event using the instruction counter.
|
||||
|
||||
Timers
|
||||
------
|
||||
|
||||
Timers are used to execute callbacks from different subsystems of QEMU
|
||||
at the specified moments of time. There are several kinds of timers:
|
||||
* Real time clock. Based on host time and used only for callbacks that
|
||||
do not change the virtual machine state. For this reason real time
|
||||
clock and timers does not affect deterministic replay at all.
|
||||
* Virtual clock. These timers run only during the emulation. In icount
|
||||
mode virtual clock value is calculated using executed instructions counter.
|
||||
That is why it is completely deterministic and does not have to be recorded.
|
||||
* Host clock. This clock is used by device models that simulate real time
|
||||
sources (e.g. real time clock chip). Host clock is the one of the sources
|
||||
of non-determinism. Host clock read operations should be logged to
|
||||
make the execution deterministic.
|
||||
* Virtual real time clock. This clock is similar to real time clock but
|
||||
it is used only for increasing virtual clock while virtual machine is
|
||||
sleeping. Due to its nature it is also non-deterministic as the host clock
|
||||
and has to be logged too.
|
||||
|
||||
Checkpoints
|
||||
-----------
|
||||
|
||||
Replaying of the execution of virtual machine is bound by sources of
|
||||
non-determinism. These are inputs from clock and peripheral devices,
|
||||
and QEMU thread scheduling. Thread scheduling affect on processing events
|
||||
from timers, asynchronous input-output, and bottom halves.
|
||||
|
||||
Invocations of timers are coupled with clock reads and changing the state
|
||||
of the virtual machine. Reads produce non-deterministic data taken from
|
||||
host clock. And VM state changes should preserve their order. Their relative
|
||||
order in replay mode must replicate the order of callbacks in record mode.
|
||||
To preserve this order we use checkpoints. When a specific clock is processed
|
||||
in record mode we save to the log special "checkpoint" event.
|
||||
Checkpoints here do not refer to virtual machine snapshots. They are just
|
||||
record/replay events used for synchronization.
|
||||
|
||||
QEMU in replay mode will try to invoke timers processing in random moment
|
||||
of time. That's why we do not process a group of timers until the checkpoint
|
||||
event will be read from the log. Such an event allows synchronizing CPU
|
||||
execution and timer events.
|
||||
|
||||
Two other checkpoints govern the "warping" of the virtual clock.
|
||||
While the virtual machine is idle, the virtual clock increments at
|
||||
1 ns per *real time* nanosecond. This is done by setting up a timer
|
||||
(called the warp timer) on the virtual real time clock, so that the
|
||||
timer fires at the next deadline of the virtual clock; the virtual clock
|
||||
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. 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 icount_start_warp_timer.
|
||||
|
||||
Bottom halves
|
||||
-------------
|
||||
|
||||
Disk I/O events are completely deterministic in our model, because
|
||||
in both record and replay modes we start virtual machine from the same
|
||||
disk state. But callbacks that virtual disk controller uses for reading and
|
||||
writing the disk may occur at different moments of time in record and replay
|
||||
modes.
|
||||
|
||||
Reading and writing requests are created by CPU thread of QEMU. Later these
|
||||
requests proceed to block layer which creates "bottom halves". Bottom
|
||||
halves consist of callback and its parameters. They are processed when
|
||||
main loop locks the global mutex. These locks are not synchronized with
|
||||
replaying process because main loop also processes the events that do not
|
||||
affect the virtual machine state (like user interaction with monitor).
|
||||
|
||||
That is why we had to implement saving and replaying bottom halves callbacks
|
||||
synchronously to the CPU execution. When the callback is about to execute
|
||||
it is added to the queue in the replay module. This queue is written to the
|
||||
log when its callbacks are executed. In replay mode callbacks are not processed
|
||||
until the corresponding event is read from the events log file.
|
||||
|
||||
Sometimes the block layer uses asynchronous callbacks for its internal purposes
|
||||
(like reading or writing VM snapshots or disk image cluster tables). In this
|
||||
case bottom halves are not marked as "replayable" and do not saved
|
||||
into the log.
|
||||
|
||||
Block devices
|
||||
-------------
|
||||
|
||||
Block devices record/replay module intercepts calls of
|
||||
bdrv coroutine functions at the top of block drivers stack.
|
||||
To record and replay block operations the drive must be configured
|
||||
as following:
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
blkreplay driver should be inserted between disk image and virtual driver
|
||||
controller. Therefore all disk requests may be recorded and replayed.
|
||||
|
||||
All block completion operations are added to the queue in the coroutines.
|
||||
Queue is flushed at checkpoints and information about processed requests
|
||||
is recorded to the log. In replay phase the queue is matched with
|
||||
events read from the log. Therefore block devices requests are processed
|
||||
deterministically.
|
||||
|
||||
Snapshotting
|
||||
------------
|
||||
|
||||
New VM snapshots may be created in replay mode. They can be used later
|
||||
to recover the desired VM state. All VM states created in replay mode
|
||||
are associated with the moment of time in the replay scenario.
|
||||
After recovering the VM state replay will start from that position.
|
||||
|
||||
Default starting snapshot name may be specified with icount field
|
||||
rrsnapshot as follows:
|
||||
-icount shift=7,rr=record,rrfile=replay.bin,rrsnapshot=snapshot_name
|
||||
|
||||
This snapshot is created at start of recording and restored at start
|
||||
of replaying. It also can be loaded while replaying to roll back
|
||||
the execution.
|
||||
|
||||
'snapshot' flag of the disk image must be removed to save the snapshots
|
||||
in the overlay (or original image) instead of using the temporary overlay.
|
||||
-drive file=disk.ovl,if=none,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
Use QEMU monitor to create additional snapshots. 'savevm <name>' command
|
||||
created the snapshot and 'loadvm <name>' restores it. To prevent corruption
|
||||
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
|
||||
---------------
|
||||
|
||||
Record and replay for network interactions is performed with the network filter.
|
||||
Each backend must have its own instance of the replay filter as follows:
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
|
||||
Replay network filter is used to record and replay network packets. While
|
||||
recording the virtual machine this filter puts all packets coming from
|
||||
the outer world into the log. In replay mode packets from the log are
|
||||
injected into the network device. All interactions with network backend
|
||||
in replay mode are disabled.
|
||||
|
||||
Audio devices
|
||||
-------------
|
||||
|
||||
Audio data is recorded and replay automatically. The command line for recording
|
||||
and replaying must contain identical specifications of audio hardware, e.g.:
|
||||
-soundhw ac97
|
||||
|
||||
Serial ports
|
||||
------------
|
||||
|
||||
Serial ports input is recorded and replay automatically. The command lines
|
||||
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 snapshot 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
|
||||
-----------------
|
||||
|
||||
Record/replay log consists of the header and the sequence of execution
|
||||
events. The header includes 4-byte replay version id and 8-byte reserved
|
||||
field. Version is updated every time replay log format changes to prevent
|
||||
using replay log created by another build of qemu.
|
||||
|
||||
The sequence of the events describes virtual machine state changes.
|
||||
It includes all non-deterministic inputs of VM, synchronization marks and
|
||||
instruction counts used to correctly inject inputs at replay.
|
||||
|
||||
Synchronization marks (checkpoints) are used for synchronizing qemu threads
|
||||
that perform operations with virtual hardware. These operations may change
|
||||
system's state (e.g., change some register or generate interrupt) and
|
||||
therefore should execute synchronously with CPU thread.
|
||||
|
||||
Every event in the log includes 1-byte event id and optional arguments.
|
||||
When argument is an array, it is stored as 4-byte array length
|
||||
and corresponding number of bytes with data.
|
||||
Here is the list of events that are written into the log:
|
||||
|
||||
- EVENT_INSTRUCTION. Instructions executed since last event.
|
||||
Argument: 4-byte number of executed instructions.
|
||||
- EVENT_INTERRUPT. Used to synchronize interrupt processing.
|
||||
- EVENT_EXCEPTION. Used to synchronize exception handling.
|
||||
- EVENT_ASYNC. This is a group of events. They are always processed
|
||||
together with checkpoints. When such an event is generated, it is
|
||||
stored in the queue and processed only when checkpoint occurs.
|
||||
Every such event is followed by 1-byte checkpoint id and 1-byte
|
||||
async event id from the following list:
|
||||
- REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes
|
||||
callbacks that affect virtual machine state, but normally called
|
||||
asynchronously.
|
||||
Argument: 8-byte operation id.
|
||||
- REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains
|
||||
parameters of keyboard and mouse input operations
|
||||
(key press/release, mouse pointer movement).
|
||||
Arguments: 9-16 bytes depending of input event.
|
||||
- REPLAY_ASYNC_EVENT_INPUT_SYNC. Internal input synchronization event.
|
||||
- REPLAY_ASYNC_EVENT_CHAR_READ. Character (e.g., serial port) device input
|
||||
initiated by the sender.
|
||||
Arguments: 1-byte character device id.
|
||||
Array with bytes were read.
|
||||
- REPLAY_ASYNC_EVENT_BLOCK. Block device operation. Used to synchronize
|
||||
operations with disk and flash drives with CPU.
|
||||
Argument: 8-byte operation id.
|
||||
- REPLAY_ASYNC_EVENT_NET. Incoming network packet.
|
||||
Arguments: 1-byte network adapter id.
|
||||
4-byte packet flags.
|
||||
Array with packet bytes.
|
||||
- EVENT_SHUTDOWN. Occurs when user sends shutdown event to qemu,
|
||||
e.g., by closing the window.
|
||||
- EVENT_CHAR_WRITE. Used to synchronize character output operations.
|
||||
Arguments: 4-byte output function return value.
|
||||
4-byte offset in the output array.
|
||||
- EVENT_CHAR_READ_ALL. Used to synchronize character input operations,
|
||||
initiated by qemu.
|
||||
Argument: Array with bytes that were read.
|
||||
- EVENT_CHAR_READ_ALL_ERROR. Unsuccessful character input operation,
|
||||
initiated by qemu.
|
||||
Argument: 4-byte error code.
|
||||
- EVENT_CLOCK + clock_id. Group of events for host clock read operations.
|
||||
Argument: 8-byte clock value.
|
||||
- EVENT_CHECKPOINT + checkpoint_id. Checkpoint for synchronization of
|
||||
CPU, internal threads, and asynchronous input events. May be followed
|
||||
by one or more EVENT_ASYNC events.
|
||||
- EVENT_END. Last event in the log.
|
|
@ -27,6 +27,7 @@ or Hypervisor.Framework.
|
|||
secrets
|
||||
authz
|
||||
gdb
|
||||
replay
|
||||
managed-startup
|
||||
bootindex
|
||||
cpu-hotplug
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
.. _replay:
|
||||
|
||||
..
|
||||
Copyright (c) 2010-2022 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.
|
||||
|
||||
Record/replay
|
||||
=============
|
||||
|
||||
Record/replay functions are used for the deterministic replay of qemu execution.
|
||||
Execution recording writes a non-deterministic events log, which can be later
|
||||
used for replaying the execution anywhere and for unlimited number of times.
|
||||
It also supports checkpointing for faster rewind to the specific replay moment.
|
||||
Execution replaying reads the log and replays all non-deterministic events
|
||||
including external input, hardware clocks, and interrupts.
|
||||
|
||||
Deterministic replay has the following features:
|
||||
|
||||
* Deterministically replays whole system execution and all contents of
|
||||
the memory, state of the hardware devices, clocks, and screen of the VM.
|
||||
* Writes execution log into the file for later replaying for multiple times
|
||||
on different machines.
|
||||
* Supports i386, x86_64, ARM, AArch64, Risc-V, MIPS, MIPS64, S390X, Alpha,
|
||||
PowerPC, PowerPC64, M68000, Microblaze, OpenRISC, Nios II, SPARC,
|
||||
and Xtensa hardware platforms.
|
||||
* Performs deterministic replay of all operations with keyboard and mouse
|
||||
input devices, serial ports, and network.
|
||||
|
||||
Usage of the record/replay:
|
||||
|
||||
* First, record the execution with the following command line:
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| \\
|
||||
-icount shift=auto,rr=record,rrfile=replay.bin \\
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct \\
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \\
|
||||
-device ide-hd,drive=img-blkreplay \\
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \\
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
|
||||
* After recording, you can replay it by using another command line:
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| \\
|
||||
-icount shift=auto,rr=replay,rrfile=replay.bin \\
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct \\
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \\
|
||||
-device ide-hd,drive=img-blkreplay \\
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \\
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
|
||||
The only difference with recording is changing the rr option
|
||||
from record to replay.
|
||||
* Block device images are not actually changed in the recording mode,
|
||||
because all of the changes are written to the temporary overlay file.
|
||||
This behavior is enabled by using blkreplay driver. It should be used
|
||||
for every enabled block device, as described in :ref:`block-label` section.
|
||||
* ``-net none`` option should be specified when network is not used,
|
||||
because QEMU adds network card by default. When network is needed,
|
||||
it should be configured explicitly with replay filter, as described
|
||||
in :ref:`network-label` section.
|
||||
* Interaction with audio devices and serial ports are recorded and replayed
|
||||
automatically when such devices are enabled.
|
||||
|
||||
Core idea
|
||||
---------
|
||||
|
||||
Record/replay system is based on saving and replaying non-deterministic
|
||||
events (e.g. keyboard input) and simulating deterministic ones (e.g. reading
|
||||
from HDD or memory of the VM). Saving only non-deterministic events makes
|
||||
log file smaller and simulation faster.
|
||||
|
||||
The following non-deterministic data from peripheral devices is saved into
|
||||
the log: mouse and keyboard input, network packets, audio controller input,
|
||||
serial port input, and hardware clocks (they are non-deterministic
|
||||
too, because their values are taken from the host machine). Inputs from
|
||||
simulated hardware, memory of VM, software interrupts, and execution of
|
||||
instructions are not saved into the log, because they are deterministic and
|
||||
can be replayed by simulating the behavior of virtual machine starting from
|
||||
initial state.
|
||||
|
||||
Instruction counting
|
||||
--------------------
|
||||
|
||||
QEMU should work in icount mode to use record/replay feature. icount was
|
||||
designed to allow deterministic execution in absence of external inputs
|
||||
of the virtual machine. Record/replay feature is enabled through ``-icount``
|
||||
command-line option, making possible deterministic execution of the machine,
|
||||
interacting with user or network.
|
||||
|
||||
.. _block-label:
|
||||
|
||||
Block devices
|
||||
-------------
|
||||
|
||||
Block devices record/replay module intercepts calls of
|
||||
bdrv coroutine functions at the top of block drivers stack.
|
||||
To record and replay block operations the drive must be configured
|
||||
as following:
|
||||
|
||||
.. parsed-literal::
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
blkreplay driver should be inserted between disk image and virtual driver
|
||||
controller. Therefore all disk requests may be recorded and replayed.
|
||||
|
||||
.. _snapshotting-label:
|
||||
|
||||
Snapshotting
|
||||
------------
|
||||
|
||||
New VM snapshots may be created in replay mode. They can be used later
|
||||
to recover the desired VM state. All VM states created in replay mode
|
||||
are associated with the moment of time in the replay scenario.
|
||||
After recovering the VM state replay will start from that position.
|
||||
|
||||
Default starting snapshot name may be specified with icount field
|
||||
rrsnapshot as follows:
|
||||
|
||||
.. parsed-literal::
|
||||
-icount shift=auto,rr=record,rrfile=replay.bin,rrsnapshot=snapshot_name
|
||||
|
||||
This snapshot is created at start of recording and restored at start
|
||||
of replaying. It also can be loaded while replaying to roll back
|
||||
the execution.
|
||||
|
||||
``snapshot`` flag of the disk image must be removed to save the snapshots
|
||||
in the overlay (or original image) instead of using the temporary overlay.
|
||||
|
||||
.. parsed-literal::
|
||||
-drive file=disk.ovl,if=none,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
Use QEMU monitor to create additional snapshots. ``savevm <name>`` command
|
||||
created the snapshot and ``loadvm <name>`` restores it. To prevent corruption
|
||||
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:
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| \\
|
||||
-icount shift=auto,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-label:
|
||||
|
||||
Network devices
|
||||
---------------
|
||||
|
||||
Record and replay for network interactions is performed with the network filter.
|
||||
Each backend must have its own instance of the replay filter as follows:
|
||||
|
||||
.. parsed-literal::
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1
|
||||
-object filter-replay,id=replay,netdev=net1
|
||||
|
||||
Replay network filter is used to record and replay network packets. While
|
||||
recording the virtual machine this filter puts all packets coming from
|
||||
the outer world into the log. In replay mode packets from the log are
|
||||
injected into the network device. All interactions with network backend
|
||||
in replay mode are disabled.
|
||||
|
||||
Audio devices
|
||||
-------------
|
||||
|
||||
Audio data is recorded and replay automatically. The command line for recording
|
||||
and replaying must contain identical specifications of audio hardware, e.g.:
|
||||
|
||||
.. parsed-literal::
|
||||
-soundhw ac97
|
||||
|
||||
Serial ports
|
||||
------------
|
||||
|
||||
Serial ports input is recorded and replay automatically. The command lines
|
||||
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:
|
||||
|
||||
#. loading the snapshot
|
||||
#. replaying to examine the breakpoints
|
||||
#. if breakpoint or watchpoint was met
|
||||
|
||||
* loading the snapshot again
|
||||
* replaying to the required breakpoint
|
||||
|
||||
#. else
|
||||
|
||||
* proceeding to the p.1 with the earlier snapshot
|
||||
|
||||
Therefore usage of the reverse debugging requires at least one snapshot
|
||||
created. This can be done by omitting ``snapshot`` option
|
||||
for the block drives and adding ``rrsnapshot`` for both record and replay
|
||||
command lines.
|
||||
See the :ref:`snapshotting-label` section to learn more about running record/replay
|
||||
and creating the snapshot in these modes.
|
||||
|
||||
When ``rrsnapshot`` is not used, then snapshot named ``start_debugging``
|
||||
created in temporary overlay. This allows using reverse debugging, but with
|
||||
temporary snapshots (existing within the session).
|
|
@ -2515,6 +2515,7 @@ static const TypeInfo qxl_primary_info = {
|
|||
.class_init = qxl_primary_class_init,
|
||||
};
|
||||
module_obj("qxl-vga");
|
||||
module_kconfig(QXL);
|
||||
|
||||
static void qxl_secondary_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
|
|
@ -44,6 +44,7 @@ static const VirtioPCIDeviceTypeInfo vhost_user_gpu_pci_info = {
|
|||
.instance_init = vhost_user_gpu_pci_initfn,
|
||||
};
|
||||
module_obj(TYPE_VHOST_USER_GPU_PCI);
|
||||
module_kconfig(VHOST_USER_GPU);
|
||||
|
||||
static void vhost_user_gpu_pci_register_types(void)
|
||||
{
|
||||
|
|
|
@ -606,6 +606,7 @@ static const TypeInfo vhost_user_gpu_info = {
|
|||
.class_init = vhost_user_gpu_class_init,
|
||||
};
|
||||
module_obj(TYPE_VHOST_USER_GPU);
|
||||
module_kconfig(VHOST_USER_GPU);
|
||||
|
||||
static void vhost_user_gpu_register_types(void)
|
||||
{
|
||||
|
|
|
@ -45,6 +45,7 @@ static const VirtioPCIDeviceTypeInfo vhost_user_vga_info = {
|
|||
.instance_init = vhost_user_vga_inst_initfn,
|
||||
};
|
||||
module_obj(TYPE_VHOST_USER_VGA);
|
||||
module_kconfig(VHOST_USER_VGA);
|
||||
|
||||
static void vhost_user_vga_register_types(void)
|
||||
{
|
||||
|
|
|
@ -260,6 +260,7 @@ static const TypeInfo virtio_gpu_base_info = {
|
|||
.abstract = true
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_GPU_BASE);
|
||||
module_kconfig(VIRTIO_GPU);
|
||||
|
||||
static void
|
||||
virtio_register_types(void)
|
||||
|
|
|
@ -160,6 +160,7 @@ static const TypeInfo virtio_gpu_gl_info = {
|
|||
.class_init = virtio_gpu_gl_class_init,
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_GPU_GL);
|
||||
module_kconfig(VIRTIO_GPU);
|
||||
|
||||
static void virtio_register_types(void)
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@ static const VirtioPCIDeviceTypeInfo virtio_gpu_gl_pci_info = {
|
|||
.instance_init = virtio_gpu_gl_initfn,
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_GPU_GL_PCI);
|
||||
module_kconfig(VIRTIO_PCI);
|
||||
|
||||
static void virtio_gpu_gl_pci_register_types(void)
|
||||
{
|
||||
|
|
|
@ -65,6 +65,7 @@ static const TypeInfo virtio_gpu_pci_base_info = {
|
|||
.abstract = true
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_GPU_PCI_BASE);
|
||||
module_kconfig(VIRTIO_PCI);
|
||||
|
||||
#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
|
||||
typedef struct VirtIOGPUPCI VirtIOGPUPCI;
|
||||
|
|
|
@ -1452,6 +1452,7 @@ static const TypeInfo virtio_gpu_info = {
|
|||
.class_init = virtio_gpu_class_init,
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_GPU);
|
||||
module_kconfig(VIRTIO_GPU);
|
||||
|
||||
static void virtio_register_types(void)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,7 @@ static VirtioPCIDeviceTypeInfo virtio_vga_gl_info = {
|
|||
.instance_init = virtio_vga_gl_inst_initfn,
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_VGA_GL);
|
||||
module_kconfig(VIRTIO_VGA);
|
||||
|
||||
static void virtio_vga_register_types(void)
|
||||
{
|
||||
|
|
|
@ -231,6 +231,7 @@ static const TypeInfo virtio_vga_base_info = {
|
|||
.abstract = true,
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_VGA_BASE);
|
||||
module_kconfig(VIRTIO_VGA);
|
||||
|
||||
#define TYPE_VIRTIO_VGA "virtio-vga"
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ static const TypeInfo virtio_ccw_gpu = {
|
|||
.class_init = virtio_ccw_gpu_class_init,
|
||||
};
|
||||
module_obj(TYPE_VIRTIO_GPU_CCW);
|
||||
module_kconfig(VIRTIO_CCW);
|
||||
|
||||
static void virtio_ccw_gpu_register(void)
|
||||
{
|
||||
|
|
|
@ -613,6 +613,7 @@ static const TypeInfo emulated_card_info = {
|
|||
.class_init = emulated_class_initfn,
|
||||
};
|
||||
module_obj(TYPE_EMULATED_CCID);
|
||||
module_kconfig(USB);
|
||||
|
||||
static void ccid_card_emulated_register_types(void)
|
||||
{
|
||||
|
|
|
@ -415,6 +415,7 @@ static const TypeInfo passthru_card_info = {
|
|||
.class_init = passthru_class_initfn,
|
||||
};
|
||||
module_obj(TYPE_CCID_PASSTHRU);
|
||||
module_kconfig(USB);
|
||||
|
||||
static void ccid_card_passthru_register_types(void)
|
||||
{
|
||||
|
|
|
@ -1809,6 +1809,7 @@ static const TypeInfo usb_host_dev_info = {
|
|||
.instance_init = usb_host_instance_init,
|
||||
};
|
||||
module_obj(TYPE_USB_HOST_DEVICE);
|
||||
module_kconfig(USB);
|
||||
|
||||
static void usb_host_register_types(void)
|
||||
{
|
||||
|
|
|
@ -2620,6 +2620,7 @@ static const TypeInfo usbredir_dev_info = {
|
|||
.instance_init = usbredir_instance_init,
|
||||
};
|
||||
module_obj(TYPE_USB_REDIR);
|
||||
module_kconfig(USB);
|
||||
|
||||
static void usbredir_register_types(void)
|
||||
{
|
||||
|
|
|
@ -135,6 +135,16 @@ void module_allow_arch(const char *arch);
|
|||
*/
|
||||
#define module_opts(name) modinfo(opts, name)
|
||||
|
||||
/**
|
||||
* module_kconfig
|
||||
*
|
||||
* @name: Kconfig requirement necessary to load the module
|
||||
*
|
||||
* This module requires a core module that should be implemented and
|
||||
* enabled in Kconfig.
|
||||
*/
|
||||
#define module_kconfig(name) modinfo(kconfig, name)
|
||||
|
||||
/*
|
||||
* module info database
|
||||
*
|
||||
|
|
|
@ -59,6 +59,7 @@ 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);
|
||||
void icount_notify_exit(void);
|
||||
|
||||
/*
|
||||
* CPU Ticks and Clock
|
||||
|
|
|
@ -160,9 +160,14 @@ void replay_shutdown_request(ShutdownCause cause);
|
|||
Returns 0 in PLAY mode if checkpoint was not found.
|
||||
Returns 1 in all other cases. */
|
||||
bool replay_checkpoint(ReplayCheckpoint checkpoint);
|
||||
/*! Used to determine that checkpoint is pending.
|
||||
/*! Used to determine that checkpoint or async event is pending.
|
||||
Does not proceed to the next event in the log. */
|
||||
bool replay_has_checkpoint(void);
|
||||
bool replay_has_event(void);
|
||||
/*
|
||||
* Processes the async events added to the queue (while recording)
|
||||
* or reads the events from the file (while replaying).
|
||||
*/
|
||||
void replay_async_events(void);
|
||||
|
||||
/* Asynchronous events queue */
|
||||
|
||||
|
|
28
meson.build
28
meson.build
|
@ -3175,14 +3175,23 @@ foreach d, list : target_modules
|
|||
endforeach
|
||||
|
||||
if enable_modules
|
||||
modinfo_src = custom_target('modinfo.c',
|
||||
output: 'modinfo.c',
|
||||
input: modinfo_files,
|
||||
command: [modinfo_generate, '@INPUT@'],
|
||||
capture: true)
|
||||
modinfo_lib = static_library('modinfo', modinfo_src)
|
||||
modinfo_dep = declare_dependency(link_whole: modinfo_lib)
|
||||
softmmu_ss.add(modinfo_dep)
|
||||
foreach target : target_dirs
|
||||
if target.endswith('-softmmu')
|
||||
config_target = config_target_mak[target]
|
||||
config_devices_mak = target + '-config-devices.mak'
|
||||
modinfo_src = custom_target('modinfo-' + target + '.c',
|
||||
output: 'modinfo-' + target + '.c',
|
||||
input: modinfo_files,
|
||||
command: [modinfo_generate, '--devices', config_devices_mak, '@INPUT@'],
|
||||
capture: true)
|
||||
|
||||
modinfo_lib = static_library('modinfo-' + target + '.c', modinfo_src)
|
||||
modinfo_dep = declare_dependency(link_with: modinfo_lib)
|
||||
|
||||
arch = config_target['TARGET_NAME'] == 'sparc64' ? 'sparc64' : config_target['TARGET_BASE_ARCH']
|
||||
hw_arch[arch].add(modinfo_dep)
|
||||
endif
|
||||
endforeach
|
||||
endif
|
||||
|
||||
nm = find_program('nm')
|
||||
|
@ -3285,6 +3294,9 @@ foreach m : block_mods + softmmu_mods
|
|||
install: true,
|
||||
install_dir: qemu_moddir)
|
||||
endforeach
|
||||
if emulator_modules.length() > 0
|
||||
alias_target('modules', emulator_modules)
|
||||
endif
|
||||
|
||||
softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp)
|
||||
common_ss.add(qom, qemuutil)
|
||||
|
|
|
@ -40,7 +40,9 @@ async def flush(writer: asyncio.StreamWriter) -> None:
|
|||
drain. The flow control limits are restored after the call is
|
||||
completed.
|
||||
"""
|
||||
transport = cast(asyncio.WriteTransport, writer.transport)
|
||||
transport = cast( # type: ignore[redundant-cast]
|
||||
asyncio.WriteTransport, writer.transport
|
||||
)
|
||||
|
||||
# https://github.com/python/typeshed/issues/5779
|
||||
low, high = transport.get_write_buffer_limits() # type: ignore
|
||||
|
|
|
@ -79,6 +79,7 @@ strict = True
|
|||
python_version = 3.6
|
||||
warn_unused_configs = True
|
||||
namespace_packages = True
|
||||
warn_unused_ignores = False
|
||||
|
||||
[mypy-qemu.utils.qom_fuse]
|
||||
# fusepy has no type stubs:
|
||||
|
|
|
@ -125,7 +125,7 @@ if targetos == 'windows'
|
|||
wixl, '-o', '@OUTPUT0@', '@INPUT0@',
|
||||
qemu_ga_msi_arch[cpu],
|
||||
qemu_ga_msi_vss,
|
||||
'-D', 'BUILD_DIR=' + meson.build_root(),
|
||||
'-D', 'BUILD_DIR=' + meson.project_build_root(),
|
||||
'-D', 'BIN_DIR=' + glib.get_variable('bindir'),
|
||||
'-D', 'QEMU_GA_VERSION=' + config_host['QEMU_GA_VERSION'],
|
||||
'-D', 'QEMU_GA_MANUFACTURER=' + config_host['QEMU_GA_MANUFACTURER'],
|
||||
|
|
|
@ -170,13 +170,12 @@ void replay_block_event(QEMUBH *bh, uint64_t id)
|
|||
}
|
||||
}
|
||||
|
||||
static void replay_save_event(Event *event, int checkpoint)
|
||||
static void replay_save_event(Event *event)
|
||||
{
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
/* put the event into the file */
|
||||
replay_put_event(EVENT_ASYNC);
|
||||
replay_put_byte(checkpoint);
|
||||
replay_put_byte(event->event_kind);
|
||||
g_assert(event->event_kind < REPLAY_ASYNC_COUNT);
|
||||
replay_put_event(EVENT_ASYNC + event->event_kind);
|
||||
|
||||
/* save event-specific data */
|
||||
switch (event->event_kind) {
|
||||
|
@ -206,36 +205,25 @@ static void replay_save_event(Event *event, int checkpoint)
|
|||
}
|
||||
|
||||
/* Called with replay mutex locked */
|
||||
void replay_save_events(int checkpoint)
|
||||
void replay_save_events(void)
|
||||
{
|
||||
g_assert(replay_mutex_locked());
|
||||
g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
|
||||
g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL);
|
||||
while (!QTAILQ_EMPTY(&events_list)) {
|
||||
Event *event = QTAILQ_FIRST(&events_list);
|
||||
replay_save_event(event, checkpoint);
|
||||
replay_save_event(event);
|
||||
replay_run_event(event);
|
||||
QTAILQ_REMOVE(&events_list, event, events);
|
||||
g_free(event);
|
||||
}
|
||||
}
|
||||
|
||||
static Event *replay_read_event(int checkpoint)
|
||||
static Event *replay_read_event(void)
|
||||
{
|
||||
Event *event;
|
||||
if (replay_state.read_event_kind == -1) {
|
||||
replay_state.read_event_checkpoint = replay_get_byte();
|
||||
replay_state.read_event_kind = replay_get_byte();
|
||||
replay_state.read_event_id = -1;
|
||||
replay_check_error();
|
||||
}
|
||||
|
||||
if (checkpoint != replay_state.read_event_checkpoint) {
|
||||
return NULL;
|
||||
}
|
||||
ReplayAsyncEventKind event_kind = replay_state.data_kind - EVENT_ASYNC;
|
||||
|
||||
/* Events that has not to be in the queue */
|
||||
switch (replay_state.read_event_kind) {
|
||||
switch (event_kind) {
|
||||
case REPLAY_ASYNC_EVENT_BH:
|
||||
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||
if (replay_state.read_event_id == -1) {
|
||||
|
@ -244,17 +232,17 @@ static Event *replay_read_event(int checkpoint)
|
|||
break;
|
||||
case REPLAY_ASYNC_EVENT_INPUT:
|
||||
event = g_new0(Event, 1);
|
||||
event->event_kind = replay_state.read_event_kind;
|
||||
event->event_kind = event_kind;
|
||||
event->opaque = replay_read_input_event();
|
||||
return event;
|
||||
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
|
||||
event = g_new0(Event, 1);
|
||||
event->event_kind = replay_state.read_event_kind;
|
||||
event->event_kind = event_kind;
|
||||
event->opaque = 0;
|
||||
return event;
|
||||
case REPLAY_ASYNC_EVENT_CHAR_READ:
|
||||
event = g_new0(Event, 1);
|
||||
event->event_kind = replay_state.read_event_kind;
|
||||
event->event_kind = event_kind;
|
||||
event->opaque = replay_event_char_read_load();
|
||||
return event;
|
||||
case REPLAY_ASYNC_EVENT_BLOCK:
|
||||
|
@ -264,18 +252,17 @@ static Event *replay_read_event(int checkpoint)
|
|||
break;
|
||||
case REPLAY_ASYNC_EVENT_NET:
|
||||
event = g_new0(Event, 1);
|
||||
event->event_kind = replay_state.read_event_kind;
|
||||
event->event_kind = event_kind;
|
||||
event->opaque = replay_event_net_load();
|
||||
return event;
|
||||
default:
|
||||
error_report("Unknown ID %d of replay event",
|
||||
replay_state.read_event_kind);
|
||||
error_report("Unknown ID %d of replay event", event_kind);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(event, &events_list, events) {
|
||||
if (event->event_kind == replay_state.read_event_kind
|
||||
if (event->event_kind == event_kind
|
||||
&& (replay_state.read_event_id == -1
|
||||
|| replay_state.read_event_id == event->id)) {
|
||||
break;
|
||||
|
@ -284,26 +271,23 @@ static Event *replay_read_event(int checkpoint)
|
|||
|
||||
if (event) {
|
||||
QTAILQ_REMOVE(&events_list, event, events);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read event-specific data */
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/* Called with replay mutex locked */
|
||||
void replay_read_events(int checkpoint)
|
||||
void replay_read_events(void)
|
||||
{
|
||||
g_assert(replay_mutex_locked());
|
||||
while (replay_state.data_kind == EVENT_ASYNC) {
|
||||
Event *event = replay_read_event(checkpoint);
|
||||
while (replay_state.data_kind >= EVENT_ASYNC
|
||||
&& replay_state.data_kind <= EVENT_ASYNC_LAST) {
|
||||
Event *event = replay_read_event();
|
||||
if (!event) {
|
||||
break;
|
||||
}
|
||||
replay_finish_event();
|
||||
replay_state.read_event_kind = -1;
|
||||
replay_state.read_event_id = -1;
|
||||
replay_run_event(event);
|
||||
|
||||
g_free(event);
|
||||
|
@ -312,7 +296,7 @@ void replay_read_events(int checkpoint)
|
|||
|
||||
void replay_init_events(void)
|
||||
{
|
||||
replay_state.read_event_kind = -1;
|
||||
replay_state.read_event_id = -1;
|
||||
}
|
||||
|
||||
void replay_finish_events(void)
|
||||
|
|
|
@ -12,6 +12,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/* Asynchronous events IDs */
|
||||
|
||||
typedef enum ReplayAsyncEventKind {
|
||||
REPLAY_ASYNC_EVENT_BH,
|
||||
REPLAY_ASYNC_EVENT_BH_ONESHOT,
|
||||
REPLAY_ASYNC_EVENT_INPUT,
|
||||
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
||||
REPLAY_ASYNC_EVENT_CHAR_READ,
|
||||
REPLAY_ASYNC_EVENT_BLOCK,
|
||||
REPLAY_ASYNC_EVENT_NET,
|
||||
REPLAY_ASYNC_COUNT
|
||||
} ReplayAsyncEventKind;
|
||||
|
||||
/* Any changes to order/number of events will need to bump REPLAY_VERSION */
|
||||
enum ReplayEvents {
|
||||
/* for instruction event */
|
||||
|
@ -22,6 +35,7 @@ enum ReplayEvents {
|
|||
EVENT_EXCEPTION,
|
||||
/* for async events */
|
||||
EVENT_ASYNC,
|
||||
EVENT_ASYNC_LAST = EVENT_ASYNC + REPLAY_ASYNC_COUNT - 1,
|
||||
/* for shutdown requests, range allows recovery of ShutdownCause */
|
||||
EVENT_SHUTDOWN,
|
||||
EVENT_SHUTDOWN_LAST = EVENT_SHUTDOWN + SHUTDOWN_CAUSE__MAX,
|
||||
|
@ -49,21 +63,6 @@ enum ReplayEvents {
|
|||
EVENT_COUNT
|
||||
};
|
||||
|
||||
/* Asynchronous events IDs */
|
||||
|
||||
enum ReplayAsyncEventKind {
|
||||
REPLAY_ASYNC_EVENT_BH,
|
||||
REPLAY_ASYNC_EVENT_BH_ONESHOT,
|
||||
REPLAY_ASYNC_EVENT_INPUT,
|
||||
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
||||
REPLAY_ASYNC_EVENT_CHAR_READ,
|
||||
REPLAY_ASYNC_EVENT_BLOCK,
|
||||
REPLAY_ASYNC_EVENT_NET,
|
||||
REPLAY_ASYNC_COUNT
|
||||
};
|
||||
|
||||
typedef enum ReplayAsyncEventKind ReplayAsyncEventKind;
|
||||
|
||||
typedef struct ReplayState {
|
||||
/*! Cached clock values. */
|
||||
int64_t cached_clock[REPLAY_CLOCK_COUNT];
|
||||
|
@ -83,12 +82,8 @@ typedef struct ReplayState {
|
|||
uint64_t block_request_id;
|
||||
/*! Prior value of the host clock */
|
||||
uint64_t host_clock_last;
|
||||
/*! Asynchronous event type read from the log */
|
||||
int32_t read_event_kind;
|
||||
/*! Asynchronous event id read from the log */
|
||||
uint64_t read_event_id;
|
||||
/*! Asynchronous event checkpoint id read from the log */
|
||||
int32_t read_event_checkpoint;
|
||||
} ReplayState;
|
||||
extern ReplayState replay_state;
|
||||
|
||||
|
@ -152,9 +147,9 @@ void replay_finish_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 */
|
||||
void replay_save_events(int checkpoint);
|
||||
void replay_save_events(void);
|
||||
/*! Read events from the file into the input queue */
|
||||
void replay_read_events(int checkpoint);
|
||||
void replay_read_events(void);
|
||||
/*! Adds specified async event to the queue */
|
||||
void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
|
||||
void *opaque2, uint64_t id);
|
||||
|
|
|
@ -59,9 +59,7 @@ static const VMStateDescription vmstate_replay = {
|
|||
VMSTATE_UINT32(has_unread_data, ReplayState),
|
||||
VMSTATE_UINT64(file_offset, ReplayState),
|
||||
VMSTATE_UINT64(block_request_id, ReplayState),
|
||||
VMSTATE_INT32(read_event_kind, ReplayState),
|
||||
VMSTATE_UINT64(read_event_id, ReplayState),
|
||||
VMSTATE_INT32(read_event_checkpoint, ReplayState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/* Current version of the replay mechanism.
|
||||
Increase it when file format changes. */
|
||||
#define REPLAY_VERSION 0xe0200a
|
||||
#define REPLAY_VERSION 0xe0200c
|
||||
/* Size of replay log header */
|
||||
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
|
||||
|
||||
|
@ -171,64 +171,49 @@ void replay_shutdown_request(ShutdownCause cause)
|
|||
|
||||
bool replay_checkpoint(ReplayCheckpoint checkpoint)
|
||||
{
|
||||
bool res = false;
|
||||
static bool in_checkpoint;
|
||||
assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
|
||||
|
||||
if (!replay_file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_checkpoint) {
|
||||
/*
|
||||
Recursion occurs when HW event modifies timers.
|
||||
Prevent performing icount warp in this case and
|
||||
wait for another invocation of the checkpoint.
|
||||
*/
|
||||
g_assert(replay_mode == REPLAY_MODE_PLAY);
|
||||
return false;
|
||||
}
|
||||
in_checkpoint = true;
|
||||
|
||||
replay_save_instructions();
|
||||
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
g_assert(replay_mutex_locked());
|
||||
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
|
||||
replay_finish_event();
|
||||
} else if (replay_state.data_kind != EVENT_ASYNC) {
|
||||
res = false;
|
||||
goto out;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
replay_read_events(checkpoint);
|
||||
/* replay_read_events may leave some unread events.
|
||||
Return false if not all of the events associated with
|
||||
checkpoint were processed */
|
||||
res = replay_state.data_kind != EVENT_ASYNC;
|
||||
} else if (replay_mode == REPLAY_MODE_RECORD) {
|
||||
g_assert(replay_mutex_locked());
|
||||
replay_put_event(EVENT_CHECKPOINT + checkpoint);
|
||||
/* This checkpoint belongs to several threads.
|
||||
Processing events from different threads is
|
||||
non-deterministic */
|
||||
if (checkpoint != CHECKPOINT_CLOCK_WARP_START
|
||||
/* FIXME: this is temporary fix, other checkpoints
|
||||
may also be invoked from the different threads someday.
|
||||
Asynchronous event processing should be refactored
|
||||
to create additional replay event kind which is
|
||||
nailed to the one of the threads and which processes
|
||||
the event queue. */
|
||||
&& checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
|
||||
replay_save_events(checkpoint);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
out:
|
||||
in_checkpoint = false;
|
||||
return res;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool replay_has_checkpoint(void)
|
||||
void replay_async_events(void)
|
||||
{
|
||||
static bool processing = false;
|
||||
/*
|
||||
* If we are already processing the events, recursion may occur
|
||||
* in case of incorrect implementation when HW event modifies timers.
|
||||
* Timer modification may invoke the icount warp, event processing,
|
||||
* and cause the recursion.
|
||||
*/
|
||||
g_assert(!processing);
|
||||
processing = true;
|
||||
|
||||
replay_save_instructions();
|
||||
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
g_assert(replay_mutex_locked());
|
||||
replay_read_events();
|
||||
} else if (replay_mode == REPLAY_MODE_RECORD) {
|
||||
g_assert(replay_mutex_locked());
|
||||
replay_save_events();
|
||||
}
|
||||
processing = false;
|
||||
}
|
||||
|
||||
bool replay_has_event(void)
|
||||
{
|
||||
bool res = false;
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
|
@ -236,6 +221,8 @@ bool replay_has_checkpoint(void)
|
|||
replay_account_executed_instructions();
|
||||
res = EVENT_CHECKPOINT <= replay_state.data_kind
|
||||
&& replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
|
||||
res = res || (EVENT_ASYNC <= replay_state.data_kind
|
||||
&& replay_state.data_kind <= EVENT_ASYNC_LAST);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -387,9 +374,8 @@ void replay_finish(void)
|
|||
g_free(replay_snapshot);
|
||||
replay_snapshot = NULL;
|
||||
|
||||
replay_mode = REPLAY_MODE_NONE;
|
||||
|
||||
replay_finish_events();
|
||||
replay_mode = REPLAY_MODE_NONE;
|
||||
}
|
||||
|
||||
void replay_add_blocker(Error *reason)
|
||||
|
|
|
@ -33,10 +33,18 @@ import re
|
|||
import random
|
||||
import argparse
|
||||
from itertools import chain
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
|
||||
from qemu.machine import QEMUMachine
|
||||
from qemu.qmp import ConnectError
|
||||
try:
|
||||
from qemu.machine import QEMUMachine
|
||||
from qemu.qmp import ConnectError
|
||||
except ModuleNotFoundError as exc:
|
||||
path = Path(__file__).resolve()
|
||||
print(f"Module '{exc.name}' not found.")
|
||||
print(" Try 'make check-venv' from your build directory,")
|
||||
print(" and then one way to run this script is like so:")
|
||||
print(f' > $builddir/tests/venv/bin/python3 "{path}"')
|
||||
sys.exit(1)
|
||||
|
||||
logger = logging.getLogger('device-crash-test')
|
||||
dbg = logger.debug
|
||||
|
|
|
@ -156,13 +156,13 @@ meson_options_help() {
|
|||
printf "%s\n" ' vhost-kernel vhost kernel backend support'
|
||||
printf "%s\n" ' vhost-net vhost-net kernel acceleration support'
|
||||
printf "%s\n" ' vhost-user vhost-user backend support'
|
||||
printf "%s\n" ' vmnet vmnet.framework network backend support'
|
||||
printf "%s\n" ' vhost-user-blk-server'
|
||||
printf "%s\n" ' build vhost-user-blk server'
|
||||
printf "%s\n" ' vhost-vdpa vhost-vdpa kernel backend support'
|
||||
printf "%s\n" ' virglrenderer virgl rendering support'
|
||||
printf "%s\n" ' virtfs virtio-9p support'
|
||||
printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)'
|
||||
printf "%s\n" ' vmnet vmnet.framework network backend support'
|
||||
printf "%s\n" ' vnc VNC server'
|
||||
printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server'
|
||||
printf "%s\n" ' vnc-sasl SASL authentication for VNC server'
|
||||
|
@ -430,6 +430,8 @@ _meson_option_parse() {
|
|||
--disable-virtfs) printf "%s" -Dvirtfs=disabled ;;
|
||||
--enable-virtiofsd) printf "%s" -Dvirtiofsd=enabled ;;
|
||||
--disable-virtiofsd) printf "%s" -Dvirtiofsd=disabled ;;
|
||||
--enable-vmnet) printf "%s" -Dvmnet=enabled ;;
|
||||
--disable-vmnet) printf "%s" -Dvmnet=disabled ;;
|
||||
--enable-vnc) printf "%s" -Dvnc=enabled ;;
|
||||
--disable-vnc) printf "%s" -Dvnc=disabled ;;
|
||||
--enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;;
|
||||
|
|
|
@ -32,7 +32,7 @@ def parse_line(line):
|
|||
continue
|
||||
return (kind, data)
|
||||
|
||||
def generate(name, lines):
|
||||
def generate(name, lines, enabled):
|
||||
arch = ""
|
||||
objs = []
|
||||
deps = []
|
||||
|
@ -48,6 +48,14 @@ def generate(name, lines):
|
|||
opts.append(data)
|
||||
elif kind == 'arch':
|
||||
arch = data;
|
||||
elif kind == 'kconfig':
|
||||
# don't add a module which dependency is not enabled
|
||||
# in kconfig
|
||||
if data.strip() not in enabled:
|
||||
print(" /* module {} isn't enabled in Kconfig. */"
|
||||
.format(data.strip()))
|
||||
print("/* },{ */")
|
||||
return None
|
||||
else:
|
||||
print("unknown:", kind)
|
||||
exit(1)
|
||||
|
@ -58,8 +66,8 @@ def generate(name, lines):
|
|||
print_array("objs", objs)
|
||||
print_array("deps", deps)
|
||||
print_array("opts", opts)
|
||||
print("},{");
|
||||
return deps
|
||||
print("},{")
|
||||
return {dep.strip('" ') for dep in deps}
|
||||
|
||||
def print_pre():
|
||||
print("/* generated by scripts/modinfo-generate.py */")
|
||||
|
@ -72,23 +80,38 @@ def print_post():
|
|||
print("}};")
|
||||
|
||||
def main(args):
|
||||
deps = {}
|
||||
if len(args) < 3 or args[0] != '--devices':
|
||||
print('Expected: modinfo-generate.py --devices '
|
||||
'config-device.mak [modinfo files]', file=sys.stderr)
|
||||
exit(1)
|
||||
|
||||
# get all devices enabled in kconfig, from *-config-device.mak
|
||||
enabled = set()
|
||||
with open(args[1]) as file:
|
||||
for line in file.readlines():
|
||||
config = line.split('=')
|
||||
if config[1].rstrip() == 'y':
|
||||
enabled.add(config[0][7:]) # remove CONFIG_
|
||||
|
||||
deps = set()
|
||||
modules = set()
|
||||
print_pre()
|
||||
for modinfo in args:
|
||||
for modinfo in args[2:]:
|
||||
with open(modinfo) as f:
|
||||
lines = f.readlines()
|
||||
print(" /* %s */" % modinfo)
|
||||
(basename, ext) = os.path.splitext(modinfo)
|
||||
deps[basename] = generate(basename, lines)
|
||||
(basename, _) = os.path.splitext(modinfo)
|
||||
moddeps = generate(basename, lines, enabled)
|
||||
if moddeps is not None:
|
||||
modules.add(basename)
|
||||
deps.update(moddeps)
|
||||
print_post()
|
||||
|
||||
flattened_deps = {flat.strip('" ') for dep in deps.values() for flat in dep}
|
||||
error = False
|
||||
for dep in flattened_deps:
|
||||
if dep not in deps.keys():
|
||||
print("Dependency {} cannot be satisfied".format(dep),
|
||||
file=sys.stderr)
|
||||
error = True
|
||||
for dep in deps.difference(modules):
|
||||
print("Dependency {} cannot be satisfied".format(dep),
|
||||
file=sys.stderr)
|
||||
error = True
|
||||
|
||||
if error:
|
||||
exit(1)
|
||||
|
|
|
@ -322,7 +322,7 @@ void icount_start_warp_timer(void)
|
|||
* 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()) {
|
||||
if (replay_has_event()) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
return;
|
||||
|
@ -404,6 +404,8 @@ void icount_account_warp_timer(void)
|
|||
return;
|
||||
}
|
||||
|
||||
replay_async_events();
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
||||
return;
|
||||
|
@ -486,3 +488,11 @@ void icount_configure(QemuOpts *opts, Error **errp)
|
|||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
void icount_notify_exit(void)
|
||||
{
|
||||
if (icount_enabled() && current_cpu) {
|
||||
qemu_cpu_kick(current_cpu);
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,3 +43,7 @@ void icount_account_warp_timer(void)
|
|||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void icount_notify_exit(void)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -5284,10 +5284,22 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
|||
/* cache info: needed for Core compatibility */
|
||||
if (cpu->cache_info_passthrough) {
|
||||
x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx);
|
||||
/* QEMU gives out its own APIC IDs, never pass down bits 31..26. */
|
||||
*eax &= ~0xFC000000;
|
||||
if ((*eax & 31) && cs->nr_cores > 1) {
|
||||
*eax |= (cs->nr_cores - 1) << 26;
|
||||
/*
|
||||
* QEMU has its own number of cores/logical cpus,
|
||||
* set 24..14, 31..26 bit to configured values
|
||||
*/
|
||||
if (*eax & 31) {
|
||||
int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14);
|
||||
int vcpus_per_socket = env->nr_dies * cs->nr_cores *
|
||||
cs->nr_threads;
|
||||
if (cs->nr_cores > 1) {
|
||||
*eax &= ~0xFC000000;
|
||||
*eax |= (pow2ceil(cs->nr_cores) - 1) << 26;
|
||||
}
|
||||
if (host_vcpus_per_cache > vcpus_per_socket) {
|
||||
*eax &= ~0x3FFC000;
|
||||
*eax |= (pow2ceil(vcpus_per_socket) - 1) << 14;
|
||||
}
|
||||
}
|
||||
} else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) {
|
||||
*eax = *ebx = *ecx = *edx = 0;
|
||||
|
@ -5559,7 +5571,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
|||
* supports. Features can be further restricted by userspace, but not
|
||||
* made more permissive.
|
||||
*/
|
||||
x86_cpu_get_supported_cpuid(0x12, index, eax, ebx, ecx, edx);
|
||||
x86_cpu_get_supported_cpuid(0x12, count, eax, ebx, ecx, edx);
|
||||
|
||||
if (count == 0) {
|
||||
*eax &= env->features[FEAT_SGX_12_0_EAX];
|
||||
|
|
|
@ -359,6 +359,7 @@ static int handle_mmu_fault(CPUState *cs, vaddr addr, int size,
|
|||
CPUX86State *env = &cpu->env;
|
||||
int error_code = PG_ERROR_OK;
|
||||
int pg_mode, prot, page_size;
|
||||
int32_t a20_mask;
|
||||
hwaddr paddr;
|
||||
hwaddr vaddr;
|
||||
|
||||
|
@ -368,7 +369,8 @@ static int handle_mmu_fault(CPUState *cs, vaddr addr, int size,
|
|||
#endif
|
||||
|
||||
if (!(env->cr[0] & CR0_PG_MASK)) {
|
||||
paddr = addr;
|
||||
a20_mask = x86_get_a20_mask(env);
|
||||
paddr = addr & a20_mask;
|
||||
#ifdef TARGET_X86_64
|
||||
if (!(env->hflags & HF_LMA_MASK)) {
|
||||
/* Without long mode we can only address 32bits in real mode */
|
||||
|
|
|
@ -3,28 +3,28 @@
|
|||
.PHONY: check-help
|
||||
check-help:
|
||||
@echo "Regression testing targets:"
|
||||
@echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests"
|
||||
@echo " $(MAKE) bench Run speed tests"
|
||||
@echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests"
|
||||
@echo " $(MAKE) bench Run speed tests"
|
||||
@echo
|
||||
@echo "Individual test suites:"
|
||||
@echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target"
|
||||
@echo " $(MAKE) check-qtest Run qtest tests"
|
||||
@echo " $(MAKE) check-unit Run qobject tests"
|
||||
@echo " $(MAKE) check-qapi-schema Run QAPI schema tests"
|
||||
@echo " $(MAKE) check-block Run block tests"
|
||||
@echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target"
|
||||
@echo " $(MAKE) check-qtest Run qtest tests"
|
||||
@echo " $(MAKE) check-unit Run qobject tests"
|
||||
@echo " $(MAKE) check-qapi-schema Run QAPI schema tests"
|
||||
@echo " $(MAKE) check-block Run block tests"
|
||||
ifneq ($(filter $(all-check-targets), check-softfloat),)
|
||||
@echo " $(MAKE) check-tcg Run TCG tests"
|
||||
@echo " $(MAKE) check-softfloat Run FPU emulation tests"
|
||||
@echo " $(MAKE) check-tcg Run TCG tests"
|
||||
@echo " $(MAKE) check-softfloat Run FPU emulation tests"
|
||||
endif
|
||||
@echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets"
|
||||
@echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets"
|
||||
@echo
|
||||
@echo " $(MAKE) check-report.tap Generates an aggregated TAP test report"
|
||||
@echo " $(MAKE) check-venv Creates a Python venv for tests"
|
||||
@echo " $(MAKE) check-clean Clean the tests and related data"
|
||||
@echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report"
|
||||
@echo " $(MAKE) check-venv Creates a Python venv for tests"
|
||||
@echo " $(MAKE) check-clean Clean the tests and related data"
|
||||
@echo
|
||||
@echo "The following are useful for CI builds"
|
||||
@echo " $(MAKE) check-build Build most test binaries"
|
||||
@echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)"
|
||||
@echo " $(MAKE) check-build Build most test binaries"
|
||||
@echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)"
|
||||
@echo
|
||||
@echo
|
||||
@echo "The variable SPEED can be set to control the gtester speed setting."
|
||||
|
@ -89,6 +89,7 @@ TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets)))
|
|||
TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv
|
||||
TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt
|
||||
TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results
|
||||
TESTS_PYTHON=$(TESTS_VENV_DIR)/bin/python3
|
||||
ifndef AVOCADO_TESTS
|
||||
AVOCADO_TESTS=tests/avocado
|
||||
endif
|
||||
|
@ -103,13 +104,14 @@ else
|
|||
AVOCADO_CMDLINE_TAGS=$(addprefix -t , $(AVOCADO_TAGS))
|
||||
endif
|
||||
|
||||
quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \
|
||||
$(TESTS_PYTHON) -m pip -q --disable-pip-version-check $1, \
|
||||
"VENVPIP","$1")
|
||||
|
||||
$(TESTS_VENV_DIR): $(TESTS_VENV_REQ)
|
||||
$(call quiet-command, \
|
||||
$(PYTHON) -m venv $@, \
|
||||
VENV, $@)
|
||||
$(call quiet-command, \
|
||||
$(TESTS_VENV_DIR)/bin/python -m pip -q install -r $(TESTS_VENV_REQ), \
|
||||
PIP, $(TESTS_VENV_REQ))
|
||||
$(call quiet-command, $(PYTHON) -m venv $@, VENV, $@)
|
||||
$(call quiet-venv-pip,install -e "$(SRC_PATH)/python/")
|
||||
$(call quiet-venv-pip,install -r $(TESTS_VENV_REQ))
|
||||
$(call quiet-command, touch $@)
|
||||
|
||||
$(TESTS_RESULTS_DIR):
|
||||
|
@ -126,7 +128,7 @@ FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES))
|
|||
# download one specific Fedora 31 image
|
||||
get-vm-image-fedora-31-%: check-venv
|
||||
$(call quiet-command, \
|
||||
$(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \
|
||||
$(TESTS_PYTHON) -m avocado vmimage get \
|
||||
--distro=fedora --distro-version=31 --arch=$*, \
|
||||
"AVOCADO", "Downloading avocado tests VM image for $*")
|
||||
|
||||
|
@ -135,7 +137,7 @@ get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOW
|
|||
|
||||
check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images
|
||||
$(call quiet-command, \
|
||||
$(TESTS_VENV_DIR)/bin/python -m avocado \
|
||||
$(TESTS_PYTHON) -m avocado \
|
||||
--show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \
|
||||
$(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \
|
||||
--filter-by-tags-include-empty-key) \
|
||||
|
|
|
@ -21,6 +21,11 @@ import avocado
|
|||
from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage
|
||||
from avocado.utils.path import find_command
|
||||
|
||||
from qemu.machine import QEMUMachine
|
||||
from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available,
|
||||
tcg_available)
|
||||
|
||||
|
||||
#: The QEMU build root directory. It may also be the source directory
|
||||
#: if building from the source dir, but it's safer to use BUILD_DIR for
|
||||
#: that purpose. Be aware that if this code is moved outside of a source
|
||||
|
@ -35,12 +40,6 @@ if os.path.islink(os.path.dirname(os.path.dirname(__file__))):
|
|||
else:
|
||||
SOURCE_DIR = BUILD_DIR
|
||||
|
||||
sys.path.append(os.path.join(SOURCE_DIR, 'python'))
|
||||
|
||||
from qemu.machine import QEMUMachine
|
||||
from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available,
|
||||
tcg_available)
|
||||
|
||||
|
||||
def has_cmd(name, args=None):
|
||||
"""
|
||||
|
|
|
@ -13,6 +13,7 @@ import logging
|
|||
import time
|
||||
|
||||
from avocado import skipUnless
|
||||
from avocado_qemu import BUILD_DIR
|
||||
from avocado.utils import cloudinit
|
||||
from avocado.utils import network
|
||||
from avocado.utils import vmimage
|
||||
|
@ -32,9 +33,16 @@ class ReplayLinux(LinuxTest):
|
|||
bus = 'ide'
|
||||
|
||||
def setUp(self):
|
||||
super(ReplayLinux, self).setUp()
|
||||
# LinuxTest does many replay-incompatible things, but includes
|
||||
# useful methods. Do not setup LinuxTest here and just
|
||||
# call some functions.
|
||||
super(LinuxTest, self).setUp()
|
||||
self._set_distro()
|
||||
self.boot_path = self.download_boot()
|
||||
self.cloudinit_path = self.prepare_cloudinit()
|
||||
self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0),
|
||||
self.name)
|
||||
ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys()
|
||||
self.cloudinit_path = self.prepare_cloudinit(ssh_pubkey)
|
||||
|
||||
def vm_add_disk(self, vm, path, id, device):
|
||||
bus_string = ''
|
||||
|
@ -50,7 +58,9 @@ class ReplayLinux(LinuxTest):
|
|||
vm = self.get_vm()
|
||||
vm.add_args('-smp', '1')
|
||||
vm.add_args('-m', '1024')
|
||||
vm.add_args('-object', 'filter-replay,id=replay,netdev=hub0port0')
|
||||
vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
|
||||
'-device', 'virtio-net,netdev=vnet')
|
||||
vm.add_args('-object', 'filter-replay,id=replay,netdev=vnet')
|
||||
if args:
|
||||
vm.add_args(*args)
|
||||
self.vm_add_disk(vm, self.boot_path, 0, self.hdd)
|
||||
|
@ -75,8 +85,8 @@ class ReplayLinux(LinuxTest):
|
|||
stop_check=(lambda : not vm.is_running()))
|
||||
console_drainer.start()
|
||||
if record:
|
||||
cloudinit.wait_for_phone_home(('0.0.0.0', self.phone_home_port),
|
||||
self.name)
|
||||
while not self.phone_server.instance_phoned_back:
|
||||
self.phone_server.handle_request()
|
||||
vm.shutdown()
|
||||
logger.info('finished the recording with log size %s bytes'
|
||||
% os.path.getsize(replay_path))
|
||||
|
@ -114,3 +124,68 @@ class ReplayLinuxX8664(ReplayLinux):
|
|||
:avocado: tags=machine:q35
|
||||
"""
|
||||
self.run_rr(shift=3)
|
||||
|
||||
@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
|
||||
class ReplayLinuxX8664Virtio(ReplayLinux):
|
||||
"""
|
||||
:avocado: tags=arch:x86_64
|
||||
:avocado: tags=virtio
|
||||
:avocado: tags=accel:tcg
|
||||
"""
|
||||
|
||||
hdd = 'virtio-blk-pci'
|
||||
cd = 'virtio-blk-pci'
|
||||
bus = None
|
||||
|
||||
chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0'
|
||||
|
||||
def test_pc_i440fx(self):
|
||||
"""
|
||||
:avocado: tags=machine:pc
|
||||
"""
|
||||
self.run_rr(shift=1)
|
||||
|
||||
def test_pc_q35(self):
|
||||
"""
|
||||
:avocado: tags=machine:q35
|
||||
"""
|
||||
self.run_rr(shift=3)
|
||||
|
||||
@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout')
|
||||
class ReplayLinuxAarch64(ReplayLinux):
|
||||
"""
|
||||
:avocado: tags=accel:tcg
|
||||
:avocado: tags=arch:aarch64
|
||||
:avocado: tags=machine:virt
|
||||
:avocado: tags=cpu:max
|
||||
"""
|
||||
|
||||
chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49'
|
||||
|
||||
hdd = 'virtio-blk-device'
|
||||
cd = 'virtio-blk-device'
|
||||
bus = None
|
||||
|
||||
def get_common_args(self):
|
||||
return ('-bios',
|
||||
os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd'),
|
||||
"-cpu", "max,lpa2=off",
|
||||
'-device', 'virtio-rng-pci,rng=rng0',
|
||||
'-object', 'rng-builtin,id=rng0')
|
||||
|
||||
def test_virt_gicv2(self):
|
||||
"""
|
||||
:avocado: tags=machine:gic-version=2
|
||||
"""
|
||||
|
||||
self.run_rr(shift=3,
|
||||
args=(*self.get_common_args(),
|
||||
"-machine", "virt,gic-version=2"))
|
||||
|
||||
def test_virt_gicv3(self):
|
||||
"""
|
||||
:avocado: tags=machine:gic-version=3
|
||||
"""
|
||||
|
||||
self.run_rr(shift=3,
|
||||
args=(*self.get_common_args(),
|
||||
|
|
|
@ -22,7 +22,6 @@ import os
|
|||
import re
|
||||
import logging
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.machine import QEMUMachine
|
||||
from avocado_qemu import QemuSystemTest
|
||||
from avocado import skip
|
||||
|
|
|
@ -11,7 +11,6 @@ Check compatibility of virtio device types
|
|||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.machine import QEMUMachine
|
||||
from avocado_qemu import QemuSystemTest
|
||||
|
||||
|
|
|
@ -34,4 +34,5 @@ RUN apt update && \
|
|||
python3 \
|
||||
python3-sphinx \
|
||||
python3-sphinx-rtd-theme \
|
||||
python3-venv \
|
||||
$(apt-get -s build-dep --arch-only qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Add Python module requirements, one per line, to be installed
|
||||
# in the tests/venv Python virtual environment. For more info,
|
||||
# refer to: https://pip.pypa.io/en/stable/user_guide/#id1
|
||||
# Note that qemu.git/python/ is always implicitly installed.
|
||||
avocado-framework==88.1
|
||||
pycdlib==1.11.0
|
||||
|
|
|
@ -84,10 +84,11 @@ vm-clean-all:
|
|||
|
||||
$(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
|
||||
$(SRC_PATH)/tests/vm/basevm.py \
|
||||
$(SRC_PATH)/tests/vm/Makefile.include
|
||||
$(SRC_PATH)/tests/vm/Makefile.include \
|
||||
check-venv
|
||||
@mkdir -p $(IMAGES_DIR)
|
||||
$(call quiet-command, \
|
||||
$(PYTHON) $< \
|
||||
$(TESTS_PYTHON) $< \
|
||||
$(if $(V)$(DEBUG), --debug) \
|
||||
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
|
||||
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||
|
@ -101,9 +102,9 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
|
|||
|
||||
|
||||
# Build in VM $(IMAGE)
|
||||
vm-build-%: $(IMAGES_DIR)/%.img
|
||||
vm-build-%: $(IMAGES_DIR)/%.img check-venv
|
||||
$(call quiet-command, \
|
||||
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||
$(if $(V)$(DEBUG), --debug) \
|
||||
$(if $(DEBUG), --interactive) \
|
||||
$(if $(J),--jobs $(J)) \
|
||||
|
@ -127,9 +128,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img
|
|||
-device virtio-net-pci,netdev=vnet \
|
||||
|| true
|
||||
|
||||
vm-boot-ssh-%: $(IMAGES_DIR)/%.img
|
||||
vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv
|
||||
$(call quiet-command, \
|
||||
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||
$(if $(J),--jobs $(J)) \
|
||||
$(if $(V)$(DEBUG), --debug) \
|
||||
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||
|
|
|
@ -18,9 +18,6 @@ import socket
|
|||
import logging
|
||||
import time
|
||||
import datetime
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
||||
from qemu.machine import QEMUMachine
|
||||
from qemu.utils import get_info_usernet_hostfwd_port, kvm_available
|
||||
import subprocess
|
||||
import hashlib
|
||||
import argparse
|
||||
|
@ -31,6 +28,9 @@ import multiprocessing
|
|||
import traceback
|
||||
import shlex
|
||||
|
||||
from qemu.machine import QEMUMachine
|
||||
from qemu.utils import get_info_usernet_hostfwd_port, kvm_available
|
||||
|
||||
SSH_KEY_FILE = os.path.join(os.path.dirname(__file__),
|
||||
"..", "keys", "id_rsa")
|
||||
SSH_PUB_KEY_FILE = os.path.join(os.path.dirname(__file__),
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "block/raw-aio.h"
|
||||
#include "qemu/coroutine_int.h"
|
||||
#include "qemu/coroutine-tls.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "trace.h"
|
||||
|
||||
/***********************************************************/
|
||||
|
@ -84,6 +85,13 @@ static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags)
|
|||
}
|
||||
|
||||
aio_notify(ctx);
|
||||
/*
|
||||
* Workaround for record/replay.
|
||||
* vCPU execution should be suspended when new BH is set.
|
||||
* This is needed to avoid guest timeouts caused
|
||||
* by the long cycles of the execution.
|
||||
*/
|
||||
icount_notify_exit();
|
||||
}
|
||||
|
||||
/* Only called from aio_bh_poll() and aio_ctx_finalize() */
|
||||
|
|
Loading…
Reference in New Issue