mirror of https://github.com/xemu-project/xemu.git
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
|
- cd build
|
||||||
- ../configure || { cat config.log; exit 1; }
|
- ../configure || { cat config.log; exit 1; }
|
||||||
- gmake -j8
|
- gmake -j8
|
||||||
- gmake -j8 V=1 check
|
- gmake V=1 check
|
||||||
|
|
||||||
macos_task:
|
macos_task:
|
||||||
osx_instance:
|
osx_instance:
|
||||||
|
@ -26,7 +26,7 @@ macos_task:
|
||||||
- cd build
|
- cd build
|
||||||
- ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
- ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
|
||||||
- gmake -j$(sysctl -n hw.ncpu)
|
- gmake -j$(sysctl -n hw.ncpu)
|
||||||
- gmake check -j$(sysctl -n hw.ncpu)
|
- gmake check
|
||||||
|
|
||||||
macos_xcode_task:
|
macos_xcode_task:
|
||||||
osx_instance:
|
osx_instance:
|
||||||
|
@ -39,4 +39,4 @@ macos_xcode_task:
|
||||||
- cd build
|
- cd build
|
||||||
- ../configure --cc=clang || { cat config.log; exit 1; }
|
- ../configure --cc=clang || { cat config.log; exit 1; }
|
||||||
- gmake -j$(sysctl -n hw.ncpu)
|
- gmake -j$(sysctl -n hw.ncpu)
|
||||||
- gmake check -j$(sysctl -n hw.ncpu)
|
- gmake check
|
||||||
|
|
|
@ -5,8 +5,8 @@ env:
|
||||||
global:
|
global:
|
||||||
- LC_ALL=C
|
- LC_ALL=C
|
||||||
matrix:
|
matrix:
|
||||||
# - IMAGE=debian-amd64
|
- IMAGE=debian-amd64
|
||||||
# TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
TARGET_LIST=x86_64-softmmu,x86_64-linux-user
|
||||||
- IMAGE=debian-win32-cross
|
- IMAGE=debian-win32-cross
|
||||||
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu
|
||||||
- IMAGE=debian-win64-cross
|
- IMAGE=debian-win64-cross
|
||||||
|
@ -19,10 +19,10 @@ env:
|
||||||
TARGET_LIST=aarch64-softmmu,aarch64-linux-user
|
TARGET_LIST=aarch64-softmmu,aarch64-linux-user
|
||||||
- IMAGE=debian-s390x-cross
|
- IMAGE=debian-s390x-cross
|
||||||
TARGET_LIST=s390x-softmmu,s390x-linux-user
|
TARGET_LIST=s390x-softmmu,s390x-linux-user
|
||||||
# - IMAGE=debian-mips-cross
|
- IMAGE=debian-mips-cross
|
||||||
# TARGET_LIST=mips-softmmu,mipsel-linux-user
|
TARGET_LIST=mips-softmmu,mipsel-linux-user
|
||||||
# - IMAGE=debian-mips64el-cross
|
- IMAGE=debian-mips64el-cross
|
||||||
# TARGET_LIST=mips64el-softmmu,mips64el-linux-user
|
TARGET_LIST=mips64el-softmmu,mips64el-linux-user
|
||||||
- IMAGE=debian-ppc64el-cross
|
- IMAGE=debian-ppc64el-cross
|
||||||
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
|
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
|
||||||
build:
|
build:
|
||||||
|
|
16
Makefile
16
Makefile
|
@ -526,13 +526,14 @@ $(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
|
||||||
$(TARGET_DIRS_RULES):
|
$(TARGET_DIRS_RULES):
|
||||||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
|
$(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_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
|
.PHONY: dtc/all
|
||||||
dtc/all: .git-submodule-status dtc/libfdt dtc/tests
|
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/libfdt.a,)
|
$(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
|
dtc/%: .git-submodule-status
|
||||||
@mkdir -p $@
|
@mkdir -p $@
|
||||||
|
@ -561,12 +562,6 @@ slirp/all: .git-submodule-status
|
||||||
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
|
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
|
||||||
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
|
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) \
|
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
|
||||||
$(qom-obj-y)
|
$(qom-obj-y)
|
||||||
|
|
||||||
|
@ -820,7 +815,6 @@ distclean: clean
|
||||||
rm -rf $$d || exit 1 ; \
|
rm -rf $$d || exit 1 ; \
|
||||||
done
|
done
|
||||||
rm -Rf .sdk
|
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 \
|
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 \
|
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
|
/* flush_all_helper: run fn across all cpus
|
||||||
*
|
*
|
||||||
* If the wait flag is set then the src cpu's helper will be queued as
|
* 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;
|
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)
|
bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||||
{
|
{
|
||||||
TranslationBlock *tb;
|
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 */
|
/* one-shot translation, invalidate it immediately */
|
||||||
tb_phys_invalidate(tb, -1);
|
tb_phys_invalidate(tb, -1);
|
||||||
tcg_tb_remove(tb);
|
tcg_tb_remove(tb);
|
||||||
|
tb_destroy(tb);
|
||||||
}
|
}
|
||||||
r = true;
|
r = true;
|
||||||
}
|
}
|
||||||
|
@ -541,6 +547,15 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
|
||||||
#endif
|
#endif
|
||||||
existing = atomic_cmpxchg(lp, NULL, pd);
|
existing = atomic_cmpxchg(lp, NULL, pd);
|
||||||
if (unlikely(existing)) {
|
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);
|
g_free(pd);
|
||||||
pd = existing;
|
pd = existing;
|
||||||
}
|
}
|
||||||
|
@ -1886,6 +1901,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||||
|
|
||||||
orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize);
|
orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize);
|
||||||
atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned);
|
atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned);
|
||||||
|
tb_destroy(tb);
|
||||||
return existing_tb;
|
return existing_tb;
|
||||||
}
|
}
|
||||||
tcg_tb_insert(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);
|
tb_phys_invalidate(tb->orig_tb, -1);
|
||||||
}
|
}
|
||||||
tcg_tb_remove(tb);
|
tcg_tb_remove(tb);
|
||||||
|
tb_destroy(tb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
|
/* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
|
||||||
|
|
|
@ -395,6 +395,7 @@ gprof="no"
|
||||||
debug_tcg="no"
|
debug_tcg="no"
|
||||||
debug="no"
|
debug="no"
|
||||||
sanitizers="no"
|
sanitizers="no"
|
||||||
|
tsan="no"
|
||||||
fortify_source=""
|
fortify_source=""
|
||||||
strip_opt="yes"
|
strip_opt="yes"
|
||||||
tcg_interpreter="no"
|
tcg_interpreter="no"
|
||||||
|
@ -1152,6 +1153,10 @@ for opt do
|
||||||
;;
|
;;
|
||||||
--disable-sanitizers) sanitizers="no"
|
--disable-sanitizers) sanitizers="no"
|
||||||
;;
|
;;
|
||||||
|
--enable-tsan) tsan="yes"
|
||||||
|
;;
|
||||||
|
--disable-tsan) tsan="no"
|
||||||
|
;;
|
||||||
--enable-sparse) sparse="yes"
|
--enable-sparse) sparse="yes"
|
||||||
;;
|
;;
|
||||||
--disable-sparse) sparse="no"
|
--disable-sparse) sparse="no"
|
||||||
|
@ -1764,6 +1769,7 @@ Advanced options (experts only):
|
||||||
--with-pkgversion=VERS use specified string as sub-version of the package
|
--with-pkgversion=VERS use specified string as sub-version of the package
|
||||||
--enable-debug enable common debug build options
|
--enable-debug enable common debug build options
|
||||||
--enable-sanitizers enable default sanitizers
|
--enable-sanitizers enable default sanitizers
|
||||||
|
--enable-tsan enable thread sanitizer
|
||||||
--disable-strip disable stripping binaries
|
--disable-strip disable stripping binaries
|
||||||
--disable-werror disable compilation abort on warning
|
--disable-werror disable compilation abort on warning
|
||||||
--disable-stack-protector disable compiler-provided stack protection
|
--disable-stack-protector disable compiler-provided stack protection
|
||||||
|
@ -4312,7 +4318,6 @@ EOF
|
||||||
mkdir -p dtc
|
mkdir -p dtc
|
||||||
if [ "$pwd_is_source_path" != "y" ] ; then
|
if [ "$pwd_is_source_path" != "y" ] ; then
|
||||||
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
|
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
|
||||||
symlink "$source_path/dtc/scripts" "dtc/scripts"
|
|
||||||
fi
|
fi
|
||||||
fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt"
|
fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt"
|
||||||
fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt"
|
fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt"
|
||||||
|
@ -6221,6 +6226,30 @@ if test "$fuzzing" = "yes" ; then
|
||||||
fi
|
fi
|
||||||
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
|
# check for libpmem
|
||||||
|
|
||||||
|
@ -6378,6 +6407,16 @@ if test "$have_asan" = "yes"; then
|
||||||
"Without code annotation, the report may be inferior."
|
"Without code annotation, the report may be inferior."
|
||||||
fi
|
fi
|
||||||
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
|
if test "$have_ubsan" = "yes"; then
|
||||||
QEMU_CFLAGS="-fsanitize=undefined $QEMU_CFLAGS"
|
QEMU_CFLAGS="-fsanitize=undefined $QEMU_CFLAGS"
|
||||||
QEMU_LDFLAGS="-fsanitize=undefined $QEMU_LDFLAGS"
|
QEMU_LDFLAGS="-fsanitize=undefined $QEMU_LDFLAGS"
|
||||||
|
@ -6413,7 +6452,8 @@ if test "$werror" = "yes"; then
|
||||||
QEMU_CFLAGS="-Werror $QEMU_CFLAGS"
|
QEMU_CFLAGS="-Werror $QEMU_CFLAGS"
|
||||||
fi
|
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
|
if $ld --version 2>/dev/null | grep "GNU ld" >/dev/null 2>/dev/null ; then
|
||||||
QEMU_LDFLAGS="-Wl,--warn-common $QEMU_LDFLAGS"
|
QEMU_LDFLAGS="-Wl,--warn-common $QEMU_LDFLAGS"
|
||||||
fi
|
fi
|
||||||
|
@ -7477,6 +7517,10 @@ if test "$have_asan_iface_fiber" = "yes" ; then
|
||||||
echo "CONFIG_ASAN_IFACE_FIBER=y" >> $config_host_mak
|
echo "CONFIG_ASAN_IFACE_FIBER=y" >> $config_host_mak
|
||||||
fi
|
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
|
if test "$has_environ" = "yes" ; then
|
||||||
echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak
|
echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -97,7 +97,7 @@ void cpu_list_remove(CPUState *cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct qemu_work_item {
|
struct qemu_work_item {
|
||||||
struct qemu_work_item *next;
|
QSIMPLEQ_ENTRY(qemu_work_item) node;
|
||||||
run_on_cpu_func func;
|
run_on_cpu_func func;
|
||||||
run_on_cpu_data data;
|
run_on_cpu_data data;
|
||||||
bool free, exclusive, done;
|
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)
|
static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(&cpu->work_mutex);
|
qemu_mutex_lock(&cpu->work_mutex);
|
||||||
if (cpu->queued_work_first == NULL) {
|
QSIMPLEQ_INSERT_TAIL(&cpu->work_list, wi, node);
|
||||||
cpu->queued_work_first = wi;
|
|
||||||
} else {
|
|
||||||
cpu->queued_work_last->next = wi;
|
|
||||||
}
|
|
||||||
cpu->queued_work_last = wi;
|
|
||||||
wi->next = NULL;
|
|
||||||
wi->done = false;
|
wi->done = false;
|
||||||
qemu_mutex_unlock(&cpu->work_mutex);
|
qemu_mutex_unlock(&cpu->work_mutex);
|
||||||
|
|
||||||
|
@ -306,17 +300,14 @@ void process_queued_cpu_work(CPUState *cpu)
|
||||||
{
|
{
|
||||||
struct qemu_work_item *wi;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
while (!QSIMPLEQ_EMPTY(&cpu->work_list)) {
|
||||||
qemu_mutex_lock(&cpu->work_mutex);
|
wi = QSIMPLEQ_FIRST(&cpu->work_list);
|
||||||
while (cpu->queued_work_first != NULL) {
|
QSIMPLEQ_REMOVE_HEAD(&cpu->work_list, node);
|
||||||
wi = cpu->queued_work_first;
|
|
||||||
cpu->queued_work_first = wi->next;
|
|
||||||
if (!cpu->queued_work_first) {
|
|
||||||
cpu->queued_work_last = NULL;
|
|
||||||
}
|
|
||||||
qemu_mutex_unlock(&cpu->work_mutex);
|
qemu_mutex_unlock(&cpu->work_mutex);
|
||||||
if (wi->exclusive) {
|
if (wi->exclusive) {
|
||||||
/* Running work items outside the BQL avoids the following deadlock:
|
/* 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();
|
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)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (cpu_is_stopped(cpu)) {
|
if (cpu_is_stopped(cpu)) {
|
||||||
|
@ -1518,7 +1528,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||||
cpu = first_cpu;
|
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);
|
atomic_mb_set(&tcg_current_rr_cpu, cpu);
|
||||||
current_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
|
* ``DEBUG=1``: enables debug. See the previous "Debugging a Docker test
|
||||||
failure" section.
|
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
|
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);
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
|
tlb_destroy(cpu);
|
||||||
cpu_list_remove(cpu);
|
cpu_list_remove(cpu);
|
||||||
|
|
||||||
if (cc->vmsd != NULL) {
|
if (cc->vmsd != NULL) {
|
||||||
|
|
|
@ -370,6 +370,7 @@ static void cpu_common_initfn(Object *obj)
|
||||||
cpu->nr_threads = 1;
|
cpu->nr_threads = 1;
|
||||||
|
|
||||||
qemu_mutex_init(&cpu->work_mutex);
|
qemu_mutex_init(&cpu->work_mutex);
|
||||||
|
QSIMPLEQ_INIT(&cpu->work_list);
|
||||||
QTAILQ_INIT(&cpu->breakpoints);
|
QTAILQ_INIT(&cpu->breakpoints);
|
||||||
QTAILQ_INIT(&cpu->watchpoints);
|
QTAILQ_INIT(&cpu->watchpoints);
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,11 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||||
* @cpu: CPU whose TLB should be initialized
|
* @cpu: CPU whose TLB should be initialized
|
||||||
*/
|
*/
|
||||||
void tlb_init(CPUState *cpu);
|
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:
|
* tlb_flush_page:
|
||||||
* @cpu: CPU whose TLB should be flushed
|
* @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_init(CPUState *cpu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
static inline void tlb_destroy(CPUState *cpu)
|
||||||
|
{
|
||||||
|
}
|
||||||
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -331,8 +331,8 @@ struct qemu_work_item;
|
||||||
* @opaque: User data.
|
* @opaque: User data.
|
||||||
* @mem_io_pc: Host Program Counter at which the memory was accessed.
|
* @mem_io_pc: Host Program Counter at which the memory was accessed.
|
||||||
* @kvm_fd: vCPU file descriptor for KVM.
|
* @kvm_fd: vCPU file descriptor for KVM.
|
||||||
* @work_mutex: Lock to prevent multiple access to queued_work_*.
|
* @work_mutex: Lock to prevent multiple access to @work_list.
|
||||||
* @queued_work_first: First asynchronous work pending.
|
* @work_list: List of pending asynchronous work.
|
||||||
* @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
|
* @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
|
||||||
* to @trace_dstate).
|
* to @trace_dstate).
|
||||||
* @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
|
* @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
|
||||||
|
@ -376,7 +376,7 @@ struct CPUState {
|
||||||
sigjmp_buf jmp_env;
|
sigjmp_buf jmp_env;
|
||||||
|
|
||||||
QemuMutex work_mutex;
|
QemuMutex work_mutex;
|
||||||
struct qemu_work_item *queued_work_first, *queued_work_last;
|
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
|
||||||
|
|
||||||
CPUAddressSpace *cpu_ases;
|
CPUAddressSpace *cpu_ases;
|
||||||
int num_ases;
|
int num_ases;
|
||||||
|
|
|
@ -206,6 +206,10 @@ void qemu_thread_atexit_add(struct Notifier *notifier);
|
||||||
*/
|
*/
|
||||||
void qemu_thread_atexit_remove(struct Notifier *notifier);
|
void qemu_thread_atexit_remove(struct Notifier *notifier);
|
||||||
|
|
||||||
|
#ifdef CONFIG_TSAN
|
||||||
|
#include <sanitizer/tsan_interface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct QemuSpin {
|
struct QemuSpin {
|
||||||
int value;
|
int value;
|
||||||
};
|
};
|
||||||
|
@ -213,20 +217,46 @@ struct QemuSpin {
|
||||||
static inline void qemu_spin_init(QemuSpin *spin)
|
static inline void qemu_spin_init(QemuSpin *spin)
|
||||||
{
|
{
|
||||||
__sync_lock_release(&spin->value);
|
__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)
|
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 (unlikely(__sync_lock_test_and_set(&spin->value, true))) {
|
||||||
while (atomic_read(&spin->value)) {
|
while (atomic_read(&spin->value)) {
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_TSAN
|
||||||
|
__tsan_mutex_post_lock(spin, 0, 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool qemu_spin_trylock(QemuSpin *spin)
|
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)
|
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)
|
static inline void qemu_spin_unlock(QemuSpin *spin)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_TSAN
|
||||||
|
__tsan_mutex_pre_unlock(spin, 0);
|
||||||
|
#endif
|
||||||
__sync_lock_release(&spin->value);
|
__sync_lock_release(&spin->value);
|
||||||
|
#ifdef CONFIG_TSAN
|
||||||
|
__tsan_mutex_post_unlock(spin, 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QemuLockCnt {
|
struct QemuLockCnt {
|
||||||
|
|
|
@ -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);
|
TranslationBlock *tcg_tb_alloc(TCGContext *s);
|
||||||
|
|
||||||
void tcg_region_init(void);
|
void tcg_region_init(void);
|
||||||
|
void tb_destroy(TranslationBlock *tb);
|
||||||
void tcg_region_reset_all(void);
|
void tcg_region_reset_all(void);
|
||||||
|
|
||||||
size_t tcg_code_size(void);
|
size_t tcg_code_size(void);
|
||||||
|
|
|
@ -502,6 +502,14 @@ size_t tcg_nb_tbs(void)
|
||||||
return nb_tbs;
|
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)
|
static void tcg_region_tree_reset_all(void)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -510,6 +518,7 @@ static void tcg_region_tree_reset_all(void)
|
||||||
for (i = 0; i < region.n; i++) {
|
for (i = 0; i < region.n; i++) {
|
||||||
struct tcg_region_tree *rt = region_trees + i * tree_size;
|
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 */
|
/* Increment the refcount first so that destroy acts as a reset */
|
||||||
g_tree_ref(rt->tree);
|
g_tree_ref(rt->tree);
|
||||||
g_tree_destroy(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-qdict$(EXESUF)
|
||||||
check-unit-y += tests/check-block-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-qnum$(EXESUF)
|
||||||
check-unit-y += tests/check-qstring$(EXESUF)
|
check-unit-y += tests/check-qstring$(EXESUF)
|
||||||
check-unit-y += tests/check-qlist$(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-qht-par$(EXESUF)
|
||||||
check-unit-y += tests/test-bitops$(EXESUF)
|
check-unit-y += tests/test-bitops$(EXESUF)
|
||||||
check-unit-y += tests/test-bitcnt$(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-interface$(EXESUF)
|
||||||
check-unit-y += tests/check-qom-proplist$(EXESUF)
|
check-unit-y += tests/check-qom-proplist$(EXESUF)
|
||||||
check-unit-y += tests/test-qemu-opts$(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-$(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-tlscredsx509$(EXESUF)
|
||||||
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_GNUTLS)) += tests/test-crypto-tlssession$(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)))
|
ifneq (,$(findstring qemu-ga,$(TOOLS)))
|
||||||
check-unit-$(call land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
|
check-unit-$(call land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-timed-average$(EXESUF)
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-timed-average$(EXESUF)
|
||||||
check-unit-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_INOTIFY1)) += tests/test-util-filemonitor$(EXESUF)
|
check-unit-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_INOTIFY1)) += tests/test-util-filemonitor$(EXESUF)
|
||||||
check-unit-$(CONFIG_SOFTMMU) += tests/test-util-sockets$(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
|
# Please keep this list sorted alphabetically
|
||||||
ENV PACKAGES \
|
ENV PACKAGES \
|
||||||
|
|
|
@ -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
|
|
@ -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 += hotblocks
|
||||||
NAMES += howvec
|
NAMES += howvec
|
||||||
NAMES += hotpages
|
NAMES += hotpages
|
||||||
|
NAMES += lockstep
|
||||||
|
|
||||||
SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
|
SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
|
||||||
|
|
||||||
|
|
|
@ -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
|
# QTest rules
|
||||||
|
|
||||||
TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
|
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)
|
ifeq ($(CONFIG_POSIX),y)
|
||||||
QTEST_TARGETS = $(TARGETS)
|
QTEST_TARGETS = $(TARGETS)
|
||||||
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y:%=tests/qtest/%$(EXESUF)))
|
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y:%=tests/qtest/%$(EXESUF)))
|
||||||
check-qtest-y += $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF))
|
check-qtest-y += $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF))
|
||||||
else
|
endif
|
||||||
QTEST_TARGETS =
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
qtest-obj-y = tests/qtest/libqtest.o $(test-util-obj-y)
|
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
|
# If plugins exist also include those in the tests
|
||||||
ifeq ($(CONFIG_PLUGIN),y)
|
ifeq ($(CONFIG_PLUGIN),y)
|
||||||
PLUGIN_DIR=../../plugin
|
PLUGIN_SRC=$(SRC_PATH)/tests/plugin
|
||||||
VPATH+=$(PLUGIN_DIR)
|
PLUGIN_LIB=../../plugin
|
||||||
PLUGINS=$(notdir $(wildcard $(PLUGIN_DIR)/*.so))
|
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
|
# 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
|
# pre-requistes manually here as we can't use stems to handle it. We
|
||||||
|
@ -152,7 +154,7 @@ run-%: %
|
||||||
|
|
||||||
run-plugin-%:
|
run-plugin-%:
|
||||||
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
||||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||||
-d plugin -D $*.pout \
|
-d plugin -D $*.pout \
|
||||||
$(call strip-plugin,$<), \
|
$(call strip-plugin,$<), \
|
||||||
"$* on $(TARGET_NAME)")
|
"$* on $(TARGET_NAME)")
|
||||||
|
@ -168,7 +170,7 @@ run-plugin-%:
|
||||||
$(call run-test, $@, \
|
$(call run-test, $@, \
|
||||||
$(QEMU) -monitor none -display none \
|
$(QEMU) -monitor none -display none \
|
||||||
-chardev file$(COMMA)path=$@.out$(COMMA)id=output \
|
-chardev file$(COMMA)path=$@.out$(COMMA)id=output \
|
||||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||||
-d plugin -D $*.pout \
|
-d plugin -D $*.pout \
|
||||||
$(QEMU_OPTS) $(call strip-plugin,$<), \
|
$(QEMU_OPTS) $(call strip-plugin,$<), \
|
||||||
"$* on $(TARGET_NAME)")
|
"$* on $(TARGET_NAME)")
|
||||||
|
|
|
@ -20,8 +20,9 @@ run-fcvt: fcvt
|
||||||
# Pauth Tests
|
# Pauth Tests
|
||||||
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),)
|
ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_3),)
|
||||||
AARCH64_TESTS += pauth-1 pauth-2 pauth-4
|
AARCH64_TESTS += pauth-1 pauth-2 pauth-4
|
||||||
run-pauth-%: QEMU_OPTS += -cpu max
|
|
||||||
pauth-%: CFLAGS += -march=armv8.3-a
|
pauth-%: CFLAGS += -march=armv8.3-a
|
||||||
|
run-pauth-%: QEMU_OPTS += -cpu max
|
||||||
|
run-plugin-pauth-%: QEMU_OPTS += -cpu max
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Semihosting smoke test for linux-user
|
# Semihosting smoke test for linux-user
|
||||||
|
@ -31,7 +32,7 @@ run-semihosting: semihosting
|
||||||
|
|
||||||
run-plugin-semihosting-with-%:
|
run-plugin-semihosting-with-%:
|
||||||
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
||||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||||
$(call strip-plugin,$<) 2> $<.err, \
|
$(call strip-plugin,$<) 2> $<.err, \
|
||||||
"$< on $(TARGET_NAME) with $*")
|
"$< on $(TARGET_NAME) with $*")
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ run-semihosting-arm: semihosting-arm
|
||||||
|
|
||||||
run-plugin-semihosting-with-%:
|
run-plugin-semihosting-with-%:
|
||||||
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
|
||||||
-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
|
-plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \
|
||||||
$(call strip-plugin,$<) 2> $<.err, \
|
$(call strip-plugin,$<) 2> $<.err, \
|
||||||
"$< on $(TARGET_NAME) with $*")
|
"$< on $(TARGET_NAME) with $*")
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ X86_64_TESTS:=$(filter test-i386-ssse3, $(ALL_X86_TESTS))
|
||||||
|
|
||||||
test-i386-pcmpistri: CFLAGS += -msse4.2
|
test-i386-pcmpistri: CFLAGS += -msse4.2
|
||||||
run-test-i386-pcmpistri: QEMU_OPTS += -cpu max
|
run-test-i386-pcmpistri: QEMU_OPTS += -cpu max
|
||||||
|
run-plugin-test-i386-pcmpistri-%: QEMU_OPTS += -cpu max
|
||||||
|
|
||||||
#
|
#
|
||||||
# hello-i386 is a barebones app
|
# hello-i386 is a barebones app
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_TSAN
|
||||||
|
#include <sanitizer/tsan_interface.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Coroutine base;
|
Coroutine base;
|
||||||
void *stack;
|
void *stack;
|
||||||
size_t stack_size;
|
size_t stack_size;
|
||||||
sigjmp_buf env;
|
sigjmp_buf env;
|
||||||
|
|
||||||
|
void *tsan_co_fiber;
|
||||||
|
void *tsan_caller_fiber;
|
||||||
|
|
||||||
#ifdef CONFIG_VALGRIND_H
|
#ifdef CONFIG_VALGRIND_H
|
||||||
unsigned int valgrind_stack_id;
|
unsigned int valgrind_stack_id;
|
||||||
#endif
|
#endif
|
||||||
|
@ -65,7 +72,18 @@ union cc_arg {
|
||||||
int i[2];
|
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
|
#ifdef CONFIG_ASAN
|
||||||
const void *bottom_old;
|
const void *bottom_old;
|
||||||
|
@ -78,13 +96,30 @@ static void finish_switch_fiber(void *fake_stack_save)
|
||||||
leader.stack_size = size_old;
|
leader.stack_size = size_old;
|
||||||
}
|
}
|
||||||
#endif
|
#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,
|
static inline __attribute__((always_inline)) void start_switch_fiber(
|
||||||
const void *bottom, size_t size)
|
CoroutineAction action, void **fake_stack_save,
|
||||||
|
const void *bottom, size_t size, void *new_fiber)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_ASAN
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +139,12 @@ static void coroutine_trampoline(int i0, int i1)
|
||||||
|
|
||||||
/* Initialize longjmp environment and switch back the caller */
|
/* Initialize longjmp environment and switch back the caller */
|
||||||
if (!sigsetjmp(self->env, 0)) {
|
if (!sigsetjmp(self->env, 0)) {
|
||||||
start_switch_fiber(&fake_stack_save,
|
start_switch_fiber(
|
||||||
leader.stack, leader.stack_size);
|
COROUTINE_YIELD,
|
||||||
|
&fake_stack_save,
|
||||||
|
leader.stack,
|
||||||
|
leader.stack_size,
|
||||||
|
self->tsan_caller_fiber);
|
||||||
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
|
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,12 +193,16 @@ Coroutine *qemu_coroutine_new(void)
|
||||||
|
|
||||||
arg.p = co;
|
arg.p = co;
|
||||||
|
|
||||||
|
on_new_fiber(co);
|
||||||
makecontext(&uc, (void (*)(void))coroutine_trampoline,
|
makecontext(&uc, (void (*)(void))coroutine_trampoline,
|
||||||
2, arg.i[0], arg.i[1]);
|
2, arg.i[0], arg.i[1]);
|
||||||
|
|
||||||
/* swapcontext() in, siglongjmp() back out */
|
/* swapcontext() in, siglongjmp() back out */
|
||||||
if (!sigsetjmp(old_env, 0)) {
|
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);
|
swapcontext(&old_uc, &uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,8 +259,8 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
|
||||||
|
|
||||||
ret = sigsetjmp(from->env, 0);
|
ret = sigsetjmp(from->env, 0);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
start_switch_fiber(action == COROUTINE_TERMINATE ?
|
start_switch_fiber(action, &fake_stack_save,
|
||||||
NULL : &fake_stack_save, to->stack, to->stack_size);
|
to->stack, to->stack_size, to->tsan_co_fiber);
|
||||||
siglongjmp(to->env, action);
|
siglongjmp(to->env, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +274,11 @@ Coroutine *qemu_coroutine_self(void)
|
||||||
if (!current) {
|
if (!current) {
|
||||||
current = &leader.base;
|
current = &leader.base;
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_TSAN
|
||||||
|
if (!leader.tsan_co_fiber) {
|
||||||
|
leader.tsan_co_fiber = __tsan_get_current_fiber();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "qemu/atomic.h"
|
#include "qemu/atomic.h"
|
||||||
#include "qemu/notify.h"
|
#include "qemu/notify.h"
|
||||||
#include "qemu-thread-common.h"
|
#include "qemu-thread-common.h"
|
||||||
|
#include "qemu/tsan.h"
|
||||||
|
|
||||||
static bool name_threads;
|
static bool name_threads;
|
||||||
|
|
||||||
|
@ -513,6 +514,7 @@ static void *qemu_thread_start(void *args)
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name);
|
||||||
g_free(qemu_thread_args->name);
|
g_free(qemu_thread_args->name);
|
||||||
g_free(qemu_thread_args);
|
g_free(qemu_thread_args);
|
||||||
pthread_cleanup_push(qemu_thread_atexit_notify, NULL);
|
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 *curr = head->next;
|
||||||
struct qht_bucket *prev;
|
struct qht_bucket *prev;
|
||||||
|
|
||||||
|
qemu_spin_destroy(&head->lock);
|
||||||
while (curr) {
|
while (curr) {
|
||||||
prev = curr;
|
prev = curr;
|
||||||
curr = curr->next;
|
curr = curr->next;
|
||||||
|
|
Loading…
Reference in New Issue