[CRIU] [PATCH 3/3] x86: use breakpoints instead of tracing syscalls
Andrew Vagin
avagin at parallels.com
Wed Sep 17 03:41:29 PDT 2014
On Wed, Sep 17, 2014 at 12:10:16PM +0400, Andrey Vagin wrote:
> Currently CRIU traces syscalls to catch a moment, when sigreturn() is
> called. Now we trace recv(cmd), close(logfd), close(cmdfd), sigreturn().
>
> We can reduce a number of steps by using hw breakpoints. A breakpoint is
> set before sigreturn, so we will need to trace only it.
>
> v2: In the first version a breakpoint is set after sigreturn. In this
> case we have a problem with signals. If a process has pending signals,
> it will start to precess them after exiting from sigreturn(), but before
> returning to userspace. So the breakpoint will not be triggered.
>
> And at the end Here are a few numbers how we catch sigreturn.
> Before this patch criu executes 57 syscalls and gets 12 signals.
The right number of syscalls before this patch is 36.
> With this patch criu executes 18 syscalls and gets 5 signals.
>
> Signed-off-by: Andrey Vagin <avagin at openvz.org>
> ---
> arch/aarch64/include/asm/parasite-syscall.h | 4 ++
> arch/arm/include/asm/parasite-syscall.h | 6 ++-
> arch/x86/crtools.c | 72 +++++++++++++++++++++++++++++
> arch/x86/include/asm/parasite-syscall.h | 2 +
> include/parasite-syscall.h | 1 +
> include/parasite.h | 2 +
> parasite-syscall.c | 7 +++
> pie/parasite.c | 8 +++-
> 8 files changed, 100 insertions(+), 2 deletions(-)
>
> diff --git a/arch/aarch64/include/asm/parasite-syscall.h b/arch/aarch64/include/asm/parasite-syscall.h
> index 0c07121..f3f8d90 100644
> --- a/arch/aarch64/include/asm/parasite-syscall.h
> +++ b/arch/aarch64/include/asm/parasite-syscall.h
> @@ -15,4 +15,8 @@ void *mmap_seized(struct parasite_ctl *ctl,
> void *addr, size_t length, int prot,
> int flags, int fd, off_t offset);
>
> +static inline int parasite_stop_before_sigreturn(pid_t pid, struct parasite_ctl *ctl)
> +{
> + return 0;
> +}
> #endif
> diff --git a/arch/arm/include/asm/parasite-syscall.h b/arch/arm/include/asm/parasite-syscall.h
> index 0c66bf9..9ca5345 100644
> --- a/arch/arm/include/asm/parasite-syscall.h
> +++ b/arch/arm/include/asm/parasite-syscall.h
> @@ -1,7 +1,6 @@
> #ifndef __CR_ASM_PARASITE_SYSCALL_H__
> #define __CR_ASM_PARASITE_SYSCALL_H__
>
> -
> #define ARCH_SI_TRAP TRAP_BRKPT
>
>
> @@ -15,4 +14,9 @@ void *mmap_seized(struct parasite_ctl *ctl,
> void *addr, size_t length, int prot,
> int flags, int fd, off_t offset);
>
> +static inline int parasite_stop_before_sigreturn(pid_t pid, struct parasite_ctl *ctl)
> +{
> + return 0;
> +}
> +
> #endif
> diff --git a/arch/x86/crtools.c b/arch/x86/crtools.c
> index b127934..529cf90 100644
> --- a/arch/x86/crtools.c
> +++ b/arch/x86/crtools.c
> @@ -1,6 +1,8 @@
> #include <string.h>
> #include <unistd.h>
> #include <elf.h>
> +#include <sys/user.h>
> +#include <sys/wait.h>
>
> #include "asm/processor-flags.h"
> #include "asm/restorer.h"
> @@ -494,3 +496,73 @@ int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, fpu_state_t *fpu_stat
> return 0;
> }
>
> +/* Copied from the gdb header gdb/nat/x86-dregs.h */
> +
> +/* Debug registers' indices. */
> +#define DR_FIRSTADDR 0
> +#define DR_LASTADDR 3
> +#define DR_NADDR 4 /* The number of debug address registers. */
> +#define DR_STATUS 6 /* Index of debug status register (DR6). */
> +#define DR_CONTROL 7 /* Index of debug control register (DR7). */
> +
> +#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */
> +#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */
> +#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */
> +
> +/* Locally enable the break/watchpoint in the I'th debug register. */
> +#define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
> +
> +int parasite_stop_before_sigreturn(pid_t pid, struct parasite_ctl *ctl)
> +{
> + int ret, status;
> +
> + /* Set a breakpoint */
> + if (ptrace(PTRACE_POKEUSER, pid,
> + offsetof(struct user, u_debugreg[DR_FIRSTADDR]),
> + ctl->sigreturn_addr)) {
> + pr_err("Unable to setup a breakpoint\n");
> + return -1;
> + }
> +
> + /* Clean up the DR_STATUS register */
> + if (ptrace(PTRACE_POKEUSER, pid,
> + offsetof(struct user, u_debugreg[DR_STATUS]), 0)) {
> + pr_err("Unable to clean the DR_STATUS register\n");
> + return -1;
> + }
> +
> + /* Enable the breakpoint */
> + if (ptrace(PTRACE_POKEUSER, pid,
> + offsetof(struct user, u_debugreg[DR_CONTROL]),
> + X86_DR_LOCAL_ENABLE(DR_FIRSTADDR))) {
> + pr_err("Unable to enable the breakpoint\n");
> + return -1;
> + }
> +
> + ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
> + if (ret) {
> + pr_perror("Unable to restart the stopped tracee process");
> + return -1;
> + }
> +
> + pid = wait4(pid, &status, __WALL, NULL);
> + if (pid == -1) {
> + pr_perror("wait4 failed");
> + return -1;
> + }
> +
> + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) {
> + pr_err("Task is in unexpected state: %x\n", status);
> + return -1;
> + }
> +
> + /* Disable all breakpoints */
> + if (ptrace(PTRACE_POKEUSER, pid,
> + offsetof(struct user, u_debugreg[DR_CONTROL]), 0)) {
> + pr_err("Unable to clean the DR_CONTROL register\n");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> diff --git a/arch/x86/include/asm/parasite-syscall.h b/arch/x86/include/asm/parasite-syscall.h
> index 4d56cb0..e1d13a5 100644
> --- a/arch/x86/include/asm/parasite-syscall.h
> +++ b/arch/x86/include/asm/parasite-syscall.h
> @@ -17,4 +17,6 @@ void *mmap_seized(struct parasite_ctl *ctl,
> void *addr, size_t length, int prot,
> int flags, int fd, off_t offset);
>
> +int parasite_stop_before_sigreturn(pid_t pid, struct parasite_ctl *ctl);
> +
> #endif
> diff --git a/include/parasite-syscall.h b/include/parasite-syscall.h
> index 71534da..fbc5614 100644
> --- a/include/parasite-syscall.h
> +++ b/include/parasite-syscall.h
> @@ -30,6 +30,7 @@ struct parasite_ctl {
> struct pid pid;
> void *remote_map;
> void *local_map;
> + void *sigreturn_addr; /* A place for the breakpoint */
> unsigned long map_length;
>
> /* thread leader data */
> diff --git a/include/parasite.h b/include/parasite.h
> index d6d243d..774eba0 100644
> --- a/include/parasite.h
> +++ b/include/parasite.h
> @@ -70,6 +70,8 @@ struct parasite_init_args {
> int log_level;
>
> struct rt_sigframe *sigframe;
> +
> + void *sigreturn_addr;
> };
>
> struct parasite_unmap_args {
> diff --git a/parasite-syscall.c b/parasite-syscall.c
> index 1c1a2cb..dd10e56 100644
> --- a/parasite-syscall.c
> +++ b/parasite-syscall.c
> @@ -528,6 +528,7 @@ static int parasite_init_daemon(struct parasite_ctl *ctl)
> goto err;
> }
>
> + ctl->sigreturn_addr = args->sigreturn_addr;
> ctl->daemonized = true;
> pr_info("Parasite %d has been switched to daemon mode\n", pid);
> return 0;
> @@ -868,6 +869,12 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
> if (ret)
> return -1;
>
> + /* Go to sigreturn as closer as we can */
> + ret = parasite_stop_before_sigreturn(pid, ctl);
> + if (ret)
> + return ret;
> +
> + /* Start tracing syscalls */
> ret = ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
> if (ret) {
> pr_perror("ptrace");
> diff --git a/pie/parasite.c b/pie/parasite.c
> index c30a7fb..bab67f8 100644
> --- a/pie/parasite.c
> +++ b/pie/parasite.c
> @@ -438,6 +438,11 @@ static int __parasite_daemon_wait_msg(struct ctl_msg *m)
> return -1;
> }
>
> +static noinline void fini_sigreturn(unsigned long new_sp)
> +{
> + ARCH_RT_SIGRETURN(new_sp);
> +}
> +
> static int fini()
> {
> unsigned long new_sp;
> @@ -454,7 +459,7 @@ static int fini()
> sys_close(tsock);
> log_set_fd(-1);
>
> - ARCH_RT_SIGRETURN(new_sp);
> + fini_sigreturn(new_sp);
>
> BUG();
>
> @@ -561,6 +566,7 @@ static noinline __used int parasite_init_daemon(void *data)
> struct parasite_init_args *args = data;
> int ret;
>
> + args->sigreturn_addr = fini_sigreturn;
> sigframe = args->sigframe;
>
> tsock = sys_socket(PF_UNIX, SOCK_SEQPACKET, 0);
> --
> 1.9.3
>
More information about the CRIU
mailing list