[CRIU] [PATCH 2/3] dump: use breakpoints instead of tracing syscalls (v2)
Andrey Vagin
avagin at openvz.org
Wed Sep 17 08:12:03 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 36 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/restorer.h | 5 ++++
arch/arm/include/asm/restorer.h | 5 ++++
arch/x86/crtools.c | 46 +++++++++++++++++++++++++++++++++++++
arch/x86/include/asm/restorer.h | 3 +++
include/parasite-syscall.h | 1 +
include/parasite.h | 2 ++
parasite-syscall.c | 19 +++++++++++++++
pie/parasite.c | 8 ++++++-
8 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/arch/aarch64/include/asm/restorer.h b/arch/aarch64/include/asm/restorer.h
index f4d45e6..53c3847 100644
--- a/arch/aarch64/include/asm/restorer.h
+++ b/arch/aarch64/include/asm/restorer.h
@@ -108,4 +108,9 @@ static inline void restore_tls(tls_t *ptls)
asm("msr tpidr_el0, %0" : : "r" (*ptls));
}
+static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ return 0;
+}
+
#endif
diff --git a/arch/arm/include/asm/restorer.h b/arch/arm/include/asm/restorer.h
index 9461d12..36ce4e9 100644
--- a/arch/arm/include/asm/restorer.h
+++ b/arch/arm/include/asm/restorer.h
@@ -150,4 +150,9 @@ static inline void restore_tls(tls_t *ptls) {
);
}
+static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ return 0;
+}
+
#endif
diff --git a/arch/x86/crtools.c b/arch/x86/crtools.c
index b127934..1248975 100644
--- a/arch/x86/crtools.c
+++ b/arch/x86/crtools.c
@@ -1,6 +1,7 @@
#include <string.h>
#include <unistd.h>
#include <elf.h>
+#include <sys/user.h>
#include "asm/processor-flags.h"
#include "asm/restorer.h"
@@ -494,3 +495,48 @@ 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 ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ int ret;
+
+ /* Set a breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_FIRSTADDR]),
+ addr)) {
+ pr_err("Unable to setup a breakpoint\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;
+ }
+
+ return 1;
+}
+
diff --git a/arch/x86/include/asm/restorer.h b/arch/x86/include/asm/restorer.h
index 5512155..00d4977 100644
--- a/arch/x86/include/asm/restorer.h
+++ b/arch/x86/include/asm/restorer.h
@@ -145,4 +145,7 @@ int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, fpu_state_t *fpu_stat
static inline void restore_tls(tls_t *ptls) { (void)ptls; }
+int ptrace_set_breakpoint(pid_t pid, void *addr);
+
+
#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..26aca96 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,24 @@ static int parasite_fini_seized(struct parasite_ctl *ctl)
if (ret)
return -1;
+ /* Go to sigreturn as closer as we can */
+ ret = ptrace_set_breakpoint(pid, ctl->sigreturn_addr);
+ if (ret < 0)
+ return ret;
+ if (ret > 0) {
+ 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;
+ }
+ }
+
+ /* 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