xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/testsuite/gdb.threads/slow-waitpid.c (revision 2dd295436a0082eb4f8d294f4aa73c223413d0f2)
1 /* This testcase is part of GDB, the GNU debugger.
2 
3    Copyright 2018-2020 Free Software Foundation, Inc.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 /* This file contains a library that can be preloaded into GDB on Linux
19    using the LD_PRELOAD technique.
20 
21    The library intercepts calls to WAITPID and SIGSUSPEND in order to
22    simulate the behaviour of a heavily loaded kernel.
23 
24    When GDB wants to stop all threads in an inferior each thread is sent a
25    SIGSTOP, GDB will then wait for the signal to be received by the thread
26    with a waitpid call.
27 
28    If the kernel is slow in either delivering the signal, or making the
29    result available to the waitpid call then GDB will enter a sigsuspend
30    call in order to wait for the inferior threads to change state, this is
31    signalled to GDB with a SIGCHLD.
32 
33    A bug in GDB meant that in some cases we would deadlock during this
34    process.  This was rarely seen as the kernel is usually quick at
35    delivering signals and making the results available to waitpid, so quick
36    that GDB would gather the statuses from all inferior threads in the
37    original pass.
38 
39    The idea in this library is to rate limit calls to waitpid (where pid is
40    -1 and the WNOHANG option is set) so that only 1 per second can return
41    an answer.  Any additional calls will report that no threads are
42    currently ready.  This should match the behaviour we see on a slow
43    kernel.
44 
45    However, given that usually when using this library, the kernel does
46    have the waitpid result ready this means that the kernel will never send
47    GDB a SIGCHLD.  This means that when GDB enters sigsuspend it will block
48    forever.  Alternatively, if GDB enters its polling loop the lack of
49    SIGCHLD means that we will never see an event on the child threads.  To
50    resolve these problems the library intercepts calls to sigsuspend and
51    forces the call to exit if there is a pending waitpid result.  Also,
52    when we know that there's a waitpid result that we've ignored, we create
53    a new thread which, after a short delay, will send GDB a SIGCHLD.  */
54 
55 #define _GNU_SOURCE
56 
57 #include <sys/types.h>
58 #include <sys/wait.h>
59 #include <sys/time.h>
60 #include <stdlib.h>
61 #include <stdio.h>
62 #include <dlfcn.h>
63 #include <string.h>
64 #include <stdarg.h>
65 #include <signal.h>
66 #include <errno.h>
67 #include <pthread.h>
68 #include <unistd.h>
69 
70 /* Logging.  */
71 
72 static void
73 log_msg (const char *fmt, ...)
74 {
75 #ifdef LOGGING
76   va_list ap;
77 
78   va_start (ap, fmt);
79   vfprintf (stderr, fmt, ap);
80   va_end (ap);
81 #endif /* LOGGING */
82 }
83 
84 /* Error handling, message and exit.  */
85 
86 static void
87 error (const char *fmt, ...)
88 {
89   va_list ap;
90 
91   va_start (ap, fmt);
92   vfprintf (stderr, fmt, ap);
93   va_end (ap);
94 
95   exit (EXIT_FAILURE);
96 }
97 
98 /* Cache the result of a waitpid call that has not been reported back to
99    GDB yet.  We only ever cache a single result.  Once we have a result
100    cached then later calls to waitpid with the WNOHANG option will return a
101    result of 0.  */
102 
103 static struct
104 {
105   /* Flag to indicate when we have a result cached.  */
106   int cached_p;
107 
108   /* The cached result fields from a waitpid call.  */
109   pid_t pid;
110   int wstatus;
111 } cached_wait_status;
112 
113 /* Lock to hold when modifying SIGNAL_THREAD_ACTIVE_P.  */
114 
115 static pthread_mutex_t thread_creation_lock_obj = PTHREAD_MUTEX_INITIALIZER;
116 #define thread_creation_lock (&thread_creation_lock_obj)
117 
118 /* This flag is only modified while holding the THREAD_CREATION_LOCK mutex.
119    When this flag is true then there is a signal thread alive that will be
120    sending a SIGCHLD at some point in the future.  */
121 
122 static int signal_thread_active_p;
123 
124 /* When we last allowed a waitpid to complete.  */
125 
126 static struct timeval last_waitpid_time = { 0, 0 };
127 
128 /* The number of seconds that must elapse between calls to waitpid where
129    the pid is -1 and the WNOHANG option is set.  If calls occur faster than
130    this then we force a result of 0 to be returned from waitpid.  */
131 
132 #define WAITPID_MIN_TIME (1)
133 
134 /* Return true (non-zero) if we should skip this call to waitpid, or false
135    (zero) if this waitpid call should be handled with a call to the "real"
136    waitpid function.  Allows 1 waitpid call per second.  */
137 
138 static int
139 should_skip_waitpid (void)
140 {
141   struct timeval *tv = &last_waitpid_time;
142   if (tv->tv_sec == 0)
143     {
144       if (gettimeofday (tv, NULL) < 0)
145 	error ("error: gettimeofday failed\n");
146       return 0; /* Don't skip.  */
147     }
148   else
149     {
150       struct timeval new_tv;
151 
152       if (gettimeofday (&new_tv, NULL) < 0)
153 	error ("error: gettimeofday failed\n");
154 
155       if ((new_tv.tv_sec - tv->tv_sec) < WAITPID_MIN_TIME)
156 	return 1; /* Skip.  */
157 
158       *tv = new_tv;
159     }
160 
161   /* Don't skip.  */
162   return 0;
163 }
164 
165 /* Perform a real waitpid call.  */
166 
167 static pid_t
168 real_waitpid (pid_t pid, int *wstatus, int options)
169 {
170   typedef pid_t (*fptr_t) (pid_t, int *, int);
171   static fptr_t real_func = NULL;
172 
173   if (real_func == NULL)
174     {
175       real_func = dlsym (RTLD_NEXT, "waitpid");
176       if (real_func == NULL)
177 	error ("error: failed to find real waitpid\n");
178     }
179 
180   return (*real_func) (pid, wstatus, options);
181 }
182 
183 /* Thread worker created when we cache a waitpid result.  Delays for a
184    short period of time and then sends SIGCHLD to the GDB process.  This
185    should trigger GDB to call waitpid again, at which point we will make
186    the cached waitpid result available.  */
187 
188 static void*
189 send_sigchld_thread (void *arg)
190 {
191   /* Delay one second longer than WAITPID_MIN_TIME so that there can be no
192      chance that a call to SHOULD_SKIP_WAITPID will return true once the
193      SIGCHLD is delivered and handled.  */
194   sleep (WAITPID_MIN_TIME + 1);
195 
196   pthread_mutex_lock (thread_creation_lock);
197   signal_thread_active_p = 0;
198 
199   if (cached_wait_status.cached_p)
200     {
201       log_msg ("signal-thread: sending SIGCHLD\n");
202       kill (getpid (), SIGCHLD);
203     }
204 
205   pthread_mutex_unlock (thread_creation_lock);
206   return NULL;
207 }
208 
209 /* The waitpid entry point function.  */
210 
211 pid_t
212 waitpid (pid_t pid, int *wstatus, int options)
213 {
214   log_msg ("waitpid: waitpid (%d, %p, 0x%x)\n", pid, wstatus, options);
215 
216   if ((options & WNOHANG) != 0
217       && pid == -1
218       && should_skip_waitpid ())
219     {
220       if (!cached_wait_status.cached_p)
221 	{
222 	  /* Do the waitpid call, but hold the result back.  */
223 	  pid_t tmp_pid;
224 	  int tmp_wstatus;
225 
226 	  tmp_pid = real_waitpid (-1, &tmp_wstatus, options);
227 	  if (tmp_pid > 0)
228 	    {
229 	      log_msg ("waitpid: delaying waitpid result (pid = %d)\n",
230 		       tmp_pid);
231 
232 	      /* Cache the result.  */
233 	      cached_wait_status.pid = tmp_pid;
234 	      cached_wait_status.wstatus = tmp_wstatus;
235 	      cached_wait_status.cached_p = 1;
236 
237 	      /* Is there a thread around that will be sending a signal in
238 		 the near future?  The prevents us from creating one
239 		 thread per call to waitpid when the calls occur in a
240 		 sequence.  */
241 	      pthread_mutex_lock (thread_creation_lock);
242 	      if (!signal_thread_active_p)
243 		{
244 		  sigset_t old_ss, new_ss;
245 		  pthread_t thread_id;
246 		  pthread_attr_t attr;
247 
248 		  /* Create the new signal sending thread in detached
249 		     state.  This means that the thread doesn't need to be
250 		     pthread_join'ed.  Which is fine as there's no result
251 		     we care about.  */
252 		  pthread_attr_init (&attr);
253 		  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
254 
255 		  /* Ensure the signal sending thread has all signals
256 		     blocked.  We don't want any signals to GDB to be
257 		     handled in that thread.  */
258 		  sigfillset (&new_ss);
259 		  sigprocmask (SIG_BLOCK, &new_ss, &old_ss);
260 
261 		  log_msg ("waitpid: spawn thread to signal us\n");
262 		  if (pthread_create (&thread_id, &attr,
263 				      send_sigchld_thread, NULL) != 0)
264 		    error ("error: pthread_create failed\n");
265 
266 		  signal_thread_active_p = 1;
267 		  sigprocmask (SIG_SETMASK, &old_ss, NULL);
268 		  pthread_attr_destroy (&attr);
269 		}
270 
271 	      pthread_mutex_unlock (thread_creation_lock);
272 	    }
273 	}
274 
275       log_msg ("waitpid: skipping\n");
276       return 0;
277     }
278 
279   /* If we have a cached result that is a suitable reply for this call to
280      waitpid then send that cached result back now.  */
281   if (cached_wait_status.cached_p
282       && (pid == -1 || pid == cached_wait_status.pid))
283     {
284       pid_t pid;
285 
286       pid = cached_wait_status.pid;
287       log_msg ("waitpid: return cached result (%d)\n", pid);
288       *wstatus = cached_wait_status.wstatus;
289       cached_wait_status.cached_p = 0;
290       return pid;
291     }
292 
293   log_msg ("waitpid: real waitpid call\n");
294   return real_waitpid (pid, wstatus, options);
295 }
296 
297 /* Perform a real sigsuspend call.  */
298 
299 static int
300 real_sigsuspend (const sigset_t *mask)
301 {
302   typedef int (*fptr_t) (const sigset_t *);
303   static fptr_t real_func = NULL;
304 
305   if (real_func == NULL)
306     {
307       real_func = dlsym (RTLD_NEXT, "sigsuspend");
308       if (real_func == NULL)
309 	error ("error: failed to find real sigsuspend\n");
310     }
311 
312   return (*real_func) (mask);
313 }
314 
315 /* The sigsuspend entry point function.  */
316 
317 int
318 sigsuspend (const sigset_t *mask)
319 {
320   log_msg ("sigsuspend: sigsuspend (0x%p)\n", ((void *) mask));
321 
322   /* If SIGCHLD is _not_ in MASK, and is therefore deliverable, then if we
323      have a pending wait status pretend that a signal arrived.  We will
324      have a thread alive that is going to deliver a signal but doing this
325      will boost the speed as we don't have to wait for a signal.  If the
326      signal ends up being delivered then it should be harmless, we'll just
327      perform an additional waitpid call.   */
328   if (!sigismember (mask, SIGCHLD))
329     {
330       if (cached_wait_status.cached_p)
331 	{
332 	  log_msg ("sigsuspend: interrupt for cached waitstatus\n");
333 	  last_waitpid_time.tv_sec = 0;
334 	  last_waitpid_time.tv_usec = 0;
335 	  errno = EINTR;
336 	  return -1;
337 	}
338     }
339 
340   log_msg ("sigsuspend: real sigsuspend call\n");
341   return real_sigsuspend (mask);
342 }
343