diff options
author | Pedro Alves <palves@redhat.com> | 2012-06-28 16:34:04 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2012-06-28 16:34:04 +0000 |
commit | e5ef252af07bfbf40e78b0ebcc5eef2e3b0af7a3 (patch) | |
tree | 802ef5f4182c89facf11b44253f0ae0c96a8ca53 | |
parent | dc60a23811c4d144c0a1242d30c3f64e1d13e941 (diff) | |
download | gdb-e5ef252af07bfbf40e78b0ebcc5eef2e3b0af7a3.zip gdb-e5ef252af07bfbf40e78b0ebcc5eef2e3b0af7a3.tar.gz gdb-e5ef252af07bfbf40e78b0ebcc5eef2e3b0af7a3.tar.bz2 |
gdb/
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com>
Pedro Alves <palves@redhat.com>
* gdbthread.h (ALL_THREADS): New macro.
(thread_list): Declare.
* infrun.c (handle_inferior_event) <spurious signal>: Don't keep
going, but instead fall through to the stepping handling.
* linux-nat.c (resume_lwp): New parameter 'signo'. Resume with
the passed in signal. Adjust debug output.
(resume_callback): Rename to ...
(linux_nat_resume_callback): ... this. Pass the thread's last
stop signal, if in "pass" state.
(linux_nat_resume): Adjust to rename.
(stop_wait_callback): New assertion. Don't respawn signals;
instead let the LWP remain with SIGNALLED set.
(linux_nat_wait_1): Remove flushing of pending SIGSTOPs.
* remote.c (append_pending_thread_resumptions): New.
(remote_vcont_resume): Call it.
* target.h (target_resume): Extend comment.
gdb/testsuite/
2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com>
Pedro Alves <palves@redhat.com>
* gdb.threads/siginfo-threads.exp: New file.
* gdb.threads/siginfo-threads.c: New file.
* gdb.threads/sigstep-threads.exp: New file.
* gdb.threads/sigstep-threads.c: New file.
-rw-r--r-- | gdb/ChangeLog | 20 | ||||
-rw-r--r-- | gdb/gdbthread.h | 7 | ||||
-rw-r--r-- | gdb/infrun.c | 372 | ||||
-rw-r--r-- | gdb/linux-nat.c | 208 | ||||
-rw-r--r-- | gdb/remote.c | 26 | ||||
-rw-r--r-- | gdb/target.h | 14 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/siginfo-threads.c | 457 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/siginfo-threads.exp | 99 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/sigstep-threads.c | 54 | ||||
-rw-r--r-- | gdb/testsuite/gdb.threads/sigstep-threads.exp | 73 | ||||
-rw-r--r-- | gdb/thread.c | 2 |
12 files changed, 996 insertions, 344 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index d291ae6..1780862 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,23 @@ +2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com> + Pedro Alves <palves@redhat.com> + + * gdbthread.h (ALL_THREADS): New macro. + (thread_list): Declare. + * infrun.c (handle_inferior_event) <spurious signal>: Don't keep + going, but instead fall through to the stepping handling. + * linux-nat.c (resume_lwp): New parameter 'signo'. Resume with + the passed in signal. Adjust debug output. + (resume_callback): Rename to ... + (linux_nat_resume_callback): ... this. Pass the thread's last + stop signal, if in "pass" state. + (linux_nat_resume): Adjust to rename. + (stop_wait_callback): New assertion. Don't respawn signals; + instead let the LWP remain with SIGNALLED set. + (linux_nat_wait_1): Remove flushing of pending SIGSTOPs. + * remote.c (append_pending_thread_resumptions): New. + (remote_vcont_resume): Call it. + * target.h (target_resume): Extend comment. + 2012-06-28 Iain Sandoe <iain@codesourcery.com> * auxv.c (fprint_target_auxv): Handle extended cache data tags. diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 7cd66b6..0250555 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -301,6 +301,11 @@ void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid); typedef int (*thread_callback_func) (struct thread_info *, void *); extern struct thread_info *iterate_over_threads (thread_callback_func, void *); +/* Traverse all threads. */ + +#define ALL_THREADS(T) \ + for (T = thread_list; T; T = T->next) + extern int thread_count (void); /* Switch from one thread to another. */ @@ -391,4 +396,6 @@ extern struct thread_info* inferior_thread (void); extern void update_thread_list (void); +extern struct thread_info *thread_list; + #endif /* GDBTHREAD_H */ diff --git a/gdb/infrun.c b/gdb/infrun.c index 53db335..2f66ddc 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -4363,227 +4363,227 @@ process_event_stop_test: (leaving the inferior at the step-resume-breakpoint without actually executing it). Either way continue until the breakpoint is really hit. */ - keep_going (ecs); - return; } + else + { + /* Handle cases caused by hitting a breakpoint. */ - /* Handle cases caused by hitting a breakpoint. */ - { - CORE_ADDR jmp_buf_pc; - struct bpstat_what what; - - what = bpstat_what (ecs->event_thread->control.stop_bpstat); - - if (what.call_dummy) - { - stop_stack_dummy = what.call_dummy; - } - - /* If we hit an internal event that triggers symbol changes, the - current frame will be invalidated within bpstat_what (e.g., if - we hit an internal solib event). Re-fetch it. */ - frame = get_current_frame (); - gdbarch = get_frame_arch (frame); - - switch (what.main_action) - { - case BPSTAT_WHAT_SET_LONGJMP_RESUME: - /* If we hit the breakpoint at longjmp while stepping, we - install a momentary breakpoint at the target of the - jmp_buf. */ - - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, - "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n"); + CORE_ADDR jmp_buf_pc; + struct bpstat_what what; - ecs->event_thread->stepping_over_breakpoint = 1; + what = bpstat_what (ecs->event_thread->control.stop_bpstat); - if (what.is_longjmp) - { - struct value *arg_value; - - /* If we set the longjmp breakpoint via a SystemTap probe, - then use it to extract the arguments. The destination - PC is the third argument to the probe. */ - arg_value = probe_safe_evaluate_at_pc (frame, 2); - if (arg_value) - jmp_buf_pc = value_as_address (arg_value); - else if (!gdbarch_get_longjmp_target_p (gdbarch) - || !gdbarch_get_longjmp_target (gdbarch, - frame, &jmp_buf_pc)) - { - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, - "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME " - "(!gdbarch_get_longjmp_target)\n"); - keep_going (ecs); - return; - } - - /* Insert a breakpoint at resume address. */ - insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc); - } - else - check_exception_resume (ecs, frame); - keep_going (ecs); - return; - - case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME: + if (what.call_dummy) { - struct frame_info *init_frame; - - /* There are several cases to consider. - - 1. The initiating frame no longer exists. In this case - we must stop, because the exception or longjmp has gone - too far. - - 2. The initiating frame exists, and is the same as the - current frame. We stop, because the exception or longjmp - has been caught. + stop_stack_dummy = what.call_dummy; + } - 3. The initiating frame exists and is different from the - current frame. This means the exception or longjmp has - been caught beneath the initiating frame, so keep - going. + /* If we hit an internal event that triggers symbol changes, the + current frame will be invalidated within bpstat_what (e.g., + if we hit an internal solib event). Re-fetch it. */ + frame = get_current_frame (); + gdbarch = get_frame_arch (frame); - 4. longjmp breakpoint has been placed just to protect - against stale dummy frames and user is not interested in - stopping around longjmps. */ + switch (what.main_action) + { + case BPSTAT_WHAT_SET_LONGJMP_RESUME: + /* If we hit the breakpoint at longjmp while stepping, we + install a momentary breakpoint at the target of the + jmp_buf. */ if (debug_infrun) fprintf_unfiltered (gdb_stdlog, - "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n"); + "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n"); - gdb_assert (ecs->event_thread->control.exception_resume_breakpoint - != NULL); - delete_exception_resume_breakpoint (ecs->event_thread); + ecs->event_thread->stepping_over_breakpoint = 1; if (what.is_longjmp) { - check_longjmp_breakpoint_for_call_dummy (ecs->event_thread->num); - - if (!frame_id_p (ecs->event_thread->initiating_frame)) + struct value *arg_value; + + /* If we set the longjmp breakpoint via a SystemTap + probe, then use it to extract the arguments. The + destination PC is the third argument to the + probe. */ + arg_value = probe_safe_evaluate_at_pc (frame, 2); + if (arg_value) + jmp_buf_pc = value_as_address (arg_value); + else if (!gdbarch_get_longjmp_target_p (gdbarch) + || !gdbarch_get_longjmp_target (gdbarch, + frame, &jmp_buf_pc)) { - /* Case 4. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME " + "(!gdbarch_get_longjmp_target)\n"); keep_going (ecs); return; } + + /* Insert a breakpoint at resume address. */ + insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc); } + else + check_exception_resume (ecs, frame); + keep_going (ecs); + return; - init_frame = frame_find_by_id (ecs->event_thread->initiating_frame); + case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME: + { + struct frame_info *init_frame; - if (init_frame) - { - struct frame_id current_id - = get_frame_id (get_current_frame ()); - if (frame_id_eq (current_id, - ecs->event_thread->initiating_frame)) - { - /* Case 2. Fall through. */ - } - else - { - /* Case 3. */ - keep_going (ecs); - return; - } - } + /* There are several cases to consider. - /* For Cases 1 and 2, remove the step-resume breakpoint, - if it exists. */ - delete_step_resume_breakpoint (ecs->event_thread); + 1. The initiating frame no longer exists. In this case + we must stop, because the exception or longjmp has gone + too far. - ecs->event_thread->control.stop_step = 1; - print_end_stepping_range_reason (); - stop_stepping (ecs); - } - return; - - case BPSTAT_WHAT_SINGLE: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n"); - ecs->event_thread->stepping_over_breakpoint = 1; - /* Still need to check other stuff, at least the case - where we are stepping and step out of the right range. */ - break; + 2. The initiating frame exists, and is the same as the + current frame. We stop, because the exception or + longjmp has been caught. - case BPSTAT_WHAT_STEP_RESUME: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n"); + 3. The initiating frame exists and is different from + the current frame. This means the exception or longjmp + has been caught beneath the initiating frame, so keep + going. - delete_step_resume_breakpoint (ecs->event_thread); - if (ecs->event_thread->control.proceed_to_finish - && execution_direction == EXEC_REVERSE) - { - struct thread_info *tp = ecs->event_thread; - - /* We are finishing a function in reverse, and just hit - the step-resume breakpoint at the start address of the - function, and we're almost there -- just need to back - up by one more single-step, which should take us back - to the function call. */ - tp->control.step_range_start = tp->control.step_range_end = 1; - keep_going (ecs); - return; - } - fill_in_stop_func (gdbarch, ecs); - if (stop_pc == ecs->stop_func_start - && execution_direction == EXEC_REVERSE) - { - /* We are stepping over a function call in reverse, and - just hit the step-resume breakpoint at the start - address of the function. Go back to single-stepping, - which should take us back to the function call. */ - ecs->event_thread->stepping_over_breakpoint = 1; - keep_going (ecs); - return; - } - break; + 4. longjmp breakpoint has been placed just to protect + against stale dummy frames and user is not interested + in stopping around longjmps. */ - case BPSTAT_WHAT_STOP_NOISY: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n"); - stop_print_frame = 1; + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n"); - /* We are about to nuke the step_resume_breakpointt via the - cleanup chain, so no need to worry about it here. */ + gdb_assert (ecs->event_thread->control.exception_resume_breakpoint + != NULL); + delete_exception_resume_breakpoint (ecs->event_thread); - stop_stepping (ecs); - return; + if (what.is_longjmp) + { + check_longjmp_breakpoint_for_call_dummy (ecs->event_thread->num); - case BPSTAT_WHAT_STOP_SILENT: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_SILENT\n"); - stop_print_frame = 0; + if (!frame_id_p (ecs->event_thread->initiating_frame)) + { + /* Case 4. */ + keep_going (ecs); + return; + } + } - /* We are about to nuke the step_resume_breakpoin via the - cleanup chain, so no need to worry about it here. */ + init_frame = frame_find_by_id (ecs->event_thread->initiating_frame); - stop_stepping (ecs); - return; + if (init_frame) + { + struct frame_id current_id + = get_frame_id (get_current_frame ()); + if (frame_id_eq (current_id, + ecs->event_thread->initiating_frame)) + { + /* Case 2. Fall through. */ + } + else + { + /* Case 3. */ + keep_going (ecs); + return; + } + } - case BPSTAT_WHAT_HP_STEP_RESUME: - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_HP_STEP_RESUME\n"); + /* For Cases 1 and 2, remove the step-resume breakpoint, + if it exists. */ + delete_step_resume_breakpoint (ecs->event_thread); - delete_step_resume_breakpoint (ecs->event_thread); - if (ecs->event_thread->step_after_step_resume_breakpoint) - { - /* Back when the step-resume breakpoint was inserted, we - were trying to single-step off a breakpoint. Go back - to doing that. */ - ecs->event_thread->step_after_step_resume_breakpoint = 0; - ecs->event_thread->stepping_over_breakpoint = 1; - keep_going (ecs); - return; + ecs->event_thread->control.stop_step = 1; + print_end_stepping_range_reason (); + stop_stepping (ecs); } - break; + return; - case BPSTAT_WHAT_KEEP_CHECKING: - break; - } - } + case BPSTAT_WHAT_SINGLE: + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n"); + ecs->event_thread->stepping_over_breakpoint = 1; + /* Still need to check other stuff, at least the case where + we are stepping and step out of the right range. */ + break; + + case BPSTAT_WHAT_STEP_RESUME: + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n"); + + delete_step_resume_breakpoint (ecs->event_thread); + if (ecs->event_thread->control.proceed_to_finish + && execution_direction == EXEC_REVERSE) + { + struct thread_info *tp = ecs->event_thread; + + /* We are finishing a function in reverse, and just hit + the step-resume breakpoint at the start address of + the function, and we're almost there -- just need to + back up by one more single-step, which should take us + back to the function call. */ + tp->control.step_range_start = tp->control.step_range_end = 1; + keep_going (ecs); + return; + } + fill_in_stop_func (gdbarch, ecs); + if (stop_pc == ecs->stop_func_start + && execution_direction == EXEC_REVERSE) + { + /* We are stepping over a function call in reverse, and + just hit the step-resume breakpoint at the start + address of the function. Go back to single-stepping, + which should take us back to the function call. */ + ecs->event_thread->stepping_over_breakpoint = 1; + keep_going (ecs); + return; + } + break; + + case BPSTAT_WHAT_STOP_NOISY: + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n"); + stop_print_frame = 1; + + /* We are about to nuke the step_resume_breakpointt via the + cleanup chain, so no need to worry about it here. */ + + stop_stepping (ecs); + return; + + case BPSTAT_WHAT_STOP_SILENT: + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_SILENT\n"); + stop_print_frame = 0; + + /* We are about to nuke the step_resume_breakpoin via the + cleanup chain, so no need to worry about it here. */ + + stop_stepping (ecs); + return; + + case BPSTAT_WHAT_HP_STEP_RESUME: + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_HP_STEP_RESUME\n"); + + delete_step_resume_breakpoint (ecs->event_thread); + if (ecs->event_thread->step_after_step_resume_breakpoint) + { + /* Back when the step-resume breakpoint was inserted, we + were trying to single-step off a breakpoint. Go back + to doing that. */ + ecs->event_thread->step_after_step_resume_breakpoint = 0; + ecs->event_thread->stepping_over_breakpoint = 1; + keep_going (ecs); + return; + } + break; + + case BPSTAT_WHAT_KEEP_CHECKING: + break; + } + } /* We come here if we hit a breakpoint but should not stop for it. Possibly we also were stepping diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 33b13fa..b82c248 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1901,7 +1901,7 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty) /* Resume LP. */ static void -resume_lwp (struct lwp_info *lp, int step) +resume_lwp (struct lwp_info *lp, int step, enum gdb_signal signo) { if (lp->stopped) { @@ -1919,14 +1919,18 @@ resume_lwp (struct lwp_info *lp, int step) { if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, - "RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n", - target_pid_to_str (lp->ptid)); + "RC: Resuming sibling %s, %s, %s\n", + target_pid_to_str (lp->ptid), + (signo != GDB_SIGNAL_0 + ? strsignal (gdb_signal_to_host (signo)) + : "0"), + step ? "step" : "resume"); if (linux_nat_prepare_to_resume != NULL) linux_nat_prepare_to_resume (lp); linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)), - step, GDB_SIGNAL_0); + step, signo); lp->stopped = 0; lp->step = step; memset (&lp->siginfo, 0, sizeof (lp->siginfo)); @@ -1949,10 +1953,27 @@ resume_lwp (struct lwp_info *lp, int step) } } +/* Resume LWP, with the last stop signal, if it is in pass state. */ + static int -resume_callback (struct lwp_info *lp, void *data) +linux_nat_resume_callback (struct lwp_info *lp, void *data) { - resume_lwp (lp, 0); + enum gdb_signal signo = GDB_SIGNAL_0; + + if (lp->stopped) + { + struct thread_info *thread; + + thread = find_thread_ptid (lp->ptid); + if (thread != NULL) + { + if (signal_pass_state (thread->suspend.stop_signal)) + signo = thread->suspend.stop_signal; + thread->suspend.stop_signal = GDB_SIGNAL_0; + } + } + + resume_lwp (lp, 0, signo); return 0; } @@ -2059,11 +2080,11 @@ linux_nat_resume (struct target_ops *ops, } /* Mark LWP as not stopped to prevent it from being continued by - resume_callback. */ + linux_nat_resume_callback. */ lp->stopped = 0; if (resume_many) - iterate_over_lwps (ptid, resume_callback, NULL); + iterate_over_lwps (ptid, linux_nat_resume_callback, NULL); /* Convert to something the lower layer understands. */ ptid = pid_to_ptid (GET_LWP (lp->ptid)); @@ -2881,110 +2902,39 @@ stop_wait_callback (struct lwp_info *lp, void *data) if (WSTOPSIG (status) != SIGSTOP) { - if (linux_nat_status_is_event (status)) - { - /* If a LWP other than the LWP that we're reporting an - event for has hit a GDB breakpoint (as opposed to - some random trap signal), then just arrange for it to - hit it again later. We don't keep the SIGTRAP status - and don't forward the SIGTRAP signal to the LWP. We - will handle the current event, eventually we will - resume all LWPs, and this one will get its breakpoint - trap again. - - If we do not do this, then we run the risk that the - user will delete or disable the breakpoint, but the - thread will have already tripped on it. */ - - /* Save the trap's siginfo in case we need it later. */ - save_siginfo (lp); - - save_sigtrap (lp); - - /* Now resume this LWP and get the SIGSTOP event. */ - errno = 0; - ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); - if (debug_linux_nat) - { - fprintf_unfiltered (gdb_stdlog, - "PTRACE_CONT %s, 0, 0 (%s)\n", - target_pid_to_str (lp->ptid), - errno ? safe_strerror (errno) : "OK"); - - fprintf_unfiltered (gdb_stdlog, - "SWC: Candidate SIGTRAP event in %s\n", - target_pid_to_str (lp->ptid)); - } - /* Hold this event/waitstatus while we check to see if - there are any more (we still want to get that SIGSTOP). */ - stop_wait_callback (lp, NULL); - - /* Hold the SIGTRAP for handling by linux_nat_wait. If - there's another event, throw it back into the - queue. */ - if (lp->status) - { - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "SWC: kill %s, %s\n", - target_pid_to_str (lp->ptid), - status_to_str ((int) status)); - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status)); - } + /* The thread was stopped with a signal other than SIGSTOP. */ - /* Save the sigtrap event. */ - lp->status = status; - return 0; - } - else - { - /* The thread was stopped with a signal other than - SIGSTOP, and didn't accidentally trip a breakpoint. */ + /* Save the trap's siginfo in case we need it later. */ + save_siginfo (lp); - if (debug_linux_nat) - { - fprintf_unfiltered (gdb_stdlog, - "SWC: Pending event %s in %s\n", - status_to_str ((int) status), - target_pid_to_str (lp->ptid)); - } - /* Now resume this LWP and get the SIGSTOP event. */ - errno = 0; - ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "SWC: PTRACE_CONT %s, 0, 0 (%s)\n", - target_pid_to_str (lp->ptid), - errno ? safe_strerror (errno) : "OK"); - - /* Hold this event/waitstatus while we check to see if - there are any more (we still want to get that SIGSTOP). */ - stop_wait_callback (lp, NULL); - - /* If the lp->status field is still empty, use it to - hold this event. If not, then this event must be - returned to the event queue of the LWP. */ - if (lp->status) - { - if (debug_linux_nat) - { - fprintf_unfiltered (gdb_stdlog, - "SWC: kill %s, %s\n", - target_pid_to_str (lp->ptid), - status_to_str ((int) status)); - } - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status)); - } - else - lp->status = status; - return 0; - } + save_sigtrap (lp); + + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "SWC: Pending event %s in %s\n", + status_to_str ((int) status), + target_pid_to_str (lp->ptid)); + + /* Save the sigtrap event. */ + lp->status = status; + gdb_assert (!lp->stopped); + gdb_assert (lp->signalled); + lp->stopped = 1; } else { /* We caught the SIGSTOP that we intended to catch, so there's no SIGSTOP pending. */ + + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "SWC: Delayed SIGSTOP caught for %s.\n", + target_pid_to_str (lp->ptid)); + lp->stopped = 1; + + /* Reset SIGNALLED only after the stop_wait_callback call + above as it does gdb_assert on SIGNALLED. */ lp->signalled = 0; } } @@ -3238,7 +3188,7 @@ stop_and_resume_callback (struct lwp_info *lp, void *data) fprintf_unfiltered (gdb_stdlog, "SARC: re-resuming LWP %ld\n", GET_LWP (lp->ptid)); - resume_lwp (lp, lp->step); + resume_lwp (lp, lp->step, GDB_SIGNAL_0); } else { @@ -3612,54 +3562,6 @@ retry: lp = NULL; } - if (lp && lp->signalled && lp->last_resume_kind != resume_stop) - { - /* A pending SIGSTOP may interfere with the normal stream of - events. In a typical case where interference is a problem, - we have a SIGSTOP signal pending for LWP A while - single-stepping it, encounter an event in LWP B, and take the - pending SIGSTOP while trying to stop LWP A. After processing - the event in LWP B, LWP A is continued, and we'll never see - the SIGTRAP associated with the last time we were - single-stepping LWP A. */ - - /* Resume the thread. It should halt immediately returning the - pending SIGSTOP. */ - registers_changed (); - if (linux_nat_prepare_to_resume != NULL) - linux_nat_prepare_to_resume (lp); - linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)), - lp->step, GDB_SIGNAL_0); - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "LLW: %s %s, 0, 0 (expect SIGSTOP)\n", - lp->step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT", - target_pid_to_str (lp->ptid)); - lp->stopped = 0; - gdb_assert (lp->resumed); - - /* Catch the pending SIGSTOP. */ - status = lp->status; - lp->status = 0; - - stop_wait_callback (lp, NULL); - - /* If the lp->status field isn't empty, we caught another signal - while flushing the SIGSTOP. Return it back to the event - queue of the LWP, as we already have an event to handle. */ - if (lp->status) - { - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "LLW: kill %s, %s\n", - target_pid_to_str (lp->ptid), - status_to_str (lp->status)); - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status)); - } - - lp->status = status; - } - if (!target_can_async_p ()) { /* Causes SIGINT to be passed on to the attached process. */ diff --git a/gdb/remote.c b/gdb/remote.c index 38ecd08..01e3f30 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -4644,6 +4644,28 @@ append_resumption (char *p, char *endp, return p; } +/* Append a vCont continue-with-signal action for threads that have a + non-zero stop signal. */ + +static char * +append_pending_thread_resumptions (char *p, char *endp, ptid_t ptid) +{ + struct thread_info *thread; + + ALL_THREADS (thread) + if (ptid_match (thread->ptid, ptid) + && !ptid_equal (inferior_ptid, thread->ptid) + && thread->suspend.stop_signal != GDB_SIGNAL_0 + && signal_pass_state (thread->suspend.stop_signal)) + { + p = append_resumption (p, endp, thread->ptid, + 0, thread->suspend.stop_signal); + thread->suspend.stop_signal = GDB_SIGNAL_0; + } + + return p; +} + /* Resume the remote inferior by using a "vCont" packet. The thread to be resumed is PTID; STEP and SIGGNAL indicate whether the resumed thread should be single-stepped and/or signalled. If PTID @@ -4696,6 +4718,10 @@ remote_vcont_resume (ptid_t ptid, int step, enum gdb_signal siggnal) p = append_resumption (p, endp, inferior_ptid, step, siggnal); } + /* Also pass down any pending signaled resumption for other + threads not the current. */ + p = append_pending_thread_resumptions (p, endp, ptid); + /* And continue others without a signal. */ append_resumption (p, endp, ptid, /*step=*/ 0, GDB_SIGNAL_0); } diff --git a/gdb/target.h b/gdb/target.h index 233d355..e5bdf4d 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -920,10 +920,16 @@ extern void target_detach (char *, int); extern void target_disconnect (char *, int); -/* Resume execution of the target process PTID. STEP says whether to - single-step or to run free; SIGGNAL is the signal to be given to - the target, or GDB_SIGNAL_0 for no signal. The caller may not - pass GDB_SIGNAL_DEFAULT. */ +/* Resume execution of the target process PTID (or a group of + threads). STEP says whether to single-step or to run free; SIGGNAL + is the signal to be given to the target, or GDB_SIGNAL_0 for no + signal. The caller may not pass GDB_SIGNAL_DEFAULT. A specific + PTID means `step/resume only this process id'. A wildcard PTID + (all threads, or all threads of process) means `step/resume + INFERIOR_PTID, and let other threads (for which the wildcard PTID + matches) resume with their 'thread->suspend.stop_signal' signal + (usually GDB_SIGNAL_0) if it is in "pass" state, or with no signal + if in "no pass" state. */ extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 36a3534..0e857df 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2012-06-28 Jan Kratochvil <jan.kratochvil@redhat.com> + Pedro Alves <palves@redhat.com> + + * gdb.threads/siginfo-threads.exp: New file. + * gdb.threads/siginfo-threads.c: New file. + * gdb.threads/sigstep-threads.exp: New file. + * gdb.threads/sigstep-threads.c: New file. + 2012-06-28 Tom Tromey <tromey@redhat.com> * gdb.go/package.exp: Partially revert earlier patch; use diff --git a/gdb/testsuite/gdb.threads/siginfo-threads.c b/gdb/testsuite/gdb.threads/siginfo-threads.c new file mode 100644 index 0000000..5859088 --- /dev/null +++ b/gdb/testsuite/gdb.threads/siginfo-threads.c @@ -0,0 +1,457 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2010-2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE +#include <pthread.h> +#include <stdio.h> +#include <limits.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <asm/unistd.h> + +#define gettid() syscall (__NR_gettid) +#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, tgid, tid, sig) + +/* Terminate always in the main task. It can lock up with SIGSTOPped + GDB otherwise. */ +#define TIMEOUT (gettid () == getpid() ? 10 : 15) + +static pid_t thread1_tid; +static pthread_cond_t thread1_tid_cond + = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread1_tid_mutex + = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; +static int thread1_sigusr1_hit; +static int thread1_sigusr2_hit; + +static pid_t thread2_tid; +static pthread_cond_t thread2_tid_cond + = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t thread2_tid_mutex + = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; +static int thread2_sigusr1_hit; +static int thread2_sigusr2_hit; + +static pthread_mutex_t terminate_mutex + = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; + +/* Do not use alarm as it would create a ptrace event which would hang + us up if we are being traced by GDB, which we stopped + ourselves. */ + +static void +timed_mutex_lock (pthread_mutex_t *mutex) +{ + int i; + struct timespec start, now; + + i = clock_gettime (CLOCK_MONOTONIC, &start); + assert (i == 0); + + do + { + i = pthread_mutex_trylock (mutex); + if (i == 0) + return; + assert (i == EBUSY); + + i = clock_gettime (CLOCK_MONOTONIC, &now); + assert (i == 0); + assert (now.tv_sec >= start.tv_sec); + } + while (now.tv_sec - start.tv_sec < TIMEOUT); + + fprintf (stderr, "Timed out waiting for internal lock!\n"); + exit (EXIT_FAILURE); +} + +static void +handler (int signo, siginfo_t *siginfo, void *exception) +{ + int *varp; + + assert (siginfo->si_signo == signo); + assert (siginfo->si_code == SI_TKILL); + assert (siginfo->si_pid == getpid ()); + + if (gettid () == thread1_tid) + { + if (signo == SIGUSR1) + varp = &thread1_sigusr1_hit; + else if (signo == SIGUSR2) + varp = &thread1_sigusr2_hit; + else + assert (0); + } + else if (gettid () == thread2_tid) + { + if (signo == SIGUSR1) + varp = &thread2_sigusr1_hit; + else if (signo == SIGUSR2) + varp = &thread2_sigusr2_hit; + else + assert (0); + } + else + assert (0); + + if (*varp) + { + fprintf (stderr, "Signal %d for TID %lu has been already hit!\n", signo, + (unsigned long) gettid ()); + exit (EXIT_FAILURE); + } + *varp = 1; +} + +static void * +thread1_func (void *unused) +{ + int i; + + timed_mutex_lock (&thread1_tid_mutex); + + /* THREAD1_TID_MUTEX must be already locked to avoid a race. */ + thread1_tid = gettid (); + + i = pthread_cond_signal (&thread1_tid_cond); + assert (i == 0); + i = pthread_mutex_unlock (&thread1_tid_mutex); + assert (i == 0); + + /* Be sure the "t (tracing stop)" test can proceed for both + threads. */ + timed_mutex_lock (&terminate_mutex); + i = pthread_mutex_unlock (&terminate_mutex); + assert (i == 0); + + if (!thread1_sigusr1_hit) + { + fprintf (stderr, "Thread 1 signal SIGUSR1 not hit!\n"); + exit (EXIT_FAILURE); + } + if (!thread1_sigusr2_hit) + { + fprintf (stderr, "Thread 1 signal SIGUSR2 not hit!\n"); + exit (EXIT_FAILURE); + } + + return NULL; +} + +static void * +thread2_func (void *unused) +{ + int i; + + timed_mutex_lock (&thread2_tid_mutex); + + /* THREAD2_TID_MUTEX must be already locked to avoid a race. */ + thread2_tid = gettid (); + + i = pthread_cond_signal (&thread2_tid_cond); + assert (i == 0); + i = pthread_mutex_unlock (&thread2_tid_mutex); + assert (i == 0); + + /* Be sure the "t (tracing stop)" test can proceed for both + threads. */ + timed_mutex_lock (&terminate_mutex); + i = pthread_mutex_unlock (&terminate_mutex); + assert (i == 0); + + if (!thread2_sigusr1_hit) + { + fprintf (stderr, "Thread 2 signal SIGUSR1 not hit!\n"); + exit (EXIT_FAILURE); + } + if (!thread2_sigusr2_hit) + { + fprintf (stderr, "Thread 2 signal SIGUSR2 not hit!\n"); + exit (EXIT_FAILURE); + } + + return NULL; +} + +static const char * +proc_string (const char *filename, const char *line) +{ + FILE *f; + static char buf[LINE_MAX]; + size_t line_len = strlen (line); + + f = fopen (filename, "r"); + if (f == NULL) + { + fprintf (stderr, "fopen (\"%s\") for \"%s\": %s\n", filename, line, + strerror (errno)); + exit (EXIT_FAILURE); + } + while (errno = 0, fgets (buf, sizeof (buf), f)) + { + char *s; + + s = strchr (buf, '\n'); + assert (s != NULL); + *s = 0; + + if (strncmp (buf, line, line_len) != 0) + continue; + + if (fclose (f)) + { + fprintf (stderr, "fclose (\"%s\") for \"%s\": %s\n", filename, line, + strerror (errno)); + exit (EXIT_FAILURE); + } + + return &buf[line_len]; + } + if (errno != 0) + { + fprintf (stderr, "fgets (\"%s\": %s\n", filename, strerror (errno)); + exit (EXIT_FAILURE); + } + fprintf (stderr, "\"%s\": No line \"%s\" found.\n", filename, line); + exit (EXIT_FAILURE); +} + +static unsigned long +proc_ulong (const char *filename, const char *line) +{ + const char *s = proc_string (filename, line); + long retval; + char *end; + + errno = 0; + retval = strtol (s, &end, 10); + if (retval < 0 || retval >= LONG_MAX || (end && *end)) + { + fprintf (stderr, "\"%s\":\"%s\": %ld, %s\n", filename, line, retval, + strerror (errno)); + exit (EXIT_FAILURE); + } + return retval; +} + +static void +state_wait (pid_t process, const char *wanted) +{ + char *filename; + int i; + struct timespec start, now; + const char *state; + + i = asprintf (&filename, "/proc/%lu/status", (unsigned long) process); + assert (i > 0); + + i = clock_gettime (CLOCK_MONOTONIC, &start); + assert (i == 0); + + do + { + state = proc_string (filename, "State:\t"); + + /* torvalds/linux-2.6.git 464763cf1c6df632dccc8f2f4c7e50163154a2c0 + has changed "T (tracing stop)" to "t (tracing stop)". Make the GDB + testcase backward compatible with older Linux kernels. */ + if (strcmp (state, "T (tracing stop)") == 0) + state = "t (tracing stop)"; + + if (strcmp (state, wanted) == 0) + { + free (filename); + return; + } + + if (sched_yield ()) + { + perror ("sched_yield()"); + exit (EXIT_FAILURE); + } + + i = clock_gettime (CLOCK_MONOTONIC, &now); + assert (i == 0); + assert (now.tv_sec >= start.tv_sec); + } + while (now.tv_sec - start.tv_sec < TIMEOUT); + + fprintf (stderr, "Timed out waiting for PID %lu \"%s\" (now it is \"%s\")!\n", + (unsigned long) process, wanted, state); + exit (EXIT_FAILURE); +} + +static volatile pid_t tracer = 0; +static pthread_t thread1, thread2; + +static void +cleanup (void) +{ + printf ("Resuming GDB PID %lu.\n", (unsigned long) tracer); + + if (tracer) + { + int i; + int tracer_save = tracer; + + tracer = 0; + + i = kill (tracer_save, SIGCONT); + assert (i == 0); + } +} + +int +main (int argc, char **argv) +{ + int i; + int standalone = 0; + struct sigaction act; + + if (argc == 2 && strcmp (argv[1], "-s") == 0) + standalone = 1; + else + assert (argc == 1); + + setbuf (stdout, NULL); + + timed_mutex_lock (&thread1_tid_mutex); + timed_mutex_lock (&thread2_tid_mutex); + + timed_mutex_lock (&terminate_mutex); + + errno = 0; + memset (&act, 0, sizeof (act)); + act.sa_sigaction = handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + i = sigemptyset (&act.sa_mask); + assert_perror (errno); + assert (i == 0); + i = sigaction (SIGUSR1, &act, NULL); + assert_perror (errno); + assert (i == 0); + i = sigaction (SIGUSR2, &act, NULL); + assert_perror (errno); + assert (i == 0); + + i = pthread_create (&thread1, NULL, thread1_func, NULL); + assert (i == 0); + + i = pthread_create (&thread2, NULL, thread2_func, NULL); + assert (i == 0); + + if (!standalone) + { + tracer = proc_ulong ("/proc/self/status", "TracerPid:\t"); + if (tracer == 0) + { + fprintf (stderr, "The testcase must be run by GDB!\n"); + exit (EXIT_FAILURE); + } + if (tracer != getppid ()) + { + fprintf (stderr, "The testcase parent must be our GDB tracer!\n"); + exit (EXIT_FAILURE); + } + } + + /* SIGCONT our debugger in the case of our crash as we would deadlock + otherwise. */ + + atexit (cleanup); + + printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer); + + if (tracer) + { + i = kill (tracer, SIGSTOP); + assert (i == 0); + state_wait (tracer, "T (stopped)"); + } + + /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex) + and so they could not trigger the signals before GDB is unstopped + later. Threads get resumed by the pthread_cond_wait below. Use + `while' loops for protection against spurious pthread_cond_wait + wakeups. */ + + printf ("Waiting till the threads initialize their TIDs.\n"); + + while (thread1_tid == 0) + { + i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex); + assert (i == 0); + } + + while (thread2_tid == 0) + { + i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex); + assert (i == 0); + } + + printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n", + (unsigned long) thread1_tid, (unsigned long) thread2_tid, + (unsigned long) getpid ()); + + errno = 0; + i = tgkill (getpid (), thread1_tid, SIGUSR1); + assert_perror (errno); + assert (i == 0); + i = tgkill (getpid (), thread1_tid, SIGUSR2); + assert_perror (errno); + assert (i == 0); + i = tgkill (getpid (), thread2_tid, SIGUSR1); + assert_perror (errno); + assert (i == 0); + i = tgkill (getpid (), thread2_tid, SIGUSR2); + assert_perror (errno); + assert (i == 0); + + printf ("Waiting till the threads are trapped by the signals.\n"); + + if (tracer) + { + /* s390x-unknown-linux-gnu will fail with "R (running)". */ + + state_wait (thread1_tid, "t (tracing stop)"); + + state_wait (thread2_tid, "t (tracing stop)"); + } + + cleanup (); + + printf ("Joining the threads.\n"); + + i = pthread_mutex_unlock (&terminate_mutex); + assert (i == 0); + + i = pthread_join (thread1, NULL); + assert (i == 0); + + i = pthread_join (thread2, NULL); + assert (i == 0); + + printf ("Exiting.\n"); /* break-at-exit */ + + return EXIT_SUCCESS; +} diff --git a/gdb/testsuite/gdb.threads/siginfo-threads.exp b/gdb/testsuite/gdb.threads/siginfo-threads.exp new file mode 100644 index 0000000..f3f9c51 --- /dev/null +++ b/gdb/testsuite/gdb.threads/siginfo-threads.exp @@ -0,0 +1,99 @@ +# Copyright 2010-2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +standard_testfile +set executable ${testfile} + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" \ + ${binfile} \ + executable \ + [list debug additional_flags=-lrt]] != "" } { + return -1 +} + +clean_restart $testfile + +if ![runto_main] { + return -1 +} + +# `nostop noprint pass' could in some cases report false PASS due to +# the (preempt 'handle') code path in linux-nat.c. + +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\].*" +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\].*" + +gdb_breakpoint [gdb_get_line_number "break-at-exit"] + +set test "get pid" +gdb_test_multiple "p getpid ()" $test { + -re " = (\[0-9\]+)\r\n$gdb_prompt $" { + set pid $expect_out(1,string) + pass $test + } +} + +for {set sigcount 0} {$sigcount < 4} {incr sigcount} { + set test "catch signal $sigcount" + set sigusr "" + gdb_test_multiple "continue" $test { + -re "Program received signal SIGUSR(\[12\]), User defined signal \[12\]\\.\r\n.*\r\n$gdb_prompt $" { + set sigusr $expect_out(1,string) + pass $test + } + } + if {$sigusr == ""} { + return -1 + } + + set test "signal $sigcount si_signo" + if {$sigusr == 1} { + set signo 10 + } else { + set signo 12 + } + gdb_test_multiple {p $_siginfo.si_signo} $test { + -re " = $signo\r\n$gdb_prompt $" { + pass $test + } + -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" { + unsupported $test + } + } + + set test "signal $sigcount si_code is SI_TKILL" + gdb_test_multiple {p $_siginfo.si_code} $test { + -re " = -6\r\n$gdb_prompt $" { + pass $test + } + -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" { + unsupported $test + } + } + + set test "signal $sigcount si_pid" + gdb_test_multiple {p $_siginfo._sifields._kill.si_pid} $test { + -re " = $pid\r\n$gdb_prompt $" { + pass $test + } + -re "Attempt to extract a component of a value that is not a structure\\.\r\n$gdb_prompt $" { + unsupported $test + } + } +} + +gdb_continue_to_breakpoint break-at-exit ".*break-at-exit.*" diff --git a/gdb/testsuite/gdb.threads/sigstep-threads.c b/gdb/testsuite/gdb.threads/sigstep-threads.c new file mode 100644 index 0000000..ca77fe4 --- /dev/null +++ b/gdb/testsuite/gdb.threads/sigstep-threads.c @@ -0,0 +1,54 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2010-2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <assert.h> +#include <signal.h> + +#include <asm/unistd.h> +#include <unistd.h> +#define tgkill(tgid, tid, sig) syscall (__NR_tgkill, (tgid), (tid), (sig)) +#define gettid() syscall (__NR_gettid) + +static volatile int var; + +static void +handler (int signo) /* step-0 */ +{ /* step-0 */ + var++; /* step-1 */ + tgkill (getpid (), gettid (), SIGUSR1); /* step-2 */ +} + +static void * +start (void *arg) +{ + tgkill (getpid (), gettid (), SIGUSR1); + assert (0); + return NULL; +} + +int +main (void) +{ + pthread_t thread; + + signal (SIGUSR1, handler); + + pthread_create (&thread, NULL, start, NULL); + start (NULL); /* main-start */ + return 0; +} diff --git a/gdb/testsuite/gdb.threads/sigstep-threads.exp b/gdb/testsuite/gdb.threads/sigstep-threads.exp new file mode 100644 index 0000000..484ca37 --- /dev/null +++ b/gdb/testsuite/gdb.threads/sigstep-threads.exp @@ -0,0 +1,73 @@ +# Copyright 2010-2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +standard_testfile +set executable ${testfile} + +if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested ${testfile}.exp + return -1 +} + +clean_restart $executable + +if ![runto_main] { + return -1; +} + +# `noprint' would not test the full logic of GDB. +gdb_test "handle SIGUSR1 nostop print pass" \ + "\r\nSIGUSR1\[ \t\]+No\[ \t\]+Yes\[ \t\]+Yes\[ \t\].*" + +gdb_test_no_output "set scheduler-locking off" + +gdb_breakpoint [gdb_get_line_number "step-1"] +gdb_test_no_output {set $step1=$bpnum} +gdb_continue_to_breakpoint "step-1" ".* step-1 .*" +gdb_test_no_output {disable $step1} + +# 1 as we are now stopped at the `step-1' label. +set step_at 1 +for {set i 0} {$i < 100} {incr i} { + set test "step $i" + # Presume this step failed - as in the case of a timeout. + set failed 1 + gdb_test_multiple "step" $test { + -re "\r\nProgram received signal SIGUSR1, User defined signal 1.\r\n" { + exp_continue -continue_timer + } + -re "step-(\[012\]).*\r\n$gdb_prompt $" { + set now $expect_out(1,string) + if {$step_at == 2 && $now == 1} { + set failed 0 + } elseif {$step_at == 1 && $now == 2} { + set failed 0 + # Continue over the re-signalling back to the handle entry. + gdb_test_no_output {enable $step1} "" + gdb_test "continue" " step-1 .*" "" + set now 1 + gdb_test_no_output {disable $step1} "" + } else { + fail $test + } + set step_at $now + } + } + if $failed { + return + } +} +# We can never reliably say the racy problematic case has been tested. +pass "step" diff --git a/gdb/thread.c b/gdb/thread.c index d361dd8..ff5220d 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -54,7 +54,7 @@ void _initialize_thread (void); /* Prototypes for local functions. */ -static struct thread_info *thread_list = NULL; +struct thread_info *thread_list = NULL; static int highest_thread_num; static void thread_command (char *tidstr, int from_tty); |