[CRIU] [PATCH] Add test case for code page sharing

Andrey Wagin avagin at gmail.com
Thu Apr 2 13:30:01 PDT 2015


2015-03-31 20:51 GMT+03:00 Christopher Covington <cov at codeaurora.org>:
> We've observed at least one scenario where processes originally
> sharing physical memory no longer do so after dump and restore.
> This increases memory usage and degrades performance. Here is a
> test case (created outside of the ZDTM framework for expedience)
> that demonstrates the issue. The test fails with CRIU 1.4. Run
> `make && ./test_sharing.sh` to build and run the test.

I've found a root cause of this problem. CRIU modifies code to inject
a parasite:
https://github.com/xemul/criu/blob/master/parasite-syscall.c#L219

So we affects only one page in a process. Is this really so critical?

Here is a small modification for your test to prove this idea.

diff --git a/test/code-sharing/test_sharing.sh
b/test/code-sharing/test_sharing.sh
index 1c4233b..9115d1d 100755
--- a/test/code-sharing/test_sharing.sh
+++ b/test/code-sharing/test_sharing.sh
@@ -29,7 +29,7 @@ cat /dev/null | setsid /tmp/test_sharing &> /dev/null &
 pid1=$!

 mkdir /tmp/criudump-$pid1
-criu dump -t $pid1 -v4 -o dump-$pid1.log -D /tmp/criudump-$pid1 ||
echo "Error: 'criu dump' exited with nonzero exit code!"
+criu dump -t $pid1 -v4 -o dump-$pid1.log -D /tmp/criudump-$pid1
--leave-running || echo "Error: 'criu dump' exited with nonzero exit
code!"

 # Start pid2 (will be the second process criu dumped)
 cat /dev/null | setsid /tmp/test_sharing &> /dev/null &
@@ -38,7 +38,7 @@ pid2=$!
 mkdir /tmp/criudump-$pid2
 criu dump -t $pid2 -v4 -o dump-$pid2.log -D /tmp/criudump-$pid2 ||
echo "Error: 'criu dump' exited with nonzero exit code!"

-criu restore -v2 -d -R -D /tmp/criudump-$pid1
+#criu restore -v2 -d -R -D /tmp/criudump-$pid1
 criu restore -v2 -d -R -D /tmp/criudump-$pid2

 # Start pid3 (is not criu dumped)

# ./test_sharing.sh
pid0: PA for main() in pid 1889: 0x400bc1 0xa08000000006b361
pid1: PA for main() in pid 1891: 0x400bc1 0x808000000006bbc3
pid2: PA for main() in pid 1895: 0x400bc1 0x808000000006b96b
pid3: PA for main() in pid 1897: 0x400bc1 0xa08000000006b361


