| 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 */ |