forked from rpms/glibc
357 lines
11 KiB
Diff
357 lines
11 KiB
Diff
|
commit dfe9aa91564c1bf2a23b5589a5db42f9da5d29b5
|
||
|
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||
|
Date: Tue Nov 19 17:17:05 2019 -0300
|
||
|
|
||
|
support: Add support_process_state_wait
|
||
|
|
||
|
It allows parent process to wait for child state using a polling
|
||
|
strategy over procfs on Linux. The polling is used over ptrace to
|
||
|
avoid the need to handle signals on the target pid and to handle some
|
||
|
system specific limitation (such as YAMA).
|
||
|
|
||
|
The polling has some limitations, such as resource consumption due
|
||
|
the procfs read in a loop and the lack of synchronization after the
|
||
|
state is obtained.
|
||
|
|
||
|
The interface idea is to simplify some sleep synchronization waitid
|
||
|
tests and is to reduce timeouts by replacing arbitrary waits.
|
||
|
|
||
|
diff --git a/support/Makefile b/support/Makefile
|
||
|
index 79d03bd6bfe02540..117cfdd4f22fc405 100644
|
||
|
--- a/support/Makefile
|
||
|
+++ b/support/Makefile
|
||
|
@@ -58,6 +58,7 @@ libsupport-routines = \
|
||
|
support_format_hostent \
|
||
|
support_format_netent \
|
||
|
support_isolate_in_subprocess \
|
||
|
+ support_process_state \
|
||
|
support_ptrace \
|
||
|
support_openpty \
|
||
|
support_paths \
|
||
|
@@ -90,6 +91,7 @@ libsupport-routines = \
|
||
|
xfopen \
|
||
|
xfork \
|
||
|
xftruncate \
|
||
|
+ xgetline \
|
||
|
xgetsockname \
|
||
|
xlisten \
|
||
|
xlseek \
|
||
|
@@ -217,6 +219,7 @@ tests = \
|
||
|
tst-support_capture_subprocess \
|
||
|
tst-support_descriptors \
|
||
|
tst-support_format_dns_packet \
|
||
|
+ tst-support-process_state \
|
||
|
tst-support_quote_blob \
|
||
|
tst-support_quote_string \
|
||
|
tst-support_record_failure \
|
||
|
diff --git a/support/process_state.h b/support/process_state.h
|
||
|
new file mode 100644
|
||
|
index 0000000000000000..6c19afdbb7462277
|
||
|
--- /dev/null
|
||
|
+++ b/support/process_state.h
|
||
|
@@ -0,0 +1,43 @@
|
||
|
+/* Wait for process state.
|
||
|
+ Copyright (C) 2020 Free Software Foundation, Inc.
|
||
|
+ This file is part of the GNU C Library.
|
||
|
+
|
||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||
|
+ modify it under the terms of the GNU Lesser General Public
|
||
|
+ License as published by the Free Software Foundation; either
|
||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||
|
+
|
||
|
+ The GNU C Library 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
|
||
|
+ Lesser General Public License for more details.
|
||
|
+
|
||
|
+ You should have received a copy of the GNU Lesser General Public
|
||
|
+ License along with the GNU C Library; if not, see
|
||
|
+ <https://www.gnu.org/licenses/>. */
|
||
|
+
|
||
|
+#ifndef SUPPORT_PROCESS_STATE_H
|
||
|
+#define SUPPORT_PROCESS_STATE_H
|
||
|
+
|
||
|
+#include <sys/types.h>
|
||
|
+
|
||
|
+enum support_process_state
|
||
|
+{
|
||
|
+ support_process_state_running = 0x01, /* R (running). */
|
||
|
+ support_process_state_sleeping = 0x02, /* S (sleeping). */
|
||
|
+ support_process_state_disk_sleep = 0x04, /* D (disk sleep). */
|
||
|
+ support_process_state_stopped = 0x08, /* T (stopped). */
|
||
|
+ support_process_state_tracing_stop = 0x10, /* t (tracing stop). */
|
||
|
+ support_process_state_dead = 0x20, /* X (dead). */
|
||
|
+ support_process_state_zombie = 0x40, /* Z (zombie). */
|
||
|
+ support_process_state_parked = 0x80, /* P (parked). */
|
||
|
+};
|
||
|
+
|
||
|
+/* Wait for process PID to reach state STATE. It can be a combination of
|
||
|
+ multiple possible states ('process_state_running | process_state_sleeping')
|
||
|
+ where the function return when any of these state are observed.
|
||
|
+ For an invalid state not represented by SUPPORT_PROCESS_STATE, it fallbacks
|
||
|
+ to a 2 second sleep. */
|
||
|
+void support_process_state_wait (pid_t pid, enum support_process_state state);
|
||
|
+
|
||
|
+#endif
|
||
|
diff --git a/support/support_process_state.c b/support/support_process_state.c
|
||
|
new file mode 100644
|
||
|
index 0000000000000000..76dc798728ece0d9
|
||
|
--- /dev/null
|
||
|
+++ b/support/support_process_state.c
|
||
|
@@ -0,0 +1,92 @@
|
||
|
+/* Wait for process state.
|
||
|
+ Copyright (C) 2020 Free Software Foundation, Inc.
|
||
|
+ This file is part of the GNU C Library.
|
||
|
+
|
||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||
|
+ modify it under the terms of the GNU Lesser General Public
|
||
|
+ License as published by the Free Software Foundation; either
|
||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||
|
+
|
||
|
+ The GNU C Library 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
|
||
|
+ Lesser General Public License for more details.
|
||
|
+
|
||
|
+ You should have received a copy of the GNU Lesser General Public
|
||
|
+ License along with the GNU C Library; if not, see
|
||
|
+ <https://www.gnu.org/licenses/>. */
|
||
|
+
|
||
|
+#include <stdlib.h>
|
||
|
+#include <time.h>
|
||
|
+#include <string.h>
|
||
|
+
|
||
|
+#include <array_length.h>
|
||
|
+
|
||
|
+#include <support/process_state.h>
|
||
|
+#include <support/xstdio.h>
|
||
|
+#include <support/check.h>
|
||
|
+
|
||
|
+void
|
||
|
+support_process_state_wait (pid_t pid, enum support_process_state state)
|
||
|
+{
|
||
|
+#ifdef __linux__
|
||
|
+ /* For Linux it does a polling check on /proc/<pid>/status checking on
|
||
|
+ third field. */
|
||
|
+
|
||
|
+ /* It mimics the kernel states from fs/proc/array.c */
|
||
|
+ static const struct process_states
|
||
|
+ {
|
||
|
+ enum support_process_state s;
|
||
|
+ char v;
|
||
|
+ } process_states[] = {
|
||
|
+ { support_process_state_running, 'R' },
|
||
|
+ { support_process_state_sleeping, 'S' },
|
||
|
+ { support_process_state_disk_sleep, 'D' },
|
||
|
+ { support_process_state_stopped, 'T' },
|
||
|
+ { support_process_state_tracing_stop, 't' },
|
||
|
+ { support_process_state_dead, 'X' },
|
||
|
+ { support_process_state_zombie, 'Z' },
|
||
|
+ { support_process_state_parked, 'P' },
|
||
|
+ };
|
||
|
+
|
||
|
+ char spath[sizeof ("/proc/" + 3) * sizeof (pid_t) + sizeof ("/status") + 1];
|
||
|
+ snprintf (spath, sizeof (spath), "/proc/%i/status", pid);
|
||
|
+
|
||
|
+ FILE *fstatus = xfopen (spath, "r");
|
||
|
+ char *line = NULL;
|
||
|
+ size_t linesiz = 0;
|
||
|
+
|
||
|
+ for (;;)
|
||
|
+ {
|
||
|
+ char cur_state = -1;
|
||
|
+ while (xgetline (&line, &linesiz, fstatus) != -1)
|
||
|
+ if (strncmp (line, "State:", strlen ("State:")) == 0)
|
||
|
+ {
|
||
|
+ TEST_COMPARE (sscanf (line, "%*s %c", &cur_state), 1);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ /* Fallback to nanosleep for invalid state. */
|
||
|
+ if (cur_state == -1)
|
||
|
+ break;
|
||
|
+
|
||
|
+ for (size_t i = 0; i < array_length (process_states); ++i)
|
||
|
+ if (state & process_states[i].s && cur_state == process_states[i].v)
|
||
|
+ {
|
||
|
+ free (line);
|
||
|
+ xfclose (fstatus);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ rewind (fstatus);
|
||
|
+ fflush (fstatus);
|
||
|
+
|
||
|
+ if (nanosleep (&(struct timespec) { 0, 10000000 }, NULL) != 0)
|
||
|
+ FAIL_EXIT1 ("nanosleep: %m");
|
||
|
+ }
|
||
|
+
|
||
|
+ free (line);
|
||
|
+ xfclose (fstatus);
|
||
|
+ /* Fallback to nanosleep if an invalid state is found. */
|
||
|
+#endif
|
||
|
+ nanosleep (&(struct timespec) { 2, 0 }, NULL);
|
||
|
+}
|
||
|
diff --git a/support/tst-support-process_state.c b/support/tst-support-process_state.c
|
||
|
new file mode 100644
|
||
|
index 0000000000000000..3fc103ab9205ddb0
|
||
|
--- /dev/null
|
||
|
+++ b/support/tst-support-process_state.c
|
||
|
@@ -0,0 +1,105 @@
|
||
|
+/* Wait for process state tests.
|
||
|
+ Copyright (C) 2020 Free Software Foundation, Inc.
|
||
|
+ This file is part of the GNU C Library.
|
||
|
+
|
||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||
|
+ modify it under the terms of the GNU Lesser General Public
|
||
|
+ License as published by the Free Software Foundation; either
|
||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||
|
+
|
||
|
+ The GNU C Library 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
|
||
|
+ Lesser General Public License for more details.
|
||
|
+
|
||
|
+ You should have received a copy of the GNU Lesser General Public
|
||
|
+ License along with the GNU C Library; if not, see
|
||
|
+ <https://www.gnu.org/licenses/>. */
|
||
|
+
|
||
|
+#include <stdio.h>
|
||
|
+#include <sys/wait.h>
|
||
|
+#include <unistd.h>
|
||
|
+#include <errno.h>
|
||
|
+
|
||
|
+#include <support/test-driver.h>
|
||
|
+#include <support/process_state.h>
|
||
|
+#include <support/check.h>
|
||
|
+#include <support/xsignal.h>
|
||
|
+#include <support/xunistd.h>
|
||
|
+
|
||
|
+#ifndef WEXITED
|
||
|
+# define WEXITED 0
|
||
|
+#endif
|
||
|
+
|
||
|
+static void
|
||
|
+sigusr1_handler (int signo)
|
||
|
+{
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+test_child (void)
|
||
|
+{
|
||
|
+ xsignal (SIGUSR1, sigusr1_handler);
|
||
|
+
|
||
|
+ raise (SIGSTOP);
|
||
|
+
|
||
|
+ TEST_COMPARE (pause (), -1);
|
||
|
+ TEST_COMPARE (errno, EINTR);
|
||
|
+
|
||
|
+ while (1)
|
||
|
+ asm ("");
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+do_test (void)
|
||
|
+{
|
||
|
+ pid_t pid = xfork ();
|
||
|
+ if (pid == 0)
|
||
|
+ {
|
||
|
+ test_child ();
|
||
|
+ _exit (127);
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Adding process_state_tracing_stop ('t') allows the test to work under
|
||
|
+ trace programs such as ptrace. */
|
||
|
+ enum support_process_state stop_state = support_process_state_stopped
|
||
|
+ | support_process_state_tracing_stop;
|
||
|
+
|
||
|
+ if (test_verbose)
|
||
|
+ printf ("info: waiting pid %d, state_stopped/state_tracing_stop\n",
|
||
|
+ (int) pid);
|
||
|
+ support_process_state_wait (pid, stop_state);
|
||
|
+
|
||
|
+ if (kill (pid, SIGCONT) != 0)
|
||
|
+ FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
|
||
|
+
|
||
|
+ if (test_verbose)
|
||
|
+ printf ("info: waiting pid %d, state_sleeping\n", (int) pid);
|
||
|
+ support_process_state_wait (pid, support_process_state_sleeping);
|
||
|
+
|
||
|
+ if (kill (pid, SIGUSR1) != 0)
|
||
|
+ FAIL_RET ("kill (%d, SIGUSR1): %m\n", pid);
|
||
|
+
|
||
|
+ if (test_verbose)
|
||
|
+ printf ("info: waiting pid %d, state_running\n", (int) pid);
|
||
|
+ support_process_state_wait (pid, support_process_state_running);
|
||
|
+
|
||
|
+ if (kill (pid, SIGKILL) != 0)
|
||
|
+ FAIL_RET ("kill (%d, SIGKILL): %m\n", pid);
|
||
|
+
|
||
|
+ if (test_verbose)
|
||
|
+ printf ("info: waiting pid %d, state_zombie\n", (int) pid);
|
||
|
+ support_process_state_wait (pid, support_process_state_zombie);
|
||
|
+
|
||
|
+ siginfo_t info;
|
||
|
+ int r = waitid (P_PID, pid, &info, WEXITED);
|
||
|
+ TEST_COMPARE (r, 0);
|
||
|
+ TEST_COMPARE (info.si_signo, SIGCHLD);
|
||
|
+ TEST_COMPARE (info.si_code, CLD_KILLED);
|
||
|
+ TEST_COMPARE (info.si_status, SIGKILL);
|
||
|
+ TEST_COMPARE (info.si_pid, pid);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+#include <support/test-driver.c>
|
||
|
diff --git a/support/xgetline.c b/support/xgetline.c
|
||
|
new file mode 100644
|
||
|
index 0000000000000000..180bc2db95a9c5d4
|
||
|
--- /dev/null
|
||
|
+++ b/support/xgetline.c
|
||
|
@@ -0,0 +1,33 @@
|
||
|
+/* fopen with error checking.
|
||
|
+ Copyright (C) 2020 Free Software Foundation, Inc.
|
||
|
+ This file is part of the GNU C Library.
|
||
|
+
|
||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||
|
+ modify it under the terms of the GNU Lesser General Public
|
||
|
+ License as published by the Free Software Foundation; either
|
||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||
|
+
|
||
|
+ The GNU C Library 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
|
||
|
+ Lesser General Public License for more details.
|
||
|
+
|
||
|
+ You should have received a copy of the GNU Lesser General Public
|
||
|
+ License along with the GNU C Library; if not, see
|
||
|
+ <https://www.gnu.org/licenses/>. */
|
||
|
+
|
||
|
+#include <support/xstdio.h>
|
||
|
+#include <support/check.h>
|
||
|
+#include <errno.h>
|
||
|
+
|
||
|
+ssize_t
|
||
|
+xgetline (char **lineptr, size_t *n, FILE *stream)
|
||
|
+{
|
||
|
+ int old_errno = errno;
|
||
|
+ errno = 0;
|
||
|
+ size_t ret = getline (lineptr, n, stream);
|
||
|
+ if (!feof (stream) && ferror (stream))
|
||
|
+ FAIL_EXIT1 ("getline failed: %m");
|
||
|
+ errno = old_errno;
|
||
|
+ return ret;
|
||
|
+}
|
||
|
diff --git a/support/xstdio.h b/support/xstdio.h
|
||
|
index e7d0274474706380..9446b1f27b0f881e 100644
|
||
|
--- a/support/xstdio.h
|
||
|
+++ b/support/xstdio.h
|
||
|
@@ -27,6 +27,8 @@ __BEGIN_DECLS
|
||
|
FILE *xfopen (const char *path, const char *mode);
|
||
|
void xfclose (FILE *);
|
||
|
|
||
|
+ssize_t xgetline (char **lineptr, size_t *n, FILE *stream);
|
||
|
+
|
||
|
__END_DECLS
|
||
|
|
||
|
#endif /* SUPPORT_XSTDIO_H */
|