* 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
|
IMAGE: debian-amd64
|
||||||
script:
|
script:
|
||||||
- cd build
|
- 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:
|
build-system-fedora:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
|
@ -155,8 +156,9 @@ crash-test-fedora:
|
||||||
IMAGE: fedora
|
IMAGE: fedora
|
||||||
script:
|
script:
|
||||||
- cd build
|
- cd build
|
||||||
- scripts/device-crash-test -q ./qemu-system-ppc
|
- make check-venv
|
||||||
- scripts/device-crash-test -q ./qemu-system-riscv32
|
- 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:
|
build-system-centos:
|
||||||
extends: .native_build_job_template
|
extends: .native_build_job_template
|
||||||
|
|
|
@ -84,8 +84,7 @@ void icount_handle_deadline(void)
|
||||||
* Don't interrupt cpu thread, when these events are waiting
|
* Don't interrupt cpu thread, when these events are waiting
|
||||||
* (i.e., there is no checkpoint)
|
* (i.e., there is no checkpoint)
|
||||||
*/
|
*/
|
||||||
if (deadline == 0
|
if (deadline == 0) {
|
||||||
&& (replay_mode != REPLAY_MODE_PLAY || replay_has_checkpoint())) {
|
|
||||||
icount_notify_aio_contexts();
|
icount_notify_aio_contexts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +108,7 @@ void icount_prepare_for_run(CPUState *cpu)
|
||||||
|
|
||||||
replay_mutex_lock();
|
replay_mutex_lock();
|
||||||
|
|
||||||
if (cpu->icount_budget == 0 && replay_has_checkpoint()) {
|
if (cpu->icount_budget == 0) {
|
||||||
icount_notify_aio_contexts();
|
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)
|
--with-git-submodules=ignore do not update or check git submodules (default if no .git dir)
|
||||||
--static enable static build [$static]
|
--static enable static build [$static]
|
||||||
--bindir=PATH install binaries in PATH
|
--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]
|
--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-features default all --enable-* options to "disabled"
|
||||||
--without-default-devices do not include any device that is not needed to
|
--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
|
multi-thread-tcg
|
||||||
tcg-icount
|
tcg-icount
|
||||||
tcg-plugins
|
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
|
secrets
|
||||||
authz
|
authz
|
||||||
gdb
|
gdb
|
||||||
|
replay
|
||||||
managed-startup
|
managed-startup
|
||||||
bootindex
|
bootindex
|
||||||
cpu-hotplug
|
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,
|
.class_init = qxl_primary_class_init,
|
||||||
};
|
};
|
||||||
module_obj("qxl-vga");
|
module_obj("qxl-vga");
|
||||||
|
module_kconfig(QXL);
|
||||||
|
|
||||||
static void qxl_secondary_class_init(ObjectClass *klass, void *data)
|
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,
|
.instance_init = vhost_user_gpu_pci_initfn,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VHOST_USER_GPU_PCI);
|
module_obj(TYPE_VHOST_USER_GPU_PCI);
|
||||||
|
module_kconfig(VHOST_USER_GPU);
|
||||||
|
|
||||||
static void vhost_user_gpu_pci_register_types(void)
|
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,
|
.class_init = vhost_user_gpu_class_init,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VHOST_USER_GPU);
|
module_obj(TYPE_VHOST_USER_GPU);
|
||||||
|
module_kconfig(VHOST_USER_GPU);
|
||||||
|
|
||||||
static void vhost_user_gpu_register_types(void)
|
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,
|
.instance_init = vhost_user_vga_inst_initfn,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VHOST_USER_VGA);
|
module_obj(TYPE_VHOST_USER_VGA);
|
||||||
|
module_kconfig(VHOST_USER_VGA);
|
||||||
|
|
||||||
static void vhost_user_vga_register_types(void)
|
static void vhost_user_vga_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -260,6 +260,7 @@ static const TypeInfo virtio_gpu_base_info = {
|
||||||
.abstract = true
|
.abstract = true
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_GPU_BASE);
|
module_obj(TYPE_VIRTIO_GPU_BASE);
|
||||||
|
module_kconfig(VIRTIO_GPU);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
virtio_register_types(void)
|
virtio_register_types(void)
|
||||||
|
|
|
@ -160,6 +160,7 @@ static const TypeInfo virtio_gpu_gl_info = {
|
||||||
.class_init = virtio_gpu_gl_class_init,
|
.class_init = virtio_gpu_gl_class_init,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_GPU_GL);
|
module_obj(TYPE_VIRTIO_GPU_GL);
|
||||||
|
module_kconfig(VIRTIO_GPU);
|
||||||
|
|
||||||
static void virtio_register_types(void)
|
static void virtio_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -47,6 +47,7 @@ static const VirtioPCIDeviceTypeInfo virtio_gpu_gl_pci_info = {
|
||||||
.instance_init = virtio_gpu_gl_initfn,
|
.instance_init = virtio_gpu_gl_initfn,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_GPU_GL_PCI);
|
module_obj(TYPE_VIRTIO_GPU_GL_PCI);
|
||||||
|
module_kconfig(VIRTIO_PCI);
|
||||||
|
|
||||||
static void virtio_gpu_gl_pci_register_types(void)
|
static void virtio_gpu_gl_pci_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,6 +65,7 @@ static const TypeInfo virtio_gpu_pci_base_info = {
|
||||||
.abstract = true
|
.abstract = true
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_GPU_PCI_BASE);
|
module_obj(TYPE_VIRTIO_GPU_PCI_BASE);
|
||||||
|
module_kconfig(VIRTIO_PCI);
|
||||||
|
|
||||||
#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
|
#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci"
|
||||||
typedef struct VirtIOGPUPCI VirtIOGPUPCI;
|
typedef struct VirtIOGPUPCI VirtIOGPUPCI;
|
||||||
|
|
|
@ -1452,6 +1452,7 @@ static const TypeInfo virtio_gpu_info = {
|
||||||
.class_init = virtio_gpu_class_init,
|
.class_init = virtio_gpu_class_init,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_GPU);
|
module_obj(TYPE_VIRTIO_GPU);
|
||||||
|
module_kconfig(VIRTIO_GPU);
|
||||||
|
|
||||||
static void virtio_register_types(void)
|
static void virtio_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,6 +37,7 @@ static VirtioPCIDeviceTypeInfo virtio_vga_gl_info = {
|
||||||
.instance_init = virtio_vga_gl_inst_initfn,
|
.instance_init = virtio_vga_gl_inst_initfn,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_VGA_GL);
|
module_obj(TYPE_VIRTIO_VGA_GL);
|
||||||
|
module_kconfig(VIRTIO_VGA);
|
||||||
|
|
||||||
static void virtio_vga_register_types(void)
|
static void virtio_vga_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -231,6 +231,7 @@ static const TypeInfo virtio_vga_base_info = {
|
||||||
.abstract = true,
|
.abstract = true,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_VGA_BASE);
|
module_obj(TYPE_VIRTIO_VGA_BASE);
|
||||||
|
module_kconfig(VIRTIO_VGA);
|
||||||
|
|
||||||
#define TYPE_VIRTIO_VGA "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,
|
.class_init = virtio_ccw_gpu_class_init,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_VIRTIO_GPU_CCW);
|
module_obj(TYPE_VIRTIO_GPU_CCW);
|
||||||
|
module_kconfig(VIRTIO_CCW);
|
||||||
|
|
||||||
static void virtio_ccw_gpu_register(void)
|
static void virtio_ccw_gpu_register(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -613,6 +613,7 @@ static const TypeInfo emulated_card_info = {
|
||||||
.class_init = emulated_class_initfn,
|
.class_init = emulated_class_initfn,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_EMULATED_CCID);
|
module_obj(TYPE_EMULATED_CCID);
|
||||||
|
module_kconfig(USB);
|
||||||
|
|
||||||
static void ccid_card_emulated_register_types(void)
|
static void ccid_card_emulated_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -415,6 +415,7 @@ static const TypeInfo passthru_card_info = {
|
||||||
.class_init = passthru_class_initfn,
|
.class_init = passthru_class_initfn,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_CCID_PASSTHRU);
|
module_obj(TYPE_CCID_PASSTHRU);
|
||||||
|
module_kconfig(USB);
|
||||||
|
|
||||||
static void ccid_card_passthru_register_types(void)
|
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,
|
.instance_init = usb_host_instance_init,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_USB_HOST_DEVICE);
|
module_obj(TYPE_USB_HOST_DEVICE);
|
||||||
|
module_kconfig(USB);
|
||||||
|
|
||||||
static void usb_host_register_types(void)
|
static void usb_host_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2620,6 +2620,7 @@ static const TypeInfo usbredir_dev_info = {
|
||||||
.instance_init = usbredir_instance_init,
|
.instance_init = usbredir_instance_init,
|
||||||
};
|
};
|
||||||
module_obj(TYPE_USB_REDIR);
|
module_obj(TYPE_USB_REDIR);
|
||||||
|
module_kconfig(USB);
|
||||||
|
|
||||||
static void usbredir_register_types(void)
|
static void usbredir_register_types(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -135,6 +135,16 @@ void module_allow_arch(const char *arch);
|
||||||
*/
|
*/
|
||||||
#define module_opts(name) modinfo(opts, name)
|
#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
|
* 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. */
|
/* if the CPUs are idle, start accounting real time to virtual clock. */
|
||||||
void icount_start_warp_timer(void);
|
void icount_start_warp_timer(void);
|
||||||
void icount_account_warp_timer(void);
|
void icount_account_warp_timer(void);
|
||||||
|
void icount_notify_exit(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CPU Ticks and Clock
|
* 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 0 in PLAY mode if checkpoint was not found.
|
||||||
Returns 1 in all other cases. */
|
Returns 1 in all other cases. */
|
||||||
bool replay_checkpoint(ReplayCheckpoint checkpoint);
|
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. */
|
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 */
|
/* Asynchronous events queue */
|
||||||
|
|
||||||
|
|
24
meson.build
24
meson.build
|
@ -3175,14 +3175,23 @@ foreach d, list : target_modules
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
if enable_modules
|
if enable_modules
|
||||||
modinfo_src = custom_target('modinfo.c',
|
foreach target : target_dirs
|
||||||
output: 'modinfo.c',
|
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,
|
input: modinfo_files,
|
||||||
command: [modinfo_generate, '@INPUT@'],
|
command: [modinfo_generate, '--devices', config_devices_mak, '@INPUT@'],
|
||||||
capture: true)
|
capture: true)
|
||||||
modinfo_lib = static_library('modinfo', modinfo_src)
|
|
||||||
modinfo_dep = declare_dependency(link_whole: modinfo_lib)
|
modinfo_lib = static_library('modinfo-' + target + '.c', modinfo_src)
|
||||||
softmmu_ss.add(modinfo_dep)
|
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
|
endif
|
||||||
|
|
||||||
nm = find_program('nm')
|
nm = find_program('nm')
|
||||||
|
@ -3285,6 +3294,9 @@ foreach m : block_mods + softmmu_mods
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: qemu_moddir)
|
install_dir: qemu_moddir)
|
||||||
endforeach
|
endforeach
|
||||||
|
if emulator_modules.length() > 0
|
||||||
|
alias_target('modules', emulator_modules)
|
||||||
|
endif
|
||||||
|
|
||||||
softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp)
|
softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp)
|
||||||
common_ss.add(qom, qemuutil)
|
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
|
drain. The flow control limits are restored after the call is
|
||||||
completed.
|
completed.
|
||||||
"""
|
"""
|
||||||
transport = cast(asyncio.WriteTransport, writer.transport)
|
transport = cast( # type: ignore[redundant-cast]
|
||||||
|
asyncio.WriteTransport, writer.transport
|
||||||
|
)
|
||||||
|
|
||||||
# https://github.com/python/typeshed/issues/5779
|
# https://github.com/python/typeshed/issues/5779
|
||||||
low, high = transport.get_write_buffer_limits() # type: ignore
|
low, high = transport.get_write_buffer_limits() # type: ignore
|
||||||
|
|
|
@ -79,6 +79,7 @@ strict = True
|
||||||
python_version = 3.6
|
python_version = 3.6
|
||||||
warn_unused_configs = True
|
warn_unused_configs = True
|
||||||
namespace_packages = True
|
namespace_packages = True
|
||||||
|
warn_unused_ignores = False
|
||||||
|
|
||||||
[mypy-qemu.utils.qom_fuse]
|
[mypy-qemu.utils.qom_fuse]
|
||||||
# fusepy has no type stubs:
|
# fusepy has no type stubs:
|
||||||
|
|
|
@ -125,7 +125,7 @@ if targetos == 'windows'
|
||||||
wixl, '-o', '@OUTPUT0@', '@INPUT0@',
|
wixl, '-o', '@OUTPUT0@', '@INPUT0@',
|
||||||
qemu_ga_msi_arch[cpu],
|
qemu_ga_msi_arch[cpu],
|
||||||
qemu_ga_msi_vss,
|
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', 'BIN_DIR=' + glib.get_variable('bindir'),
|
||||||
'-D', 'QEMU_GA_VERSION=' + config_host['QEMU_GA_VERSION'],
|
'-D', 'QEMU_GA_VERSION=' + config_host['QEMU_GA_VERSION'],
|
||||||
'-D', 'QEMU_GA_MANUFACTURER=' + config_host['QEMU_GA_MANUFACTURER'],
|
'-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) {
|
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||||
/* put the event into the file */
|
/* put the event into the file */
|
||||||
replay_put_event(EVENT_ASYNC);
|
g_assert(event->event_kind < REPLAY_ASYNC_COUNT);
|
||||||
replay_put_byte(checkpoint);
|
replay_put_event(EVENT_ASYNC + event->event_kind);
|
||||||
replay_put_byte(event->event_kind);
|
|
||||||
|
|
||||||
/* save event-specific data */
|
/* save event-specific data */
|
||||||
switch (event->event_kind) {
|
switch (event->event_kind) {
|
||||||
|
@ -206,36 +205,25 @@ static void replay_save_event(Event *event, int checkpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with replay mutex locked */
|
/* Called with replay mutex locked */
|
||||||
void replay_save_events(int checkpoint)
|
void replay_save_events(void)
|
||||||
{
|
{
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
|
|
||||||
g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL);
|
|
||||||
while (!QTAILQ_EMPTY(&events_list)) {
|
while (!QTAILQ_EMPTY(&events_list)) {
|
||||||
Event *event = QTAILQ_FIRST(&events_list);
|
Event *event = QTAILQ_FIRST(&events_list);
|
||||||
replay_save_event(event, checkpoint);
|
replay_save_event(event);
|
||||||
replay_run_event(event);
|
replay_run_event(event);
|
||||||
QTAILQ_REMOVE(&events_list, event, events);
|
QTAILQ_REMOVE(&events_list, event, events);
|
||||||
g_free(event);
|
g_free(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Event *replay_read_event(int checkpoint)
|
static Event *replay_read_event(void)
|
||||||
{
|
{
|
||||||
Event *event;
|
Event *event;
|
||||||
if (replay_state.read_event_kind == -1) {
|
ReplayAsyncEventKind event_kind = replay_state.data_kind - EVENT_ASYNC;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Events that has not to be in the queue */
|
/* 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:
|
||||||
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||||
if (replay_state.read_event_id == -1) {
|
if (replay_state.read_event_id == -1) {
|
||||||
|
@ -244,17 +232,17 @@ static Event *replay_read_event(int checkpoint)
|
||||||
break;
|
break;
|
||||||
case REPLAY_ASYNC_EVENT_INPUT:
|
case REPLAY_ASYNC_EVENT_INPUT:
|
||||||
event = g_new0(Event, 1);
|
event = g_new0(Event, 1);
|
||||||
event->event_kind = replay_state.read_event_kind;
|
event->event_kind = event_kind;
|
||||||
event->opaque = replay_read_input_event();
|
event->opaque = replay_read_input_event();
|
||||||
return event;
|
return event;
|
||||||
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
|
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
|
||||||
event = g_new0(Event, 1);
|
event = g_new0(Event, 1);
|
||||||
event->event_kind = replay_state.read_event_kind;
|
event->event_kind = event_kind;
|
||||||
event->opaque = 0;
|
event->opaque = 0;
|
||||||
return event;
|
return event;
|
||||||
case REPLAY_ASYNC_EVENT_CHAR_READ:
|
case REPLAY_ASYNC_EVENT_CHAR_READ:
|
||||||
event = g_new0(Event, 1);
|
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();
|
event->opaque = replay_event_char_read_load();
|
||||||
return event;
|
return event;
|
||||||
case REPLAY_ASYNC_EVENT_BLOCK:
|
case REPLAY_ASYNC_EVENT_BLOCK:
|
||||||
|
@ -264,18 +252,17 @@ static Event *replay_read_event(int checkpoint)
|
||||||
break;
|
break;
|
||||||
case REPLAY_ASYNC_EVENT_NET:
|
case REPLAY_ASYNC_EVENT_NET:
|
||||||
event = g_new0(Event, 1);
|
event = g_new0(Event, 1);
|
||||||
event->event_kind = replay_state.read_event_kind;
|
event->event_kind = event_kind;
|
||||||
event->opaque = replay_event_net_load();
|
event->opaque = replay_event_net_load();
|
||||||
return event;
|
return event;
|
||||||
default:
|
default:
|
||||||
error_report("Unknown ID %d of replay event",
|
error_report("Unknown ID %d of replay event", event_kind);
|
||||||
replay_state.read_event_kind);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_FOREACH(event, &events_list, events) {
|
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 == -1
|
||||||
|| replay_state.read_event_id == event->id)) {
|
|| replay_state.read_event_id == event->id)) {
|
||||||
break;
|
break;
|
||||||
|
@ -284,26 +271,23 @@ static Event *replay_read_event(int checkpoint)
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
QTAILQ_REMOVE(&events_list, event, events);
|
QTAILQ_REMOVE(&events_list, event, events);
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read event-specific data */
|
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with replay mutex locked */
|
/* Called with replay mutex locked */
|
||||||
void replay_read_events(int checkpoint)
|
void replay_read_events(void)
|
||||||
{
|
{
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
while (replay_state.data_kind == EVENT_ASYNC) {
|
while (replay_state.data_kind >= EVENT_ASYNC
|
||||||
Event *event = replay_read_event(checkpoint);
|
&& replay_state.data_kind <= EVENT_ASYNC_LAST) {
|
||||||
|
Event *event = replay_read_event();
|
||||||
if (!event) {
|
if (!event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
replay_finish_event();
|
replay_finish_event();
|
||||||
replay_state.read_event_kind = -1;
|
replay_state.read_event_id = -1;
|
||||||
replay_run_event(event);
|
replay_run_event(event);
|
||||||
|
|
||||||
g_free(event);
|
g_free(event);
|
||||||
|
@ -312,7 +296,7 @@ void replay_read_events(int checkpoint)
|
||||||
|
|
||||||
void replay_init_events(void)
|
void replay_init_events(void)
|
||||||
{
|
{
|
||||||
replay_state.read_event_kind = -1;
|
replay_state.read_event_id = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void replay_finish_events(void)
|
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 */
|
/* Any changes to order/number of events will need to bump REPLAY_VERSION */
|
||||||
enum ReplayEvents {
|
enum ReplayEvents {
|
||||||
/* for instruction event */
|
/* for instruction event */
|
||||||
|
@ -22,6 +35,7 @@ enum ReplayEvents {
|
||||||
EVENT_EXCEPTION,
|
EVENT_EXCEPTION,
|
||||||
/* for async events */
|
/* for async events */
|
||||||
EVENT_ASYNC,
|
EVENT_ASYNC,
|
||||||
|
EVENT_ASYNC_LAST = EVENT_ASYNC + REPLAY_ASYNC_COUNT - 1,
|
||||||
/* for shutdown requests, range allows recovery of ShutdownCause */
|
/* for shutdown requests, range allows recovery of ShutdownCause */
|
||||||
EVENT_SHUTDOWN,
|
EVENT_SHUTDOWN,
|
||||||
EVENT_SHUTDOWN_LAST = EVENT_SHUTDOWN + SHUTDOWN_CAUSE__MAX,
|
EVENT_SHUTDOWN_LAST = EVENT_SHUTDOWN + SHUTDOWN_CAUSE__MAX,
|
||||||
|
@ -49,21 +63,6 @@ enum ReplayEvents {
|
||||||
EVENT_COUNT
|
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 {
|
typedef struct ReplayState {
|
||||||
/*! Cached clock values. */
|
/*! Cached clock values. */
|
||||||
int64_t cached_clock[REPLAY_CLOCK_COUNT];
|
int64_t cached_clock[REPLAY_CLOCK_COUNT];
|
||||||
|
@ -83,12 +82,8 @@ typedef struct ReplayState {
|
||||||
uint64_t block_request_id;
|
uint64_t block_request_id;
|
||||||
/*! Prior value of the host clock */
|
/*! Prior value of the host clock */
|
||||||
uint64_t host_clock_last;
|
uint64_t host_clock_last;
|
||||||
/*! Asynchronous event type read from the log */
|
|
||||||
int32_t read_event_kind;
|
|
||||||
/*! Asynchronous event id read from the log */
|
/*! Asynchronous event id read from the log */
|
||||||
uint64_t read_event_id;
|
uint64_t read_event_id;
|
||||||
/*! Asynchronous event checkpoint id read from the log */
|
|
||||||
int32_t read_event_checkpoint;
|
|
||||||
} ReplayState;
|
} ReplayState;
|
||||||
extern ReplayState replay_state;
|
extern ReplayState replay_state;
|
||||||
|
|
||||||
|
@ -152,9 +147,9 @@ void replay_finish_events(void);
|
||||||
/*! Returns true if there are any unsaved events in the queue */
|
/*! Returns true if there are any unsaved events in the queue */
|
||||||
bool replay_has_events(void);
|
bool replay_has_events(void);
|
||||||
/*! Saves events from queue into the file */
|
/*! 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 */
|
/*! 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 */
|
/*! Adds specified async event to the queue */
|
||||||
void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
|
void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
|
||||||
void *opaque2, uint64_t id);
|
void *opaque2, uint64_t id);
|
||||||
|
|
|
@ -59,9 +59,7 @@ static const VMStateDescription vmstate_replay = {
|
||||||
VMSTATE_UINT32(has_unread_data, ReplayState),
|
VMSTATE_UINT32(has_unread_data, ReplayState),
|
||||||
VMSTATE_UINT64(file_offset, ReplayState),
|
VMSTATE_UINT64(file_offset, ReplayState),
|
||||||
VMSTATE_UINT64(block_request_id, ReplayState),
|
VMSTATE_UINT64(block_request_id, ReplayState),
|
||||||
VMSTATE_INT32(read_event_kind, ReplayState),
|
|
||||||
VMSTATE_UINT64(read_event_id, ReplayState),
|
VMSTATE_UINT64(read_event_id, ReplayState),
|
||||||
VMSTATE_INT32(read_event_checkpoint, ReplayState),
|
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
/* Current version of the replay mechanism.
|
/* Current version of the replay mechanism.
|
||||||
Increase it when file format changes. */
|
Increase it when file format changes. */
|
||||||
#define REPLAY_VERSION 0xe0200a
|
#define REPLAY_VERSION 0xe0200c
|
||||||
/* Size of replay log header */
|
/* Size of replay log header */
|
||||||
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
|
#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 replay_checkpoint(ReplayCheckpoint checkpoint)
|
||||||
{
|
{
|
||||||
bool res = false;
|
|
||||||
static bool in_checkpoint;
|
|
||||||
assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
|
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();
|
replay_save_instructions();
|
||||||
|
|
||||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
|
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
|
||||||
replay_finish_event();
|
replay_finish_event();
|
||||||
} else if (replay_state.data_kind != EVENT_ASYNC) {
|
} else {
|
||||||
res = false;
|
return false;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
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) {
|
} else if (replay_mode == REPLAY_MODE_RECORD) {
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
replay_put_event(EVENT_CHECKPOINT + checkpoint);
|
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;
|
return true;
|
||||||
}
|
|
||||||
out:
|
|
||||||
in_checkpoint = false;
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
bool res = false;
|
||||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||||
|
@ -236,6 +221,8 @@ bool replay_has_checkpoint(void)
|
||||||
replay_account_executed_instructions();
|
replay_account_executed_instructions();
|
||||||
res = EVENT_CHECKPOINT <= replay_state.data_kind
|
res = EVENT_CHECKPOINT <= replay_state.data_kind
|
||||||
&& replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
|
&& replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
|
||||||
|
res = res || (EVENT_ASYNC <= replay_state.data_kind
|
||||||
|
&& replay_state.data_kind <= EVENT_ASYNC_LAST);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -387,9 +374,8 @@ void replay_finish(void)
|
||||||
g_free(replay_snapshot);
|
g_free(replay_snapshot);
|
||||||
replay_snapshot = NULL;
|
replay_snapshot = NULL;
|
||||||
|
|
||||||
replay_mode = REPLAY_MODE_NONE;
|
|
||||||
|
|
||||||
replay_finish_events();
|
replay_finish_events();
|
||||||
|
replay_mode = REPLAY_MODE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void replay_add_blocker(Error *reason)
|
void replay_add_blocker(Error *reason)
|
||||||
|
|
|
@ -33,10 +33,18 @@ import re
|
||||||
import random
|
import random
|
||||||
import argparse
|
import argparse
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
|
try:
|
||||||
from qemu.machine import QEMUMachine
|
from qemu.machine import QEMUMachine
|
||||||
from qemu.qmp import ConnectError
|
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')
|
logger = logging.getLogger('device-crash-test')
|
||||||
dbg = logger.debug
|
dbg = logger.debug
|
||||||
|
|
|
@ -156,13 +156,13 @@ meson_options_help() {
|
||||||
printf "%s\n" ' vhost-kernel vhost kernel backend support'
|
printf "%s\n" ' vhost-kernel vhost kernel backend support'
|
||||||
printf "%s\n" ' vhost-net vhost-net kernel acceleration support'
|
printf "%s\n" ' vhost-net vhost-net kernel acceleration support'
|
||||||
printf "%s\n" ' vhost-user vhost-user backend 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" ' vhost-user-blk-server'
|
||||||
printf "%s\n" ' build 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" ' vhost-vdpa vhost-vdpa kernel backend support'
|
||||||
printf "%s\n" ' virglrenderer virgl rendering support'
|
printf "%s\n" ' virglrenderer virgl rendering support'
|
||||||
printf "%s\n" ' virtfs virtio-9p support'
|
printf "%s\n" ' virtfs virtio-9p support'
|
||||||
printf "%s\n" ' virtiofsd build virtiofs daemon (virtiofsd)'
|
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 VNC server'
|
||||||
printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server'
|
printf "%s\n" ' vnc-jpeg JPEG lossy compression for VNC server'
|
||||||
printf "%s\n" ' vnc-sasl SASL authentication 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 ;;
|
--disable-virtfs) printf "%s" -Dvirtfs=disabled ;;
|
||||||
--enable-virtiofsd) printf "%s" -Dvirtiofsd=enabled ;;
|
--enable-virtiofsd) printf "%s" -Dvirtiofsd=enabled ;;
|
||||||
--disable-virtiofsd) printf "%s" -Dvirtiofsd=disabled ;;
|
--disable-virtiofsd) printf "%s" -Dvirtiofsd=disabled ;;
|
||||||
|
--enable-vmnet) printf "%s" -Dvmnet=enabled ;;
|
||||||
|
--disable-vmnet) printf "%s" -Dvmnet=disabled ;;
|
||||||
--enable-vnc) printf "%s" -Dvnc=enabled ;;
|
--enable-vnc) printf "%s" -Dvnc=enabled ;;
|
||||||
--disable-vnc) printf "%s" -Dvnc=disabled ;;
|
--disable-vnc) printf "%s" -Dvnc=disabled ;;
|
||||||
--enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;;
|
--enable-vnc-jpeg) printf "%s" -Dvnc_jpeg=enabled ;;
|
||||||
|
|
|
@ -32,7 +32,7 @@ def parse_line(line):
|
||||||
continue
|
continue
|
||||||
return (kind, data)
|
return (kind, data)
|
||||||
|
|
||||||
def generate(name, lines):
|
def generate(name, lines, enabled):
|
||||||
arch = ""
|
arch = ""
|
||||||
objs = []
|
objs = []
|
||||||
deps = []
|
deps = []
|
||||||
|
@ -48,6 +48,14 @@ def generate(name, lines):
|
||||||
opts.append(data)
|
opts.append(data)
|
||||||
elif kind == 'arch':
|
elif kind == 'arch':
|
||||||
arch = data;
|
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:
|
else:
|
||||||
print("unknown:", kind)
|
print("unknown:", kind)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -58,8 +66,8 @@ def generate(name, lines):
|
||||||
print_array("objs", objs)
|
print_array("objs", objs)
|
||||||
print_array("deps", deps)
|
print_array("deps", deps)
|
||||||
print_array("opts", opts)
|
print_array("opts", opts)
|
||||||
print("},{");
|
print("},{")
|
||||||
return deps
|
return {dep.strip('" ') for dep in deps}
|
||||||
|
|
||||||
def print_pre():
|
def print_pre():
|
||||||
print("/* generated by scripts/modinfo-generate.py */")
|
print("/* generated by scripts/modinfo-generate.py */")
|
||||||
|
@ -72,20 +80,35 @@ def print_post():
|
||||||
print("}};")
|
print("}};")
|
||||||
|
|
||||||
def main(args):
|
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()
|
print_pre()
|
||||||
for modinfo in args:
|
for modinfo in args[2:]:
|
||||||
with open(modinfo) as f:
|
with open(modinfo) as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
print(" /* %s */" % modinfo)
|
print(" /* %s */" % modinfo)
|
||||||
(basename, ext) = os.path.splitext(modinfo)
|
(basename, _) = os.path.splitext(modinfo)
|
||||||
deps[basename] = generate(basename, lines)
|
moddeps = generate(basename, lines, enabled)
|
||||||
|
if moddeps is not None:
|
||||||
|
modules.add(basename)
|
||||||
|
deps.update(moddeps)
|
||||||
print_post()
|
print_post()
|
||||||
|
|
||||||
flattened_deps = {flat.strip('" ') for dep in deps.values() for flat in dep}
|
|
||||||
error = False
|
error = False
|
||||||
for dep in flattened_deps:
|
for dep in deps.difference(modules):
|
||||||
if dep not in deps.keys():
|
|
||||||
print("Dependency {} cannot be satisfied".format(dep),
|
print("Dependency {} cannot be satisfied".format(dep),
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
error = True
|
error = True
|
||||||
|
|
|
@ -322,7 +322,7 @@ void icount_start_warp_timer(void)
|
||||||
* to vCPU was processed in advance and vCPU went to sleep.
|
* to vCPU was processed in advance and vCPU went to sleep.
|
||||||
* Therefore we have to wake it up for doing someting.
|
* Therefore we have to wake it up for doing someting.
|
||||||
*/
|
*/
|
||||||
if (replay_has_checkpoint()) {
|
if (replay_has_event()) {
|
||||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -404,6 +404,8 @@ void icount_account_warp_timer(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replay_async_events();
|
||||||
|
|
||||||
/* warp clock deterministically in record/replay mode */
|
/* warp clock deterministically in record/replay mode */
|
||||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
||||||
return;
|
return;
|
||||||
|
@ -486,3 +488,11 @@ void icount_configure(QemuOpts *opts, Error **errp)
|
||||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||||
NANOSECONDS_PER_SECOND / 10);
|
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();
|
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 */
|
/* cache info: needed for Core compatibility */
|
||||||
if (cpu->cache_info_passthrough) {
|
if (cpu->cache_info_passthrough) {
|
||||||
x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx);
|
x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx);
|
||||||
/* QEMU gives out its own APIC IDs, never pass down bits 31..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 &= ~0xFC000000;
|
||||||
if ((*eax & 31) && cs->nr_cores > 1) {
|
*eax |= (pow2ceil(cs->nr_cores) - 1) << 26;
|
||||||
*eax |= (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)) {
|
} else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) {
|
||||||
*eax = *ebx = *ecx = *edx = 0;
|
*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
|
* supports. Features can be further restricted by userspace, but not
|
||||||
* made more permissive.
|
* 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) {
|
if (count == 0) {
|
||||||
*eax &= env->features[FEAT_SGX_12_0_EAX];
|
*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;
|
CPUX86State *env = &cpu->env;
|
||||||
int error_code = PG_ERROR_OK;
|
int error_code = PG_ERROR_OK;
|
||||||
int pg_mode, prot, page_size;
|
int pg_mode, prot, page_size;
|
||||||
|
int32_t a20_mask;
|
||||||
hwaddr paddr;
|
hwaddr paddr;
|
||||||
hwaddr vaddr;
|
hwaddr vaddr;
|
||||||
|
|
||||||
|
@ -368,7 +369,8 @@ static int handle_mmu_fault(CPUState *cs, vaddr addr, int size,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!(env->cr[0] & CR0_PG_MASK)) {
|
if (!(env->cr[0] & CR0_PG_MASK)) {
|
||||||
paddr = addr;
|
a20_mask = x86_get_a20_mask(env);
|
||||||
|
paddr = addr & a20_mask;
|
||||||
#ifdef TARGET_X86_64
|
#ifdef TARGET_X86_64
|
||||||
if (!(env->hflags & HF_LMA_MASK)) {
|
if (!(env->hflags & HF_LMA_MASK)) {
|
||||||
/* Without long mode we can only address 32bits in real mode */
|
/* Without long mode we can only address 32bits in real mode */
|
||||||
|
|
|
@ -18,7 +18,7 @@ ifneq ($(filter $(all-check-targets), check-softfloat),)
|
||||||
endif
|
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
|
||||||
@echo " $(MAKE) check-report.tap Generates an aggregated TAP test report"
|
@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-venv Creates a Python venv for tests"
|
||||||
@echo " $(MAKE) check-clean Clean the tests and related data"
|
@echo " $(MAKE) check-clean Clean the tests and related data"
|
||||||
@echo
|
@echo
|
||||||
|
@ -89,6 +89,7 @@ TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets)))
|
||||||
TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv
|
TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv
|
||||||
TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt
|
TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt
|
||||||
TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results
|
TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results
|
||||||
|
TESTS_PYTHON=$(TESTS_VENV_DIR)/bin/python3
|
||||||
ifndef AVOCADO_TESTS
|
ifndef AVOCADO_TESTS
|
||||||
AVOCADO_TESTS=tests/avocado
|
AVOCADO_TESTS=tests/avocado
|
||||||
endif
|
endif
|
||||||
|
@ -103,13 +104,14 @@ else
|
||||||
AVOCADO_CMDLINE_TAGS=$(addprefix -t , $(AVOCADO_TAGS))
|
AVOCADO_CMDLINE_TAGS=$(addprefix -t , $(AVOCADO_TAGS))
|
||||||
endif
|
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)
|
$(TESTS_VENV_DIR): $(TESTS_VENV_REQ)
|
||||||
$(call quiet-command, \
|
$(call quiet-command, $(PYTHON) -m venv $@, VENV, $@)
|
||||||
$(PYTHON) -m venv $@, \
|
$(call quiet-venv-pip,install -e "$(SRC_PATH)/python/")
|
||||||
VENV, $@)
|
$(call quiet-venv-pip,install -r $(TESTS_VENV_REQ))
|
||||||
$(call quiet-command, \
|
|
||||||
$(TESTS_VENV_DIR)/bin/python -m pip -q install -r $(TESTS_VENV_REQ), \
|
|
||||||
PIP, $(TESTS_VENV_REQ))
|
|
||||||
$(call quiet-command, touch $@)
|
$(call quiet-command, touch $@)
|
||||||
|
|
||||||
$(TESTS_RESULTS_DIR):
|
$(TESTS_RESULTS_DIR):
|
||||||
|
@ -126,7 +128,7 @@ FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES))
|
||||||
# download one specific Fedora 31 image
|
# download one specific Fedora 31 image
|
||||||
get-vm-image-fedora-31-%: check-venv
|
get-vm-image-fedora-31-%: check-venv
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
$(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \
|
$(TESTS_PYTHON) -m avocado vmimage get \
|
||||||
--distro=fedora --distro-version=31 --arch=$*, \
|
--distro=fedora --distro-version=31 --arch=$*, \
|
||||||
"AVOCADO", "Downloading avocado tests VM image for $*")
|
"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
|
check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
$(TESTS_VENV_DIR)/bin/python -m avocado \
|
$(TESTS_PYTHON) -m avocado \
|
||||||
--show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \
|
--show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \
|
||||||
$(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \
|
$(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \
|
||||||
--filter-by-tags-include-empty-key) \
|
--filter-by-tags-include-empty-key) \
|
||||||
|
|
|
@ -21,6 +21,11 @@ import avocado
|
||||||
from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage
|
from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage
|
||||||
from avocado.utils.path import find_command
|
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
|
#: 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
|
#: 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
|
#: 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:
|
else:
|
||||||
SOURCE_DIR = BUILD_DIR
|
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):
|
def has_cmd(name, args=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -13,6 +13,7 @@ import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from avocado import skipUnless
|
from avocado import skipUnless
|
||||||
|
from avocado_qemu import BUILD_DIR
|
||||||
from avocado.utils import cloudinit
|
from avocado.utils import cloudinit
|
||||||
from avocado.utils import network
|
from avocado.utils import network
|
||||||
from avocado.utils import vmimage
|
from avocado.utils import vmimage
|
||||||
|
@ -32,9 +33,16 @@ class ReplayLinux(LinuxTest):
|
||||||
bus = 'ide'
|
bus = 'ide'
|
||||||
|
|
||||||
def setUp(self):
|
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.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):
|
def vm_add_disk(self, vm, path, id, device):
|
||||||
bus_string = ''
|
bus_string = ''
|
||||||
|
@ -50,7 +58,9 @@ class ReplayLinux(LinuxTest):
|
||||||
vm = self.get_vm()
|
vm = self.get_vm()
|
||||||
vm.add_args('-smp', '1')
|
vm.add_args('-smp', '1')
|
||||||
vm.add_args('-m', '1024')
|
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:
|
if args:
|
||||||
vm.add_args(*args)
|
vm.add_args(*args)
|
||||||
self.vm_add_disk(vm, self.boot_path, 0, self.hdd)
|
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()))
|
stop_check=(lambda : not vm.is_running()))
|
||||||
console_drainer.start()
|
console_drainer.start()
|
||||||
if record:
|
if record:
|
||||||
cloudinit.wait_for_phone_home(('0.0.0.0', self.phone_home_port),
|
while not self.phone_server.instance_phoned_back:
|
||||||
self.name)
|
self.phone_server.handle_request()
|
||||||
vm.shutdown()
|
vm.shutdown()
|
||||||
logger.info('finished the recording with log size %s bytes'
|
logger.info('finished the recording with log size %s bytes'
|
||||||
% os.path.getsize(replay_path))
|
% os.path.getsize(replay_path))
|
||||||
|
@ -114,3 +124,68 @@ class ReplayLinuxX8664(ReplayLinux):
|
||||||
:avocado: tags=machine:q35
|
:avocado: tags=machine:q35
|
||||||
"""
|
"""
|
||||||
self.run_rr(shift=3)
|
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 re
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
|
||||||
from qemu.machine import QEMUMachine
|
from qemu.machine import QEMUMachine
|
||||||
from avocado_qemu import QemuSystemTest
|
from avocado_qemu import QemuSystemTest
|
||||||
from avocado import skip
|
from avocado import skip
|
||||||
|
|
|
@ -11,7 +11,6 @@ Check compatibility of virtio device types
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
|
|
||||||
from qemu.machine import QEMUMachine
|
from qemu.machine import QEMUMachine
|
||||||
from avocado_qemu import QemuSystemTest
|
from avocado_qemu import QemuSystemTest
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,5 @@ RUN apt update && \
|
||||||
python3 \
|
python3 \
|
||||||
python3-sphinx \
|
python3-sphinx \
|
||||||
python3-sphinx-rtd-theme \
|
python3-sphinx-rtd-theme \
|
||||||
|
python3-venv \
|
||||||
$(apt-get -s build-dep --arch-only qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2)
|
$(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
|
# Add Python module requirements, one per line, to be installed
|
||||||
# in the tests/venv Python virtual environment. For more info,
|
# in the tests/venv Python virtual environment. For more info,
|
||||||
# refer to: https://pip.pypa.io/en/stable/user_guide/#id1
|
# refer to: https://pip.pypa.io/en/stable/user_guide/#id1
|
||||||
|
# Note that qemu.git/python/ is always implicitly installed.
|
||||||
avocado-framework==88.1
|
avocado-framework==88.1
|
||||||
pycdlib==1.11.0
|
pycdlib==1.11.0
|
||||||
|
|
|
@ -84,10 +84,11 @@ vm-clean-all:
|
||||||
|
|
||||||
$(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
|
$(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
|
||||||
$(SRC_PATH)/tests/vm/basevm.py \
|
$(SRC_PATH)/tests/vm/basevm.py \
|
||||||
$(SRC_PATH)/tests/vm/Makefile.include
|
$(SRC_PATH)/tests/vm/Makefile.include \
|
||||||
|
check-venv
|
||||||
@mkdir -p $(IMAGES_DIR)
|
@mkdir -p $(IMAGES_DIR)
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
$(PYTHON) $< \
|
$(TESTS_PYTHON) $< \
|
||||||
$(if $(V)$(DEBUG), --debug) \
|
$(if $(V)$(DEBUG), --debug) \
|
||||||
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
|
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
|
||||||
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||||
|
@ -101,9 +102,9 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
|
||||||
|
|
||||||
|
|
||||||
# Build in VM $(IMAGE)
|
# Build in VM $(IMAGE)
|
||||||
vm-build-%: $(IMAGES_DIR)/%.img
|
vm-build-%: $(IMAGES_DIR)/%.img check-venv
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
|
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||||
$(if $(V)$(DEBUG), --debug) \
|
$(if $(V)$(DEBUG), --debug) \
|
||||||
$(if $(DEBUG), --interactive) \
|
$(if $(DEBUG), --interactive) \
|
||||||
$(if $(J),--jobs $(J)) \
|
$(if $(J),--jobs $(J)) \
|
||||||
|
@ -127,9 +128,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img
|
||||||
-device virtio-net-pci,netdev=vnet \
|
-device virtio-net-pci,netdev=vnet \
|
||||||
|| true
|
|| true
|
||||||
|
|
||||||
vm-boot-ssh-%: $(IMAGES_DIR)/%.img
|
vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv
|
||||||
$(call quiet-command, \
|
$(call quiet-command, \
|
||||||
$(PYTHON) $(SRC_PATH)/tests/vm/$* \
|
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \
|
||||||
$(if $(J),--jobs $(J)) \
|
$(if $(J),--jobs $(J)) \
|
||||||
$(if $(V)$(DEBUG), --debug) \
|
$(if $(V)$(DEBUG), --debug) \
|
||||||
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
|
||||||
|
|
|
@ -18,9 +18,6 @@ import socket
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import datetime
|
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 subprocess
|
||||||
import hashlib
|
import hashlib
|
||||||
import argparse
|
import argparse
|
||||||
|
@ -31,6 +28,9 @@ import multiprocessing
|
||||||
import traceback
|
import traceback
|
||||||
import shlex
|
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__),
|
SSH_KEY_FILE = os.path.join(os.path.dirname(__file__),
|
||||||
"..", "keys", "id_rsa")
|
"..", "keys", "id_rsa")
|
||||||
SSH_PUB_KEY_FILE = os.path.join(os.path.dirname(__file__),
|
SSH_PUB_KEY_FILE = os.path.join(os.path.dirname(__file__),
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "block/raw-aio.h"
|
#include "block/raw-aio.h"
|
||||||
#include "qemu/coroutine_int.h"
|
#include "qemu/coroutine_int.h"
|
||||||
#include "qemu/coroutine-tls.h"
|
#include "qemu/coroutine-tls.h"
|
||||||
|
#include "sysemu/cpu-timers.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
|
@ -84,6 +85,13 @@ static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
aio_notify(ctx);
|
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() */
|
/* Only called from aio_bh_poll() and aio_ctx_finalize() */
|
||||||
|
|
Loading…
Reference in New Issue