Commit 0b1b8151 authored by Andrey Vagin's avatar Andrey Vagin Committed by Pavel Emelyanov

dump: use breakpoints instead of tracing syscalls (v2)

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: 's avatarAndrey Vagin <avagin@openvz.org>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent e46a9f6b
......@@ -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
......@@ -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
#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;
}
......@@ -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
......@@ -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 */
......
......@@ -70,6 +70,8 @@ struct parasite_init_args {
int log_level;
struct rt_sigframe *sigframe;
void *sigreturn_addr;
};
struct parasite_unmap_args {
......
......@@ -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");
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment