[CRIU] [PATCH v2 2/2] Add ptrace-wait
Christopher Covington
cov at codeaurora.org
Mon Aug 24 07:23:56 PDT 2015
ptrace-wait allows the user to wait for any PID, regardless of
parent-child relationship, to exit, by using ptrace. Furthermore,
using perf_event_open, it allows the user to wait a specified number
of instructions rather than the full length. This can be used, for
example, to dump a checkpoint every N instructions in a program
execution, and to run each restored checkpoint for N instructions.
Given a fast system on which the dumps are performed and one or more
slow system(s) on which the restores are performed, this enables
"fast forwarding" through an application's execution.
Signed-off-by: Christopher Covington <cov at codeaurora.org>
---
.gitignore | 1 +
Makefile | 5 +-
ptrace-wait.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 195 insertions(+), 1 deletion(-)
create mode 100644 ptrace-wait.c
diff --git a/.gitignore b/.gitignore
index 9125f1f..00949ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
+ptrace-wait
stopexec
diff --git a/Makefile b/Makefile
index 33c0b5a..7b459d6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,10 @@
-OBJS=stopexec
+OBJS=stopexec ptrace-wait
all: $(OBJS)
+ptrace-wait: ptrace-wait.c
+ $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
+
stopexec: stopexec.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
diff --git a/ptrace-wait.c b/ptrace-wait.c
new file mode 100644
index 0000000..b17b8f3
--- /dev/null
+++ b/ptrace-wait.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 and only version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * ptrace-wait - Wait for any process to finish, or for it to run a given
+ * number of instructions.
+ *
+ * Written by Aaron Lindsay and Christopher Covington.
+ *
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <asm/unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#include <linux/perf_event.h>
+#include <linux/ptrace.h>
+
+static int setup_count(long long count, pid_t pid)
+{
+ struct perf_event_attr pe;
+ int pfd = -1;
+
+ memset(&pe, 0, sizeof(struct perf_event_attr));
+ pe.type = PERF_TYPE_HARDWARE;
+ pe.size = sizeof(struct perf_event_attr);
+ pe.config = PERF_COUNT_HW_INSTRUCTIONS;
+ pe.sample_period = count;
+ pe.inherit = 1;
+ pe.pinned = 1;
+ pe.exclude_kernel = 1;
+ pe.exclude_hv = 1;
+ pe.watermark = 1;
+ pe.wakeup_watermark = 1;
+ printf("Waiting %lld instructions for PID %d\n", count, pid);
+ pfd = syscall(__NR_perf_event_open, &pe, pid, -1, -1, 0);
+ if (pfd < 0) {
+ perror(NULL);
+ printf("Error setting up instruction counting\n");
+ exit(EXIT_FAILURE);
+ }
+ fcntl(pfd, F_SETOWN, pid);
+ fcntl(pfd, F_SETFL, fcntl(pfd, F_GETFL) | FASYNC);
+ ioctl(pfd, PERF_EVENT_IOC_RESET, 0);
+ return pfd;
+}
+
+int main(int argc, char *argv[])
+{
+ pid_t pid, waitedpid;
+ int fd, status, ret = 0;
+ char *end;
+ long long count;
+
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Usage: %s pid [instruction-count]\n"
+ "\n"
+ "Wait for any process to finish or run a given number of instructions.\n",
+ argv[0]);
+ return -1;
+ }
+
+ errno = 0;
+ pid = strtol(argv[1], &end, 10);
+ if (errno) {
+ perror("Invalid PID");
+ return -1;
+ } else if (end == argv[1] || *end != '\0') {
+ fprintf(stderr, "Invalid PID\n");
+ return -1;
+ }
+
+ if (ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_TRACEEXIT)) {
+ fprintf(stderr, "PTRACE_SEIZE returned error, %d\n", errno);
+ return -1;
+ }
+
+ /* TODO: Use cgroup if there is more than one thread involved */
+ if (argc == 3) {
+ errno = 0;
+ count = strtoll(argv[2], &end, 0);
+ if (errno) {
+ perror("Invalid instruction count");
+ return -1;
+ } else if (end == argv[2] || *end != '\0') {
+ fprintf(stderr, "Invalid instruction count\n");
+ return -1;
+ }
+ fd = setup_count(count, pid);
+ kill(pid, SIGCONT);
+ }
+
+ while (1) {
+ waitedpid = waitpid(pid, &status, 0);
+ if (waitedpid != pid) {
+ fprintf(stderr, "Error waiting for pid %d: %d\n", pid,
+ waitedpid);
+ ret = -1;
+ break;
+ }
+
+ /* Exit if the process we're waiting on exited */
+ if ((status>>8) == (SIGTRAP | (PTRACE_EVENT_EXIT<<8)))
+ break;
+
+ if (WIFSTOPPED(status)) {
+ if ((status>>16 != PTRACE_EVENT_STOP) ||
+ (WSTOPSIG(status) != SIGSTOP &&
+ WSTOPSIG(status) != SIGTSTP &&
+ WSTOPSIG(status) != SIGTTIN &&
+ WSTOPSIG(status) != SIGTTOU)) {
+ if (argc == 3 && WSTOPSIG(status) == SIGIO) {
+ /*
+ * If we set up an instruction count,
+ * assume that any SIGIO is caused by
+ * the perf event and transform it into
+ * a SIGSTOP. Once CRIU has support
+ * for cgroup frozen processes, using
+ * that may be preferable to SIGSTOP.
+ */
+ ptrace(PTRACE_CONT, pid, 0, SIGSTOP);
+ break;
+ }
+
+ /*
+ * Handle signal-delivery-stop by
+ * passing along the signal to the
+ * tracee via PTRACE_CONT, as long as
+ * it wasn't a group-stop for a
+ * stopping signal.
+ */
+ ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
+ } else {
+ /*
+ * If we received a group-stop (like SIGSTOP),
+ * PTRACE_LISTEN instead of
+ * PTRACE_CONT. This allows the tracee to
+ * remain in the stopped state (as if it
+ * received SIGSTOP while not being traced),
+ * while allowing it to receive future SIGCONT
+ * signals. If we use PTRACE_CONT in the
+ * SIGSTOP case, the tracee will run instead of
+ * remaining in the stopped state as we expect.
+ * See the 'Group-stop' section in `man 2
+ * ptrace` for more information about this
+ * behavior.
+ */
+ ptrace(PTRACE_LISTEN, pid, 0, 0);
+ }
+ } else {
+ fprintf(stderr,
+ "Unexpected wakeup from waitpid for pid %d\n",
+ pid);
+ ret = -1;
+ break;
+ }
+ }
+
+ if (argc == 3) {
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & !FASYNC);
+ if (read(fd, &count, sizeof(count)) != sizeof(count))
+ fprintf(stderr, "Could not read from perf event\n");
+ else
+ printf("Counted %lld instructions\n", count);
+ close(fd);
+ }
+
+ printf("Detaching from pid %d\n", pid);
+ ptrace(PTRACE_DETACH, pid);
+
+ return ret;
+}
--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
More information about the CRIU
mailing list