diff --git a/scripts/ci/run-ci-tests.sh b/scripts/ci/run-ci-tests.sh index 8ee734fbc..ef2dffb1a 100755 --- a/scripts/ci/run-ci-tests.sh +++ b/scripts/ci/run-ci-tests.sh @@ -262,6 +262,9 @@ make -C test/others/rpc/ run ./test/zdtm.py run -t zdtm/static/env00 --sibling +./test/zdtm.py run -t zdtm/static/maps00 --preload-libfault +./test/zdtm.py run -t zdtm/static/maps02 --preload-libfault + ./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --dedup ./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --noauto-dedup ./test/zdtm.py run -t zdtm/transition/maps007 --pre 2 --page-server diff --git a/test/libfault/Makefile b/test/libfault/Makefile new file mode 100644 index 000000000..cbe47fdf2 --- /dev/null +++ b/test/libfault/Makefile @@ -0,0 +1,21 @@ +CC = gcc +CFLAGS = -c -fPIC -ldl + +SRC = libfault.c +OBJ = $(SRC:.c=.o) + +LIB = libfault.so + +.PHONY: all clean run + +all: $(LIB) + +$(LIB): $(OBJ) + $(CC) -shared -o $(LIB) $(OBJ) + +$(OBJ): $(SRC) + $(CC) $(CFLAGS) $< + +clean: + rm -f $(OBJ) $(LIB) + diff --git a/test/libfault/libfault.c b/test/libfault/libfault.c new file mode 100644 index 000000000..650bf08ca --- /dev/null +++ b/test/libfault/libfault.c @@ -0,0 +1,31 @@ +#define _GNU_SOURCE +#include +#include +#include + +ssize_t (*original_pread)(int fd, void *buf, size_t count, off_t offset) = NULL; + +/** + * This function is a wrapper around pread() that is used for testing CRIU's + * handling of cases where pread() returns less data than requested. + * + * pmc_fill() in criu/pagemap.c is a good example of where this can happen. + */ +ssize_t pread64(int fd, void *buf, size_t count, off_t offset) +{ + if (!original_pread) { + original_pread = dlsym(RTLD_NEXT, "pread"); + if (!original_pread) { + errno = EIO; + return -1; + } + } + + /* The following aims to simulate the case when pread() returns less + * data than requested. We need to ensure that CRIU handles such cases. */ + if (count > 2048) { + count -= 1024; + } + + return original_pread(fd, buf, count, offset); +} diff --git a/test/zdtm.py b/test/zdtm.py index 7a7cdfd3b..fbb3400c4 100755 --- a/test/zdtm.py +++ b/test/zdtm.py @@ -32,6 +32,14 @@ from zdtm.criu_config import criu_config # File to store content of streamed images STREAMED_IMG_FILE_NAME = "img.criu" +# A library used to preload C functions to simulate +# cases such as partial read with pread(). +LIBFAULT_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "libfault", + "libfault.so" +) + prev_line = None uuid = uuid.uuid4() @@ -628,6 +636,8 @@ class zdtm_test: ["make", "zdtm_ct"], env=dict(os.environ, MAKEFLAGS="")) if not os.access("zdtm/lib/libzdtmtst.a", os.F_OK): subprocess.check_call(["make", "-C", "zdtm/"]) + if 'preload_libfault' in opts and opts['preload_libfault']: + subprocess.check_call(["make", "-C", "libfault/"]) if 'rootless' in opts and opts['rootless']: return subprocess.check_call( @@ -880,6 +890,7 @@ class criu_cli: fault=None, strace=[], preexec=None, + preload_libfault=False, nowait=False, timeout=60): env = dict( @@ -890,6 +901,9 @@ class criu_cli: print("Forcing %s fault" % fault) env['CRIU_FAULT'] = fault + if preload_libfault: + env['LD_PRELOAD'] = LIBFAULT_PATH + cr = subprocess.Popen(strace + [criu_bin, action, "--no-default-config"] + args, env=env, @@ -980,6 +994,7 @@ class criu_rpc: fault=None, strace=[], preexec=None, + preload_libfault=False, nowait=False, timeout=None): if fault: @@ -1065,6 +1080,7 @@ class criu: self.__criu_bin = opts['criu_bin'] self.__crit_bin = opts['crit_bin'] self.__pre_dump_mode = opts['pre_dump_mode'] + self.__preload_libfault = bool(opts['preload_libfault']) self.__mntns_compat_mode = bool(opts['mntns_compat_mode']) if opts['rpc']: @@ -1192,8 +1208,10 @@ class criu: with open("/proc/sys/kernel/ns_last_pid") as ns_last_pid_fd: ns_last_pid = ns_last_pid_fd.read() + preload_libfault = self.__preload_libfault and action in ['dump', 'pre-dump', 'restore'] + ret = self.__criu.run(action, s_args, self.__criu_bin, self.__fault, - strace, preexec, nowait) + strace, preexec, preload_libfault, nowait) if nowait: os.close(status_fds[1]) @@ -2083,7 +2101,7 @@ class Launcher: 'dedup', 'sbs', 'freezecg', 'user', 'dry_run', 'noauto_dedup', 'remote_lazy_pages', 'show_stats', 'lazy_migrate', 'stream', 'tls', 'criu_bin', 'crit_bin', 'pre_dump_mode', 'mntns_compat_mode', - 'rootless') + 'rootless', 'preload_libfault') arg = repr((name, desc, flavor, {d: self.__opts[d] for d in nd})) if self.__use_log: @@ -2788,6 +2806,7 @@ def get_cli_args(): help="Select tests for a shard (0-based)") rp.add_argument("--test-shard-count", type=int, default=0, help="Specify how many shards are being run (0=sharding disabled; must be the same for all shards)") + rp.add_argument("--preload-libfault", action="store_true", help="Run criu with library preload to simulate special cases") lp = sp.add_parser("list", help="List tests") lp.set_defaults(action=list_tests) diff --git a/test/zdtm/criu_config.py b/test/zdtm/criu_config.py index 487becfb4..221c23292 100644 --- a/test/zdtm/criu_config.py +++ b/test/zdtm/criu_config.py @@ -11,6 +11,7 @@ class criu_config: fault=None, strace=[], preexec=None, + preload=False, nowait=False): config_path = tempfile.mktemp(".conf", "criu-%s-" % action)