[RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set

Pedro Alves pedro@codesourcery.com
Mon Nov 28 15:40:00 GMT 2011


This adds support for setting a breakpoint that only triggers on a
given set (a superset of the current thread specific breakpoints
support).  In addition, it adds support for specifying the set of
threads that are suspended when the breakpoint is triggered.

Breakpoints need two sets.  The trigger set, which is a generalization
of the "break foo thread N", meaning the set of inferiors/threads
where the breakpoint should fire, and, a suspend/stop set, which is
the set of inferiors/threads that should be suspended when the
breakpoint fires.

The trigger set of breakpoints is set from the current set at the time
the breakpoint is created.  The stop set is passed explicitly as
optional switch.  E.g.,:

 [TRIGGER-SET] break [-stop [STOP-SET]] LINESPEC

This leaves LINESPEC last, so that we can keep supporting the current
form, but avoid more hacks in linespecs like the special termination
for "thread/task/if" in the lexers --- that wouldn't work for `['.

So the old:

 (gdb) break LINESPEC

still works just the same.  The breakpoint's trigger set will be
inferred from the current set as set by itfocus, or a [SET] prefix,
and, the stop set is inferred from the "set non-stop" global option.
If non-stop is on, only the thread that triggers the breakpoint should
be suspended; if non-stop is off, then all threads will be suspended
when the breakpoint fires.

E.g.,

(gdb) info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2296) "threads" (running)
* 2    Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running)
(gdb) [.2] break -stop [.3] 63
Breakpoint 4 at 0x40076d: file threads.c, line 63.

Breakpoint 4 triggers on thread 2 (equivalent to break 63 thread 2),
and when the breakpoint fires, thread 3 is suspended.  Like so:

(gdb) c -a&
Continuing.
(gdb)
Breakpoint 4, thread_function0 (arg=0x0) at threads.c:63
63              (*myp) ++;
info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2    Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running)

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
4       breakpoint     keep y   0x000000000040076d in thread_function0 at threads.c:63
        stop only in trigger-set: [.2]
        suspend all in stop-set: [.3]
        breakpoint already hit 1 time

We can make a breakpoint that stops the world with (recall I
had non-stop on):

(gdb) del 4
(gdb) [.2] break -stop [all] 63
Breakpoint 5 at 0x40076d: file threads.c, line 63.
(gdb) c -a&
Continuing.
(gdb)
Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63
63              (*myp) ++;
info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2    Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2290) "threads" 0x00007ffff7bc606d in pthread_join () from /lib/x86_64-linux-gnu/libpthread.so.0

