]>
Commit | Line | Data |
---|---|---|
51a5ef0f PS |
1 | http://sourceware.org/ml/gdb-patches/2010-09/msg00360.html |
2 | Subject: [patch 3/4]#3 linux-nat: Do not respawn signals | |
3 | ||
4 | Hi, | |
5 | ||
6 | linux-nat.c is fixed to never respawn signals; possibly keeping SIGSTOP | |
7 | pending, as is done in current in FSF gdbserver and as suggested by Pedro: | |
8 | http://sourceware.org/ml/gdb-patches/2010-08/msg00544.html | |
9 | ||
10 | The last linux-nat.c removed patch chunk comes from the initial implementation | |
11 | by Mark Kettenis: | |
12 | [PATCH] New Linux threads support | |
13 | http://sourceware.org/ml/gdb-patches/2000-09/msg00020.html | |
14 | 92280a75e017683bf8e4f339f4f85640b0700509 | |
15 | It gets in part reimplemented into the new stop_wait_callback <if (lp->step)> | |
16 | part and partially just not needed as currently GDB never drops the signals as | |
17 | it does not PTRACE_CONT the thread; signal is kept for processing: | |
18 | "RC: Not resuming sibling %s (has pending)\n" | |
19 | ||
20 | In stop_wait_callback I believe breakpoints cancellation is not needed here, | |
21 | it would be done later. | |
22 | ||
23 | ||
24 | The testcase sigstep-threads.exp was written to catch a regression-like | |
25 | appearance then the new <if (lp->step)> part of stop_wait_callback gets | |
26 | removed. Still the tecase fails even with FSF HEAD: | |
27 | ||
28 | 32 var++; /* step-1 */ | |
29 | (gdb) step | |
30 | Program received signal SIGUSR1, User defined signal 1. | |
31 | Program received signal SIGUSR1, User defined signal 1. | |
32 | 31 { /* step-0 */ | |
33 | ||
34 | There is no reason why it shouldn't stop on line 33, between line 32 and line | |
35 | 33 no signal would occur. Stepping of the current thread should not be | |
36 | affected by whatever happens in the other threads as select_event_lwp has: | |
37 | /* Give preference to any LWP that is being single-stepped. */ | |
38 | ||
39 | There is a problem that with FSF HEAD GDB does PTRACE_SINGLESTEP for thread A, | |
40 | PTRACE_CONT for thread B (because of set scheduler-locking off), thread B hits | |
41 | SIGUSR1, so GDB tkills thread A with SIGSTOP and it can receive SIGSTOP for | |
42 | thread A before the SIGTRAP for completed PTRACE_SINGLESTEP. At that moment | |
43 | select_event_lwp. forgets it was stepping thread A because there is no pending | |
44 | SIGTRAP event. currently_stepping still remembers thread A was stepping so it | |
45 | will later stop but as thread A was PTRACE_CONT-ed in the meantime it is too | |
46 | late. | |
47 | ||
48 | There is the new <if (lp->step)> part of stop_wait_callback to always track | |
49 | thread A is stepping. Due to different scheduling without this part the | |
50 | changed GDB would very rarely stop in this testcase otherwise, making it look | |
51 | as a regression. | |
52 | ||
53 | I have some another patch I may post separately as if multiple signals happen | |
54 | besides SIGTRAP GDB still may switch from thread A away (as not considering it | |
55 | stepping) to thread B for SIGUSR and accidentally PTRACE_CONT thread A. | |
56 | But I do not find this as a prerequisite for this patchset. | |
57 | ||
58 | ||
59 | ||
60 | Thanks, | |
61 | Jan | |
62 | ||
63 | ||
64 | gdb/ | |
65 | 2010-09-20 Jan Kratochvil <jan.kratochvil@redhat.com> | |
66 | ||
67 | * linux-nat.c (stop_wait_callback): New gdb_assert. Remove signals | |
68 | respawning; keep TP with SIGNALLED. New debugging message "SWC: | |
69 | Delayed SIGSTOP caught for %s.". Catch next signal if SIGSTOP has | |
70 | been caught and LP->STEP is set. | |
71 | (linux_nat_wait_1) <lp && lp->signalled>: Remove. | |
72 | ||
73 | gdb/testsuite/ | |
74 | 2010-09-20 Jan Kratochvil <jan.kratochvil@redhat.com> | |
75 | ||
76 | * gdb.threads/siginfo-threads.exp: New file. | |
77 | * gdb.threads/siginfo-threads.c: New file. | |
78 | * gdb.threads/sigstep-threads.exp: New file. | |
79 | * gdb.threads/sigstep-threads.c: New file. | |
80 | ||
f412e1b4 | 81 | Index: gdb-7.4.50.20111218/gdb/linux-nat.c |
51a5ef0f | 82 | =================================================================== |
f412e1b4 PS |
83 | --- gdb-7.4.50.20111218.orig/gdb/linux-nat.c 2011-12-19 01:25:42.000000000 +0100 |
84 | +++ gdb-7.4.50.20111218/gdb/linux-nat.c 2011-12-19 02:17:05.412607735 +0100 | |
85 | @@ -2843,6 +2843,8 @@ stop_wait_callback (struct lwp_info *lp, | |
51a5ef0f PS |
86 | { |
87 | int status; | |
88 | ||
89 | + gdb_assert (lp->resumed); | |
90 | + | |
91 | status = wait_lwp (lp); | |
92 | if (status == 0) | |
93 | return 0; | |
f412e1b4 | 94 | @@ -2868,110 +2870,61 @@ stop_wait_callback (struct lwp_info *lp, |
51a5ef0f PS |
95 | |
96 | if (WSTOPSIG (status) != SIGSTOP) | |
97 | { | |
6ed6bacf | 98 | - if (linux_nat_status_is_event (status)) |
51a5ef0f PS |
99 | - { |
100 | - /* If a LWP other than the LWP that we're reporting an | |
101 | - event for has hit a GDB breakpoint (as opposed to | |
102 | - some random trap signal), then just arrange for it to | |
103 | - hit it again later. We don't keep the SIGTRAP status | |
104 | - and don't forward the SIGTRAP signal to the LWP. We | |
105 | - will handle the current event, eventually we will | |
106 | - resume all LWPs, and this one will get its breakpoint | |
107 | - trap again. | |
108 | - | |
109 | - If we do not do this, then we run the risk that the | |
110 | - user will delete or disable the breakpoint, but the | |
111 | - thread will have already tripped on it. */ | |
112 | - | |
113 | - /* Save the trap's siginfo in case we need it later. */ | |
114 | - save_siginfo (lp); | |
115 | - | |
116 | - save_sigtrap (lp); | |
117 | - | |
6ed6bacf | 118 | - /* Now resume this LWP and get the SIGSTOP event. */ |
51a5ef0f PS |
119 | - errno = 0; |
120 | - ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); | |
121 | - if (debug_linux_nat) | |
122 | - { | |
123 | - fprintf_unfiltered (gdb_stdlog, | |
124 | - "PTRACE_CONT %s, 0, 0 (%s)\n", | |
125 | - target_pid_to_str (lp->ptid), | |
126 | - errno ? safe_strerror (errno) : "OK"); | |
127 | - | |
128 | - fprintf_unfiltered (gdb_stdlog, | |
129 | - "SWC: Candidate SIGTRAP event in %s\n", | |
130 | - target_pid_to_str (lp->ptid)); | |
131 | - } | |
132 | - /* Hold this event/waitstatus while we check to see if | |
6ed6bacf | 133 | - there are any more (we still want to get that SIGSTOP). */ |
51a5ef0f PS |
134 | - stop_wait_callback (lp, NULL); |
135 | + /* The thread was stopped with a signal other than SIGSTOP. */ | |
136 | ||
137 | - /* Hold the SIGTRAP for handling by linux_nat_wait. If | |
138 | - there's another event, throw it back into the | |
6ed6bacf | 139 | - queue. */ |
51a5ef0f PS |
140 | - if (lp->status) |
141 | - { | |
142 | - if (debug_linux_nat) | |
143 | - fprintf_unfiltered (gdb_stdlog, | |
144 | - "SWC: kill %s, %s\n", | |
145 | - target_pid_to_str (lp->ptid), | |
146 | - status_to_str ((int) status)); | |
147 | - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status)); | |
148 | - } | |
f412e1b4 | 149 | - |
6ed6bacf | 150 | - /* Save the sigtrap event. */ |
51a5ef0f PS |
151 | - lp->status = status; |
152 | - return 0; | |
153 | - } | |
154 | - else | |
155 | - { | |
156 | - /* The thread was stopped with a signal other than | |
6ed6bacf | 157 | - SIGSTOP, and didn't accidentally trip a breakpoint. */ |
f412e1b4 PS |
158 | + /* Save the trap's siginfo in case we need it later. */ |
159 | + save_siginfo (lp); | |
51a5ef0f PS |
160 | |
161 | - if (debug_linux_nat) | |
162 | - { | |
163 | - fprintf_unfiltered (gdb_stdlog, | |
164 | - "SWC: Pending event %s in %s\n", | |
165 | - status_to_str ((int) status), | |
166 | - target_pid_to_str (lp->ptid)); | |
167 | - } | |
6ed6bacf | 168 | - /* Now resume this LWP and get the SIGSTOP event. */ |
51a5ef0f PS |
169 | - errno = 0; |
170 | - ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); | |
171 | - if (debug_linux_nat) | |
172 | - fprintf_unfiltered (gdb_stdlog, | |
173 | - "SWC: PTRACE_CONT %s, 0, 0 (%s)\n", | |
174 | - target_pid_to_str (lp->ptid), | |
175 | - errno ? safe_strerror (errno) : "OK"); | |
f412e1b4 PS |
176 | + save_sigtrap (lp); |
177 | ||
51a5ef0f | 178 | - /* Hold this event/waitstatus while we check to see if |
6ed6bacf | 179 | - there are any more (we still want to get that SIGSTOP). */ |
51a5ef0f PS |
180 | - stop_wait_callback (lp, NULL); |
181 | + if (debug_linux_nat) | |
182 | + fprintf_unfiltered (gdb_stdlog, | |
183 | + "SWC: Pending event %s in %s\n", | |
184 | + status_to_str ((int) status), | |
185 | + target_pid_to_str (lp->ptid)); | |
186 | ||
187 | - /* If the lp->status field is still empty, use it to | |
188 | - hold this event. If not, then this event must be | |
189 | - returned to the event queue of the LWP. */ | |
190 | - if (lp->status) | |
191 | - { | |
192 | - if (debug_linux_nat) | |
193 | - { | |
194 | - fprintf_unfiltered (gdb_stdlog, | |
195 | - "SWC: kill %s, %s\n", | |
196 | - target_pid_to_str (lp->ptid), | |
197 | - status_to_str ((int) status)); | |
198 | - } | |
199 | - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status)); | |
200 | - } | |
201 | - else | |
202 | - lp->status = status; | |
203 | - return 0; | |
204 | - } | |
6ed6bacf | 205 | + /* Save the sigtrap event. */ |
51a5ef0f PS |
206 | + lp->status = status; |
207 | + gdb_assert (! lp->stopped); | |
208 | + gdb_assert (lp->signalled); | |
209 | + lp->stopped = 1; | |
210 | } | |
211 | else | |
212 | { | |
213 | /* We caught the SIGSTOP that we intended to catch, so | |
214 | there's no SIGSTOP pending. */ | |
215 | - lp->stopped = 1; | |
216 | + | |
217 | + if (debug_linux_nat) | |
218 | + fprintf_unfiltered (gdb_stdlog, | |
219 | + "SWC: Delayed SIGSTOP caught for %s.\n", | |
220 | + target_pid_to_str (lp->ptid)); | |
221 | + | |
222 | + if (lp->step) | |
223 | + { | |
224 | + /* LP->STATUS is 0 here. That means SIGTRAP from | |
225 | + PTRACE_SINGLESTEP still has to be delivered for this inferior | |
226 | + stop. Catching the SIGTRAP event is important to prevent | |
227 | + starvation in select_event_lwp. */ | |
228 | + | |
229 | + registers_changed (); | |
230 | + linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)), | |
231 | + 1, TARGET_SIGNAL_0); | |
232 | + if (debug_linux_nat) | |
233 | + fprintf_unfiltered (gdb_stdlog, | |
234 | + "SWC: %s %s, 0, 0 (discard SIGSTOP)\n", | |
235 | + "PTRACE_SINGLESTEP", | |
236 | + target_pid_to_str (lp->ptid)); | |
237 | + | |
238 | + lp->stopped = 0; | |
239 | + gdb_assert (lp->resumed); | |
240 | + stop_wait_callback (lp, NULL); | |
241 | + gdb_assert (lp->stopped); | |
242 | + } | |
243 | + else | |
244 | + lp->stopped = 1; | |
245 | + | |
246 | + /* Reset SIGNALLED only after the stop_wait_callback call above as | |
247 | + it does gdb_assert on SIGNALLED. */ | |
248 | lp->signalled = 0; | |
249 | } | |
250 | } | |
f412e1b4 | 251 | @@ -3627,54 +3580,6 @@ retry: |
51a5ef0f PS |
252 | lp = NULL; |
253 | } | |
254 | ||
f412e1b4 | 255 | - if (lp && lp->signalled && lp->last_resume_kind != resume_stop) |
51a5ef0f PS |
256 | - { |
257 | - /* A pending SIGSTOP may interfere with the normal stream of | |
258 | - events. In a typical case where interference is a problem, | |
259 | - we have a SIGSTOP signal pending for LWP A while | |
260 | - single-stepping it, encounter an event in LWP B, and take the | |
261 | - pending SIGSTOP while trying to stop LWP A. After processing | |
262 | - the event in LWP B, LWP A is continued, and we'll never see | |
263 | - the SIGTRAP associated with the last time we were | |
264 | - single-stepping LWP A. */ | |
265 | - | |
266 | - /* Resume the thread. It should halt immediately returning the | |
267 | - pending SIGSTOP. */ | |
268 | - registers_changed (); | |
f412e1b4 PS |
269 | - if (linux_nat_prepare_to_resume != NULL) |
270 | - linux_nat_prepare_to_resume (lp); | |
51a5ef0f PS |
271 | - linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)), |
272 | - lp->step, TARGET_SIGNAL_0); | |
273 | - if (debug_linux_nat) | |
274 | - fprintf_unfiltered (gdb_stdlog, | |
275 | - "LLW: %s %s, 0, 0 (expect SIGSTOP)\n", | |
276 | - lp->step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT", | |
277 | - target_pid_to_str (lp->ptid)); | |
278 | - lp->stopped = 0; | |
279 | - gdb_assert (lp->resumed); | |
280 | - | |
281 | - /* Catch the pending SIGSTOP. */ | |
282 | - status = lp->status; | |
283 | - lp->status = 0; | |
284 | - | |
285 | - stop_wait_callback (lp, NULL); | |
286 | - | |
287 | - /* If the lp->status field isn't empty, we caught another signal | |
288 | - while flushing the SIGSTOP. Return it back to the event | |
289 | - queue of the LWP, as we already have an event to handle. */ | |
290 | - if (lp->status) | |
291 | - { | |
292 | - if (debug_linux_nat) | |
293 | - fprintf_unfiltered (gdb_stdlog, | |
294 | - "LLW: kill %s, %s\n", | |
295 | - target_pid_to_str (lp->ptid), | |
296 | - status_to_str (lp->status)); | |
297 | - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status)); | |
298 | - } | |
299 | - | |
300 | - lp->status = status; | |
301 | - } | |
302 | - | |
303 | if (!target_can_async_p ()) | |
304 | { | |
305 | /* Causes SIGINT to be passed on to the attached process. */ | |
f412e1b4 | 306 | Index: gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/siginfo-threads.c |
51a5ef0f PS |
307 | =================================================================== |
308 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
f412e1b4 | 309 | +++ gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/siginfo-threads.c 2011-12-19 02:16:35.236720272 +0100 |
51a5ef0f PS |
310 | @@ -0,0 +1,447 @@ |
311 | +/* This testcase is part of GDB, the GNU debugger. | |
312 | + | |
313 | + Copyright 2010 Free Software Foundation, Inc. | |
314 | + | |
315 | + This program is free software; you can redistribute it and/or modify | |
316 | + it under the terms of the GNU General Public License as published by | |
317 | + the Free Software Foundation; either version 3 of the License, or | |
318 | + (at your option) any later version. | |
319 | + | |
320 | + This program is distributed in the hope that it will be useful, | |
321 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
322 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
323 | + GNU General Public License for more details. | |
324 | + | |
325 | + You should have received a copy of the GNU General Public License | |
326 | + along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
327 | + | |
328 | +#define _GNU_SOURCE | |
329 | +#include <pthread.h> | |
330 | +#include <stdio.h> | |
331 | +#include <limits.h> | |
332 | +#include <errno.h> | |
333 | +#include <stdlib.h> | |
334 | +#include <string.h> | |
335 | +#include <assert.h> | |
336 | +#include <sys/types.h> | |
337 | +#include <signal.h> | |
338 | +#include <unistd.h> | |
339 | +#include <asm/unistd.h> | |
340 | + | |
341 | +#define gettid() syscall (__NR_gettid) | |
342 | +#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig) | |
343 | + | |
344 | +/* Terminate always in the main task, it can lock up with SIGSTOPped GDB | |
345 | + otherwise. */ | |
346 | +#define TIMEOUT (gettid () == getpid() ? 10 : 15) | |
347 | + | |
348 | +static pid_t thread1_tid; | |
349 | +static pthread_cond_t thread1_tid_cond = PTHREAD_COND_INITIALIZER; | |
350 | +static pthread_mutex_t thread1_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; | |
351 | +static int thread1_sigusr1_hit; | |
352 | +static int thread1_sigusr2_hit; | |
353 | + | |
354 | +static pid_t thread2_tid; | |
355 | +static pthread_cond_t thread2_tid_cond = PTHREAD_COND_INITIALIZER; | |
356 | +static pthread_mutex_t thread2_tid_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; | |
357 | +static int thread2_sigusr1_hit; | |
358 | +static int thread2_sigusr2_hit; | |
359 | + | |
360 | +static pthread_mutex_t terminate_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; | |
361 | + | |
362 | +/* Do not use alarm as it would create a ptrace event which would hang up us if | |
363 | + we are being traced by GDB which we stopped ourselves. */ | |
364 | + | |
365 | +static void timed_mutex_lock (pthread_mutex_t *mutex) | |
366 | +{ | |
367 | + int i; | |
368 | + struct timespec start, now; | |
369 | + | |
370 | + i = clock_gettime (CLOCK_MONOTONIC, &start); | |
371 | + assert (i == 0); | |
372 | + | |
373 | + do | |
374 | + { | |
375 | + i = pthread_mutex_trylock (mutex); | |
376 | + if (i == 0) | |
377 | + return; | |
378 | + assert (i == EBUSY); | |
379 | + | |
380 | + i = clock_gettime (CLOCK_MONOTONIC, &now); | |
381 | + assert (i == 0); | |
382 | + assert (now.tv_sec >= start.tv_sec); | |
383 | + } | |
384 | + while (now.tv_sec - start.tv_sec < TIMEOUT); | |
385 | + | |
386 | + fprintf (stderr, "Timed out waiting for internal lock!\n"); | |
387 | + exit (EXIT_FAILURE); | |
388 | +} | |
389 | + | |
390 | +static void | |
391 | +handler (int signo, siginfo_t *siginfo, void *exception) | |
392 | +{ | |
393 | + int *varp; | |
394 | + | |
395 | + assert (siginfo->si_signo == signo); | |
396 | + assert (siginfo->si_code == SI_TKILL); | |
397 | + assert (siginfo->si_pid == getpid ()); | |
398 | + | |
399 | + if (gettid () == thread1_tid) | |
400 | + { | |
401 | + if (signo == SIGUSR1) | |
402 | + varp = &thread1_sigusr1_hit; | |
403 | + else if (signo == SIGUSR2) | |
404 | + varp = &thread1_sigusr2_hit; | |
405 | + else | |
406 | + assert (0); | |
407 | + } | |
408 | + else if (gettid () == thread2_tid) | |
409 | + { | |
410 | + if (signo == SIGUSR1) | |
411 | + varp = &thread2_sigusr1_hit; | |
412 | + else if (signo == SIGUSR2) | |
413 | + varp = &thread2_sigusr2_hit; | |
414 | + else | |
415 | + assert (0); | |
416 | + } | |
417 | + else | |
418 | + assert (0); | |
419 | + | |
420 | + if (*varp) | |
421 | + { | |
422 | + fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo, | |
423 | + (unsigned long) gettid ()); | |
424 | + exit (EXIT_FAILURE); | |
425 | + } | |
426 | + *varp = 1; | |
427 | +} | |
428 | + | |
429 | +static void * | |
430 | +thread1_func (void *unused) | |
431 | +{ | |
432 | + int i; | |
433 | + | |
434 | + timed_mutex_lock (&thread1_tid_mutex); | |
435 | + | |
436 | + /* THREAD1_TID_MUTEX must be already locked to avoid race. */ | |
437 | + thread1_tid = gettid (); | |
438 | + | |
439 | + i = pthread_cond_signal (&thread1_tid_cond); | |
440 | + assert (i == 0); | |
441 | + i = pthread_mutex_unlock (&thread1_tid_mutex); | |
442 | + assert (i == 0); | |
443 | + | |
444 | + /* Be sure the "t (tracing stop)" test can proceed for both threads. */ | |
445 | + timed_mutex_lock (&terminate_mutex); | |
446 | + i = pthread_mutex_unlock (&terminate_mutex); | |
447 | + assert (i == 0); | |
448 | + | |
449 | + if (! thread1_sigusr1_hit) | |
450 | + { | |
451 | + fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n"); | |
452 | + exit (EXIT_FAILURE); | |
453 | + } | |
454 | + if (! thread1_sigusr2_hit) | |
455 | + { | |
456 | + fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n"); | |
457 | + exit (EXIT_FAILURE); | |
458 | + } | |
459 | + | |
460 | + return NULL; | |
461 | +} | |
462 | + | |
463 | +static void * | |
464 | +thread2_func (void *unused) | |
465 | +{ | |
466 | + int i; | |
467 | + | |
468 | + timed_mutex_lock (&thread2_tid_mutex); | |
469 | + | |
470 | + /* THREAD2_TID_MUTEX must be already locked to avoid race. */ | |
471 | + thread2_tid = gettid (); | |
472 | + | |
473 | + i = pthread_cond_signal (&thread2_tid_cond); | |
474 | + assert (i == 0); | |
475 | + i = pthread_mutex_unlock (&thread2_tid_mutex); | |
476 | + assert (i == 0); | |
477 | + | |
478 | + /* Be sure the "t (tracing stop)" test can proceed for both threads. */ | |
479 | + timed_mutex_lock (&terminate_mutex); | |
480 | + i = pthread_mutex_unlock (&terminate_mutex); | |
481 | + assert (i == 0); | |
482 | + | |
483 | + if (! thread2_sigusr1_hit) | |
484 | + { | |
485 | + fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n"); | |
486 | + exit (EXIT_FAILURE); | |
487 | + } | |
488 | + if (! thread2_sigusr2_hit) | |
489 | + { | |
490 | + fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n"); | |
491 | + exit (EXIT_FAILURE); | |
492 | + } | |
493 | + | |
494 | + return NULL; | |
495 | +} | |
496 | + | |
497 | +static const char * | |
498 | +proc_string (const char *filename, const char *line) | |
499 | +{ | |
500 | + FILE *f; | |
501 | + static char buf[LINE_MAX]; | |
502 | + size_t line_len = strlen (line); | |
503 | + | |
504 | + f = fopen (filename, "r"); | |
505 | + if (f == NULL) | |
506 | + { | |
507 | + fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line, | |
508 | + strerror (errno)); | |
509 | + exit (EXIT_FAILURE); | |
510 | + } | |
511 | + while (errno = 0, fgets (buf, sizeof (buf), f)) | |
512 | + { | |
513 | + char *s; | |
514 | + | |
515 | + s = strchr (buf, '\n'); | |
516 | + assert (s != NULL); | |
517 | + *s = 0; | |
518 | + | |
519 | + if (strncmp (buf, line, line_len) != 0) | |
520 | + continue; | |
521 | + | |
522 | + if (fclose (f)) | |
523 | + { | |
524 | + fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line, | |
525 | + strerror (errno)); | |
526 | + exit (EXIT_FAILURE); | |
527 | + } | |
528 | + | |
529 | + return &buf[line_len]; | |
530 | + } | |
531 | + if (errno != 0) | |
532 | + { | |
533 | + fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno)); | |
534 | + exit (EXIT_FAILURE); | |
535 | + } | |
536 | + fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line); | |
537 | + exit (EXIT_FAILURE); | |
538 | +} | |
539 | + | |
540 | +static unsigned long | |
541 | +proc_ulong (const char *filename, const char *line) | |
542 | +{ | |
543 | + const char *s = proc_string (filename, line); | |
544 | + long retval; | |
545 | + char *end; | |
546 | + | |
547 | + errno = 0; | |
548 | + retval = strtol (s, &end, 10); | |
549 | + if (retval < 0 || retval >= LONG_MAX || (end && *end)) | |
550 | + { | |
551 | + fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval, | |
552 | + strerror (errno)); | |
553 | + exit (EXIT_FAILURE); | |
554 | + } | |
555 | + return retval; | |
556 | +} | |
557 | + | |
558 | +static void | |
559 | +state_wait (pid_t process, const char *wanted) | |
560 | +{ | |
561 | + char *filename; | |
562 | + int i; | |
563 | + struct timespec start, now; | |
564 | + const char *state; | |
565 | + | |
566 | + i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process); | |
567 | + assert (i > 0); | |
568 | + | |
569 | + i = clock_gettime (CLOCK_MONOTONIC, &start); | |
570 | + assert (i == 0); | |
571 | + | |
572 | + do | |
573 | + { | |
574 | + state = proc_string (filename, "State:\t"); | |
575 | + | |
576 | + /* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0 | |
577 | + has changed "T (tracing stop)" to "t (tracing stop)". Make the GDB | |
578 | + testcase backward compatible with older Linux kernels. */ | |
579 | + if (strcmp (state, "T (tracing stop)") == 0) | |
580 | + state = "t (tracing stop)"; | |
581 | + | |
582 | + if (strcmp (state, wanted) == 0) | |
583 | + { | |
584 | + free (filename); | |
585 | + return; | |
586 | + } | |
587 | + | |
588 | + if (sched_yield ()) | |
589 | + { | |
590 | + perror ("sched_yield()"); | |
591 | + exit (EXIT_FAILURE); | |
592 | + } | |
593 | + | |
594 | + i = clock_gettime (CLOCK_MONOTONIC, &now); | |
595 | + assert (i == 0); | |
596 | + assert (now.tv_sec >= start.tv_sec); | |
597 | + } | |
598 | + while (now.tv_sec - start.tv_sec < TIMEOUT); | |
599 | + | |
600 | + fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n", | |
601 | + (unsigned long) process, wanted, state); | |
602 | + exit (EXIT_FAILURE); | |
603 | +} | |
604 | + | |
605 | +static volatile pid_t tracer = 0; | |
606 | +static pthread_t thread1, thread2; | |
607 | + | |
608 | +static void | |
609 | +cleanup (void) | |
610 | +{ | |
611 | + printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer); | |
612 | + | |
613 | + if (tracer) | |
614 | + { | |
615 | + int i; | |
616 | + int tracer_save = tracer; | |
617 | + | |
618 | + tracer = 0; | |
619 | + | |
620 | + i = kill (tracer_save, SIGCONT); | |
621 | + assert (i == 0); | |
622 | + } | |
623 | +} | |
624 | + | |
625 | +int | |
626 | +main (int argc, char **argv) | |
627 | +{ | |
628 | + int i; | |
629 | + int standalone = 0; | |
630 | + struct sigaction act; | |
631 | + | |
632 | + if (argc == 2 && strcmp (argv[1], "-s") == 0) | |
633 | + standalone = 1; | |
634 | + else | |
635 | + assert (argc == 1); | |
636 | + | |
637 | + setbuf (stdout, NULL); | |
638 | + | |
639 | + timed_mutex_lock (&thread1_tid_mutex); | |
640 | + timed_mutex_lock (&thread2_tid_mutex); | |
641 | + | |
642 | + timed_mutex_lock (&terminate_mutex); | |
643 | + | |
644 | + errno = 0; | |
645 | + memset (&act, 0, sizeof (act)); | |
646 | + act.sa_sigaction = handler; | |
647 | + act.sa_flags = SA_RESTART | SA_SIGINFO; | |
648 | + i = sigemptyset (&act.sa_mask); | |
649 | + assert_perror (errno); | |
650 | + assert (i == 0); | |
651 | + i = sigaction (SIGUSR1, &act, NULL); | |
652 | + assert_perror (errno); | |
653 | + assert (i == 0); | |
654 | + i = sigaction (SIGUSR2, &act, NULL); | |
655 | + assert_perror (errno); | |
656 | + assert (i == 0); | |
657 | + | |
658 | + i = pthread_create (&thread1, NULL, thread1_func, NULL); | |
659 | + assert (i == 0); | |
660 | + | |
661 | + i = pthread_create (&thread2, NULL, thread2_func, NULL); | |
662 | + assert (i == 0); | |
663 | + | |
664 | + if (!standalone) | |
665 | + { | |
666 | + tracer = proc_ulong ("/proc/self/status", "TracerPid:\t"); | |
667 | + if (tracer == 0) | |
668 | + { | |
669 | + fprintf (stderr, "The testcase must be run by GDB!\n"); | |
670 | + exit (EXIT_FAILURE); | |
671 | + } | |
672 | + if (tracer != getppid ()) | |
673 | + { | |
674 | + fprintf (stderr, "The testcase parent must be our GDB tracer!\n"); | |
675 | + exit (EXIT_FAILURE); | |
676 | + } | |
677 | + } | |
678 | + | |
679 | + /* SIGCONT our debugger in the case of our crash as we would deadlock | |
680 | + otherwise. */ | |
681 | + | |
682 | + atexit (cleanup); | |
683 | + | |
684 | + printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer); | |
685 | + | |
686 | + if (tracer) | |
687 | + { | |
688 | + i = kill (tracer, SIGSTOP); | |
689 | + assert (i == 0); | |
690 | + state_wait (tracer, "T (stopped)"); | |
691 | + } | |
692 | + | |
693 | + /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex) and so | |
694 | + they could not trigger the signals before GDB gets unstopped later. | |
695 | + Threads get resumed at pthread_cond_wait below. Use `while' loops for | |
696 | + protection against spurious pthread_cond_wait wakeups. */ | |
697 | + | |
698 | + printf ("Waiting till the threads initialize their TIDs.\n"); | |
699 | + | |
700 | + while (thread1_tid == 0) | |
701 | + { | |
702 | + i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex); | |
703 | + assert (i == 0); | |
704 | + } | |
705 | + | |
706 | + while (thread2_tid == 0) | |
707 | + { | |
708 | + i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex); | |
709 | + assert (i == 0); | |
710 | + } | |
711 | + | |
712 | + printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n", | |
713 | + (unsigned long) thread1_tid, (unsigned long) thread2_tid, | |
714 | + (unsigned long) getpid ()); | |
715 | + | |
716 | + errno = 0; | |
717 | + i = tgkill (getpid (), thread1_tid, SIGUSR1); | |
718 | + assert_perror (errno); | |
719 | + assert (i == 0); | |
720 | + i = tgkill (getpid (), thread1_tid, SIGUSR2); | |
721 | + assert_perror (errno); | |
722 | + assert (i == 0); | |
723 | + i = tgkill (getpid (), thread2_tid, SIGUSR1); | |
724 | + assert_perror (errno); | |
725 | + assert (i == 0); | |
726 | + i = tgkill (getpid (), thread2_tid, SIGUSR2); | |
727 | + assert_perror (errno); | |
728 | + assert (i == 0); | |
729 | + | |
730 | + printf ("Waiting till the threads get trapped by the signals.\n"); | |
731 | + | |
732 | + if (tracer) | |
733 | + { | |
734 | + /* s390x-unknown-linux-gnu will fail with "R (running)". */ | |
735 | + | |
736 | + state_wait (thread1_tid, "t (tracing stop)"); | |
737 | + | |
738 | + state_wait (thread2_tid, "t (tracing stop)"); | |
739 | + } | |
740 | + | |
741 | + cleanup (); | |
742 | + | |
743 | + printf ("Joining the threads.\n"); | |
744 | + | |
745 | + i = pthread_mutex_unlock (&terminate_mutex); | |
746 | + assert (i == 0); | |
747 | + | |
748 | + i = pthread_join (thread1, NULL); | |
749 | + assert (i == 0); | |
750 | + | |
751 | + i = pthread_join (thread2, NULL); | |
752 | + assert (i == 0); | |
753 | + | |
754 | + printf ("Exiting.\n"); /* break-at-exit */ | |
755 | + | |
756 | + return EXIT_SUCCESS; | |
757 | +} | |
f412e1b4 | 758 | Index: gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/siginfo-threads.exp |
51a5ef0f PS |
759 | =================================================================== |
760 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
f412e1b4 | 761 | +++ gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/siginfo-threads.exp 2011-12-19 02:16:35.237720268 +0100 |
51a5ef0f PS |
762 | @@ -0,0 +1,94 @@ |
763 | +# Copyright 2010 Free Software Foundation, Inc. | |
764 | + | |
765 | +# This program is free software; you can redistribute it and/or modify | |
766 | +# it under the terms of the GNU General Public License as published by | |
767 | +# the Free Software Foundation; either version 3 of the License, or | |
768 | +# (at your option) any later version. | |
769 | +# | |
770 | +# This program is distributed in the hope that it will be useful, | |
771 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
772 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
773 | +# GNU General Public License for more details. | |
774 | +# | |
775 | +# You should have received a copy of the GNU General Public License | |
776 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
777 | + | |
778 | +set testfile "siginfo-threads" | |
779 | +set srcfile ${testfile}.c | |
780 | +set binfile ${objdir}/${subdir}/${testfile} | |
781 | +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" ${binfile} executable [list debug additional_flags=-lrt]] != "" } { | |
782 | + return -1 | |
783 | +} | |
784 | + | |
785 | +clean_restart $testfile | |
786 | + | |
787 | +if ![runto_main] { | |
788 | + return -1 | |
789 | +} | |
790 | + | |
791 | +# `nostop noprint pass' could in some cases report false PASS due to the | |
792 | +# (preempt 'handle') code path. | |
793 | + | |
794 | +gdb_test "handle SIGUSR1 stop print pass" "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR1\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*" | |
795 | +gdb_test "handle SIGUSR2 stop print pass" "Signal\[ \t\]+Stop\[ \t\]+Print\[ \t\]+Pass to program\[ \t\]+Description\r\nSIGUSR2\[ \t\]+Yes\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*" | |
796 | + | |
797 | +gdb_breakpoint [gdb_get_line_number "break-at-exit"] | |
798 | + | |
799 | +set test "get pid" | |
800 | +gdb_test_multiple "p getpid ()" $test { | |
801 | + -re " = (\[0-9\]+)\r\n$gdb_prompt $" { | |
802 | + set pid $expect_out(1,string) | |
803 | + pass $test | |
804 | + } | |
805 | +} | |
806 | + | |
807 | +for {set sigcount 0} {$sigcount < 4} {incr sigcount} { | |
808 | + set test "catch signal $sigcount" | |
809 | + set sigusr "" | |
810 | + gdb_test_multiple "continue" $test { | |
811 | + -re "Program received signal SIGUSR(\[12\]), User defined signal \[12\]\\.\r\n.*\r\n$gdb_prompt $" { | |
812 | + set sigusr $expect_out(1,string) | |
813 | + pass $test | |
814 | + } | |
815 | + } | |
816 | + if {$sigusr == ""} { | |
817 | + return -1 | |
818 | + } | |
819 | + | |
820 | + set test "signal $sigcount si_signo" | |
821 | + if {$sigusr == 1} { | |
822 | + set signo 10 | |
823 | + } else { | |
824 | + set signo 12 | |
825 | + } | |
826 | + gdb_test_multiple {p $_siginfo.si_signo} $test { | |
827 | + -re " = $signo\r\n$gdb_prompt $" { | |
828 | + pass $test | |
829 | + } | |
830 | + -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" { | |
831 | + unsupported $test | |
832 | + } | |
833 | + } | |
834 | + | |
835 | + set test "signal $sigcount si_code is SI_TKILL" | |
836 | + gdb_test_multiple {p $_siginfo.si_code} $test { | |
837 | + -re " = -6\r\n$gdb_prompt $" { | |
838 | + pass $test | |
839 | + } | |
840 | + -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" { | |
841 | + unsupported $test | |
842 | + } | |
843 | + } | |
844 | + | |
845 | + set test "signal $sigcount si_pid" | |
846 | + gdb_test_multiple {p $_siginfo._sifields._kill.si_pid} $test { | |
847 | + -re " = $pid\r\n$gdb_prompt $" { | |
848 | + pass $test | |
849 | + } | |
850 | + -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" { | |
851 | + unsupported $test | |
852 | + } | |
853 | + } | |
854 | +} | |
855 | + | |
856 | +gdb_continue_to_breakpoint break-at-exit ".*break-at-exit.*" | |
f412e1b4 | 857 | Index: gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/sigstep-threads.c |
51a5ef0f PS |
858 | =================================================================== |
859 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
f412e1b4 | 860 | +++ gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/sigstep-threads.c 2011-12-19 02:16:35.237720268 +0100 |
51a5ef0f PS |
861 | @@ -0,0 +1,54 @@ |
862 | +/* This testcase is part of GDB, the GNU debugger. | |
863 | + | |
864 | + Copyright 2010 Free Software Foundation, Inc. | |
865 | + | |
866 | + This program is free software; you can redistribute it and/or modify | |
867 | + it under the terms of the GNU General Public License as published by | |
868 | + the Free Software Foundation; either version 3 of the License, or | |
869 | + (at your option) any later version. | |
870 | + | |
871 | + This program is distributed in the hope that it will be useful, | |
872 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
873 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
874 | + GNU General Public License for more details. | |
875 | + | |
876 | + You should have received a copy of the GNU General Public License | |
877 | + along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
878 | + | |
879 | +#include <pthread.h> | |
880 | +#include <assert.h> | |
881 | +#include <signal.h> | |
882 | + | |
883 | +#include <asm/unistd.h> | |
884 | +#include <unistd.h> | |
885 | +#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, (tgid), (tid), (sig)) | |
886 | +#define gettid() syscall (__NR_gettid) | |
887 | + | |
888 | +static volatile int var; | |
889 | + | |
890 | +static void | |
891 | +handler (int signo) /* step-0 */ | |
892 | +{ /* step-0 */ | |
893 | + var++; /* step-1 */ | |
894 | + tgkill (getpid (), gettid (), SIGUSR1); /* step-2 */ | |
895 | +} | |
896 | + | |
897 | +static void * | |
898 | +start (void *arg) | |
899 | +{ | |
900 | + signal (SIGUSR1, handler); | |
901 | + tgkill (getpid (), gettid (), SIGUSR1); | |
902 | + assert (0); | |
903 | + | |
904 | + return NULL; | |
905 | +} | |
906 | + | |
907 | +int | |
908 | +main (void) | |
909 | +{ | |
910 | + pthread_t thread; | |
911 | + | |
912 | + pthread_create (&thread, NULL, start, NULL); | |
913 | + start (NULL); /* main-start */ | |
914 | + return 0; | |
915 | +} | |
f412e1b4 | 916 | Index: gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/sigstep-threads.exp |
51a5ef0f PS |
917 | =================================================================== |
918 | --- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
f412e1b4 | 919 | +++ gdb-7.4.50.20111218/gdb/testsuite/gdb.threads/sigstep-threads.exp 2011-12-19 02:16:35.237720268 +0100 |
51a5ef0f PS |
920 | @@ -0,0 +1,74 @@ |
921 | +# Copyright 2010 Free Software Foundation, Inc. | |
922 | + | |
923 | +# This program is free software; you can redistribute it and/or modify | |
924 | +# it under the terms of the GNU General Public License as published by | |
925 | +# the Free Software Foundation; either version 3 of the License, or | |
926 | +# (at your option) any later version. | |
927 | +# | |
928 | +# This program is distributed in the hope that it will be useful, | |
929 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
930 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
931 | +# GNU General Public License for more details. | |
932 | +# | |
933 | +# You should have received a copy of the GNU General Public License | |
934 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
935 | + | |
936 | +set testfile sigstep-threads | |
937 | +set srcfile ${testfile}.c | |
938 | +set executable ${testfile} | |
939 | +set binfile ${objdir}/${subdir}/${executable} | |
940 | + | |
941 | +if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { | |
942 | + untested ${testfile}.exp | |
943 | + return -1 | |
944 | +} | |
945 | + | |
946 | +clean_restart $executable | |
947 | + | |
948 | +if ![runto_main] { | |
949 | + return -1; | |
950 | +} | |
951 | + | |
952 | +# `noprint' would not test the full logic of GDB. | |
953 | +gdb_test "handle SIGUSR1 nostop print pass" "\r\nSIGUSR1\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*" | |
954 | + | |
955 | +gdb_test_no_output "set scheduler-locking off" | |
956 | + | |
957 | +gdb_breakpoint [gdb_get_line_number "step-1"] | |
958 | +gdb_test_no_output {set $step1=$bpnum} | |
959 | +gdb_continue_to_breakpoint "step-1" ".* step-1 .*" | |
960 | +gdb_test_no_output {disable $step1} | |
961 | + | |
962 | +# 1 as we are now stopped at the `step-1' label. | |
963 | +set step_at 1 | |
964 | +for {set i 0} {$i < 100} {incr i} { | |
965 | + set test "step $i" | |
966 | + # Presume this step failed - as in the case of a timeout. | |
967 | + set failed 1 | |
968 | + gdb_test_multiple "step" $test { | |
969 | + -re "\r\nProgram received signal SIGUSR1, User defined signal 1.\r\n" { | |
970 | + exp_continue -continue_timer | |
971 | + } | |
972 | + -re "step-(\[012\]).*\r\n$gdb_prompt $" { | |
973 | + set now $expect_out(1,string) | |
974 | + if {$step_at == 2 && $now == 1} { | |
975 | + set failed 0 | |
976 | + } elseif {$step_at == 1 && $now == 2} { | |
977 | + set failed 0 | |
978 | + # Continue over the re-signalling back to the handle entry. | |
979 | + gdb_test_no_output {enable $step1} "" | |
980 | + gdb_test "continue" " step-1 .*" "" | |
981 | + set now 1 | |
982 | + gdb_test_no_output {disable $step1} "" | |
983 | + } else { | |
984 | + fail $test | |
985 | + } | |
986 | + set step_at $now | |
987 | + } | |
988 | + } | |
989 | + if $failed { | |
990 | + return | |
991 | + } | |
992 | +} | |
993 | +# We can never reliably say the racy problematic case has been tested. | |
994 | +pass "step" |