[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