Testing and plugin updates
- clear up dtc warnings - add support for --enable-tsan builds - re-enable shippable cross builds - serialise cirrus check steps - fix check-tcg plugin issues - add lockstep plugin -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAl7ozhEACgkQ+9DbCVqe KkSunwf/T1Bsg9RIIIvWsRDWHndQjh1OJc1WyBdg8ZZslxxhxV7jSRGLw6JtUU40 yIBjtinNMKIBNGJTExghB6UXDBj9LjB7qjalDO+hpR6UONU11ITwOwHzoRc5R2Zl jeqelHImtDaNvRWqyWmdkT/VsHl80GFTTalJZrVyn+EcYK5RPQDzUitdE1V3GUVg 3we253m+9S/Ao73yn3WPZtIXNUcAR5+vK1BtI8necoNsFuEgI0KRxJAFKAJB3LxN pfaXAwfpMJ99AAlNCE/ObNCCQ8zy3dyt8GfYERn4pPPMs5HEhs+lS5vZ+KU4b/Gd 5rsSO/ELl1C9uiTGvzlLCfu6MvL8zw== =NXiy -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stsquad/tags/pull-testing-and-plugin-160620-2' into staging Testing and plugin updates - clear up dtc warnings - add support for --enable-tsan builds - re-enable shippable cross builds - serialise cirrus check steps - fix check-tcg plugin issues - add lockstep plugin # gpg: Signature made Tue 16 Jun 2020 14:50:09 BST # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [full] # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * remotes/stsquad/tags/pull-testing-and-plugin-160620-2: (21 commits) plugins: new lockstep plugin for debugging TCG changes tests/tcg: ensure -cpu max also used for plugin run tests/tcg: build plugin list from contents of src directory cirrus.yml: serialise make check Revert ".shippable: temporaily disable some cross builds" tests: Disable select tests under TSan, which hit TSan issue. docs: Added details on TSan to testing.rst util: Added tsan annotate for thread name. include/qemu: Added tsan.h for annotations. tests/docker: Added docker build support for TSan. thread: add tsan annotations to QemuSpin translate-all: call qemu_spin_destroy for PageDesc tcg: call qemu_spin_destroy for tb->jmp_lock qht: call qemu_spin_destroy for head buckets cputlb: destroy CPUTLB with tlb_destroy thread: add qemu_spin_destroy cpu: convert queued work to a QSIMPLEQ configure: add --enable-tsan flag + fiber annotations for coroutine-ucontext Makefile: remove old compatibility gunks Makefile: dtc: update, build the libfdt target ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5c24bce305
@ -14,7 +14,7 @@ freebsd_12_task:
|
||||
- cd build
|
||||
- ../configure || { cat config.log; exit 1; }
|
||||
- gmake -j8
|
||||
- gmake -j8 V=1 check
|
||||
- gmake V=1 check
|
||||
|
||||
macos_task:
|
||||
osx_instance:
|
||||
@ -26,7 +26,7 @@ macos_task:
|
||||
- cd build
|
||||
- ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check -j$(sysctl -n hw.ncpu)
|
||||
- gmake check
|
||||
|
||||
macos_xcode_task:
|
||||
osx_instance:
|
||||
@ -39,4 +39,4 @@ macos_xcode_task:
|
||||
- cd build
|
||||
- ../configure --cc=clang || { cat config.log; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check -j$(sysctl -n hw.ncpu)
|
||||
- gmake check
|
||||
|
@ -5,8 +5,8 @@ env:
|
||||
global:
|
||||
- LC_ALL=C
|
||||
matrix:
|
||||
# - IMAGE=debian-amd64
|
||||
# TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
||||
- IMAGE=debian-amd64
|
||||
TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
||||
- IMAGE=debian-win32-cross
|
||||
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
||||
- IMAGE=debian-win64-cross
|
||||
@ -19,10 +19,10 @@ env:
|
||||
TARGET_LIST=aarch64-softmmu,aarch64-linux-user
|
||||
- IMAGE=debian-s390x-cross
|
||||
TARGET_LIST=s390x-softmmu,s390x-linux-user
|
||||
# - IMAGE=debian-mips-cross
|
||||
# TARGET_LIST=mips-softmmu,mipsel-linux-user
|
||||
# - IMAGE=debian-mips64el-cross
|
||||
# TARGET_LIST=mips64el-softmmu,mips64el-linux-user
|
||||
- IMAGE=debian-mips-cross
|
||||
TARGET_LIST=mips-softmmu,mipsel-linux-user
|
||||
- IMAGE=debian-mips64el-cross
|
||||
TARGET_LIST=mips64el-softmmu,mips64el-linux-user
|
||||
- IMAGE=debian-ppc64el-cross
|
||||
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
|
||||
build:
|
||||
|
16
Makefile
16
Makefile
@ -526,13 +526,14 @@ $(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
|
||||
$(TARGET_DIRS_RULES):
|
||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
|
||||
|
||||
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_srcdir=$(SRC_PATH)/dtc/libfdt
|
||||
# LIBFDT_lib="": avoid breaking existing trees with objects requiring -fPIC
|
||||
DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_lib=""
|
||||
DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS)
|
||||
DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt
|
||||
DTC_CPPFLAGS=-I$(SRC_PATH)/dtc/libfdt
|
||||
|
||||
.PHONY: dtc/all
|
||||
dtc/all: .git-submodule-status dtc/libfdt dtc/tests
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
|
||||
dtc/all: .git-submodule-status dtc/libfdt
|
||||
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt,)
|
||||
|
||||
dtc/%: .git-submodule-status
|
||||
@mkdir -p $@
|
||||
@ -561,12 +562,6 @@ slirp/all: .git-submodule-status
|
||||
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
|
||||
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
|
||||
|
||||
# Compatibility gunk to keep make working across the rename of targets
|
||||
# for recursion, to be removed some time after 4.1.
|
||||
subdir-dtc: dtc/all
|
||||
subdir-capstone: capstone/all
|
||||
subdir-slirp: slirp/all
|
||||
|
||||
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||
$(qom-obj-y)
|
||||
|
||||
@ -820,7 +815,6 @@ distclean: clean
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
rm -Rf .sdk
|
||||
if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi
|
||||
|
||||
KEYMAPS=da en-gb et fr fr-ch is lt no pt-br sv \
|
||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
|
@ -270,6 +270,21 @@ void tlb_init(CPUState *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_destroy(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int i;
|
||||
|
||||
qemu_spin_destroy(&env_tlb(env)->c.lock);
|
||||
for (i = 0; i < NB_MMU_MODES; i++) {
|
||||
CPUTLBDesc *desc = &env_tlb(env)->d[i];
|
||||
CPUTLBDescFast *fast = &env_tlb(env)->f[i];
|
||||
|
||||
g_free(fast->table);
|
||||
g_free(desc->iotlb);
|
||||
}
|
||||
}
|
||||
|
||||
/* flush_all_helper: run fn across all cpus
|
||||
*
|
||||
* If the wait flag is set then the src cpu's helper will be queued as
|
||||
|
@ -384,6 +384,11 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tb_destroy(TranslationBlock *tb)
|
||||
{
|
||||
qemu_spin_destroy(&tb->jmp_lock);
|
||||
}
|
||||
|
||||
bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
@ -413,6 +418,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
/* one-shot translation, invalidate it immediately */
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tcg_tb_remove(tb);
|
||||
tb_destroy(tb);
|
||||
}
|
||||
r = true;
|
||||
}
|
||||
@ -541,6 +547,15 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
|
||||
#endif
|
||||
existing = atomic_cmpxchg(lp, NULL, pd);
|
||||
if (unlikely(existing)) {
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < V_L2_SIZE; i++) {
|
||||
qemu_spin_destroy(&pd[i].lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
g_free(pd);
|
||||
pd = existing;
|
||||
}
|
||||
@ -1886,6 +1901,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize);
|
||||
atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned);
|
||||
tb_destroy(tb);
|
||||
return existing_tb;
|
||||
}
|
||||
tcg_tb_insert(tb);
|
||||
@ -2235,6 +2251,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
tb_phys_invalidate(tb->orig_tb, -1);
|
||||
}
|
||||
tcg_tb_remove(tb);
|
||||
tb_destroy(tb);
|
||||
}
|
||||
|
||||
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
|
||||
|
48
configure
vendored
48
configure
vendored
@ -395,6 +395,7 @@ gprof="no"
|
||||
debug_tcg="no"
|
||||
debug="no"
|
||||
sanitizers="no"
|
||||
tsan="no"
|
||||
fortify_source=""
|
||||
strip_opt="yes"
|
||||
tcg_interpreter="no"
|
||||
@ -1152,6 +1153,10 @@ for opt do
|
||||
;;
|
||||
--disable-sanitizers) sanitizers="no"
|
||||
;;
|
||||
--enable-tsan) tsan="yes"
|
||||
;;
|
||||
--disable-tsan) tsan="no"
|
||||
;;
|
||||
--enable-sparse) sparse="yes"
|
||||
;;
|
||||
--disable-sparse) sparse="no"
|
||||
@ -1764,6 +1769,7 @@ Advanced options (experts only):
|
||||
--with-pkgversion=VERS use specified string as sub-version of the package
|
||||
--enable-debug enable common debug build options
|
||||
--enable-sanitizers enable default sanitizers
|
||||
--enable-tsan enable thread sanitizer
|
||||
--disable-strip disable stripping binaries
|
||||
--disable-werror disable compilation abort on warning
|
||||
--disable-stack-protector disable compiler-provided stack protection
|
||||
@ -4312,7 +4318,6 @@ EOF
|
||||
mkdir -p dtc
|
||||
if [ "$pwd_is_source_path" != "y" ] ; then
|
||||
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
|
||||
symlink "$source_path/dtc/scripts" "dtc/scripts"
|
||||
fi
|
||||
fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt"
|
||||
fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt"
|
||||
@ -6221,6 +6226,30 @@ if test "$fuzzing" = "yes" ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Thread sanitizer is, for now, much noisier than the other sanitizers;
|
||||
# keep it separate until that is not the case.
|
||||
if test "$tsan" = "yes" && test "$sanitizers" = "yes"; then
|
||||
error_exit "TSAN is not supported with other sanitiziers."
|
||||
fi
|
||||
have_tsan=no
|
||||
have_tsan_iface_fiber=no
|
||||
if test "$tsan" = "yes" ; then
|
||||
write_c_skeleton
|
||||
if compile_prog "$CPU_CFLAGS -Werror -fsanitize=thread" "" ; then
|
||||
have_tsan=yes
|
||||
fi
|
||||
cat > $TMPC << EOF
|
||||
#include <sanitizer/tsan_interface.h>
|
||||
int main(void) {
|
||||
__tsan_create_fiber(0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if compile_prog "$CPU_CFLAGS -Werror -fsanitize=thread" "" ; then
|
||||
have_tsan_iface_fiber=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# check for libpmem
|
||||
|
||||
@ -6378,6 +6407,16 @@ if test "$have_asan" = "yes"; then
|
||||
"Without code annotation, the report may be inferior."
|
||||
fi
|
||||
fi
|
||||
if test "$have_tsan" = "yes" ; then
|
||||
if test "$have_tsan_iface_fiber" = "yes" ; then
|
||||
QEMU_CFLAGS="-fsanitize=thread $QEMU_CFLAGS"
|
||||
QEMU_LDFLAGS="-fsanitize=thread $QEMU_LDFLAGS"
|
||||
else
|
||||
error_exit "Cannot enable TSAN due to missing fiber annotation interface."
|
||||
fi
|
||||
elif test "$tsan" = "yes" ; then
|
||||
error_exit "Cannot enable TSAN due to missing sanitize thread interface."
|
||||
fi
|
||||
if test "$have_ubsan" = "yes"; then
|
||||
QEMU_CFLAGS="-fsanitize=undefined $QEMU_CFLAGS"
|
||||
QEMU_LDFLAGS="-fsanitize=undefined $QEMU_LDFLAGS"
|
||||
@ -6413,7 +6452,8 @@ if test "$werror" = "yes"; then
|
||||
QEMU_CFLAGS="-Werror $QEMU_CFLAGS"
|
||||
fi
|
||||
|
||||
if test "$solaris" = "no" ; then
|
||||
# Exclude --warn-common with TSan to suppress warnings from the TSan libraries.
|
||||
if test "$solaris" = "no" && test "$tsan" = "no"; then
|
||||
if $ld --version 2>/dev/null | grep "GNU ld" >/dev/null 2>/dev/null ; then
|
||||
QEMU_LDFLAGS="-Wl,--warn-common $QEMU_LDFLAGS"
|
||||
fi
|
||||
@ -7477,6 +7517,10 @@ if test "$have_asan_iface_fiber" = "yes" ; then
|
||||
echo "CONFIG_ASAN_IFACE_FIBER=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$have_tsan" = "yes" && test "$have_tsan_iface_fiber" = "yes" ; then
|
||||
echo "CONFIG_TSAN=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$has_environ" = "yes" ; then
|
||||
echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -97,7 +97,7 @@ void cpu_list_remove(CPUState *cpu)
|
||||
}
|
||||
|
||||
struct qemu_work_item {
|
||||
struct qemu_work_item *next;
|
||||
QSIMPLEQ_ENTRY(qemu_work_item) node;
|
||||
run_on_cpu_func func;
|
||||
run_on_cpu_data data;
|
||||
bool free, exclusive, done;
|
||||
@ -106,13 +106,7 @@ struct qemu_work_item {
|
||||
static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
|
||||
{
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
cpu->queued_work_first = wi;
|
||||
} else {
|
||||
cpu->queued_work_last->next = wi;
|
||||
}
|
||||
cpu->queued_work_last = wi;
|
||||
wi->next = NULL;
|
||||
QSIMPLEQ_INSERT_TAIL(&cpu->work_list, wi, node);
|
||||
wi->done = false;
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
|
||||
@ -306,17 +300,14 @@ void process_queued_cpu_work(CPUState *cpu)
|
||||
{
|
||||
struct qemu_work_item *wi;
|
||||
|
||||
if (cpu->queued_work_first == NULL) {
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
if (QSIMPLEQ_EMPTY(&cpu->work_list)) {
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
while (cpu->queued_work_first != NULL) {
|
||||
wi = cpu->queued_work_first;
|
||||
cpu->queued_work_first = wi->next;
|
||||
if (!cpu->queued_work_first) {
|
||||
cpu->queued_work_last = NULL;
|
||||
}
|
||||
while (!QSIMPLEQ_EMPTY(&cpu->work_list)) {
|
||||
wi = QSIMPLEQ_FIRST(&cpu->work_list);
|
||||
QSIMPLEQ_REMOVE_HEAD(&cpu->work_list, node);
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
if (wi->exclusive) {
|
||||
/* Running work items outside the BQL avoids the following deadlock:
|
||||
|
14
cpus.c
14
cpus.c
@ -97,9 +97,19 @@ bool cpu_is_stopped(CPUState *cpu)
|
||||
return cpu->stopped || !runstate_is_running();
|
||||
}
|
||||
|
||||
static inline bool cpu_work_list_empty(CPUState *cpu)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
qemu_mutex_lock(&cpu->work_mutex);
|
||||
ret = QSIMPLEQ_EMPTY(&cpu->work_list);
|
||||
qemu_mutex_unlock(&cpu->work_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool cpu_thread_is_idle(CPUState *cpu)
|
||||
{
|
||||
if (cpu->stop || cpu->queued_work_first) {
|
||||
if (cpu->stop || !cpu_work_list_empty(cpu)) {
|
||||
return false;
|
||||
}
|
||||
if (cpu_is_stopped(cpu)) {
|
||||
@ -1518,7 +1528,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
cpu = first_cpu;
|
||||
}
|
||||
|
||||
while (cpu && !cpu->queued_work_first && !cpu->exit_request) {
|
||||
while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
|
||||
|
||||
atomic_mb_set(&tcg_current_rr_cpu, cpu);
|
||||
current_cpu = cpu;
|
||||
|
@ -397,6 +397,113 @@ list is in the ``make docker`` help text. The frequently used ones are:
|
||||
* ``DEBUG=1``: enables debug. See the previous "Debugging a Docker test
|
||||
failure" section.
|
||||
|
||||
Thread Sanitizer
|
||||
================
|
||||
|
||||
Thread Sanitizer (TSan) is a tool which can detect data races. QEMU supports
|
||||
building and testing with this tool.
|
||||
|
||||
For more information on TSan:
|
||||
|
||||
https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual
|
||||
|
||||
Thread Sanitizer in Docker
|
||||
---------------------------
|
||||
TSan is currently supported in the ubuntu2004 docker.
|
||||
|
||||
The test-tsan test will build using TSan and then run make check.
|
||||
|
||||
.. code::
|
||||
|
||||
make docker-test-tsan@ubuntu2004
|
||||
|
||||
TSan warnings under docker are placed in files located at build/tsan/.
|
||||
|
||||
We recommend using DEBUG=1 to allow launching the test from inside the docker,
|
||||
and to allow review of the warnings generated by TSan.
|
||||
|
||||
Building and Testing with TSan
|
||||
------------------------------
|
||||
|
||||
It is possible to build and test with TSan, with a few additional steps.
|
||||
These steps are normally done automatically in the docker.
|
||||
|
||||
There is a one time patch needed in clang-9 or clang-10 at this time:
|
||||
|
||||
.. code::
|
||||
|
||||
sed -i 's/^const/static const/g' \
|
||||
/usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h
|
||||
|
||||
To configure the build for TSan:
|
||||
|
||||
.. code::
|
||||
|
||||
../configure --enable-tsan --cc=clang-10 --cxx=clang++-10 \
|
||||
--disable-werror --extra-cflags="-O0"
|
||||
|
||||
The runtime behavior of TSAN is controlled by the TSAN_OPTIONS environment
|
||||
variable.
|
||||
|
||||
More information on the TSAN_OPTIONS can be found here:
|
||||
|
||||
https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags
|
||||
|
||||
For example:
|
||||
|
||||
.. code::
|
||||
|
||||
export TSAN_OPTIONS=suppressions=<path to qemu>/tests/tsan/suppressions.tsan \
|
||||
detect_deadlocks=false history_size=7 exitcode=0 \
|
||||
log_path=<build path>/tsan/tsan_warning
|
||||
|
||||
The above exitcode=0 has TSan continue without error if any warnings are found.
|
||||
This allows for running the test and then checking the warnings afterwards.
|
||||
If you want TSan to stop and exit with error on warnings, use exitcode=66.
|
||||
|
||||
TSan Suppressions
|
||||
-----------------
|
||||
Keep in mind that for any data race warning, although there might be a data race
|
||||
detected by TSan, there might be no actual bug here. TSan provides several
|
||||
different mechanisms for suppressing warnings. In general it is recommended
|
||||
to fix the code if possible to eliminate the data race rather than suppress
|
||||
the warning.
|
||||
|
||||
A few important files for suppressing warnings are:
|
||||
|
||||
tests/tsan/suppressions.tsan - Has TSan warnings we wish to suppress at runtime.
|
||||
The comment on each supression will typically indicate why we are
|
||||
suppressing it. More information on the file format can be found here:
|
||||
|
||||
https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions
|
||||
|
||||
tests/tsan/blacklist.tsan - Has TSan warnings we wish to disable
|
||||
at compile time for test or debug.
|
||||
Add flags to configure to enable:
|
||||
|
||||
"--extra-cflags=-fsanitize-blacklist=<src path>/tests/tsan/blacklist.tsan"
|
||||
|
||||
More information on the file format can be found here under "Blacklist Format":
|
||||
|
||||
https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags
|
||||
|
||||
TSan Annotations
|
||||
----------------
|
||||
include/qemu/tsan.h defines annotations. See this file for more descriptions
|
||||
of the annotations themselves. Annotations can be used to suppress
|
||||
TSan warnings or give TSan more information so that it can detect proper
|
||||
relationships between accesses of data.
|
||||
|
||||
Annotation examples can be found here:
|
||||
|
||||
https://github.com/llvm/llvm-project/tree/master/compiler-rt/test/tsan/
|
||||
|
||||
Good files to start with are: annotate_happens_before.cpp and ignore_race.cpp
|
||||
|
||||
The full set of annotations can be found here:
|
||||
|
||||
https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
|
||||
|
||||
VM testing
|
||||
==========
|
||||
|
||||
|
2
dtc
2
dtc
@ -1 +1 @@
|
||||
Subproject commit 88f18909db731a627456f26d779445f84e449536
|
||||
Subproject commit 85e5d839847af54efab170f2b1331b2a6421e647
|
1
exec.c
1
exec.c
@ -892,6 +892,7 @@ void cpu_exec_unrealizefn(CPUState *cpu)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
tlb_destroy(cpu);
|
||||
cpu_list_remove(cpu);
|
||||
|
||||
if (cc->vmsd != NULL) {
|
||||
|
@ -370,6 +370,7 @@ static void cpu_common_initfn(Object *obj)
|
||||
cpu->nr_threads = 1;
|
||||
|
||||
qemu_mutex_init(&cpu->work_mutex);
|
||||
QSIMPLEQ_INIT(&cpu->work_list);
|
||||
QTAILQ_INIT(&cpu->breakpoints);
|
||||
QTAILQ_INIT(&cpu->watchpoints);
|
||||
|
||||
|
@ -124,6 +124,11 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||
* @cpu: CPU whose TLB should be initialized
|
||||
*/
|
||||
void tlb_init(CPUState *cpu);
|
||||
/**
|
||||
* tlb_destroy - destroy a CPU's TLB
|
||||
* @cpu: CPU whose TLB should be destroyed
|
||||
*/
|
||||
void tlb_destroy(CPUState *cpu);
|
||||
/**
|
||||
* tlb_flush_page:
|
||||
* @cpu: CPU whose TLB should be flushed
|
||||
@ -284,6 +289,9 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
static inline void tlb_init(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
static inline void tlb_destroy(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
}
|
||||
|
@ -331,8 +331,8 @@ struct qemu_work_item;
|
||||
* @opaque: User data.
|
||||
* @mem_io_pc: Host Program Counter at which the memory was accessed.
|
||||
* @kvm_fd: vCPU file descriptor for KVM.
|
||||
* @work_mutex: Lock to prevent multiple access to queued_work_*.
|
||||
* @queued_work_first: First asynchronous work pending.
|
||||
* @work_mutex: Lock to prevent multiple access to @work_list.
|
||||
* @work_list: List of pending asynchronous work.
|
||||
* @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
|
||||
* to @trace_dstate).
|
||||
* @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
|
||||
@ -376,7 +376,7 @@ struct CPUState {
|
||||
sigjmp_buf jmp_env;
|
||||
|
||||
QemuMutex work_mutex;
|
||||
struct qemu_work_item *queued_work_first, *queued_work_last;
|
||||
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
|
||||
|
||||
CPUAddressSpace *cpu_ases;
|
||||
int num_ases;
|
||||
|
@ -206,6 +206,10 @@ void qemu_thread_atexit_add(struct Notifier *notifier);
|
||||
*/
|
||||
void qemu_thread_atexit_remove(struct Notifier *notifier);
|
||||
|
||||
#ifdef CONFIG_TSAN
|
||||
#include <sanitizer/tsan_interface.h>
|
||||
#endif
|
||||
|
||||
struct QemuSpin {
|
||||
int value;
|
||||
};
|
||||
@ -213,20 +217,46 @@ struct QemuSpin {
|
||||
static inline void qemu_spin_init(QemuSpin *spin)
|
||||
{
|
||||
__sync_lock_release(&spin->value);
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_create(spin, __tsan_mutex_not_static);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* const parameter because the only purpose here is the TSAN annotation */
|
||||
static inline void qemu_spin_destroy(const QemuSpin *spin)
|
||||
{
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_destroy((void *)spin, __tsan_mutex_not_static);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void qemu_spin_lock(QemuSpin *spin)
|
||||
{
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_pre_lock(spin, 0);
|
||||
#endif
|
||||
while (unlikely(__sync_lock_test_and_set(&spin->value, true))) {
|
||||
while (atomic_read(&spin->value)) {
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_post_lock(spin, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool qemu_spin_trylock(QemuSpin *spin)
|
||||
{
|
||||
return __sync_lock_test_and_set(&spin->value, true);
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_pre_lock(spin, __tsan_mutex_try_lock);
|
||||
#endif
|
||||
bool busy = __sync_lock_test_and_set(&spin->value, true);
|
||||
#ifdef CONFIG_TSAN
|
||||
unsigned flags = __tsan_mutex_try_lock;
|
||||
flags |= busy ? __tsan_mutex_try_lock_failed : 0;
|
||||
__tsan_mutex_post_lock(spin, flags, 0);
|
||||
#endif
|
||||
return busy;
|
||||
}
|
||||
|
||||
static inline bool qemu_spin_locked(QemuSpin *spin)
|
||||
@ -236,7 +266,13 @@ static inline bool qemu_spin_locked(QemuSpin *spin)
|
||||
|
||||
static inline void qemu_spin_unlock(QemuSpin *spin)
|
||||
{
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_pre_unlock(spin, 0);
|
||||
#endif
|
||||
__sync_lock_release(&spin->value);
|
||||
#ifdef CONFIG_TSAN
|
||||
__tsan_mutex_post_unlock(spin, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct QemuLockCnt {
|
||||
|
71
include/qemu/tsan.h
Normal file
71
include/qemu/tsan.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef QEMU_TSAN_H
|
||||
#define QEMU_TSAN_H
|
||||
/*
|
||||
* tsan.h
|
||||
*
|
||||
* This file defines macros used to give ThreadSanitizer
|
||||
* additional information to help suppress warnings.
|
||||
* This is necessary since TSan does not provide a header file
|
||||
* for these annotations. The standard way to include these
|
||||
* is via the below macros.
|
||||
*
|
||||
* Annotation examples can be found here:
|
||||
* https://github.com/llvm/llvm-project/tree/master/compiler-rt/test/tsan
|
||||
* annotate_happens_before.cpp or ignore_race.cpp are good places to start.
|
||||
*
|
||||
* The full set of annotations can be found here in tsan_interface_ann.cpp.
|
||||
* https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_TSAN
|
||||
/*
|
||||
* Informs TSan of a happens before/after relationship.
|
||||
*/
|
||||
#define QEMU_TSAN_ANNOTATE_HAPPENS_BEFORE(addr) \
|
||||
AnnotateHappensBefore(__FILE__, __LINE__, (void *)(addr))
|
||||
#define QEMU_TSAN_ANNOTATE_HAPPENS_AFTER(addr) \
|
||||
AnnotateHappensAfter(__FILE__, __LINE__, (void *)(addr))
|
||||
/*
|
||||
* Gives TSan more information about thread names it can report the
|
||||
* name of the thread in the warning report.
|
||||
*/
|
||||
#define QEMU_TSAN_ANNOTATE_THREAD_NAME(name) \
|
||||
AnnotateThreadName(__FILE__, __LINE__, (void *)(name))
|
||||
/*
|
||||
* Allows defining a region of code on which TSan will not record memory READS.
|
||||
* This has the effect of disabling race detection for this section of code.
|
||||
*/
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_READS_BEGIN() \
|
||||
AnnotateIgnoreReadsBegin(__FILE__, __LINE__)
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_READS_END() \
|
||||
AnnotateIgnoreReadsEnd(__FILE__, __LINE__)
|
||||
/*
|
||||
* Allows defining a region of code on which TSan will not record memory
|
||||
* WRITES. This has the effect of disabling race detection for this
|
||||
* section of code.
|
||||
*/
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN() \
|
||||
AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_WRITES_END() \
|
||||
AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
|
||||
#else
|
||||
#define QEMU_TSAN_ANNOTATE_HAPPENS_BEFORE(addr)
|
||||
#define QEMU_TSAN_ANNOTATE_HAPPENS_AFTER(addr)
|
||||
#define QEMU_TSAN_ANNOTATE_THREAD_NAME(name)
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_READS_BEGIN()
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_READS_END()
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN()
|
||||
#define QEMU_TSAN_ANNOTATE_IGNORE_WRITES_END()
|
||||
#endif
|
||||
|
||||
void AnnotateHappensBefore(const char *f, int l, void *addr);
|
||||
void AnnotateHappensAfter(const char *f, int l, void *addr);
|
||||
void AnnotateThreadName(const char *f, int l, char *name);
|
||||
void AnnotateIgnoreReadsBegin(const char *f, int l);
|
||||
void AnnotateIgnoreReadsEnd(const char *f, int l);
|
||||
void AnnotateIgnoreWritesBegin(const char *f, int l);
|
||||
void AnnotateIgnoreWritesEnd(const char *f, int l);
|
||||
#endif
|
@ -819,6 +819,7 @@ void tcg_pool_reset(TCGContext *s);
|
||||
TranslationBlock *tcg_tb_alloc(TCGContext *s);
|
||||
|
||||
void tcg_region_init(void);
|
||||
void tb_destroy(TranslationBlock *tb);
|
||||
void tcg_region_reset_all(void);
|
||||
|
||||
size_t tcg_code_size(void);
|
||||
|
@ -502,6 +502,14 @@ size_t tcg_nb_tbs(void)
|
||||
return nb_tbs;
|
||||
}
|
||||
|
||||
static gboolean tcg_region_tree_traverse(gpointer k, gpointer v, gpointer data)
|
||||
{
|
||||
TranslationBlock *tb = v;
|
||||
|
||||
tb_destroy(tb);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void tcg_region_tree_reset_all(void)
|
||||
{
|
||||
size_t i;
|
||||
@ -510,6 +518,7 @@ static void tcg_region_tree_reset_all(void)
|
||||
for (i = 0; i < region.n; i++) {
|
||||
struct tcg_region_tree *rt = region_trees + i * tree_size;
|
||||
|
||||
g_tree_foreach(rt->tree, tcg_region_tree_traverse, NULL);
|
||||
/* Increment the refcount first so that destroy acts as a reset */
|
||||
g_tree_ref(rt->tree);
|
||||
g_tree_destroy(rt->tree);
|
||||
|
@ -55,7 +55,6 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
|
||||
|
||||
check-unit-y += tests/check-qdict$(EXESUF)
|
||||
check-unit-y += tests/check-block-qdict$(EXESUF)
|
||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-char$(EXESUF)
|
||||
check-unit-y += tests/check-qnum$(EXESUF)
|
||||
check-unit-y += tests/check-qstring$(EXESUF)
|
||||
check-unit-y += tests/check-qlist$(EXESUF)
|
||||
@ -108,7 +107,6 @@ check-unit-y += tests/test-qht$(EXESUF)
|
||||
check-unit-y += tests/test-qht-par$(EXESUF)
|
||||
check-unit-y += tests/test-bitops$(EXESUF)
|
||||
check-unit-y += tests/test-bitcnt$(EXESUF)
|
||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-qdev-global-props$(EXESUF)
|
||||
check-unit-y += tests/check-qom-interface$(EXESUF)
|
||||
check-unit-y += tests/check-qom-proplist$(EXESUF)
|
||||
check-unit-y += tests/test-qemu-opts$(EXESUF)
|
||||
@ -123,9 +121,16 @@ check-speed-$(CONFIG_BLOCK) += tests/benchmark-crypto-cipher$(EXESUF)
|
||||
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-secret$(EXESUF)
|
||||
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_GNUTLS)) += tests/test-crypto-tlscredsx509$(EXESUF)
|
||||
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_GNUTLS)) += tests/test-crypto-tlssession$(EXESUF)
|
||||
ifndef CONFIG_TSAN
|
||||
# Some tests: test-char, test-qdev-global-props, and test-qga,
|
||||
# are not runnable under TSan due to a known issue.
|
||||
# https://github.com/google/sanitizers/issues/1116
|
||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-char$(EXESUF)
|
||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-qdev-global-props$(EXESUF)
|
||||
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||
check-unit-$(call land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
|
||||
endif
|
||||
endif
|
||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-timed-average$(EXESUF)
|
||||
check-unit-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_INOTIFY1)) += tests/test-util-filemonitor$(EXESUF)
|
||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-util-sockets$(EXESUF)
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM fedora:30
|
||||
FROM fedora:32
|
||||
|
||||
# Please keep this list sorted alphabetically
|
||||
ENV PACKAGES \
|
||||
|
65
tests/docker/dockerfiles/ubuntu2004.docker
Normal file
65
tests/docker/dockerfiles/ubuntu2004.docker
Normal file
@ -0,0 +1,65 @@
|
||||
FROM ubuntu:20.04
|
||||
ENV PACKAGES flex bison \
|
||||
ccache \
|
||||
clang-10\
|
||||
gcc \
|
||||
gettext \
|
||||
git \
|
||||
glusterfs-common \
|
||||
libaio-dev \
|
||||
libattr1-dev \
|
||||
libbrlapi-dev \
|
||||
libbz2-dev \
|
||||
libcacard-dev \
|
||||
libcap-ng-dev \
|
||||
libcurl4-gnutls-dev \
|
||||
libdrm-dev \
|
||||
libepoxy-dev \
|
||||
libfdt-dev \
|
||||
libgbm-dev \
|
||||
libgtk-3-dev \
|
||||
libibverbs-dev \
|
||||
libiscsi-dev \
|
||||
libjemalloc-dev \
|
||||
libjpeg-turbo8-dev \
|
||||
liblzo2-dev \
|
||||
libncurses5-dev \
|
||||
libncursesw5-dev \
|
||||
libnfs-dev \
|
||||
libnss3-dev \
|
||||
libnuma-dev \
|
||||
libpixman-1-dev \
|
||||
librados-dev \
|
||||
librbd-dev \
|
||||
librdmacm-dev \
|
||||
libsasl2-dev \
|
||||
libsdl2-dev \
|
||||
libseccomp-dev \
|
||||
libsnappy-dev \
|
||||
libspice-protocol-dev \
|
||||
libspice-server-dev \
|
||||
libssh-dev \
|
||||
libusb-1.0-0-dev \
|
||||
libusbredirhost-dev \
|
||||
libvdeplug-dev \
|
||||
libvte-2.91-dev \
|
||||
libxen-dev \
|
||||
libzstd-dev \
|
||||
make \
|
||||
python3-yaml \
|
||||
python3-sphinx \
|
||||
sparse \
|
||||
texinfo \
|
||||
xfslibs-dev\
|
||||
vim
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES
|
||||
RUN dpkg -l $PACKAGES | sort > /packages.txt
|
||||
ENV FEATURES clang tsan pyyaml sdl2
|
||||
|
||||
# https://bugs.launchpad.net/qemu/+bug/1838763
|
||||
ENV QEMU_CONFIGURE_OPTS --disable-libssh
|
||||
|
||||
# Apply patch https://reviews.llvm.org/D75820
|
||||
# This is required for TSan in clang-10 to compile with QEMU.
|
||||
RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h
|
44
tests/docker/test-tsan
Executable file
44
tests/docker/test-tsan
Executable file
@ -0,0 +1,44 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# This test will use TSan as part of a build and a make check.
|
||||
#
|
||||
# Copyright (c) 2020 Linaro
|
||||
# Copyright (c) 2016 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Robert Foley <robert.foley@linaro.org>
|
||||
# Originally based on test-quick from Fam Zheng <famz@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2
|
||||
# or (at your option) any later version. See the COPYING file in
|
||||
# the top-level directory.
|
||||
|
||||
. common.rc
|
||||
|
||||
setup_tsan()
|
||||
{
|
||||
requires clang tsan
|
||||
tsan_log_dir="/tmp/qemu-test/build/tsan"
|
||||
mkdir -p $tsan_log_dir > /dev/null || true
|
||||
EXTRA_CONFIGURE_OPTS="${EXTRA_CONFIGURE_OPTS} --enable-tsan \
|
||||
--cc=clang-10 --cxx=clang++-10 \
|
||||
--disable-werror --extra-cflags=-O0"
|
||||
# detect deadlocks is false currently simply because
|
||||
# TSan crashes immediately with deadlock detector enabled.
|
||||
# We have maxed out the history size to get the best chance of finding
|
||||
# warnings during testing.
|
||||
# Note, to get TSan to fail on warning, use exitcode=66 below.
|
||||
tsan_opts="suppressions=/tmp/qemu-test/src/tests/tsan/suppressions.tsan\
|
||||
detect_deadlocks=false history_size=7\
|
||||
halt_on_error=0 exitcode=0 verbose=5\
|
||||
log_path=$tsan_log_dir/tsan_warning"
|
||||
export TSAN_OPTIONS="$tsan_opts"
|
||||
}
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \
|
||||
setup_tsan
|
||||
build_qemu
|
||||
check_qemu
|
||||
install_qemu
|
@ -13,6 +13,7 @@ NAMES += mem
|
||||
NAMES += hotblocks
|
||||
NAMES += howvec
|
||||
NAMES += hotpages
|
||||
NAMES += lockstep
|
||||
|
||||
SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
|
||||
|
||||
|
340
tests/plugin/lockstep.c
Normal file
340
tests/plugin/lockstep.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Lockstep Execution Plugin
|
||||
*
|
||||
* Allows you to execute two QEMU instances in lockstep and report
|
||||
* when their execution diverges. This is mainly useful for developers
|
||||
* who want to see where a change to TCG code generation has
|
||||
* introduced a subtle and hard to find bug.
|
||||
*
|
||||
* Caveats:
|
||||
* - single-threaded linux-user apps only with non-deterministic syscalls
|
||||
* - no MTTCG enabled system emulation (icount may help)
|
||||
*
|
||||
* While icount makes things more deterministic it doesn't mean a
|
||||
* particular run may execute the exact same sequence of blocks. An
|
||||
* asynchronous event (for example X11 graphics update) may cause a
|
||||
* block to end early and a new partial block to start. This means
|
||||
* serial only test cases are a better bet. -d nochain may also help.
|
||||
*
|
||||
* This code is not thread safe!
|
||||
*
|
||||
* Copyright (c) 2020 Linaro Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <qemu-plugin.h>
|
||||
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
|
||||
|
||||
/* saved so we can uninstall later */
|
||||
static qemu_plugin_id_t our_id;
|
||||
|
||||
static unsigned long bb_count;
|
||||
static unsigned long insn_count;
|
||||
|
||||
/* Information about a translated block */
|
||||
typedef struct {
|
||||
uint64_t pc;
|
||||
uint64_t insns;
|
||||
} BlockInfo;
|
||||
|
||||
/* Information about an execution state in the log */
|
||||
typedef struct {
|
||||
BlockInfo *block;
|
||||
unsigned long insn_count;
|
||||
unsigned long block_count;
|
||||
} ExecInfo;
|
||||
|
||||
/* The execution state we compare */
|
||||
typedef struct {
|
||||
uint64_t pc;
|
||||
unsigned long insn_count;
|
||||
} ExecState;
|
||||
|
||||
typedef struct {
|
||||
GSList *log_pos;
|
||||
int distance;
|
||||
} DivergeState;
|
||||
|
||||
/* list of translated block info */
|
||||
static GSList *blocks;
|
||||
|
||||
/* execution log and points of divergence */
|
||||
static GSList *log, *divergence_log;
|
||||
|
||||
static int socket_fd;
|
||||
static char *path_to_unlink;
|
||||
|
||||
static bool verbose;
|
||||
|
||||
static void plugin_cleanup(qemu_plugin_id_t id)
|
||||
{
|
||||
/* Free our block data */
|
||||
g_slist_free_full(blocks, &g_free);
|
||||
g_slist_free_full(log, &g_free);
|
||||
g_slist_free(divergence_log);
|
||||
|
||||
close(socket_fd);
|
||||
if (path_to_unlink) {
|
||||
unlink(path_to_unlink);
|
||||
}
|
||||
}
|
||||
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
{
|
||||
g_autoptr(GString) out = g_string_new("No divergence :-)\n");
|
||||
g_string_append_printf(out, "Executed %ld/%d blocks\n",
|
||||
bb_count, g_slist_length(log));
|
||||
g_string_append_printf(out, "Executed ~%ld instructions\n", insn_count);
|
||||
qemu_plugin_outs(out->str);
|
||||
|
||||
plugin_cleanup(id);
|
||||
}
|
||||
|
||||
static void report_divergance(ExecState *us, ExecState *them)
|
||||
{
|
||||
DivergeState divrec = { log, 0 };
|
||||
g_autoptr(GString) out = g_string_new("");
|
||||
bool diverged = false;
|
||||
|
||||
/*
|
||||
* If we have diverged before did we get back on track or are we
|
||||
* totally loosing it?
|
||||
*/
|
||||
if (divergence_log) {
|
||||
DivergeState *last = (DivergeState *) divergence_log->data;
|
||||
GSList *entry;
|
||||
|
||||
for (entry = log; g_slist_next(entry); entry = g_slist_next(entry)) {
|
||||
if (entry == last->log_pos) {
|
||||
break;
|
||||
}
|
||||
divrec.distance++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the last two records are so close it is likely we will
|
||||
* not recover synchronisation with the other end.
|
||||
*/
|
||||
if (divrec.distance == 1 && last->distance == 1) {
|
||||
diverged = true;
|
||||
}
|
||||
}
|
||||
divergence_log = g_slist_prepend(divergence_log,
|
||||
g_memdup(&divrec, sizeof(divrec)));
|
||||
|
||||
/* Output short log entry of going out of sync... */
|
||||
if (verbose || divrec.distance == 1 || diverged) {
|
||||
g_string_printf(out, "@ %#016lx vs %#016lx (%d/%d since last)\n",
|
||||
us->pc, them->pc, g_slist_length(divergence_log),
|
||||
divrec.distance);
|
||||
qemu_plugin_outs(out->str);
|
||||
}
|
||||
|
||||
if (diverged) {
|
||||
int i;
|
||||
GSList *entry;
|
||||
|
||||
g_string_printf(out, "Δ insn_count @ %#016lx (%ld) vs %#016lx (%ld)\n",
|
||||
us->pc, us->insn_count, them->pc, them->insn_count);
|
||||
|
||||
for (entry = log, i = 0;
|
||||
g_slist_next(entry) && i < 5;
|
||||
entry = g_slist_next(entry), i++) {
|
||||
ExecInfo *prev = (ExecInfo *) entry->data;
|
||||
g_string_append_printf(out,
|
||||
" previously @ %#016lx/%ld (%ld insns)\n",
|
||||
prev->block->pc, prev->block->insns,
|
||||
prev->insn_count);
|
||||
}
|
||||
qemu_plugin_outs(out->str);
|
||||
qemu_plugin_outs("too much divergence... giving up.");
|
||||
qemu_plugin_uninstall(our_id, plugin_cleanup);
|
||||
}
|
||||
}
|
||||
|
||||
static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
BlockInfo *bi = (BlockInfo *) udata;
|
||||
ExecState us, them;
|
||||
ssize_t bytes;
|
||||
ExecInfo *exec;
|
||||
|
||||
us.pc = bi->pc;
|
||||
us.insn_count = insn_count;
|
||||
|
||||
/*
|
||||
* Write our current position to the other end. If we fail the
|
||||
* other end has probably died and we should shut down gracefully.
|
||||
*/
|
||||
bytes = write(socket_fd, &us, sizeof(ExecState));
|
||||
if (bytes < sizeof(ExecState)) {
|
||||
qemu_plugin_outs(bytes < 0 ?
|
||||
"problem writing to socket" :
|
||||
"wrote less than expected to socket");
|
||||
qemu_plugin_uninstall(our_id, plugin_cleanup);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now read where our peer has reached. Again a failure probably
|
||||
* indicates the other end died and we should close down cleanly.
|
||||
*/
|
||||
bytes = read(socket_fd, &them, sizeof(ExecState));
|
||||
if (bytes < sizeof(ExecState)) {
|
||||
qemu_plugin_outs(bytes < 0 ?
|
||||
"problem reading from socket" :
|
||||
"read less than expected");
|
||||
qemu_plugin_uninstall(our_id, plugin_cleanup);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare and report if we have diverged.
|
||||
*/
|
||||
if (us.pc != them.pc) {
|
||||
report_divergance(&us, &them);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assume this block will execute fully and record it
|
||||
* in the execution log.
|
||||
*/
|
||||
insn_count += bi->insns;
|
||||
bb_count++;
|
||||
exec = g_new0(ExecInfo, 1);
|
||||
exec->block = bi;
|
||||
exec->insn_count = insn_count;
|
||||
exec->block_count = bb_count;
|
||||
log = g_slist_prepend(log, exec);
|
||||
}
|
||||
|
||||
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
{
|
||||
BlockInfo *bi = g_new0(BlockInfo, 1);
|
||||
bi->pc = qemu_plugin_tb_vaddr(tb);
|
||||
bi->insns = qemu_plugin_tb_n_insns(tb);
|
||||
|
||||
/* save a reference so we can free later */
|
||||
blocks = g_slist_prepend(blocks, bi);
|
||||
qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
|
||||
QEMU_PLUGIN_CB_NO_REGS, (void *)bi);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Instead of encoding master/slave status into what is essentially
|
||||
* two peers we shall just take the simple approach of checking for
|
||||
* the existence of the pipe and assuming if it's not there we are the
|
||||
* first process.
|
||||
*/
|
||||
static bool setup_socket(const char *path)
|
||||
{
|
||||
struct sockaddr_un sockaddr;
|
||||
int fd;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
perror("create socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr.sun_family = AF_UNIX;
|
||||
g_strlcpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
|
||||
if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
|
||||
perror("bind socket");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* remember to clean-up */
|
||||
path_to_unlink = g_strdup(path);
|
||||
|
||||
if (listen(fd, 1) < 0) {
|
||||
perror("listen socket");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
socket_fd = accept(fd, NULL, NULL);
|
||||
if (socket_fd < 0 && errno != EINTR) {
|
||||
perror("accept socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
qemu_plugin_outs("setup_socket::ready\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool connect_socket(const char *path)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr_un sockaddr;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
perror("create socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr.sun_family = AF_UNIX;
|
||||
g_strlcpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
|
||||
perror("failed to connect");
|
||||
return false;
|
||||
}
|
||||
|
||||
qemu_plugin_outs("connect_socket::ready\n");
|
||||
|
||||
socket_fd = fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_unix_socket(const char *path)
|
||||
{
|
||||
if (g_file_test(path, G_FILE_TEST_EXISTS)) {
|
||||
return connect_socket(path);
|
||||
} else {
|
||||
return setup_socket(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
const qemu_info_t *info,
|
||||
int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!argc || !argv[0]) {
|
||||
qemu_plugin_outs("Need a socket path to talk to other instance.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *p = argv[i];
|
||||
if (strcmp(p, "verbose") == 0) {
|
||||
verbose = true;
|
||||
} else if (!setup_unix_socket(argv[0])) {
|
||||
qemu_plugin_outs("Failed to setup socket for communications.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
our_id = id;
|
||||
|
||||
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
|
||||
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
|
||||
return 0;
|
||||
}
|
@ -314,12 +314,15 @@ tests/qtest/tpm-tis-device-test$(EXESUF): tests/qtest/tpm-tis-device-test.o test
|
||||
# QTest rules
|
||||
|
||||
TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
|
||||
QTEST_TARGETS =
|
||||
# The qtests are not runnable (yet) under TSan due to a known issue.
|
||||
# https://github.com/google/sanitizers/issues/1116
|
||||
ifndef CONFIG_TSAN
|
||||
ifeq ($(CONFIG_POSIX),y)
|
||||
QTEST_TARGETS = $(TARGETS)
|
||||
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y:%=tests/qtest/%$(EXESUF)))
|
||||
check-qtest-y += $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF))
|
||||
else
|
||||
QTEST_TARGETS =
|
||||
endif
|
||||
endif
|
||||
|
||||
qtest-obj-y = tests/qtest/libqtest.o $(test-util-obj-y)
|
||||
|
@ -126,9 +126,11 @@ RUN_TESTS=$(patsubst %,run-%, $(TESTS))
|
||||
|
||||
# If plugins exist also include those in the tests
|
||||
ifeq ($(CONFIG_PLUGIN),y)
|
||||
PLUGIN_DIR=../../plugin
|
||||
VPATH+=$(PLUGIN_DIR)
|
||||
PLUGINS=$(notdir $(wildcard $(PLUGIN_DIR)/*.so))
|
||||
PLUGIN_SRC=$(SRC_PATH)/tests/plugin
|
||||
PLUGIN_LIB=../../plugin
|
||||
VPATH+=$(PLUGIN_LIB)
|
||||
PLUGINS=$(filter-out liblockstep.so,\
|
||||
$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))))
|
||||
|
||||
# We need to ensure expand the run-plugin-TEST-with-PLUGIN
|
||||
# pre-requistes manually here as we can't use stems to handle it. We
|
||||
@ -152,7 +154,7 @@ run-%: %
|
||||
|
||||
run-plugin-%:
|
||||
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
||||
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||
-d plugin -D $*.pout \
|
||||
$(call strip-plugin,$<), \
|
||||
"$* on $(TARGET_NAME)")
|
||||
@ -168,7 +170,7 @@ run-plugin-%:
|
||||
$(call run-test, $@, \
|
||||
$(QEMU) -monitor none -display none \
|
||||
-chardev file$(COMMA)path=$@.out$(COMMA)id=output \
|
||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
||||
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||
-d plugin -D $*.pout \
|
||||
$(QEMU_OPTS) $(call strip-plugin,$<), \
|
||||
"$* on $(TARGET_NAME)")
|
||||
|
@ -20,8 +20,9 @@ run-fcvt: fcvt
|
||||
# Pauth Tests
|
||||
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),)
|
||||
AARCH64_TESTS += pauth-1 pauth-2 pauth-4
|
||||
run-pauth-%: QEMU_OPTS += -cpu max
|
||||
pauth-%: CFLAGS += -march=armv8.3-a
|
||||
run-pauth-%: QEMU_OPTS += -cpu max
|
||||
run-plugin-pauth-%: QEMU_OPTS += -cpu max
|
||||
endif
|
||||
|
||||
# Semihosting smoke test for linux-user
|
||||
@ -31,7 +32,7 @@ run-semihosting: semihosting
|
||||
|
||||
run-plugin-semihosting-with-%:
|
||||
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
||||
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||
$(call strip-plugin,$<) 2> $<.err, \
|
||||
"$< on $(TARGET_NAME) with $*")
|
||||
|
||||
|
@ -45,7 +45,7 @@ run-semihosting-arm: semihosting-arm
|
||||
|
||||
run-plugin-semihosting-with-%:
|
||||
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
||||
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||
$(call strip-plugin,$<) 2> $<.err, \
|
||||
"$< on $(TARGET_NAME) with $*")
|
||||
|
||||
|
@ -12,6 +12,7 @@ X86_64_TESTS:=$(filter test-i386-ssse3, $(ALL_X86_TESTS))
|
||||
|
||||
test-i386-pcmpistri: CFLAGS += -msse4.2
|
||||
run-test-i386-pcmpistri: QEMU_OPTS += -cpu max
|
||||
run-plugin-test-i386-pcmpistri-%: QEMU_OPTS += -cpu max
|
||||
|
||||
#
|
||||
# hello-i386 is a barebones app
|
||||
|
10
tests/tsan/blacklist.tsan
Normal file
10
tests/tsan/blacklist.tsan
Normal file
@ -0,0 +1,10 @@
|
||||
# This is an example blacklist.
|
||||
# To enable use of the blacklist add this to configure:
|
||||
# "--extra-cflags=-fsanitize-blacklist=<src path>/tests/tsan/blacklist.tsan"
|
||||
# The eventual goal would be to fix these warnings.
|
||||
|
||||
# TSan is not happy about setting/getting of dirty bits,
|
||||
# for example, cpu_physical_memory_set_dirty_range,
|
||||
# and cpu_physical_memory_get_dirty.
|
||||
src:bitops.c
|
||||
src:bitmap.c
|
14
tests/tsan/suppressions.tsan
Normal file
14
tests/tsan/suppressions.tsan
Normal file
@ -0,0 +1,14 @@
|
||||
# This is the set of runtime suppressions of TSan warnings.
|
||||
# The goal would be to have here only items we do not
|
||||
# plan to fix, and to explain why for each item.
|
||||
|
||||
# TSan reports a double lock on RECURSIVE mutexes.
|
||||
# Since the recursive lock is intentional, we choose to ignore it.
|
||||
mutex:aio_context_acquire
|
||||
mutex:pthread_mutex_lock
|
||||
|
||||
# TSan reports a race betwen pthread_mutex_init() and
|
||||
# pthread_mutex_lock(). Since this is outside of QEMU,
|
||||
# we choose to ignore it.
|
||||
race:pthread_mutex_init
|
||||
race:pthread_mutex_lock
|
@ -37,12 +37,19 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TSAN
|
||||
#include <sanitizer/tsan_interface.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
Coroutine base;
|
||||
void *stack;
|
||||
size_t stack_size;
|
||||
sigjmp_buf env;
|
||||
|
||||
void *tsan_co_fiber;
|
||||
void *tsan_caller_fiber;
|
||||
|
||||
#ifdef CONFIG_VALGRIND_H
|
||||
unsigned int valgrind_stack_id;
|
||||
#endif
|
||||
@ -65,7 +72,18 @@ union cc_arg {
|
||||
int i[2];
|
||||
};
|
||||
|
||||
static void finish_switch_fiber(void *fake_stack_save)
|
||||
/* QEMU_ALWAYS_INLINE only does so if __OPTIMIZE__, so we cannot use it. */
|
||||
static inline __attribute__((always_inline))
|
||||
void on_new_fiber(CoroutineUContext *co)
|
||||
{
|
||||
#ifdef CONFIG_TSAN
|
||||
co->tsan_co_fiber = __tsan_create_fiber(0); /* flags: sync on switch */
|
||||
co->tsan_caller_fiber = __tsan_get_current_fiber();
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void finish_switch_fiber(void *fake_stack_save)
|
||||
{
|
||||
#ifdef CONFIG_ASAN
|
||||
const void *bottom_old;
|
||||
@ -78,13 +96,30 @@ static void finish_switch_fiber(void *fake_stack_save)
|
||||
leader.stack_size = size_old;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_TSAN
|
||||
if (fake_stack_save) {
|
||||
__tsan_release(fake_stack_save);
|
||||
__tsan_switch_to_fiber(fake_stack_save, 0); /* 0=synchronize */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void start_switch_fiber(void **fake_stack_save,
|
||||
const void *bottom, size_t size)
|
||||
static inline __attribute__((always_inline)) void start_switch_fiber(
|
||||
CoroutineAction action, void **fake_stack_save,
|
||||
const void *bottom, size_t size, void *new_fiber)
|
||||
{
|
||||
#ifdef CONFIG_ASAN
|
||||
__sanitizer_start_switch_fiber(fake_stack_save, bottom, size);
|
||||
__sanitizer_start_switch_fiber(
|
||||
action == COROUTINE_TERMINATE ? NULL : fake_stack_save,
|
||||
bottom, size);
|
||||
#endif
|
||||
#ifdef CONFIG_TSAN
|
||||
void *curr_fiber =
|
||||
__tsan_get_current_fiber();
|
||||
__tsan_acquire(curr_fiber);
|
||||
|
||||
*fake_stack_save = curr_fiber;
|
||||
__tsan_switch_to_fiber(new_fiber, 0); /* 0=synchronize */
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -104,8 +139,12 @@ static void coroutine_trampoline(int i0, int i1)
|
||||
|
||||
/* Initialize longjmp environment and switch back the caller */
|
||||
if (!sigsetjmp(self->env, 0)) {
|
||||
start_switch_fiber(&fake_stack_save,
|
||||
leader.stack, leader.stack_size);
|
||||
start_switch_fiber(
|
||||
COROUTINE_YIELD,
|
||||
&fake_stack_save,
|
||||
leader.stack,
|
||||
leader.stack_size,
|
||||
self->tsan_caller_fiber);
|
||||
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
|
||||
}
|
||||
|
||||
@ -154,12 +193,16 @@ Coroutine *qemu_coroutine_new(void)
|
||||
|
||||
arg.p = co;
|
||||
|
||||
on_new_fiber(co);
|
||||
makecontext(&uc, (void (*)(void))coroutine_trampoline,
|
||||
2, arg.i[0], arg.i[1]);
|
||||
|
||||
/* swapcontext() in, siglongjmp() back out */
|
||||
if (!sigsetjmp(old_env, 0)) {
|
||||
start_switch_fiber(&fake_stack_save, co->stack, co->stack_size);
|
||||
start_switch_fiber(
|
||||
COROUTINE_YIELD,
|
||||
&fake_stack_save,
|
||||
co->stack, co->stack_size, co->tsan_co_fiber);
|
||||
swapcontext(&old_uc, &uc);
|
||||
}
|
||||
|
||||
@ -216,8 +259,8 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
|
||||
|
||||
ret = sigsetjmp(from->env, 0);
|
||||
if (ret == 0) {
|
||||
start_switch_fiber(action == COROUTINE_TERMINATE ?
|
||||
NULL : &fake_stack_save, to->stack, to->stack_size);
|
||||
start_switch_fiber(action, &fake_stack_save,
|
||||
to->stack, to->stack_size, to->tsan_co_fiber);
|
||||
siglongjmp(to->env, action);
|
||||
}
|
||||
|
||||
@ -231,6 +274,11 @@ Coroutine *qemu_coroutine_self(void)
|
||||
if (!current) {
|
||||
current = &leader.base;
|
||||
}
|
||||
#ifdef CONFIG_TSAN
|
||||
if (!leader.tsan_co_fiber) {
|
||||
leader.tsan_co_fiber = __tsan_get_current_fiber();
|
||||
}
|
||||
#endif
|
||||
return current;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu-thread-common.h"
|
||||
#include "qemu/tsan.h"
|
||||
|
||||
static bool name_threads;
|
||||
|
||||
@ -513,6 +514,7 @@ static void *qemu_thread_start(void *args)
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name);
|
||||
g_free(qemu_thread_args->name);
|
||||
g_free(qemu_thread_args);
|
||||
pthread_cleanup_push(qemu_thread_atexit_notify, NULL);
|
||||
|
@ -348,6 +348,7 @@ static inline void qht_chain_destroy(const struct qht_bucket *head)
|
||||
struct qht_bucket *curr = head->next;
|
||||
struct qht_bucket *prev;
|
||||
|
||||
qemu_spin_destroy(&head->lock);
|
||||
while (curr) {
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
|
Loading…
Reference in New Issue
Block a user