aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog26
-rw-r--r--gdb/breakpoint.c5
-rw-r--r--gdb/breakpoint.h3
-rw-r--r--gdb/infrun.c308
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.c67
-rw-r--r--gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.exp90
7 files changed, 303 insertions, 201 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 7f99a5a..e0a58aa 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,31 @@
2014-03-20 Pedro Alves <palves@redhat.com>
+ * breakpoint.c (single_step_breakpoint_inserted_here_p): Make
+ extern.
+ * breakpoint.h (single_step_breakpoint_inserted_here_p): Declare.
+ * infrun.c (saved_singlestep_ptid)
+ (stepping_past_singlestep_breakpoint): Delete.
+ (resume): Remove stepping_past_singlestep_breakpoint handling.
+ (proceed): Store the prev_pc of the stepping thread too.
+ (init_wait_for_inferior): Adjust. Clear singlestep_ptid and
+ singlestep_pc.
+ (enum infwait_states): Delete infwait_thread_hop_state.
+ (struct execution_control_state) <hit_singlestep_breakpoint>: New
+ field.
+ (handle_inferior_event): Adjust.
+ (handle_signal_stop): Delete stepping_past_singlestep_breakpoint
+ handling and the thread-hop code. Before removing single-step
+ breakpoints, check whether the thread hit a single-step breakpoint
+ of another thread. If it did, the trap is not a random signal.
+ (switch_back_to_stepped_thread): If the event thread hit a
+ single-step breakpoint, unblock it before switching to the
+ stepping thread. Handle the case of the stepped thread having
+ advanced already.
+ (keep_going): Handle the case of the current thread moving past a
+ single-step breakpoint.
+
+2014-03-20 Pedro Alves <palves@redhat.com>
+
PR breakpoints/7143
* breakpoint.c (should_be_inserted): Don't insert breakpoints that
are being stepped over.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 03b21cb..19af9df 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -228,9 +228,6 @@ static void tcatch_command (char *arg, int from_tty);
static void detach_single_step_breakpoints (void);
-static int single_step_breakpoint_inserted_here_p (struct address_space *,
- CORE_ADDR pc);
-
static void free_bp_location (struct bp_location *loc);
static void incref_bp_location (struct bp_location *loc);
static void decref_bp_location (struct bp_location **loc);
@@ -15148,7 +15145,7 @@ detach_single_step_breakpoints (void)
/* Check whether a software single-step breakpoint is inserted at
PC. */
-static int
+int
single_step_breakpoint_inserted_here_p (struct address_space *aspace,
CORE_ADDR pc)
{
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index b4abcb8..d8e88fc 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1126,6 +1126,9 @@ extern int regular_breakpoint_inserted_here_p (struct address_space *,
extern int software_breakpoint_inserted_here_p (struct address_space *,
CORE_ADDR);
+extern int single_step_breakpoint_inserted_here_p (struct address_space *,
+ CORE_ADDR);
+
/* Returns true if there's a hardware watchpoint or access watchpoint
inserted in the range defined by ADDR and LEN. */
extern int hardware_watchpoint_inserted_in_range (struct address_space *,
diff --git a/gdb/infrun.c b/gdb/infrun.c
index ed8ec30..60abd8c 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -972,11 +972,6 @@ static ptid_t singlestep_ptid;
/* PC when we started this single-step. */
static CORE_ADDR singlestep_pc;
-/* If another thread hit the singlestep breakpoint, we save the original
- thread here so that we can resume single-stepping it later. */
-static ptid_t saved_singlestep_ptid;
-static int stepping_past_singlestep_breakpoint;
-
/* Info about an instruction that is being stepped over. Invalid if
ASPACE is NULL. */
@@ -1943,24 +1938,8 @@ a command like `return' or `jump' to continue execution."));
resume_ptid = user_visible_resume_ptid (step);
/* Maybe resume a single thread after all. */
- if (singlestep_breakpoints_inserted_p
- && stepping_past_singlestep_breakpoint)
- {
- /* The situation here is as follows. In thread T1 we wanted to
- single-step. Lacking hardware single-stepping we've
- set breakpoint at the PC of the next instruction -- call it
- P. After resuming, we've hit that breakpoint in thread T2.
- Now we've removed original breakpoint, inserted breakpoint
- at P+1, and try to step to advance T2 past breakpoint.
- We need to step only T2, as if T1 is allowed to freely run,
- it can run past P, and if other threads are allowed to run,
- they can hit breakpoint at P+1, and nested hits of single-step
- breakpoints is not something we'd want -- that's complicated
- to support, and has no value. */
- resume_ptid = inferior_ptid;
- }
- else if ((step || singlestep_breakpoints_inserted_p)
- && tp->control.trap_expected)
+ if ((step || singlestep_breakpoints_inserted_p)
+ && tp->control.trap_expected)
{
/* We're allowing a thread to run past a breakpoint it has
hit, by single-stepping the thread with the breakpoint
@@ -2222,6 +2201,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
gdbarch = get_regcache_arch (regcache);
aspace = get_regcache_aspace (regcache);
pc = regcache_read_pc (regcache);
+ tp = inferior_thread ();
if (step > 0)
step_start_function = find_pc_function (pc);
@@ -2278,13 +2258,19 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
thread that reported the most recent event. If a step-over
is required it returns TRUE and sets the current thread to
the old thread. */
+
+ /* Store the prev_pc for the stepping thread too, needed by
+ switch_back_to_stepping thread. */
+ tp->prev_pc = regcache_read_pc (get_current_regcache ());
+
if (prepare_to_proceed (step))
- force_step = 1;
+ {
+ force_step = 1;
+ /* The current thread changed. */
+ tp = inferior_thread ();
+ }
}
- /* prepare_to_proceed may change the current thread. */
- tp = inferior_thread ();
-
if (force_step)
tp->control.trap_expected = 1;
@@ -2434,8 +2420,6 @@ init_wait_for_inferior (void)
clear_proceed_status ();
- stepping_past_singlestep_breakpoint = 0;
-
target_last_wait_ptid = minus_one_ptid;
previous_inferior_ptid = inferior_ptid;
@@ -2443,6 +2427,9 @@ init_wait_for_inferior (void)
/* Discard any skipped inlined frames. */
clear_inline_frame_state (minus_one_ptid);
+
+ singlestep_ptid = null_ptid;
+ singlestep_pc = 0;
}
@@ -2453,7 +2440,6 @@ init_wait_for_inferior (void)
enum infwait_states
{
infwait_normal_state,
- infwait_thread_hop_state,
infwait_step_watch_state,
infwait_nonstep_watch_state
};
@@ -2484,6 +2470,12 @@ struct execution_control_state
infwait_nonstep_watch_state state, and the thread reported an
event. */
int stepped_after_stopped_by_watchpoint;
+
+ /* True if the event thread hit the single-step breakpoint of
+ another thread. Thus the event doesn't cause a stop, the thread
+ needs to be single-stepped past the single-step breakpoint before
+ we can switch back to the original stepping thread. */
+ int hit_singlestep_breakpoint;
};
static void handle_inferior_event (struct execution_control_state *ecs);
@@ -3364,11 +3356,6 @@ handle_inferior_event (struct execution_control_state *ecs)
switch (infwait_state)
{
- case infwait_thread_hop_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: infwait_thread_hop_state\n");
- break;
-
case infwait_normal_state:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n");
@@ -3937,163 +3924,6 @@ handle_signal_stop (struct execution_control_state *ecs)
return;
}
- if (stepping_past_singlestep_breakpoint)
- {
- gdb_assert (singlestep_breakpoints_inserted_p);
- gdb_assert (ptid_equal (singlestep_ptid, ecs->ptid));
- gdb_assert (!ptid_equal (singlestep_ptid, saved_singlestep_ptid));
-
- stepping_past_singlestep_breakpoint = 0;
-
- /* We've either finished single-stepping past the single-step
- breakpoint, or stopped for some other reason. It would be nice if
- we could tell, but we can't reliably. */
- if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: stepping_past_"
- "singlestep_breakpoint\n");
- /* Pull the single step breakpoints out of the target. */
- if (!ptid_equal (ecs->ptid, inferior_ptid))
- context_switch (ecs->ptid);
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
-
- ecs->event_thread->control.trap_expected = 0;
-
- context_switch (saved_singlestep_ptid);
- if (deprecated_context_hook)
- deprecated_context_hook (pid_to_thread_id (saved_singlestep_ptid));
-
- resume (1, GDB_SIGNAL_0);
- prepare_to_wait (ecs);
- return;
- }
- }
-
- /* See if a thread hit a thread-specific breakpoint that was meant for
- another thread. If so, then step that thread past the breakpoint,
- and continue it. */
-
- if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
- {
- int thread_hop_needed = 0;
- struct address_space *aspace =
- get_regcache_aspace (get_thread_regcache (ecs->ptid));
-
- /* Check if a regular breakpoint has been hit before checking
- for a potential single step breakpoint. Otherwise, GDB will
- not see this breakpoint hit when stepping onto breakpoints. */
- if (regular_breakpoint_inserted_here_p (aspace, stop_pc))
- {
- if (!breakpoint_thread_match (aspace, stop_pc, ecs->ptid))
- thread_hop_needed = 1;
- }
- else if (singlestep_breakpoints_inserted_p)
- {
- /* We have not context switched yet, so this should be true
- no matter which thread hit the singlestep breakpoint. */
- gdb_assert (ptid_equal (inferior_ptid, singlestep_ptid));
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: software single step "
- "trap for %s\n",
- target_pid_to_str (ecs->ptid));
-
- /* The call to in_thread_list is necessary because PTIDs sometimes
- change when we go from single-threaded to multi-threaded. If
- the singlestep_ptid is still in the list, assume that it is
- really different from ecs->ptid. */
- if (!ptid_equal (singlestep_ptid, ecs->ptid)
- && in_thread_list (singlestep_ptid))
- {
- /* If the PC of the thread we were trying to single-step
- has changed, discard this event (which we were going
- to ignore anyway), and pretend we saw that thread
- trap. This prevents us continuously moving the
- single-step breakpoint forward, one instruction at a
- time. If the PC has changed, then the thread we were
- trying to single-step has trapped or been signalled,
- but the event has not been reported to GDB yet.
-
- There might be some cases where this loses signal
- information, if a signal has arrived at exactly the
- same time that the PC changed, but this is the best
- we can do with the information available. Perhaps we
- should arrange to report all events for all threads
- when they stop, or to re-poll the remote looking for
- this particular thread (i.e. temporarily enable
- schedlock). */
-
- CORE_ADDR new_singlestep_pc
- = regcache_read_pc (get_thread_regcache (singlestep_ptid));
-
- if (new_singlestep_pc != singlestep_pc)
- {
- enum gdb_signal stop_signal;
-
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: unexpected thread,"
- " but expected thread advanced also\n");
-
- /* The current context still belongs to
- singlestep_ptid. Don't swap here, since that's
- the context we want to use. Just fudge our
- state and continue. */
- stop_signal = ecs->event_thread->suspend.stop_signal;
- ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
- ecs->ptid = singlestep_ptid;
- ecs->event_thread = find_thread_ptid (ecs->ptid);
- ecs->event_thread->suspend.stop_signal = stop_signal;
- stop_pc = new_singlestep_pc;
- }
- else
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: unexpected thread\n");
-
- thread_hop_needed = 1;
- stepping_past_singlestep_breakpoint = 1;
- saved_singlestep_ptid = singlestep_ptid;
- }
- }
- }
-
- if (thread_hop_needed)
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
-
- /* Switch context before touching inferior memory, the
- previous thread may have exited. */
- if (!ptid_equal (inferior_ptid, ecs->ptid))
- context_switch (ecs->ptid);
-
- /* Saw a breakpoint, but it was hit by the wrong thread.
- Just continue. */
-
- if (singlestep_breakpoints_inserted_p)
- {
- /* Pull the single step breakpoints out of the target. */
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
- }
-
- if (!non_stop)
- {
- /* Only need to require the next event from this thread
- in all-stop mode. */
- waiton_ptid = ecs->ptid;
- infwait_state = infwait_thread_hop_state;
- }
-
- ecs->event_thread->stepping_over_breakpoint = 1;
- keep_going (ecs);
- return;
- }
- }
-
/* See if something interesting happened to the non-current thread. If
so, then switch to that thread. */
if (!ptid_equal (ecs->ptid, inferior_ptid))
@@ -4111,9 +3941,36 @@ handle_signal_stop (struct execution_control_state *ecs)
frame = get_current_frame ();
gdbarch = get_frame_arch (frame);
+ /* Pull the single step breakpoints out of the target. */
if (singlestep_breakpoints_inserted_p)
{
- /* Pull the single step breakpoints out of the target. */
+ /* However, before doing so, if this single-step breakpoint was
+ actually for another thread, set this thread up for moving
+ past it. */
+ if (!ptid_equal (ecs->ptid, singlestep_ptid)
+ && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
+ {
+ struct regcache *regcache;
+ struct address_space *aspace;
+ CORE_ADDR pc;
+
+ regcache = get_thread_regcache (ecs->ptid);
+ aspace = get_regcache_aspace (regcache);
+ pc = regcache_read_pc (regcache);
+ if (single_step_breakpoint_inserted_here_p (aspace, pc))
+ {
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: [%s] hit step over single-step"
+ " breakpoint of [%s]\n",
+ target_pid_to_str (ecs->ptid),
+ target_pid_to_str (singlestep_ptid));
+ }
+ ecs->hit_singlestep_breakpoint = 1;
+ }
+ }
+
remove_single_step_breakpoints ();
singlestep_breakpoints_inserted_p = 0;
}
@@ -4308,6 +4165,12 @@ handle_signal_stop (struct execution_control_state *ecs)
random_signal = !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
&& currently_stepping (ecs->event_thread));
+ /* Perhaps the thread hit a single-step breakpoint of _another_
+ thread. Single-step breakpoints are transparent to the
+ breakpoints module. */
+ if (random_signal)
+ random_signal = !ecs->hit_singlestep_breakpoint;
+
/* No? Perhaps we got a moribund watchpoint. */
if (random_signal)
random_signal = !stopped_by_watchpoint;
@@ -5271,12 +5134,16 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
ecs->event_thread);
if (tp)
{
+ struct frame_info *frame;
+ struct gdbarch *gdbarch;
+
/* However, if the current thread is blocked on some internal
breakpoint, and we simply need to step over that breakpoint
to get it going again, do that first. */
if ((ecs->event_thread->control.trap_expected
&& ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_TRAP)
- || ecs->event_thread->stepping_over_breakpoint)
+ || ecs->event_thread->stepping_over_breakpoint
+ || ecs->hit_singlestep_breakpoint)
{
keep_going (ecs);
return 1;
@@ -5326,7 +5193,52 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
ecs->event_thread = tp;
ecs->ptid = tp->ptid;
context_switch (ecs->ptid);
- keep_going (ecs);
+
+ stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
+ frame = get_current_frame ();
+ gdbarch = get_frame_arch (frame);
+
+ /* If the PC of the thread we were trying to single-step has
+ changed, then the thread we were trying to single-step
+ has trapped or been signalled, but the event has not been
+ reported to GDB yet. Re-poll the remote looking for this
+ particular thread (i.e. temporarily enable schedlock) by:
+
+ - setting a break at the current PC
+ - resuming that particular thread, only (by setting
+ trap expected)
+
+ This prevents us continuously moving the single-step
+ breakpoint forward, one instruction at a time,
+ overstepping. */
+
+ if (gdbarch_software_single_step_p (gdbarch)
+ && stop_pc != tp->prev_pc)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: expected thread advanced also\n");
+
+ insert_single_step_breakpoint (get_frame_arch (frame),
+ get_frame_address_space (frame),
+ stop_pc);
+ singlestep_breakpoints_inserted_p = 1;
+ ecs->event_thread->control.trap_expected = 1;
+ singlestep_ptid = inferior_ptid;
+ singlestep_pc = stop_pc;
+
+ resume (0, GDB_SIGNAL_0);
+ prepare_to_wait (ecs);
+ }
+ else
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: expected thread still "
+ "hasn't advanced\n");
+ keep_going (ecs);
+ }
+
return 1;
}
}
@@ -5799,7 +5711,8 @@ keep_going (struct execution_control_state *ecs)
(watchpoints, etc.) but the one we're stepping over, step one
instruction, and then re-insert the breakpoint when that step
is finished. */
- if (ecs->event_thread->stepping_over_breakpoint
+ if ((ecs->hit_singlestep_breakpoint
+ || ecs->event_thread->stepping_over_breakpoint)
&& !use_displaced_stepping (get_regcache_arch (regcache)))
{
set_step_over_info (get_regcache_aspace (regcache),
@@ -5821,7 +5734,8 @@ keep_going (struct execution_control_state *ecs)
}
ecs->event_thread->control.trap_expected
- = ecs->event_thread->stepping_over_breakpoint;
+ = (ecs->event_thread->stepping_over_breakpoint
+ || ecs->hit_singlestep_breakpoint);
/* Do not deliver GDB_SIGNAL_TRAP (except when the user
explicitly specifies that such a signal should be delivered
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index ee71b746..8537359 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,10 @@
2014-03-20 Pedro Alves <palves@redhat.com>
+ * gdb.threads/step-over-trips-on-watchpoint.c: New file.
+ * gdb.threads/step-over-trips-on-watchpoint.exp: New file.
+
+2014-03-20 Pedro Alves <palves@redhat.com>
+
PR breakpoints/7143
* gdb.base/watchpoint.exp: Mention bugzilla bug number instead of
old gnats gdb/38. Remove kfail. Adjust to use gdb_test instead
diff --git a/gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.c b/gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.c
new file mode 100644
index 0000000..4e8ca9c
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.c
@@ -0,0 +1,67 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2014 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 <unistd.h>
+#include <stdlib.h>
+
+pthread_barrier_t barrier;
+pthread_t child_thread;
+
+volatile unsigned int counter = 1;
+volatile unsigned int watch_me;
+volatile unsigned int other;
+
+void *
+child_function (void *arg)
+{
+ pthread_barrier_wait (&barrier);
+
+ while (counter > 0)
+ {
+ counter++;
+
+ watch_me = 1; /* set breakpoint child here */
+ other = 1; /* set thread-specific breakpoint here */
+ usleep (1);
+ }
+
+ pthread_exit (NULL);
+}
+
+static int
+wait_threads (void)
+{
+ return 1; /* in wait_threads */
+}
+
+int
+main ()
+{
+ int res;
+ long i;
+
+ pthread_barrier_init (&barrier, NULL, 2);
+
+ res = pthread_create (&child_thread, NULL, child_function, NULL);
+ pthread_barrier_wait (&barrier);
+ wait_threads (); /* set wait-thread breakpoint here */
+
+ pthread_join (child_thread, NULL);
+
+ exit (EXIT_SUCCESS);
+}
diff --git a/gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.exp b/gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.exp
new file mode 100644
index 0000000..c6c58cc
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/step-over-trips-on-watchpoint.exp
@@ -0,0 +1,90 @@
+# Copyright (C) 2014 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/>.
+
+# Test that when a step-over trips on a watchpoint, that watchpoint is
+# reported.
+
+standard_testfile
+set executable ${testfile}
+
+# This test verifies that a watchpoint is detected in a multithreaded
+# program so the test is only meaningful on a system with hardware
+# watchpoints.
+if {[skip_hw_watchpoint_tests]} {
+ return 0
+}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+ executable [list debug "incdir=${objdir}"]] != "" } {
+ return -1
+}
+
+proc do_test { with_bp } {
+ global executable
+
+ if ${with_bp} {
+ set prefix "with thread-specific bp"
+ } else {
+ set prefix "no thread-specific bp"
+ }
+ with_test_prefix $prefix {
+ # Cover both stepping and non-stepping execution commands.
+ foreach command {"step" "next" "continue" } {
+ with_test_prefix $command {
+ clean_restart $executable
+
+ if ![runto_main] {
+ continue
+ }
+
+ gdb_breakpoint [gdb_get_line_number "set wait-thread breakpoint here"]
+ gdb_continue_to_breakpoint "run to wait-thread breakpoint"
+ gdb_test "info threads" "2 .*\\\* 1.*" "info threads shows all threads"
+
+ gdb_test_no_output "set scheduler-locking on"
+
+ delete_breakpoints
+
+ gdb_breakpoint [gdb_get_line_number "set breakpoint child here"]
+ gdb_test "thread 2" "Switching to .*"
+ gdb_continue_to_breakpoint "run to breakpoint in thread 2"
+ gdb_test "p counter = 0" " = 0" "unbreak loop in thread 2"
+ gdb_test "p watch_me = 0" " = 0" "clear watch_me"
+ gdb_test "watch watch_me" "Hardware watchpoint .*"
+
+ if ${with_bp} {
+ # Set a thread-specific breakpoint (for the wrong
+ # thread) right after instruction that triggers
+ # the watchpoint.
+ set linenum [gdb_get_line_number "set thread-specific breakpoint here"]
+ gdb_test "b $linenum thread 1"
+ }
+
+ # Switch back to thread 1 and disable scheduler locking.
+ gdb_test "thread 1" "Switching to .*"
+ gdb_test_no_output "set scheduler-locking off"
+
+ # Thread 2 is still stopped at a breakpoint that needs to be
+ # stepped over before proceeding thread 1. However, right
+ # where the step-over lands there's another breakpoint
+ # installed, which should trap and be reported to the user.
+ gdb_test "$command" "Hardware watchpoint.*: watch_me.*New value = 1.*"
+ }
+ }
+ }
+}
+
+do_test 0
+do_test 1