]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: only insert thread-specific breakpoints in the relevant inferior
authorAndrew Burgess <aburgess@redhat.com>
Fri, 3 Mar 2023 19:03:15 +0000 (19:03 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Sat, 7 Sep 2024 20:48:35 +0000 (21:48 +0100)
This commit updates GDB so that thread or inferior specific
breakpoints are only inserted into the program space in which the
specific thread or inferior is running.

In terms of implementation, getting this basically working is easy
enough, now that a breakpoint's thread or inferior field is setup
prior to GDB looking for locations, we can easily use this information
to find a suitable program_space and pass this to as a filter when
creating the sals.

Or we could if breakpoint_ops::create_sals_from_location_spec allowed
us to pass in a filter program_space.

So, this commit extends breakpoint_ops::create_sals_from_location_spec
to take a program_space argument, and uses this to filter the set of
returned sals.  This accounts for about half the change in this patch.

The second set of changes starts from breakpoint_set_thread and
breakpoint_set_inferior, this is called when the thread or inferior
for a breakpoint changes, e.g. from the Python API.

Previously this call would never result in the locations of a
breakpoint changing, after all, locations were inserted in every
program space, and we just use the thread or inferior variable to
decide when we should stop.  Now though, changing a breakpoint's
thread or inferior can mean we need to figure out a new set of
breakpoint locations.

To support this I've added a new breakpoint_re_set_one function, which
is like breakpoint_re_set, but takes a single breakpoint, and just
updates the locations for that one breakpoint.  We only need to call
this function if the program_space in which a breakpoint's thread (or
inferior) is running actually changes.  If the program_space does
change then we call the new breakpoint_re_set_one function passing in
the program_space which should be used to filter the new locations (or
nullptr to indicate we should set locations in all program spaces).
This filter program_space needs to propagate down to all the re_set
methods, this accounts for the remaining half of the changes in this
patch.

There were a couple of existing tests that created thread or inferior
specific breakpoints and then checked the 'info breakpoints' output,
these needed updating.  These were:

  gdb.mi/user-selected-context-sync.exp
  gdb.multi/bp-thread-specific.exp
  gdb.multi/multi-target-continue.exp
  gdb.multi/multi-target-ping-pong-next.exp
  gdb.multi/tids.exp
  gdb.mi/new-ui-bp-deleted.exp
  gdb.multi/inferior-specific-bp.exp
  gdb.multi/pending-bp-del-inferior.exp

I've also added some additional tests to:

  gdb.multi/pending-bp.exp

I've updated the documentation and added a NEWS entry.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
14 files changed:
gdb/NEWS
gdb/ada-lang.c
gdb/break-catch-throw.c
gdb/breakpoint.c
gdb/breakpoint.h
gdb/testsuite/gdb.mi/new-ui-bp-deleted.exp
gdb/testsuite/gdb.mi/user-selected-context-sync.exp
gdb/testsuite/gdb.multi/bp-thread-specific.exp
gdb/testsuite/gdb.multi/inferior-specific-bp.exp
gdb/testsuite/gdb.multi/multi-target-continue.exp
gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
gdb/testsuite/gdb.multi/pending-bp-del-inferior.exp
gdb/testsuite/gdb.multi/pending-bp.exp
gdb/testsuite/gdb.multi/tids.exp

index 3ee6a61b21006757e0546262db2f604ab1c9d04e..fa57b56237d3d1576d23909ac30a772b1de3d9e7 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
   'thread' or 'task' keywords are parsed at the time the breakpoint is
   created, rather than at the time the breakpoint becomes non-pending.
 
+* Thread-specific breakpoints are only inserted into the program space
+  in which the thread of interest is running.  In most cases program
+  spaces are unique for each inferior, so this means that
+  thread-specific breakpoints will usually only be inserted for the
+  inferior containing the thread of interest.  The breakpoint will
+  be hit no less than before.
+
 * Python API
 
   ** Added gdb.record.clear.  Clears the trace data of the current recording.
index 1e2ee45164426f2e7ee2103c69c943e2ac09baf3..14d6abda699fda04f06e553d21d441b00b69881f 100644 (file)
@@ -12073,11 +12073,11 @@ struct ada_catchpoint : public code_breakpoint
     enable_state = enabled ? bp_enabled : bp_disabled;
     language = language_ada;
 
-    re_set ();
+    re_set (pspace);
   }
 
   struct bp_location *allocate_location () override;
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   void check_status (struct bpstat *bs) override;
   enum print_stop_action print_it (const bpstat *bs) const override;
   bool print_one (const bp_location **) const override;
@@ -12122,7 +12122,7 @@ static struct symtab_and_line ada_exception_sal
    catchpoint kinds.  */
 
 void
-ada_catchpoint::re_set ()
+ada_catchpoint::re_set (program_space *pspace)
 {
   std::vector<symtab_and_line> sals;
   try
index b7e29a79dd8b13aed3206a6f48aa40de9d3c3e02..c1c88d96e10dec45a6dee7cded36bd5da1b8bb5d 100644 (file)
@@ -81,10 +81,10 @@ struct exception_catchpoint : public code_breakpoint
                                     _("invalid type-matching regexp")))
   {
     pspace = current_program_space;
-    re_set ();
+    re_set (pspace);
   }
 
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   enum print_stop_action print_it (const bpstat *bs) const override;
   bool print_one (const bp_location **) const override;
   void print_mention () const override;
@@ -197,7 +197,7 @@ exception_catchpoint::check_status (struct bpstat *bs)
 /* Implement the 're_set' method.  */
 
 void
