]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/testsuite/gdb.threads/slow-waitpid.c
gdb: Don't drop SIGSTOP during stop_all_threads
[thirdparty/binutils-gdb.git] / gdb / testsuite / gdb.threads / slow-waitpid.c
CommitLineData
7010835a
AB
1/* This testcase is part of GDB, the GNU debugger.
2
3 Copyright 2018 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
72static void
73log_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
86static void
87error (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
103static 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
115static 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
122static int signal_thread_active_p;
123
124/* When we last allowed a waitpid to complete. */
125
126static 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
138static int
139should_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
167static pid_t
168real_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
188static void*
189send_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
211pid_t
212waitpid (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
299static int
300real_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
317int
318sigsuspend (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}