Since all-stop was reimplemented on top of the target running in
non-stop mode, when "set non-stop" is off, not specifying a stop set
(or specifying it as empty, like -stop []) means that all threads stop
by default, e.g., `[.2] break 63', but you can still override that
explicitly by specifying "-stop [set]", so the breakpoint only forces
suspension of a given itset, with:

(gdb) [.2] break -stop [.3] 63
Breakpoint 5 at 0x40076d: file threads.c, line 63.
(gdb) c &
Continuing.
(gdb)
Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63
63              (*myp) ++;
info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2645) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2    Thread 0x7ffff7829700 (LWP 2644) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2610) "threads" (running)
---
 gdb/breakpoint.c |  135 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 gdb/breakpoint.h |   13 +++++
 gdb/infrun.c     |   52 +++++++++++++++++----
 3 files changed, 182 insertions(+), 18 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index bdc5d38..23bae18 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -34,6 +34,7 @@
 #include "value.h"
 #include "command.h"
 #include "inferior.h"
+#include "itset.h"
 #include "gdbthread.h"
 #include "target.h"
 #include "language.h"
@@ -3035,6 +3036,21 @@ hardware_watchpoint_inserted_in_range (struct address_space *aspace,
   return 0;
 }
 
+/* Test whether this stopping thread is in the I/T set for this
+   breakpoint.  */
+
+static int
+bpstat_check_trigger_set (const struct breakpoint *b, struct thread_info *thread)
+{
+  if (b->trigger_set == NULL)
+    return 1;
+
+  if (itset_contains_thread (b->trigger_set, thread))
+    return 1;
+
+  return 0;
+}
+
 /* breakpoint_thread_match (PC, PTID) returns true if the breakpoint at
    PC is valid for process/thread PTID.  */
 
@@ -3044,7 +3060,7 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
 {
   struct bp_location *bl, **blp_tmp;
   /* The thread and task IDs associated to PTID, computed lazily.  */
-  int thread = -1;
+  struct thread_info *thread = NULL;
   int task = 0;
   
   ALL_BP_LOCATIONS (bl, blp_tmp)
@@ -3066,9 +3082,9 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
 	  /* This is a thread-specific breakpoint.  Check that ptid
 	     matches that thread.  If thread hasn't been computed yet,
 	     it is now time to do so.  */
-	  if (thread == -1)
-	    thread = pid_to_thread_id (ptid);
-	  if (bl->owner->thread != thread)
+	  if (thread == NULL)
+	    thread = find_thread_ptid (ptid);
+	  if (bl->owner->thread != thread->num)
 	    continue;
 	}
 
@@ -3083,6 +3099,16 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
 	    continue;
         }
 
+      if (bl->owner->trigger_set != NULL)
+	{
+	  /* A breakpoint with a trigger itset.  Check that ptid
+	     matches that set.  */
+	  if (thread == NULL)
+	    thread = find_thread_ptid (ptid);
+	  if (!bpstat_check_trigger_set (bl->owner, thread))
+	    continue;
+	}
+
       if (overlay_debugging 
 	  && section_is_overlay (bl->section)
 	  && !section_is_mapped (bl->section))
@@ -3974,7 +4000,7 @@ bpstat_check_watchpoint (bpstat bs)
 static void
 bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
 {
-  int thread_id = pid_to_thread_id (ptid);
+  struct thread_info *thread = find_thread_ptid (ptid);
   const struct bp_location *bl;
   struct breakpoint *b;
 
@@ -4076,7 +4102,12 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
 	{
 	  bs->stop = 0;
 	}
-      else if (b->thread != -1 && b->thread != thread_id)
+      else if (b->thread != -1 && b->thread != thread->num)
+	{
+	  bs->stop = 0;
+	}
+      else if (b->trigger_set != NULL
+	       && !bpstat_check_trigger_set (b, thread))
 	{
 	  bs->stop = 0;
 	}
@@ -4504,6 +4535,33 @@ bpstat_what (bpstat bs_head)
   return retval;
 }
 
+/* Tell us what we should suspend.  */
+
+struct itset *
+bpstat_stop_set (bpstat bs_head)
+{
+  bpstat bs;
+
+  for (bs = bs_head; bs != NULL; bs = bs->next)
+    {
+      if (bs->breakpoint_at == NULL)
+	{
+	  /* I suspect this can happen if it was a momentary
+	     breakpoint which has since been deleted.  */
+	  continue;
+	}
+
+      if (!bs->stop)
+	continue;
+
+      /* XXX: Should be the union of the sets of all breakpoints that
+	 caused a stop?  */
+      return itset_reference (bs->breakpoint_at->stop_set);
+    }
+
+  return NULL;
+}
+
 /* Nonzero if we should step constantly (e.g. watchpoints on machines
    without hardware support).  This isn't related to a specific bpstat,
    just to things like whether watchpoints are set.  */
@@ -4903,7 +4961,27 @@ print_one_breakpoint_location (struct breakpoint *b,
       ui_out_field_int (uiout, "thread", b->thread);
       ui_out_text (uiout, "\n");
     }
-  
+
+  if (!part_of_multiple && b->trigger_set != NULL)
+    {
+      ui_out_text (uiout, "\tstop only in trigger-set: [");
+      if (itset_name (b->trigger_set) != NULL)
+	ui_out_field_string (uiout, "trigger-set", itset_name (b->trigger_set));
+      else
+	ui_out_field_string (uiout, "trigger-set", itset_spec (b->trigger_set));
+      ui_out_text (uiout, "]\n");
+    }
+
+  if (!part_of_multiple && b->stop_set != NULL)
+    {
+      ui_out_text (uiout, "\tsuspend all in stop-set: [");
+      if (itset_name (b->stop_set) != NULL)
+	ui_out_field_string (uiout, "stop-set", itset_name (b->stop_set));
+      else
+	ui_out_field_string (uiout, "stop-set", itset_spec (b->stop_set));
+      ui_out_text (uiout, "]\n");
+    }
+
   if (!part_of_multiple && b->hit_count)
     {
       /* FIXME should make an annotation for this.  */
@@ -5731,6 +5809,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
   b->language = current_language->la_language;
   b->input_radix = input_radix;
   b->thread = -1;
+  b->trigger_set = NULL;
   b->enable_state = bp_enabled;
   b->next = 0;
   b->silent = 0;
@@ -7274,6 +7353,7 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 		     struct symtabs_and_lines sals, char *addr_string,
 		     char *cond_string,
 		     enum bptype type, enum bpdisp disposition,
+		     struct itset *trigger_set, struct itset *stop_set,
 		     int thread, int task, int ignore_count,
 		     const struct breakpoint_ops *ops, int from_tty,
 		     int enabled, int internal, int display_canonical)
@@ -7314,6 +7394,8 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 	  init_raw_breakpoint (b, gdbarch, sal, type, ops);
 	  b->thread = thread;
 	  b->task = task;
+	  b->trigger_set = trigger_set;
+	  b->stop_set = stop_set;
   
 	  b->cond_string = cond_string;
 	  b->ignore_count = ignore_count;
@@ -7399,6 +7481,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 		       struct symtabs_and_lines sals, char *addr_string,
 		       char *cond_string,
 		       enum bptype type, enum bpdisp disposition,
+		       struct itset *trigger_set, struct itset *stop_set,
 		       int thread, int task, int ignore_count,
 		       const struct breakpoint_ops *ops, int from_tty,
 		       int enabled, int internal, int display_canonical)
@@ -7422,6 +7505,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 		       sals, addr_string,
 		       cond_string,
 		       type, disposition,
+		       trigger_set, stop_set,
 		       thread, task, ignore_count,
 		       ops, from_tty,
 		       enabled, internal, display_canonical);
@@ -7583,6 +7667,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 			struct linespec_result *canonical,
 			char *cond_string,
 			enum bptype type, enum bpdisp disposition,
+			struct itset *trigger_set, struct itset *stop_set,
 			int thread, int task, int ignore_count,
 			const struct breakpoint_ops *ops, int from_tty,
 			int enabled, int internal)
@@ -7596,6 +7681,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 
       create_breakpoint_sal (gdbarch, expanded, canonical->canonical[i],
 			     cond_string, type, disposition,
+			     trigger_set, stop_set,
 			     thread, task, ignore_count, ops,
 			     from_tty, enabled, internal,
 			     canonical->special_display);
@@ -7619,7 +7705,8 @@ parse_breakpoint_sals (char **address,
 
   /* If no arg given, or if first arg is 'if ', use the default
      breakpoint.  */
-  if ((*address) == NULL
+  if (*address == NULL
+      || **address == '\0'
       || (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2])))
     {
       /* The last displayed codepoint, if it's valid, is our default breakpoint
@@ -7884,6 +7971,9 @@ create_breakpoint (struct gdbarch *gdbarch,
   int pending = 0;
   int task = 0;
   int prev_bkpt_count = breakpoint_count;
+  char *p;
+  struct itset *trigger_set = itset_reference (current_itset);
+  struct itset *stop_set = itset_reference (trigger_set);
 
   gdb_assert (ops != NULL);
 
@@ -7904,6 +7994,30 @@ create_breakpoint (struct gdbarch *gdbarch,
       goto done;
     }
 
+  if (arg != NULL)
+    {
+      while (*arg)
+	{
+	  arg = skip_spaces (arg);
+	  p = skip_to_space (arg);
+
+	  if (strncmp (arg, "-stop", p - arg) == 0)
+	    {
+	      p = skip_spaces (p);
+	      itset_free (stop_set);
+	      stop_set = itset_create (&p);
+	      arg = p;
+	    }
+	  else if (strcmp (arg, "--") == 0)
+	    {
+	      arg += 2;
+	      break;
+	    }
+	  else
+	    break;
+	}
+    }
+
   TRY_CATCH (e, RETURN_MASK_ALL)
     {
       parse_breakpoint_sals (&arg, &sals, &canonical);
@@ -8046,7 +8160,7 @@ create_breakpoint (struct gdbarch *gdbarch,
 				   canonical.canonical[i],
 				   cond_string, type_wanted,
 				   tempflag ? disp_del : disp_donttouch,
-				   thread, task, ignore_count, ops,
+				   NULL, NULL, thread, task, ignore_count, ops,
 				   from_tty, enabled, internal,
 				   canonical.special_display);
 	      /* Given that its possible to have multiple markers with
@@ -8066,6 +8180,7 @@ create_breakpoint (struct gdbarch *gdbarch,
 	create_breakpoints_sal (gdbarch, sals, &canonical, cond_string,
 				type_wanted,
 				tempflag ? disp_del : disp_donttouch,
+				trigger_set, stop_set,
 				thread, task, ignore_count, ops, from_tty,
 				enabled, internal);
     }
@@ -10941,6 +11056,8 @@ base_breakpoint_dtor (struct breakpoint *self)
   xfree (self->addr_string);
   xfree (self->addr_string_range_end);
   xfree (self->source_file);
+  itset_free (self->trigger_set);
+  itset_free (self->stop_set);
 }
 
 static struct bp_location *
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index df50438..45c98cb 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -32,6 +32,7 @@ struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
 struct bp_location;
+struct itset;
 
 /* This is the maximum number of bytes a breakpoint instruction can
    take.  Feel free to increase it.  It's just used in a few places to
@@ -609,6 +610,15 @@ struct breakpoint
        or 0 if don't care.  */
     int task;
 
+    /* I/T set controlling where this breakpoint will stop.  */
+    struct itset *trigger_set;
+
+    /* The set that should be suspended once the breakpoint has
+       triggered.  If empty, defaults to consulting the "non-stop"
+       setting: if that is on, just the triggering thread should stop;
+       otherwise, all threads in the TRIGGER_SET set stop.  */
+    struct itset *stop_set;
+
     /* Count of the number of times this breakpoint was taken, dumped
        with the info, but not used for anything else.  Useful for
        seeing how many times you hit a break prior to the program
@@ -860,6 +870,9 @@ enum print_stop_action
 
 /* Tell what to do about this bpstat.  */
 struct bpstat_what bpstat_what (bpstat);
+
+/* Tell us what we should suspend.  */
+extern struct itset *bpstat_stop_set (bpstat);
 

 /* Find the bpstat associated with a breakpoint.  NULL otherwise.  */
 bpstat bpstat_find_breakpoint (bpstat, struct breakpoint *);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index e0b282f..c279508 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -26,6 +26,7 @@
 #include "symtab.h"
 #include "frame.h"
 #include "inferior.h"
+#include "itset.h"
 #include "exceptions.h"
 #include "breakpoint.h"
 #include "gdb_wait.h"
@@ -2527,6 +2528,8 @@ struct execution_control_state
   char *stop_func_name;
   int new_thread_event;
   int wait_some_more;
+
+  struct itset *stop_set;
 };
 
 static void handle_inferior_event (struct execution_control_state *ecs);
@@ -2900,7 +2903,7 @@ prepare_for_detach (void)
   discard_cleanups (old_chain_1);
 }
 
-static void stop_all_threads (void);
+static void stop_all_threads (struct itset *stop_set);
 static int adjust_pc_after_break (struct thread_info *thread,
 				  struct target_waitstatus *ws);
 
@@ -3002,7 +3005,7 @@ wait_for_inferior (void)
 
       if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed)
 	{
-	  stop_all_threads ();
+	  stop_all_threads (NULL);
 
 	  if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
 	      && ecs->ws.kind != TARGET_WAITKIND_EXITED
@@ -3173,7 +3176,7 @@ fetch_inferior_event (void *client_data)
   if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed
       && ecs->ws.kind != TARGET_WAITKIND_IGNORE)
     {
-      stop_all_threads ();
+      stop_all_threads (NULL);
       /* select event thread */
       /* cancel breakpoints */
     }
@@ -3580,8 +3583,25 @@ wait_one (ptid_t wait_ptid, struct target_waitstatus *ws)
   return event_ptid;
 }
 
+#define itset_spec_empty(SET) \
+  ((SET) == NULL || itset_spec (SET)[0] == '\0')
+
+static int
+stop_set_match (struct itset *stop_set, struct thread_info *t)
+{
+  int inf_id;
+
+  if (itset_spec_empty (stop_set))
+    return !non_stop;
+
+  if (itset_contains_thread (stop_set, t))
+    return 1;
+
+  return 0;
+}
+
 static void
-stop_all_threads (void)
+stop_all_threads (struct itset *stop_set)
 {
   /* We may need multiple passes to discover all threads.  */
   int pass;
@@ -3611,6 +3631,7 @@ stop_all_threads (void)
 	  /* Go through all threads looking for threads that we need
 	     to tell the target to stop.  */
 	  ALL_LIVE_THREADS (t)
+	    if (stop_set_match (stop_set, t))
 	    {
 	      if (t->executing)
 		{
@@ -3655,6 +3676,11 @@ stop_all_threads (void)
 	    pass = -1;
 
 	  event_ptid = wait_one (minus_one_ptid, &ws);
+
+	  /* FIXME: if EVENT_PTID isn't in the trigger or stop sets,
+	     we'll need to re-resume or handle the event
+	     afterwards.  */
+
 	  if (ws.kind == TARGET_WAITKIND_NO_RESUMED)
 	    /* All resumed threads exited.  */
 	    ;
@@ -5129,7 +5155,11 @@ process_event_stop_test:
 	/* We are about to nuke the step_resume_breakpointt via the
 	   cleanup chain, so no need to worry about it here.  */
 
+	ecs->stop_set
+	  = bpstat_stop_set (ecs->event_thread->control.stop_bpstat);
 	stop_stepping (ecs);
+	itset_free (ecs->stop_set);
+	ecs->stop_set = NULL;
 	return;
 
       case BPSTAT_WHAT_STOP_SILENT:
@@ -5140,7 +5170,11 @@ process_event_stop_test:
 	/* We are about to nuke the step_resume_breakpoin via the
 	   cleanup chain, so no need to worry about it here.  */
 
+	ecs->stop_set
+	  = bpstat_stop_set (ecs->event_thread->control.stop_bpstat);
 	stop_stepping (ecs);
+	itset_free (ecs->stop_set);
+	ecs->stop_set = NULL;
 	return;
 
       case BPSTAT_WHAT_HP_STEP_RESUME:
@@ -6157,13 +6191,12 @@ stop_stepping (struct execution_control_state *ecs)
   /* Let callers know we don't want to wait for the inferior anymore.  */
   ecs->wait_some_more = 0;
 
-  if (!non_stop
-      && target_is_non_stop_p ()
+  if (target_is_non_stop_p ()
       && stop_only_if_needed)
     {
       struct thread_info *t;
 
-      stop_all_threads ();
+      stop_all_threads (ecs->stop_set);
 
       cancel_breakpoints ();
 
@@ -6171,7 +6204,8 @@ stop_stepping (struct execution_control_state *ecs)
 	 are now stopped until a new resume action is sent
 	 over.  */
       ALL_LIVE_THREADS (t)
-	t->control.resumed = 0;
+	if (stop_set_match (ecs->stop_set, t))
+	  t->control.resumed = 0;
     }
 }
 
@@ -6230,7 +6264,7 @@ keep_going (struct execution_control_state *ecs)
 	      /* Since we can't do a displaced step, we have to remove
 		 the breakpoint while we step it.  To keep things
 		 simple, we remove them all.  */
-	      stop_all_threads ();
+	      stop_all_threads (NULL);
 
 	      cancel_breakpoints ();
 	      /* In all-stop, from the core's perspective, all threads



More information about the Gdb-patches mailing list