[CRIU] [PATCH 3/3] x86: use breakpoints instead of tracing syscalls

Andrey Vagin avagin at openvz.org
Wed Sep 17 01:10:16 PDT 2014


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.
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