>
> Acked-by: Aaron Lindsay <alindsay at qti.qualcomm.com>
> Signed-off-by: Christopher Covington <cov at codeaurora.org>
>
> Change-Id: If0cacb7ed8c12c7dee2dff7d6016fbddf1d13157
> ---
>  test/code-sharing/Makefile        | 11 +++++
>  test/code-sharing/test_sharing.c  | 90 +++++++++++++++++++++++++++++++++++++++
>  test/code-sharing/test_sharing.sh | 63 +++++++++++++++++++++++++++
>  3 files changed, 164 insertions(+)
>  create mode 100644 test/code-sharing/Makefile
>  create mode 100644 test/code-sharing/test_sharing.c
>  create mode 100755 test/code-sharing/test_sharing.sh
>
> diff --git a/test/code-sharing/Makefile b/test/code-sharing/Makefile
> new file mode 100644
> index 0000000..e30d6f3
> --- /dev/null
> +++ b/test/code-sharing/Makefile
> @@ -0,0 +1,11 @@
> +OBJS=test_sharing
> +
> +all: $(OBJS)
> +.PHONY: all
> +
> +run: all
> +       ./test_sharing.sh
> +
> +clean:
> +       rm -f $(OBJS) test-*.out
> +.PHONY: clean
> diff --git a/test/code-sharing/test_sharing.c b/test/code-sharing/test_sharing.c
> new file mode 100644
> index 0000000..43553e3
> --- /dev/null
> +++ b/test/code-sharing/test_sharing.c
> @@ -0,0 +1,90 @@
> +/*
> + * Copyright (c) 2015, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <inttypes.h>
> +#include <signal.h>
> +
> +/*
> + * This binary sleeps continuously, but prints the current physical address for
> + * main() to a file (in the current directory, named like test-$pid.out) every
> + * time it receives SIGTERM.
> + */
> +
> +FILE *out;
> +
> +int main(int argc, char *argv);
> +
> +void error(char *error) {
> +       fprintf(out, "ERROR %d: %s\n", errno, error);
> +       printf("ERROR %d: %s\n", errno, error);
> +       fflush(out);
> +       exit(1);
> +}
> +
> +void print_pa(int signal) {
> +       int fd;
> +       uint64_t entry;
> +
> +       //ensure main()'s page is present
> +       fd = open("/proc/self/mem", O_RDONLY);
> +       if (!fd)
> +               error("Failed to open /proc/self/mem");
> +       if (lseek(fd, (unsigned long) &main, SEEK_SET) == -1)
> +               error("Failed to seek in /proc/self/mem");
> +       if (read(fd, &entry, sizeof(entry)) != sizeof(entry))
> +               error("Failed to read in /proc/self/mem");
> +       close(fd);
> +
> +       //read PA for main()
> +       fd = open("/proc/self/pagemap", O_RDONLY);
> +       if (!fd)
> +               error("Failed to open /proc/self/pagemap");
> +       if (lseek64(fd, ((long long) &main / getpagesize()) * sizeof(entry), SEEK_SET) == -1)
> +               error("Failed to seek in /proc/self/pagemap");
> +       if (read(fd, &entry, sizeof(entry)) != sizeof(entry))
> +               error("Failed to read in /proc/self/pagemap");
> +       close(fd);
> +
> +       fprintf(out, "PA for main() in pid %d: 0x%" PRIx64 "\n", getpid(), entry & 0x3fffffffffffff);
> +       fflush(out);
> +}
> +
> +int main(int argc, char *argv) {
> +       char out_filename[50];
> +       struct sigaction sig = {
> +               .sa_handler = print_pa
> +       };
> +
> +       if (sprintf(out_filename, "test-%d.out", getpid()) < 0) {
> +               printf("ERROR: Unable to write log filename\n");
> +               return 1;
> +       }
> +       out = fopen(out_filename, "a");
> +       if (!out) {
> +               printf("ERROR: Unable to open log file\n");
> +               return 1;
> +       }
> +
> +       sigaction(SIGTERM, &sig, NULL);
> +
> +       while (1) {
> +               sleep(1);
> +       }
> +}
> diff --git a/test/code-sharing/test_sharing.sh b/test/code-sharing/test_sharing.sh
> new file mode 100755
> index 0000000..1c4233b
> --- /dev/null
> +++ b/test/code-sharing/test_sharing.sh
> @@ -0,0 +1,63 @@
> +# Copyright (c) 2015, The Linux Foundation. All rights reserved.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 2 and
> +# only version 2 as published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +
> +set -ex
> +
> +if [[ -f ./test_sharing ]]; then
> +  cp ./test_sharing /tmp/test_sharing
> +elif [[ ! -f /tmp/test_sharing ]]; then
> +  echo "ERROR: Please copy the test_sharing binary from test/code-sharing to" \
> +    "/tmp/test_sharing if not running from the build directory"
> +  exit 1
> +fi
> +chmod +x /tmp/test_sharing
> +
> +# Start pid0 (will never be criu dumped)
> +cat /dev/null | setsid /tmp/test_sharing &> /dev/null &
> +pid0=$!
> +
> +# Start pid1 (will be the first process criu dumped)
> +cat /dev/null | setsid /tmp/test_sharing &> /dev/null &
> +pid1=$!
> +
> +mkdir /tmp/criudump-$pid1
> +criu dump -t $pid1 -v4 -o dump-$pid1.log -D /tmp/criudump-$pid1 || echo "Error: 'criu dump' exited with nonzero exit code!"
> +
> +# Start pid2 (will be the second process criu dumped)
> +cat /dev/null | setsid /tmp/test_sharing &> /dev/null &
> +pid2=$!
> +
> +mkdir /tmp/criudump-$pid2
> +criu dump -t $pid2 -v4 -o dump-$pid2.log -D /tmp/criudump-$pid2 || echo "Error: 'criu dump' exited with nonzero exit code!"
> +
> +criu restore -v2 -d -R -D /tmp/criudump-$pid1
> +criu restore -v2 -d -R -D /tmp/criudump-$pid2
> +
> +# Start pid3 (is not criu dumped)
> +cat /dev/null | setsid /tmp/test_sharing &> /dev/null &
> +pid3=$!
> +
> +sleep 1s #ensure pid3 is started before sending SIGTERM
> +# Sent SIGTERM to cause all processes to write out the PA of main()
> +kill -SIGTERM $pid0 $pid1 $pid2 $pid3
> +sleep 1s
> +
> +set +x
> +echo "pid0: $(cat test-$pid0.out)"
> +echo "pid1: $(cat test-$pid1.out)"
> +echo "pid2: $(cat test-$pid2.out)"
> +echo "pid3: $(cat test-$pid3.out)"
> +
> +echo "We would expect that pid's 0, 1, 2, 3 would all have the same PA, as they" \
> +  "would if they were not criu dumped (note that pids 0 and 3 share the same" \
> +  "PA since they were not)."
> +
> +kill -SIGKILL $pid0 $pid1 $pid2 $pid3
> --
> Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu


More information about the CRIU mailing list