-exception_catchpoint::re_set ()
+exception_catchpoint::re_set (program_space *pspace)
 {
   std::vector<symtab_and_line> sals;
   struct program_space *filter_pspace = current_program_space;
index 8860ec48fc0726c610a61bb306923a6d778c1ff2..7fd50ba63fcaf1c748994bda5e1db1b107c3c48a 100644 (file)
 static void map_breakpoint_numbers (const char *,
                                    gdb::function_view<void (breakpoint *)>);
 
-static void
-  create_sals_from_location_spec_default (location_spec *locspec,
-                                         linespec_result *canonical);
+static void parse_breakpoint_sals (location_spec *locspec,
+                                  linespec_result *canonical,
+                                  program_space *search_pspace);
+
+static void breakpoint_re_set_one (breakpoint *b,
+                                  program_space *filter_pspace);
 
 static void create_breakpoints_sal (struct gdbarch *,
                                    struct linespec_result *,
@@ -283,11 +286,12 @@ static bool strace_marker_p (struct breakpoint *b);
 
 static void bkpt_probe_create_sals_from_location_spec
      (location_spec *locspec,
-      struct linespec_result *canonical);
+      struct linespec_result *canonical,
+      struct program_space *search_pspace);
 
 const struct breakpoint_ops code_breakpoint_ops =
 {
-  create_sals_from_location_spec_default,
+  parse_breakpoint_sals,
   create_breakpoints_sal,
 };
 
@@ -352,7 +356,7 @@ struct internal_breakpoint : public code_breakpoint
     disposition = disp_donttouch;
   }
 
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   void check_status (struct bpstat *bs) override;
   enum print_stop_action print_it (const bpstat *bs) const override;
   void print_mention () const override;
@@ -389,7 +393,7 @@ struct momentary_breakpoint : public code_breakpoint
     gdb_assert (inferior == -1);
   }
 
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   void check_status (struct bpstat *bs) override;
   enum print_stop_action print_it (const bpstat *bs) const override;
   void print_mention () const override;
@@ -400,7 +404,7 @@ struct dprintf_breakpoint : public ordinary_breakpoint
 {
   using ordinary_breakpoint::ordinary_breakpoint;
 
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   int breakpoint_hit (const struct bp_location *bl,
                      const address_space *aspace,
                      CORE_ADDR bp_addr,
@@ -1549,7 +1553,36 @@ breakpoint_set_thread (struct breakpoint *b, int thread)
   int old_thread = b->thread;
   b->thread = thread;
   if (old_thread != thread)
-    notify_breakpoint_modified (b);
+    {
+      /* If THREAD is in a different program_space than OLD_THREAD, or the
+        breakpoint has switched to or from being thread-specific, then we
+        need to re-set the locations of this breakpoint.  First, figure
+        out the program_space for the old and new threads, use a value of
+        nullptr to indicate the breakpoint is in all program spaces.  */
+      program_space *old_pspace = nullptr;
+      if (old_thread != -1)
+       {
+         struct thread_info *thr = find_thread_global_id (old_thread);
+         gdb_assert (thr != nullptr);
+         old_pspace = thr->inf->pspace;
+       }
+
+      program_space *new_pspace = nullptr;
+      if (thread != -1)
+       {
+         struct thread_info *thr = find_thread_global_id (thread);
+         gdb_assert (thr != nullptr);
+         new_pspace = thr->inf->pspace;
+       }
+
+      /* If the program space has changed for this breakpoint, then
+        re-evaluate it's locations.  */
+      if (old_pspace != new_pspace)
+       breakpoint_re_set_one (b, new_pspace);
+
+      /* Let others know the breakpoint has changed.  */
+      notify_breakpoint_modified (b);
+    }
 }
 
 /* See breakpoint.h.  */
@@ -1568,7 +1601,34 @@ breakpoint_set_inferior (struct breakpoint *b, int inferior)
   int old_inferior = b->inferior;
   b->inferior = inferior;
   if (old_inferior != inferior)
-    notify_breakpoint_modified (b);
+    {
+      /* If INFERIOR is in a different program_space than OLD_INFERIOR, or
+        the breakpoint has switch to or from inferior-specific, then we
+        need to re-set the locations of this breakpoint.  First, figure
+        out the program_space for the old and new inferiors, use a value
+        of nullptr to indicate the breakpoint is in all program
+        spaces.  */
+      program_space *old_pspace = nullptr;
+      if (old_inferior != -1)
+       {
+         struct inferior *inf = find_inferior_id (old_inferior);
+         gdb_assert (inf != nullptr);
+         old_pspace = inf->pspace;
+       }
+
+      program_space *new_pspace = nullptr;
+      if (inferior != -1)
+       {
+         struct inferior *inf = find_inferior_id (inferior);
+         gdb_assert (inf != nullptr);
+         new_pspace = inf->pspace;
+       }
+
+      if (old_pspace != new_pspace)
+       breakpoint_re_set_one (b, new_pspace);
+
+      notify_breakpoint_modified (b);
+    }
 }
 
 /* See breakpoint.h.  */
@@ -8124,11 +8184,16 @@ catchpoint::catchpoint (struct gdbarch *gdbarch, bool temp,
 /* See breakpoint.h.  */
 
 void
-catchpoint::re_set ()
+catchpoint::re_set (program_space *filter_pspace)
 {
   /* All catchpoints are associated with a specific program_space.  */
   gdb_assert (pspace != nullptr);
 
+  /* If only a single program space changed, and it's not the program space
+     for which this catchpoint applies, then there's nothing to do.  */
+  if (filter_pspace != nullptr && filter_pspace != pspace)
+    return;
+
   /* Catchpoints have a single dummy location.  */
   gdb_assert (locations ().size () == 1);
   bp_location &bl = m_locations.front ();
@@ -8852,7 +8917,8 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 
 static void
 parse_breakpoint_sals (location_spec *locspec,
-                      struct linespec_result *canonical)
+                      struct linespec_result *canonical,
+                      struct program_space *search_pspace)
 {
   if (locspec->type () == LINESPEC_LOCATION_SPEC)
     {
@@ -8916,7 +8982,7 @@ parse_breakpoint_sals (location_spec *locspec,
              && strchr ("+-", spec[0]) != NULL
              && spec[1] != '['))
        {
-         decode_line_full (locspec, DECODE_LINE_FUNFIRSTLINE, NULL,
+         decode_line_full (locspec, DECODE_LINE_FUNFIRSTLINE, search_pspace,
                            get_last_displayed_symtab (),
                            get_last_displayed_line (),
                            canonical, NULL, NULL);
@@ -8924,7 +8990,7 @@ parse_breakpoint_sals (location_spec *locspec,
        }
     }
 
-  decode_line_full (locspec, DECODE_LINE_FUNFIRSTLINE, NULL,
+  decode_line_full (locspec, DECODE_LINE_FUNFIRSTLINE, search_pspace,
                    cursal.symtab, cursal.line, canonical, NULL, NULL);
 }
 
@@ -9023,6 +9089,39 @@ breakpoint_ops_for_location_spec_type (enum location_spec_type locspec_type,
     }
 }
 
+/* Return the program space to use as a filter when searching for locations
+   of a breakpoint specific to THREAD or INFERIOR.  If THREAD and INFERIOR
+   are both -1, meaning all threads/inferiors, then this function returns
+   nullptr, indicating no program space filtering should be performed.
+   Otherwise, this function returns the program space for the inferior that
+   contains THREAD (when THREAD is not -1), or the program space for
+   INFERIOR (when INFERIOR is not -1).  */
+
+static struct program_space *
+find_program_space_for_breakpoint (int thread, int inferior)
+{
+  if (thread != -1)
+    {
+      gdb_assert (inferior == -1);
+
+      struct thread_info *thr = find_thread_global_id (thread);
+      gdb_assert (thr != nullptr);
+      gdb_assert (thr->inf != nullptr);
+      return thr->inf->pspace;
+    }
+  else if (inferior != -1)
+    {
+      gdb_assert (thread == -1);
+
+      struct inferior *inf = find_inferior_id (inferior);
+      gdb_assert (inf != nullptr);
+
+      return inf->pspace;
+    }
+
+  return nullptr;
+}
+
 /* See breakpoint.h.  */
 
 const struct breakpoint_ops *
@@ -9124,7 +9223,10 @@ create_breakpoint (struct gdbarch *gdbarch,
 
   try
     {
-      ops->create_sals_from_location_spec (locspec, &canonical);
+      struct program_space *search_pspace
+       = find_program_space_for_breakpoint (thread, inferior);
+      ops->create_sals_from_location_spec (locspec, &canonical,
+                                          search_pspace);
     }
   catch (const gdb_exception_error &e)
     {
@@ -9597,7 +9699,7 @@ break_range_command (const char *arg, int from_tty)
   arg_start = arg;
   location_spec_up start_locspec
     = string_to_location_spec (&arg, current_language);
-  parse_breakpoint_sals (start_locspec.get (), &canonical_start);
+  parse_breakpoint_sals (start_locspec.get (), &canonical_start, nullptr);
 
   if (arg[0] != ',')
     error (_("Too few arguments."));
@@ -9698,7 +9800,7 @@ watchpoint_exp_is_const (const struct expression *exp)
 /* Implement the "re_set" method for watchpoints.  */
 
 void
-watchpoint::re_set ()
+watchpoint::re_set (struct program_space *pspace)
 {
   /* Watchpoint can be either on expression using entirely global
      variables, or it can be on local variables.
@@ -11810,7 +11912,7 @@ breakpoint::print_recreate (struct ui_file *fp) const
 /* Default breakpoint_ops methods.  */
 
 void
-code_breakpoint::re_set ()
+code_breakpoint::re_set (struct program_space *pspace)
 {
   /* FIXME: is this still reachable?  */
   if (breakpoint_location_spec_empty_p (this))
@@ -11820,7 +11922,7 @@ code_breakpoint::re_set ()
       return;
     }
 
-  re_set_default ();
+  re_set_default (pspace);
 }
 
 int
@@ -12026,7 +12128,7 @@ code_breakpoint::decode_location_spec (location_spec *locspec,
 /* Virtual table for internal breakpoints.  */
 
 void
-internal_breakpoint::re_set ()
+internal_breakpoint::re_set (struct program_space *pspace)
 {
   switch (type)
     {
@@ -12119,7 +12221,7 @@ internal_breakpoint::print_mention () const
 /* Virtual table for momentary breakpoints  */
 
 void
-momentary_breakpoint::re_set ()
+momentary_breakpoint::re_set (struct program_space *pspace)
 {
   /* Keep temporary breakpoints, which can be encountered when we step
      over a dlopen call and solib_add is resetting the breakpoints.
@@ -12160,12 +12262,13 @@ longjmp_breakpoint::~longjmp_breakpoint ()
 
 static void
 bkpt_probe_create_sals_from_location_spec (location_spec *locspec,
-                                          struct linespec_result *canonical)
+                                          struct linespec_result *canonical,
+                                          struct program_space *search_pspace)
 
 {
   struct linespec_sals lsal;
 
-  lsal.sals = parse_probes (locspec, NULL, canonical);
+  lsal.sals = parse_probes (locspec, search_pspace, canonical);
   lsal.canonical = xstrdup (canonical->locspec->to_string ());
   canonical->lsals.push_back (std::move (lsal));
 }
@@ -12255,9 +12358,9 @@ tracepoint::print_recreate (struct ui_file *fp) const
 }
 
 void
-dprintf_breakpoint::re_set ()
+dprintf_breakpoint::re_set (struct program_space *pspace)
 {
-  re_set_default ();
+  re_set_default (pspace);
 
   /* 1 - connect to target 1, that can run breakpoint commands.
      2 - create a dprintf, which resolves fine.
@@ -12311,8 +12414,10 @@ dprintf_breakpoint::after_condition_true (struct bpstat *bs)
    markers (`-m').  */
 
 static void
-strace_marker_create_sals_from_location_spec (location_spec *locspec,
-                                             struct linespec_result *canonical)
+strace_marker_create_sals_from_location_spec
+       (location_spec *locspec,
+        struct linespec_result *canonical,
+        struct program_space *search_pspace)
 {
   struct linespec_sals lsal;
   const char *arg_start, *arg;
@@ -12836,12 +12941,32 @@ update_breakpoint_locations (code_breakpoint *b,
      all locations are in the same shared library, that was unloaded.
      We'd like to retain the location, so that when the library is
      loaded again, we don't loose the enabled/disabled status of the
-     individual locations.  */
+     individual locations.
+
+     Thread specific breakpoints will also trigger this case if the thread
+     is changed to a different program space, and all of the old locations
+     go out of scope.  In this case we do (currently) discard the old
+     locations -- we assume the change in thread is permanent and the old
+     locations will never come back into scope.  */
   if (all_locations_are_pending (b, filter_pspace) && sals.empty ())
-    return;
+    {
+      if (b->thread != -1)
+       b->clear_locations ();
+      return;
+    }
 
   bp_location_list existing_locations = b->steal_locations (filter_pspace);
 
+  /* If this is a thread-specific breakpoint then any locations left on the
+     breakpoint are for a program space in which the thread of interest
+     does not operate.  This can happen when the user changes the thread of
+     a thread-specific breakpoint.
+
+     We assume that the change in thread is permanent, and that the old
+     locations will never be used again, so discard them now.  */
+  if (b->thread != -1)
+    b->clear_locations ();
+
   for (const auto &sal : sals)
     {
       struct bp_location *new_loc;
@@ -13007,40 +13132,45 @@ code_breakpoint::location_spec_to_sals (location_spec *locspec,
    locations.  */
 
 void
-code_breakpoint::re_set_default ()
+code_breakpoint::re_set_default (struct program_space *filter_pspace)
 {
-  struct program_space *filter_pspace = current_program_space;
   std::vector<symtab_and_line> expanded, expanded_end;
 
-  int found;
-  std::vector<symtab_and_line> sals = location_spec_to_sals (locspec.get (),
-                                                            filter_pspace,
-                                                            &found);
-  if (found)
-    expanded = std::move (sals);
-
-  if (locspec_range_end != nullptr)
-    {
-      std::vector<symtab_and_line> sals_end
-       = location_spec_to_sals (locspec_range_end.get (),
-                                filter_pspace, &found);
+  /* If this breakpoint is thread-specific then find the program space in
+     which the specific thread exists.  Otherwise, for breakpoints that are
+     not thread-specific THREAD_PSPACE will be nullptr.  */
+  program_space *bp_pspace
+    = find_program_space_for_breakpoint (this->thread, this->inferior);
+
+  /* If this is not a thread or inferior specific breakpoint, or it is a
+     thread or inferior specific breakpoint but we are looking for new
+     locations in the program space that the specific thread or inferior is
+     running, then look for new locations for this breakpoint.  */
+  if (bp_pspace == nullptr || filter_pspace == bp_pspace)
+    {
+      int found;
+      std::vector<symtab_and_line> sals
+       = location_spec_to_sals (locspec.get (), filter_pspace, &found);
       if (found)
-       expanded_end = std::move (sals_end);
+       expanded = std::move (sals);
+
+      if (locspec_range_end != nullptr)
+       {
+         std::vector<symtab_and_line> sals_end
+           = location_spec_to_sals (locspec_range_end.get (),
+                                    filter_pspace, &found);
+         if (found)
+           expanded_end = std::move (sals_end);
+       }
     }
 
+  /* Update the locations for this breakpoint.  For thread-specific
+     breakpoints this will remove any old locations that are for the wrong
+     program space -- this can happen if the user changes the thread of a
+     thread-specific breakpoint.  */
   update_breakpoint_locations (this, filter_pspace, expanded, expanded_end);
 }
 
-/* Default method for creating SALs from an address string.  It basically
-   calls parse_breakpoint_sals.  Return 1 for success, zero for failure.  */
-
-static void
-create_sals_from_location_spec_default (location_spec *locspec,
-                                       struct linespec_result *canonical)
-{
-  parse_breakpoint_sals (locspec, canonical);
-}
-
 /* Re-set breakpoint locations for the current program space.
    Locations bound to other program spaces are left untouched.  */
 
@@ -13075,7 +13205,7 @@ breakpoint_re_set (void)
          {
            input_radix = b.input_radix;
            set_language (b.language);
-           b.re_set ();
+           b.re_set (current_program_space);
          }
        catch (const gdb_exception &ex)
          {
@@ -13096,6 +13226,53 @@ breakpoint_re_set (void)
   /* Now we can insert.  */
   update_global_location_list (UGLL_MAY_INSERT);
 }
+
+/* Re-set locations for breakpoint B in FILTER_PSPACE.  If FILTER_PSPACE is
+   nullptr then re-set locations for B in all program spaces.  Locations
+   bound to program spaces other than FILTER_PSPACE are left untouched.  */
+
+static void
+breakpoint_re_set_one (breakpoint *b, program_space *filter_pspace)
+{
+  {
+    scoped_restore_current_language save_language;
+    scoped_restore save_input_radix = make_scoped_restore (&input_radix);
+    scoped_restore_current_pspace_and_thread restore_pspace_thread;
+
+    /* To ::re_set each breakpoint we set the current_language to the
+       language of the breakpoint before re-evaluating the breakpoint's
+       location.  This change can unfortunately get undone by accident if
+       the language_mode is set to auto, and we either switch frames, or
+       more likely in this context, we select the current frame.
+
+       We prevent this by temporarily turning the language_mode to
+       language_mode_manual.  We restore it once all breakpoints
+       have been reset.  */
+    scoped_restore save_language_mode = make_scoped_restore (&language_mode);
+    language_mode = language_mode_manual;
+
+    /* Note: we must not try to insert locations until after all
+       breakpoints have been re-set.  Otherwise, e.g., when re-setting
+       breakpoint 1, we'd insert the locations of breakpoint 2, which
+       hadn't been re-set yet, and thus may have stale locations.  */
+
+    try
+      {
+       input_radix = b->input_radix;
+       set_language (b->language);
+       b->re_set (filter_pspace);
+      }
+    catch (const gdb_exception &ex)
+      {
+       exception_fprintf (gdb_stderr, ex,
+                          "Error in re-setting breakpoint %d: ",
+                          b->number);
+      }
+  }
+
+  /* Now we can insert.  */
+  update_global_location_list (UGLL_MAY_INSERT);
+}
 \f
 /* Reset the thread number of this breakpoint:
 
index 77b754fc7dad683faa0dfc62cbcb52bf1d7666ea..cef1d727ed256eed919914646e40a2fa06715ec8 100644 (file)
@@ -568,15 +568,15 @@ enum print_stop_action
 
 struct breakpoint_ops
 {
-  /* Create SALs from location spec, storing the result in
-     linespec_result.
-
-     For an explanation about the arguments, see the function
-     `create_sals_from_location_spec_default'.
+  /* Create SALs from LOCSPEC, storing the result in linespec_result
+     CANONICAL.  If SEARCH_PSPACE is not nullptr then only results in the
+     corresponding program space are returned.  If SEARCH_PSPACE is nullptr
+     then results for all program spaces are returned.
 
      This function is called inside `create_breakpoint'.  */
   void (*create_sals_from_location_spec) (location_spec *locspec,
-                                         struct linespec_result *canonical);
+                                         linespec_result *canonical,
+                                         program_space *search_pspace);
 
   /* This method will be responsible for creating a breakpoint given its SALs.
      Usually, it just calls `create_breakpoints_sal' (for ordinary
@@ -708,10 +708,19 @@ struct breakpoint : public intrusive_list_node<breakpoint>
 
   /* Reevaluate a breakpoint.  This is necessary after symbols change
      (e.g., an executable or DSO was loaded, or the inferior just
-     started).  This is pure virtual as, at a minimum, each sub-class must
-     recompute any cached condition expressions based off of the
-     cond_string member variable.  */
-  virtual void re_set () = 0;
+     started).
+
+     If not nullptr, then FILTER_PSPACE is the program space in which
+     symbols may have changed, we only need to add new locations in
+     FILTER_PSPACE.
+
+     If FILTER_PSPACE is nullptr then all program spaces may have changed,
+     new locations need to be searched for in every program space.
+
+     This is pure virtual as, at a minimum, each sub-class must recompute
+     any cached condition expressions based off of the cond_string member
+     variable.  */
+  virtual void re_set (program_space *filter_pspace) = 0;
 
   /* Insert the breakpoint or watchpoint or activate the catchpoint.
      Return 0 for success, 1 if the breakpoint, watchpoint or
@@ -952,7 +961,7 @@ struct code_breakpoint : public breakpoint
   /* Add a location for SAL to this breakpoint.  */
   bp_location *add_location (const symtab_and_line &sal);
 
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   int insert_location (struct bp_location *) override;
   int remove_location (struct bp_location *,
                       enum remove_bp_reason reason) override;
@@ -974,7 +983,7 @@ protected:
      struct program_space *search_pspace);
 
   /* Helper method that does the basic work of re_set.  */
-  void re_set_default ();
+  void re_set_default (program_space *pspace);
 
   /* Find the SaL locations corresponding to the given LOCATION.
      On return, FOUND will be 1 if any SaL was found, zero otherwise.  */
@@ -996,7 +1005,7 @@ struct watchpoint : public breakpoint
 {
   using breakpoint::breakpoint;
 
-  void re_set () override;
+  void re_set (program_space *pspace) override;
   int insert_location (struct bp_location *) override;
   int remove_location (struct bp_location *,
                       enum remove_bp_reason reason) override;
@@ -1140,7 +1149,7 @@ struct catchpoint : public breakpoint
 
   /* If the catchpoint has a condition set then recompute the cached
      expression within the single dummy location.  */
-  void re_set () override;
+  void re_set (program_space *filter_pspace) override;
 };
 
 \f
index f736994d2343f0559a275ee5a33169b3b5ec020e..938e6deec05e643939622b219c918e47fac81caa 100644 (file)
@@ -76,8 +76,12 @@ foreach_mi_ui_mode mode {
     set loc2 [make_bp_loc "$::decimal\\.2"]
 
     # Create the inferior-specific breakpoint.
-    mi_create_breakpoint_multi "-g i2 foo" "create breakpoint in inferior 2" \
-       -inferior "2" -locations "\\\[$loc1,$loc2\\\]"
+    mi_create_breakpoint "-g i2 foo" "create breakpoint in inferior 2" \
+       -number "$decimal" \
+       -type "breakpoint" \
+       -enabled "y" \
+       -func "foo" \
+       -inferior "2"
     set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID"]
 
     if {$mode eq "separate"} {
index 93b91b42f92ac9f17e9ae6d8712e66688d37e574..e168a5eee45f261e6799f2f9992c1c6811a5a9a8 100644 (file)
@@ -364,15 +364,9 @@ proc test_continue_to_start { mode inf } {
 
                # Consume MI output.
                with_spawn_id $mi_spawn_id {
-                   if { $inf == 1} {
-                       mi_expect_stop "breakpoint-hit" "child_sub_function" \
-                           "" "$srcfile" "$decimal" {"" "disp=\"del\""} \
-                           "thread $inf.2 stops MI"
-                   } else {
-                       mi_expect_stop "breakpoint-hit" "child_sub_function" \
-                           "" "$srcfile" "$decimal" {"" "disp=\"del\"" "locno=\"[0-9]+\""} \
-                           "thread $inf.2 stops MI"
-                   }
+                   mi_expect_stop "breakpoint-hit" "child_sub_function" \
+                       "" "$srcfile" "$decimal" {"" "disp=\"del\""} \
+                       "thread $inf.2 stops MI"
                }
            }
        }
@@ -439,7 +433,7 @@ proc_with_prefix test_setup { mode } {
 
        with_spawn_id $mi_spawn_id {
            mi_expect_stop "breakpoint-hit" "main" "" "$srcfile" "$decimal" \
-               {"" "disp=\"del\"" "locno=\"[0-9]+\""} "main stop"
+               {"" "disp=\"del\""} "main stop"
        }
 
        # Consume CLI output.
index 7635e84b913beefd79136a0080c0546b174b924a..c1d87521ee9925cd39254554e4da9befeef8f7c1 100644 (file)
@@ -50,7 +50,7 @@ gdb_test "info threads" \
 # locations ('foo' in both inferiors) even though only one of those
 # locations will ever trigger ('foo' in inferior 2).
 gdb_test "break foo thread 2.1" \
-    "Breakpoint $decimal at $hex: foo\\. \\(2 locations\\)"
+    "Breakpoint $decimal at $hex: file \[^\r\n\]+$srcfile, line $decimal\\."
 
 set bpnum [get_integer_valueof "\$bpnum" "INVALID"]
 
@@ -58,10 +58,7 @@ set bpnum [get_integer_valueof "\$bpnum" "INVALID"]
 # earlier breakpoint.  Check that the thread-id used when describing
 # the earlier breakpoints is correct.
 gdb_test "break foo thread 1.1" \
-    [multi_line \
-        "Note: breakpoint $bpnum \\(thread 2.1\\) also set at pc $hex\\." \
-        "Note: breakpoint $bpnum \\(thread 2.1\\) also set at pc $hex\\." \
-        "Breakpoint $decimal at $hex: foo\\. \\(2 locations\\)"]
+    "Breakpoint $decimal at $hex: file \[^\r\n\]+$srcfile, line $decimal\\."
 
 # Save the breakpoints into a file.
 if {[is_remote host]} {
index 46efe6f54bcf3ca56e2b1e1a9ad74f6867e979f9..52f84183589b1d63f8c9480e7d96d61a74dec476 100644 (file)
@@ -105,16 +105,8 @@ proc check_info_breakpoints { testname bp_number expected_loc_count } {
 # Create an inferior-specific breakpoint.  Use gdb_test instead of
 # gdb_breakpoint here as we want to check the breakpoint was placed in
 # multiple locations.
-#
-# Currently GDB still places inferior specific breakpoints into every
-# inferior, just like it does with thread specific breakpoints.
-# Hopefully this will change in the future, at which point, this test
-# will need updating.
-#
-# Two of these locations are in inferior 1, while the third is in
-# inferior 2.
 gdb_test "break foo inferior 1" \
-    "Breakpoint $decimal at $hex: foo\\. \\(3 locations\\)"
+    "Breakpoint $decimal at $hex: foo\\. \\(2 locations\\)"
 set bp_number [get_integer_valueof "\$bpnum" "INVALID" \
                  "get b/p number for inferior specific breakpoint"]
 
@@ -123,7 +115,7 @@ set location_count 0
 set saw_inf_cond false
 
 check_info_breakpoints "first check for inferior specific breakpoint" \
-    $bp_number 3
+    $bp_number 2
 
 # Create a multi-inferior breakpoint to stop at.
 gdb_breakpoint "stop_breakpt" message
index d2201061713bba15955de1d78ca303d11e4f197e..d4b2fc281331a5416a5743e331817d41b922010c 100644 (file)
@@ -30,7 +30,7 @@ proc test_continue {non-stop} {
 
     proc set_break {inf} {
        gdb_test "break function${inf} thread ${inf}.1" \
-       "Breakpoint .* function${inf}\\..*"
+           "Breakpoint ${::decimal} at ${::hex}: file .*, line ${::decimal}\\."
     }
 
     # Select inferior INF, and then run to a breakpoint on inferior
index 0aff708c0f35edcddfbcd3f458e493f92d9b3717..36f9d24a917765b0911d8fe62ab41487b0096183 100644 (file)
@@ -52,12 +52,12 @@ proc test_ping_pong_next {} {
     gdb_test "thread 1.1" "Switching to thread 1.1 .*"
 
     gdb_test "break $srcfile:$line1 thread 1.1" \
-       "Breakpoint .*$srcfile:$line1\\..*"
+       "Breakpoint .*$srcfile, line $line1\\."
 
     gdb_test "continue" "hit Breakpoint .*"
 
     gdb_test "break $srcfile:$line2 thread 2.1" \
-       "Breakpoint .*$srcfile:$line2\\..*"
+       "Breakpoint .*$srcfile, line $line2\\."
 
     # Now block inferior 1 and issue "next".  We should stop at the
     # breakpoint for inferior 2, given schedlock off.
index 5fcd1ef2e3952b07a256ee5498c5b0b813846ba6..12c0a84bb028481e77cc642379e6c3a94a5992e2 100644 (file)
@@ -129,10 +129,8 @@ proc do_bp_test { bp_type bp_pending } {
     } else {
        set bp_pattern_before \
            [multi_line \
-                "$bp_number\\s+breakpoint\\s+keep\\s+y\\s+<MULTIPLE>\\s*" \
-                "\\s+stop only in [string_to_regexp $bp_restriction]" \
-                "$bp_number\\.1\\s+y\\s+$::hex in $bp_func at \[^\r\n\]+ inf 1" \
-                "$bp_number\\.2\\s+y\\s+$::hex in $bp_func at \[^\r\n\]+ inf 2"]
+                "$bp_number\\s+breakpoint\\s+keep\\s+y\\s+$::hex in $bp_func at \[^\r\n\]+ inf 1" \
+                "\\s+stop only in [string_to_regexp $bp_restriction]"]
 
        set bp_pattern_after \
            [multi_line \
index 13f76f49102bc14b13850ba610b9b704866209dd..2a0644bf48efe4be0ab183f4d0dc88d3556473d1 100644 (file)
@@ -72,6 +72,48 @@ proc do_test_setup { inf_1_stop inf_2_stop } {
     return true
 }
 
+# Create a breakpoint on the function 'foo' in THREAD.  It is expected
+# that the breakpoint created will be pending, this is checked by
+# running the 'info breakpoints' command.
+#
+# Returns the number for the newly created breakpoint.
+proc do_create_pending_foo_breakpoint { {thread "1.1"} } {
+    gdb_test "break foo thread $thread" \
+       [multi_line \
+            "Function \"foo\" not defined\\." \
+            "Breakpoint $::decimal \\(foo\\) pending\."] \
+       "set pending thread-specific breakpoint"
+    set bpnum [get_integer_valueof "\$bpnum" "*INVALID*" \
+                  "get number for thread-specific breakpoint on foo"]
+    gdb_test "info breakpoints $bpnum" \
+       [multi_line \
+            "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
+            "\\s+stop only in thread [string_to_regexp $thread]"] \
+       "check thread-specific breakpoint is initially pending"
+
+    return $bpnum
+}
+
+# Create a breakpoint on the function 'foo' in THREAD.  It is expected
+# that the breakpoint created will not be pending, this is checked by
+# running the 'info breakpoints' command.
+#
+# Returns the number for the newly created breakpoint.
+proc do_create_foo_breakpoint { {thread "1.1"} } {
+    gdb_test "break foo thread $thread" \
+       "Breakpoint $::decimal at $::hex" \
+       "set thread-specific breakpoint"
+    set bpnum [get_integer_valueof "\$bpnum" "*INVALID*" \
+                  "get number for thread-specific breakpoint on foo"]
+    gdb_test "info breakpoints $bpnum" \
+       [multi_line \
+            "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s+<foo\[^>\]*> inf $::decimal" \
+            "\\s+stop only in thread [string_to_regexp $thread]"] \
+       "check thread-specific breakpoint is initially pending"
+
+    return $bpnum
+}
+
 # Check that when a breakpoint is in the pending state, but that breakpoint
 # does have some locations (those locations themselves are pending), GDB
 # doesn't display the inferior list in the 'info breakpoints' output.
@@ -122,5 +164,169 @@ proc_with_prefix test_no_inf_display {} {
        "check info breakpoints while breakpoint is pending"
 }
 
+# Setup two inferiors.  In #1 the symbol 'foo' has not yet been
+# loaded, while in #2 the symbol 'foo' has been loaded.
+#
+# Create a thread-specific breakpoint on 'foo' tied to a thread in
+# inferior #1, the breakpoint should be pending -- 'foo' is not yet
+# loaded in #1.
+#
+# Now move inferior #1 forward until 'foo' is loaded, check the
+# breakpoint is no longer pending.
+#
+# Move inferior #1 forward more until 'foo' is unloaded, check that
+# the breakpoint returns to the pending state.
+proc_with_prefix test_pending_toggle { } {
+
+    do_test_setup "Break before open" "Break before close"
+
+    set bpnum [do_create_pending_foo_breakpoint]
+
+    # Now return to inferior 1 and continue until the shared library is
+    # loaded, the breakpoint should become non-pending.
+    gdb_test "inferior 1" "Switching to inferior 1 .*" \
+       "switch back to inferior 1"
+    gdb_continue_to_breakpoint "stop in foo in inferior 1" "foo \\(\\) .*"
+
+    gdb_test "info breakpoint $bpnum" \
+       [multi_line \
+            "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex <foo\[^>\]*> inf 1" \
+            "\\s+stop only in thread 1\\.1" \
+            "\\s+breakpoint already hit 1 time"] \
+       "check thread-specific breakpoint is no longer pending"
+
+    gdb_breakpoint [gdb_get_line_number "Break after close"] temporary
+    gdb_continue_to_breakpoint "close library"
+    gdb_test "info breakpoints $bpnum" \
+       [multi_line \
+            "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
+            "\\s+stop only in thread 1\\.1" \
+            "\\s+breakpoint already hit 1 time"] \
+       "check thread-specific breakpoint is pending again"
+}
+
+# Create a Python variable VAR and set it to the gdb.Breakpoint object
+# corresponding to the breakpoint numbered BPNUM.  If THREAD is not
+# the empty string then THREAD should be an integer, check that
+# gdb.Breakpoint.thread is set to the value of THREAD.  Otherwise, if
+# THREAD is the empty string, check that gdb.Breakpoint.thread is set
+# to None.
+proc py_find_breakpoint { var bpnum {thread ""} } {
+    gdb_test_no_output \
+       "python ${var}=\[b for b in gdb.breakpoints() if b.number == $bpnum\]\[0\]" \
+       "find Python gdb.Breakpoint object"
+    if { $thread ne "" } {
+       gdb_test_no_output "python assert(${var}.thread == ${thread})" \
+           "check thread attribute is currently correct"
+    } else {
+       gdb_test_no_output "python assert(${var}.thread is None)" \
+           "check thread attribute is currently correct"
+    }
+}
+
+# Setup two inferiors.  In #1 the symbol 'foo' has not yet been
+# loaded, while in #2 the symbol 'foo' has been loaded.
+#
+# Create a thread-specific breakpoint on 'foo' tied to a thread in
+# inferior #1, the breakpoint should be pending -- 'foo' is not yet
+# loaded in #1.
+#
+# Use Python to change the thread of the thread-specific breakpoint to
+# a thread in inferior #2, at this point the thread should gain a
+# location and become non-pending.
+#
+# Set the thread back to a thread in inferior #1, the breakpoint
+# should return to the pending state.
+proc_with_prefix py_test_toggle_thread {} {
+    do_test_setup "Break before open" "Break after open"
+
+    set bpnum [do_create_pending_foo_breakpoint]
+
+    py_find_breakpoint "bp" $bpnum 1
+
+    gdb_test_no_output "python bp.thread = 2" \
+       "change thread on thread-specific breakpoint"
+    gdb_test "info breakpoint $bpnum" \
+       [multi_line \
+            "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex <foo\[^>\]*> inf 2" \
+            "\\s+stop only in thread 2\\.1"] \
+       "check thread-specific breakpoint now has a location"
+
+    gdb_test_no_output "set call_count = 2" "set call_count in inferior 2"
+    gdb_continue_to_breakpoint "stop at foo in inferior 2" "foo \\(\\) .*"
+
+    gdb_test_no_output "python bp.thread = 1" \
+       "restore thread on thread-specific breakpoint"
+    gdb_test "info breakpoints $bpnum" \
+       [multi_line \
+            "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+foo" \
+            "\\s+stop only in thread 1\\.1" \
+            "\\s+breakpoint already hit 1 time"] \
+       "check thread-specific breakpoint has returned to pending"
+
+    gdb_breakpoint [gdb_get_line_number "Break after close"] temporary
+    gdb_continue_to_breakpoint "stop after close in inferior 2" \
+       ".* Break after close\\. .*"
+
+    gdb_test "inferior 1" "Switching to inferior 1 .*" \
+       "switch to inferior 1"
+    gdb_continue_to_breakpoint "stop at foo in inferior 1" "foo \\(\\) .*"
+}
+
+# Setup two inferiors.  Both inferiors have the symbol 'foo'
+# available.
+#
+# Create a thread-specific breakpoint on 'foo' tied to a thread in
+# inferior #1, the breakpoint should not be pending, but will only
+# have a single location, the location in inferior #1.
+#
+# Use Python to change the thread of the thread-specific breakpoint to
+# None.  At this point the breakpoint should gain a second location, a
+# location in inferior #2.
+proc_with_prefix py_test_clear_thread {} {
+    do_test_setup "Break after open" "Break after open"
+
+    set bpnum [do_create_foo_breakpoint]
+
+    py_find_breakpoint "bp" $bpnum 1
+
+    gdb_test_no_output "python bp.thread = None" \
+       "clear thread on thread-specific breakpoint"
+    gdb_test "info breakpoints $bpnum" \
+       [multi_line \
+            "${bpnum}\\s+breakpoint\\s+keep y\\s+<MULTIPLE>\\s*" \
+            "${bpnum}\\.1\\s+y\\s+${::hex}\\s+<foo\[^>\]*> inf $::decimal" \
+            "${bpnum}\\.2\\s+y\\s+${::hex}\\s+<foo\[^>\]*> inf $::decimal"] \
+       "check for a location in both inferiors"
+
+    gdb_continue_to_breakpoint "stop at foo in inferior 2" "foo \\(\\) .*"
+    gdb_test_no_output "set call_count = 2" "set call_count in inferior 2"
+
+    gdb_test "inferior 1" "Switching to inferior 1 .*" \
+       "switch to inferior 1"
+    gdb_continue_to_breakpoint "stop at foo in inferior 1" "foo \\(\\) .*"
+    gdb_test_no_output "set call_count = 2" "set call_count in inferior 1"
+
+    gdb_test_no_output "python bp.thread = 2"
+    gdb_test "info breakpoints $bpnum" \
+       [multi_line \
+            "${bpnum}\\s+breakpoint\\s+keep y\\s+${::hex}\\s+<foo\[^>\]*> inf 2" \
+            "\\s+stop only in thread 2\\.1" \
+            "\\s+breakpoint already hit 2 times"] \
+       "check for a location only in inferior 2"
+
+    gdb_breakpoint [gdb_get_line_number "Break after close"] temporary
+    gdb_continue_to_breakpoint "stop after close in inferior 1" \
+       ".* Break after close\\. .*"
+
+    gdb_test "inferior 2" "Switching to inferior 2 .*" \
+       "switch back to inferior 2"
+    gdb_continue_to_breakpoint "stop at foo again in inferior 2" \
+       "foo \\(\\) .*"
+}
+
 # Run all the tests.
 test_no_inf_display
+test_pending_toggle
+py_test_toggle_thread
+py_test_clear_thread
index 573b02fdd428e49c75ef8b2076e2dfab46a9e0db..4f788844ee4e1ddcbc2bdb1fd4c63e1f6a72dc68 100644 (file)
@@ -433,11 +433,13 @@ if { [allow_python_tests] } {
 
        gdb_py_test_silent_cmd "python bp = gdb.breakpoints()\[0\]" \
            "get python breakpoint" 0
-       gdb_test "python bp.thread = 6" "thread = 6" \
+       gdb_test_no_output "python bp.thread = 6" \
            "make breakpoint thread-specific with python"
        # Check that the inferior-qualified ID is correct.
        gdb_test "info breakpoint" \
-           "stop only in thread 1.3\r\n.*" \
+           [multi_line \
+                "$decimal\\s+\[^\r\n\]+ in thread_function1 at \[^\r\n\]+" \
+                "\\s+stop only in thread 1\\.3"] \
            "thread specific breakpoint right thread"
     }
 }