[v7,2/2] gdb, breakpoint: output multiple bp locations

Message ID 20241029113343.3945855-3-klaus.gerlicher@intel.com
State New
Headers
Series gdb: setting BP with multiple locations only displays one location |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Test failed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Test failed

Commit Message

Klaus Gerlicher Oct. 29, 2024, 11:33 a.m. UTC
  From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>

When setting a breakpoint that resolves to multiple locations
only the address of the first location is printed but multiple
locations are indicated.

See this example:

01 template <typename T>
02 T
03 f (T t)
04 {
05   return t;
06 }
07
08 int
09 main (void)
10 {
11   return f<int> (1) + f<float> (1.0f) + f<char> (1)
12	    + f<short> (1) + f<bool> (1);
13 }

Setting a breakpoint on line 5 will yield 5 locations:

(gdb) b 5
Breakpoint 1 at 0x11cf: main.cpp:5. (5 locations)

Even though this indicates multiple locations, it only shows
the first one. A better way would be:

Set how many locations to be printed, then...
(gdb) set breakpoint max-breakpoint-locations-printed 4

... set breakpoint.

(gdb) b 5
Breakpoint 1 for main.cpp:5 at 5 locations:
    Location 1 at 0x11cf in function f<int>(int) in file main.cpp, line 5.
    Location 2 at 0x11e1 in function f<float>(float) in file main.cpp, line 5.
    Location 3 at 0x11f5 in function f<char>(char) in file main.cpp, line 5.
    Location 4 at 0x1209 in function f<short>(short) in file main.cpp, line 5.
1 additional location not printed.

Make the number of breakpoint locations printed configurable. Introduce
the "set breakpoint max-breakpoint-locations-printed" command.

Convert tests that use gdb_test "break" to using gdb_breakpoint because
previously the "break" output was parseable with a single regex. Now that
it has become multi-line, tests that were previously creating multiple
locations unknowingly need to be fixed to use the right regex which is
simpler (and more properly) done by using the gdb_breakpoint. gdb_breakpoint
knows which regex to use for any case.

This also adds two more options to gdb_breakpoint, -add_locs and -inferior:

  -add_locs is used for testing the additional print when there are more
            locations then allowed to be printed with the "set
            max-breakpoint-locations-printed" command.
  -inferior is used to match "on inferior X" output when multiple locations
            are on different inferiors.
---
 gdb/NEWS                                      |   4 +
 gdb/breakpoint.c                              | 135 ++++++++++++++----
 gdb/doc/gdb.texinfo                           |  45 +++++-
 gdb/testsuite/gdb.base/ctxobj.exp             |   4 +-
 .../run-control-while-bg-execution.exp        |   2 +-
 gdb/testsuite/gdb.cp/ovldbreak.exp            |   6 +-
 gdb/testsuite/gdb.linespec/cpcompletion.exp   |   2 +
 gdb/testsuite/gdb.linespec/linespec.exp       |   2 +-
 gdb/testsuite/gdb.linespec/multiple-locs.cc   |  41 ++++++
 gdb/testsuite/gdb.linespec/multiple-locs.exp  |  57 ++++++++
 .../mi-breakpoint-multiple-locations.exp      |   4 +-
 .../gdb.mi/user-selected-context-sync.exp     |  16 +--
 .../gdb.multi/inferior-specific-bp.exp        |   2 +-
 .../gdb.multi/multi-target-continue.exp       |   3 +-
 .../gdb.multi/multi-target-ping-pong-next.exp |   6 +-
 gdb/testsuite/gdb.python/py-breakpoint.exp    |   3 +-
 gdb/testsuite/lib/completion-support.exp      |   3 +
 gdb/testsuite/lib/gdb.exp                     |  34 ++++-
 18 files changed, 308 insertions(+), 61 deletions(-)
 create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.cc
 create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.exp
  

Comments

Eli Zaretskii Oct. 29, 2024, 1:10 p.m. UTC | #1
> From: Klaus Gerlicher <klaus.gerlicher@intel.com>
> Date: Tue, 29 Oct 2024 11:33:43 +0000
> 
>  gdb/NEWS                                      |   4 +
>  gdb/breakpoint.c                              | 135 ++++++++++++++----
>  gdb/doc/gdb.texinfo                           |  45 +++++-
>  gdb/testsuite/gdb.base/ctxobj.exp             |   4 +-
>  .../run-control-while-bg-execution.exp        |   2 +-
>  gdb/testsuite/gdb.cp/ovldbreak.exp            |   6 +-
>  gdb/testsuite/gdb.linespec/cpcompletion.exp   |   2 +
>  gdb/testsuite/gdb.linespec/linespec.exp       |   2 +-
>  gdb/testsuite/gdb.linespec/multiple-locs.cc   |  41 ++++++
>  gdb/testsuite/gdb.linespec/multiple-locs.exp  |  57 ++++++++
>  .../mi-breakpoint-multiple-locations.exp      |   4 +-
>  .../gdb.mi/user-selected-context-sync.exp     |  16 +--
>  .../gdb.multi/inferior-specific-bp.exp        |   2 +-
>  .../gdb.multi/multi-target-continue.exp       |   3 +-
>  .../gdb.multi/multi-target-ping-pong-next.exp |   6 +-
>  gdb/testsuite/gdb.python/py-breakpoint.exp    |   3 +-
>  gdb/testsuite/lib/completion-support.exp      |   3 +
>  gdb/testsuite/lib/gdb.exp                     |  34 ++++-
>  18 files changed, 308 insertions(+), 61 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.cc
>  create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.exp

Thanks.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,10 @@
>  
>  *** Changes since GDB 15
>  
> +* Printing multiple breakpoint locations when setting a breakpoint that is
> +  instantiated for multiple locations now prints all locations limited by
> +  "set breakpoint max-breakpoint-location-printed <count>".
> +
>  * Debugging support for Intel MPX has been removed.  This includes the
>    removal of
>    ** MPX register support

This part is OK.

> +  add_setshow_uinteger_cmd ("max-breakpoint-locations-printed",
> +			    class_breakpoint,
> +			    &breakpoint_show_max_locations, _("\
> +Set number of locations printed when setting a breakpoint."), _("\
> +Show number of locations printed when setting a breakpoint."), _("\
> +Use this to choose how many number of locations are printed\n\
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Either "how many locations" or "the number of locations".

> +@kindex show breakpoint max-breakpoint-locations-printed
> +@item show breakpoint max-breakpoint-locations-printed
> +Display the number of locations that @code{break} will print.  If the number
> +of locations is greater than the limit, the additional number of locations
> +will be listed.
   ^^^^^^^^^^^^^^
I guess "will not be listed"?

The documentation is OK with those nits fixed.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
  
Klaus Gerlicher Oct. 29, 2024, 3:57 p.m. UTC | #2
Thanks Eli. Replies inline. I'll wait until I get some feedback from others and will roll fixes into a V8.

Klaus

> -----Original Message-----
> From: Eli Zaretskii <eliz@gnu.org>
> Sent: Tuesday, October 29, 2024 2:10 PM
> To: Gerlicher, Klaus <klaus.gerlicher@intel.com>
> Cc: gdb-patches@sourceware.org; aburgess@redhat.com;
> blarsen@redhat.com
> Subject: Re: [PATCH v7 2/2] gdb, breakpoint: output multiple bp locations
> 
> > From: Klaus Gerlicher <klaus.gerlicher@intel.com>
> > Date: Tue, 29 Oct 2024 11:33:43 +0000
> >
> >  gdb/NEWS                                      |   4 +
> >  gdb/breakpoint.c                              | 135 ++++++++++++++----
> >  gdb/doc/gdb.texinfo                           |  45 +++++-
> >  gdb/testsuite/gdb.base/ctxobj.exp             |   4 +-
> >  .../run-control-while-bg-execution.exp        |   2 +-
> >  gdb/testsuite/gdb.cp/ovldbreak.exp            |   6 +-
> >  gdb/testsuite/gdb.linespec/cpcompletion.exp   |   2 +
> >  gdb/testsuite/gdb.linespec/linespec.exp       |   2 +-
> >  gdb/testsuite/gdb.linespec/multiple-locs.cc   |  41 ++++++
> >  gdb/testsuite/gdb.linespec/multiple-locs.exp  |  57 ++++++++
> >  .../mi-breakpoint-multiple-locations.exp      |   4 +-
> >  .../gdb.mi/user-selected-context-sync.exp     |  16 +--
> >  .../gdb.multi/inferior-specific-bp.exp        |   2 +-
> >  .../gdb.multi/multi-target-continue.exp       |   3 +-
> >  .../gdb.multi/multi-target-ping-pong-next.exp |   6 +-
> >  gdb/testsuite/gdb.python/py-breakpoint.exp    |   3 +-
> >  gdb/testsuite/lib/completion-support.exp      |   3 +
> >  gdb/testsuite/lib/gdb.exp                     |  34 ++++-
> >  18 files changed, 308 insertions(+), 61 deletions(-)
> >  create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.cc
> >  create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.exp
> 
> Thanks.
> 
> > --- a/gdb/NEWS
> > +++ b/gdb/NEWS
> > @@ -3,6 +3,10 @@
> >
> >  *** Changes since GDB 15
> >
> > +* Printing multiple breakpoint locations when setting a breakpoint that is
> > +  instantiated for multiple locations now prints all locations limited by
> > +  "set breakpoint max-breakpoint-location-printed <count>".
> > +
> >  * Debugging support for Intel MPX has been removed.  This includes the
> >    removal of
> >    ** MPX register support
> 
> This part is OK.
> 


Thank you.

> > +  add_setshow_uinteger_cmd ("max-breakpoint-locations-printed",
> > +			    class_breakpoint,
> > +			    &breakpoint_show_max_locations, _("\
> > +Set number of locations printed when setting a breakpoint."), _("\
> > +Show number of locations printed when setting a breakpoint."), _("\
> > +Use this to choose how many number of locations are printed\n\
>                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> Either "how many locations" or "the number of locations".
> 

I think I had looked at similar commands and tried to copy something verbatim 
and I failed miserably. Thanks for catching this.

> > +@kindex show breakpoint max-breakpoint-locations-printed
> > +@item show breakpoint max-breakpoint-locations-printed
> > +Display the number of locations that @code{break} will print.  If the
> number
> > +of locations is greater than the limit, the additional number of locations
> > +will be listed.
>    ^^^^^^^^^^^^^^
> I guess "will not be listed"?
> 

What I meant to say is that there will be a line of output that tells the user
how many more locations apart from the listed one there are.

So maybe:

"If the number of locations is greater than the limit, the breakpoint command will 
add a line reporting the number of locations that were not listed due to the limit.

Or maybe you have a better way of phrasing this?

> The documentation is OK with those nits fixed.
> 
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Eli Zaretskii Oct. 29, 2024, 4:53 p.m. UTC | #3
> From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>
> CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
> 	"aburgess@redhat.com" <aburgess@redhat.com>, "blarsen@redhat.com"
> 	<blarsen@redhat.com>
> Date: Tue, 29 Oct 2024 15:57:47 +0000
> 
> > > +@kindex show breakpoint max-breakpoint-locations-printed
> > > +@item show breakpoint max-breakpoint-locations-printed
> > > +Display the number of locations that @code{break} will print.  If the
> > number
> > > +of locations is greater than the limit, the additional number of locations
> > > +will be listed.
> >    ^^^^^^^^^^^^^^
> > I guess "will not be listed"?
> > 
> 
> What I meant to say is that there will be a line of output that tells the user
> how many more locations apart from the listed one there are.
> 
> So maybe:
> 
> "If the number of locations is greater than the limit, the breakpoint command will 
> add a line reporting the number of locations that were not listed due to the limit.

Yes, that's much better.  Bonus points for showing an example of such
output.

Thanks.
  
Klaus Gerlicher Nov. 4, 2024, 10:03 a.m. UTC | #4
> -----Original Message-----
> From: Eli Zaretskii <eliz@gnu.org>
> Sent: Tuesday, October 29, 2024 5:54 PM
> To: Gerlicher, Klaus <klaus.gerlicher@intel.com>
> Cc: gdb-patches@sourceware.org; aburgess@redhat.com;
> blarsen@redhat.com
> Subject: Re: [PATCH v7 2/2] gdb, breakpoint: output multiple bp locations
> 
> > From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>
> > CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
> > 	"aburgess@redhat.com" <aburgess@redhat.com>,
> "blarsen@redhat.com"
> > 	<blarsen@redhat.com>
> > Date: Tue, 29 Oct 2024 15:57:47 +0000
> >
> > > > +@kindex show breakpoint max-breakpoint-locations-printed
> > > > +@item show breakpoint max-breakpoint-locations-printed
> > > > +Display the number of locations that @code{break} will print.  If the
> > > number
> > > > +of locations is greater than the limit, the additional number of locations
> > > > +will be listed.
> > >    ^^^^^^^^^^^^^^
> > > I guess "will not be listed"?
> > >
> >
> > What I meant to say is that there will be a line of output that tells the user
> > how many more locations apart from the listed one there are.
> >
> > So maybe:
> >
> > "If the number of locations is greater than the limit, the breakpoint
> command will
> > add a line reporting the number of locations that were not listed due to the
> limit.
> 
> Yes, that's much better.  Bonus points for showing an example of such
> output.

The GDB doc has an example right above this text. Is there a way to easily point to this example? 
Should I maybe add a line that says, "see last line of the small example above"?

Thanks
Klaus
> 
> Thanks.

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
  
Eli Zaretskii Nov. 4, 2024, 1:22 p.m. UTC | #5
> From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>
> CC: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
> 	"aburgess@redhat.com" <aburgess@redhat.com>, "blarsen@redhat.com"
> 	<blarsen@redhat.com>
> Date: Mon, 4 Nov 2024 10:03:15 +0000
> 
> > > So maybe:
> > >
> > > "If the number of locations is greater than the limit, the breakpoint
> > command will
> > > add a line reporting the number of locations that were not listed due to the
> > limit.
> > 
> > Yes, that's much better.  Bonus points for showing an example of such
> > output.
> 
> The GDB doc has an example right above this text. Is there a way to easily point to this example? 
> Should I maybe add a line that says, "see last line of the small example above"?

Yes, thanks.
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 42b8a88fd8a..bae2fa9b93f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@ 
 
 *** Changes since GDB 15
 
+* Printing multiple breakpoint locations when setting a breakpoint that is
+  instantiated for multiple locations now prints all locations limited by
+  "set breakpoint max-breakpoint-location-printed <count>".
+
 * Debugging support for Intel MPX has been removed.  This includes the
   removal of
   ** MPX register support
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b7e4f5d0a45..7845d696de9 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -170,6 +170,19 @@  static bool bl_address_is_meaningful (bp_location *loc);
 
 static int find_loc_num_by_location (const bp_location *loc);
 
+/* Maximum number of locations printed when setting a breakpoint.  Controlled
+   by "set breakpoint max-breakpoint-locations-printed <number>".  */
+
+static unsigned int breakpoint_show_max_locations = 10;
+static void
+show_max_breakpoint_location_printed (struct ui_file *file, int from_tty,
+		    struct cmd_list_element *c, const char *value)
+{
+  gdb_printf (file,
+	      _("Number of breakpoint location printed is %s.\n"),
+	      value);
+}
+
 /* update_global_location_list's modes of operation wrt to whether to
    insert locations now.  */
 enum ugll_insert_mode
@@ -11813,39 +11826,93 @@  code_breakpoint::say_where () const
     }
   else
     {
-      const bp_location &bl = this->first_loc ();
-      if (opts.addressprint || bl.symtab == nullptr)
-	gdb_printf (" at %ps",
-		    styled_string (address_style.style (),
-				   paddress (bl.gdbarch,
-					     bl.address)));
-      if (bl.symtab != NULL)
-	{
-	  /* If there is a single location, we can print the location
-	     more nicely.  */
-	  if (!this->has_multiple_locations ())
+      if (this->has_multiple_locations ())
+	{
+	  int n = std::distance (m_locations.begin (), m_locations.end ());
+
+	  gdb_printf (" for %s at %d locations:\n", locspec->to_string (), n);
+	  int loc_idx = 0;
+	  for (const bp_location &bl : m_locations)
+	    {
+	      gdb_printf ("    Location %d", loc_idx + 1);
+	      if (opts.addressprint || bl.symtab == nullptr)
+		gdb_printf (" at %ps", styled_string (address_style.style (),
+						      paddress (bl.gdbarch,
+								bl.address)));
+
+	      if (bl.symtab != nullptr)
+		{
+		  const struct symbol *sym = bl.symbol;
+		  if (sym != nullptr)
+		    {
+		      gdb_stdout->wrap_here (6);
+		      gdb_printf (" in function %ps",
+				  styled_string (function_name_style.style (),
+						 sym->print_name ()));
+		    }
+
+		  const char *filename
+		    = symtab_to_filename_for_display (bl.symtab);
+		  gdb_stdout->wrap_here (6);
+		  gdb_printf (": file %ps, line %ps",
+			      styled_string (file_name_style.style (),
+					     filename),
+			      styled_string (line_number_style.style (),
+					     pulongest (bl.line_number)));
+		}
+
+	      if (all_inferiors ().size () > 1)
+		{
+		  for (const auto &inf : all_inferiors ())
+		    if (inf->pspace == bl.pspace)
+		      gdb_printf (" in inferior %d", inf->num);
+		}
+	      else if (bl.owner->inferior != -1)
+		gdb_printf (" in inferior %d", bl.owner->inferior);
+
+	      loc_idx++;
+
+	      gdb_printf (".");
+	      if (loc_idx < n)
+		gdb_printf ("\n");
+
+	      /* The user (or default) has selected a maximum number of
+		 locations to be printed.  */
+	      if (loc_idx >= breakpoint_show_max_locations)
+		break;
+	    }
+
+	  /* We printed less locations than there are present, let the user
+	     know how many more there are.  */
+	  if (loc_idx < n)
 	    {
-	      const char *filename
-		= symtab_to_filename_for_display (bl.symtab);
-	      gdb_printf (": file %ps, line %ps.",
-			  styled_string (file_name_style.style (),
-					 filename),
-			  styled_string (line_number_style.style (),
-					 pulongest (bl.line_number)));
+	      int omitted_locs = n - loc_idx;
+	      if (omitted_locs == 1)
+		gdb_printf (_("%u additional location not printed."),
+			    omitted_locs);
+	      else
+		gdb_printf (_("%u additional locations not printed."),
+			    omitted_locs);
 	    }
-	  else
-	    /* This is not ideal, but each location may have a
-	       different file name, and this at least reflects the
-	       real situation somewhat.  */
-	    gdb_printf (": %s.", locspec->to_string ());
 	}
-
-      if (this->has_multiple_locations ())
+      else
 	{
-	  int n = std::distance (m_locations.begin (), m_locations.end ());
-	  gdb_printf (" (%d locations)", n);
+	  const bp_location &bl = this->first_loc ();
+	  if (opts.addressprint || bl.symtab == nullptr)
+	    gdb_printf (" at %ps",
+			styled_string (address_style.style (),
+				       paddress (bl.gdbarch, bl.address)));
+	  if (bl.symtab != nullptr)
+	    {
+		const char *filename
+		  = symtab_to_filename_for_display (bl.symtab);
+		gdb_printf (": file %ps, line %ps.",
+			    styled_string (file_name_style.style (), filename),
+			    styled_string (line_number_style.style (),
+					   pulongest (bl.line_number)));
+	    }
 	}
-    }
+      }
 }
 
 /* See breakpoint.h.  */
@@ -15254,6 +15321,18 @@  Usage: agent-printf \"format string\", ARG1, ARG2, ARG3, ..., ARGN\n\
 This supports most C printf format specifications, like %s, %d, etc.\n\
 This is useful for formatted output in user-defined commands."));
 
+  add_setshow_uinteger_cmd ("max-breakpoint-locations-printed",
+			    class_breakpoint,
+			    &breakpoint_show_max_locations, _("\
+Set number of locations printed when setting a breakpoint."), _("\
+Show number of locations printed when setting a breakpoint."), _("\
+Use this to choose how many number of locations are printed\n\
+when setting a breakpoint.\n\
+A value of \"unlimited\", or zero, means there's no limit."),
+			    nullptr, /* set_cmd.  */
+			    show_max_breakpoint_location_printed,
+			    &breakpoint_set_cmdlist, &breakpoint_show_cmdlist);
+
   automatic_hardware_breakpoints = true;
 
   gdb::observers::about_to_proceed.attach (breakpoint_about_to_proceed,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index eb2aff974bb..a03ceccd5fa 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4518,16 +4518,47 @@  $1 = 1
 
 A breakpoint may be mapped to multiple code locations for example with
 inlined functions, Ada generics, C@t{++} templates or overloaded function names.
-@value{GDBN} then indicates the number of code locations in the breakpoint
+@value{GDBN} then lists the number of code locations in the breakpoint
 command output:
 @smallexample
 (gdb) b some_func
-Breakpoint 2 at 0x1179: some_func. (3 locations)
+Breakpoint 2 for some_func at 3 locations:
+    Location 1 at 0x118e in function some_func in file somefunc.cc, line 23.
+    Location 2 at 0x11ee in function some_func in file somefunc.cc, line 38.
+    Location 3 at 0x12fe in function some_func in file someother.cc, line 58.
 (gdb) p $bpnum
 $2 = 2
 (gdb)
 @end smallexample
 
+@cindex @code{break}, how many locations are displayed
+By default, @value{GDBN} prints up to ten breakpoint locations, you can change
+this using @code{set breakpoint max-breakpoint-locations-printed}:
+
+@smallexample
+(gdb) set breakpoint max-breakpoint-locations-printed 2
+(gdb) b some_func
+Breakpoint 2 for some_func at 3 locations:
+    Location 1 at 0x118e in function some_func in file somefunc.cc, line 23.
+    Location 2 at 0x11ee in function some_func in file somefunc.cc, line 38.
+1 additional location not printed.
+(gdb)
+@end smallexample
+
+@table @code
+@kindex set breakpoint max-breakpoint-locations-printed
+@item set breakpoint max-breakpoint-locations-printed @var{count}
+@itemx set breakpoint max-breakpoint-locations-printed unlimited
+Make the @code{break} command display up to @var{count} locations.
+Setting @var{count} to @code{unlimited} or 0 means there's no limit.
+
+@kindex show breakpoint max-breakpoint-locations-printed
+@item show breakpoint max-breakpoint-locations-printed
+Display the number of locations that @code{break} will print.  If the number
+of locations is greater than the limit, the additional number of locations
+will be listed.
+@end table
+
 @vindex $_hit_bpnum@r{, convenience variable}
 @vindex $_hit_locno@r{, convenience variable}
 When your program stops on a breakpoint, the convenience variables
@@ -4637,7 +4668,10 @@  warning: failed to validate condition at location 0x11ce, disabling:
   No symbol "a" in current context.
 warning: failed to validate condition at location 0x11b6, disabling:
   No symbol "a" in current context.
-Breakpoint 1 at 0x11b6: func. (3 locations)
+Breakpoint 1 for func at 3 locations:
+    Location 1 at 0x11ce in function func in file somefunc.cc, line 23.
+    Location 2 at 0x11b6 in function func in file somefunc.cc, line 38.
+    Location 3 at 0x12fe in function func in file someother.cc, line 58.
 @end smallexample
 
 Locations that are disabled because of the condition are denoted by an
@@ -4681,7 +4715,10 @@  warning: failed to validate condition at location 2, disabling:
   No symbol "foo" in current context.
 warning: failed to validate condition at location 3, disabling:
   No symbol "foo" in current context.
-Breakpoint 1 at 0x1158: test.c:18. (3 locations)
+Breakpoint 1 for func at 3 locations:
+    Location 1 at 0x1158 in function func in file test.c, line 18.
+    Location 2 at 0x11b6 in function func in file test.c, line 28.
+    Location 3 at 0x12fe in function func in file test.c, line 58.
 @end smallexample
 
 This causes all the present locations where the breakpoint would
diff --git a/gdb/testsuite/gdb.base/ctxobj.exp b/gdb/testsuite/gdb.base/ctxobj.exp
index d1137189108..e73b29fcf3a 100644
--- a/gdb/testsuite/gdb.base/ctxobj.exp
+++ b/gdb/testsuite/gdb.base/ctxobj.exp
@@ -61,9 +61,7 @@  if ![runto_main] {
 }
 
 set bp_location [gdb_get_line_number "STOP" "ctxobj-f.c"]
-gdb_test "break ctxobj-f.c:$bp_location" \
-         "Breakpoint \[0-9\]+ at 0x\[0-9a-fA-F\]+: .*" \
-         "break in get_version functions"
+gdb_breakpoint "ctxobj-f.c:$bp_location"
 
 global expect_out
 set test "continue to get_version_1"
diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
index 380047ae854..fd395f10387 100644
--- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
+++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
@@ -90,7 +90,7 @@  proc do_test { action1 action2 } {
     if { $action2 == "start" } {
 	gdb_test "start" "Temporary breakpoint $::decimal\(?:\.$::decimal\)?, main .*"
     } elseif { $action2 == "run" } {
-	gdb_test "break main" "Breakpoint $::decimal at $::hex.*"
+	gdb_breakpoint "main"
 	gdb_test "run" "Breakpoint $::decimal\(?:\.$::decimal\)?, main .*"
     } elseif { $action2 == "attach" } {
 	set test_spawn_id [spawn_wait_for_attach $::binfile]
diff --git a/gdb/testsuite/gdb.cp/ovldbreak.exp b/gdb/testsuite/gdb.cp/ovldbreak.exp
index 84efb37b8db..1f5383e92e9 100644
--- a/gdb/testsuite/gdb.cp/ovldbreak.exp
+++ b/gdb/testsuite/gdb.cp/ovldbreak.exp
@@ -332,6 +332,10 @@  gdb_test "info breakpoints" "No breakpoints, watchpoints, tracepoints, or catchp
 # Test choice "all".
 # This is copy-and-paste from set_bp_overloaded.
 
+gdb_test_no_output "set breakpoint max-breakpoint-locations-printed 0"
+
+set breakmsg [gdb_multi_loc_regex "Breakpoint $decimal" 12]
+
 incr bpnum
 send_gdb "break foo::overload1arg\n" 
 gdb_expect {
@@ -340,7 +344,7 @@  gdb_expect {
         # Choose all.
         send_gdb "1\n"
         gdb_expect {
-	    -re "Breakpoint $bpnum at $hex: foo::overload1arg. .12 locations.\r\n.*$gdb_prompt $" {
+	    -re "$breakmsg$gdb_prompt $" {
                 pass "set bp on overload1arg all"
             }
             -re ".*$gdb_prompt $" {
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index 09bd9a25d77..49d59e79918 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -1397,4 +1397,6 @@  proc test_driver {} {
     if-expression
 }
 
+gdb_test_no_output "set breakpoint max-breakpoint-locations-printed 0"
+
 test_driver
diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
index 6398309f7e4..e5a0e205e9e 100644
--- a/gdb/testsuite/gdb.linespec/linespec.exp
+++ b/gdb/testsuite/gdb.linespec/linespec.exp
@@ -69,7 +69,7 @@  if { [is_remote host] } {
 
 		send_gdb "\n"
 		gdb_test "" \
-		    "Breakpoint $decimal at $hex: thefile.cc:twodup\\(\\). \[(\]2 locations\[)\]" \
+		    "[gdb_multi_loc_regex "Breakpoint $decimal" 2]\[^\r\n\]*" \
 		    "set break at unique function name in two source files"
 	    }
 	}
diff --git a/gdb/testsuite/gdb.linespec/multiple-locs.cc b/gdb/testsuite/gdb.linespec/multiple-locs.cc
new file mode 100644
index 00000000000..e7ca889e0fa
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/multiple-locs.cc
@@ -0,0 +1,41 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 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/>.  */
+
+template <typename T>
+int
+templ1 (T param)
+{
+  return (int) param; /* Break here.  */
+}
+
+template <>
+int
+templ1 (char* param)
+{
+  return  2;
+}
+
+int
+main ()
+{
+  templ1 (1L);
+  templ1 (1.0f);
+  templ1 (1U);
+  templ1 (1UL);
+  templ1 (1ULL);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/multiple-locs.exp b/gdb/testsuite/gdb.linespec/multiple-locs.exp
new file mode 100644
index 00000000000..d829e93b1ed
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/multiple-locs.exp
@@ -0,0 +1,57 @@ 
+# Copyright 2024 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/>.
+
+# Tests of printing of multiple breakpoint locations when setting a breakpoint.
+
+standard_testfile .cc
+
+require allow_cplus_tests
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+    return -1
+}
+
+gdb_test_no_output "set confirm off"
+
+gdb_test_no_output "set multiple-symbols all"
+
+set bp_location [gdb_get_line_number "Break here." $srcfile]
+gdb_breakpoint "$bp_location" -locs 5
+
+delete_breakpoints
+
+gdb_test_no_output "set breakpoint max-breakpoint-locations-printed 2"
+
+set bp_location [gdb_get_line_number "Break here." $srcfile]
+
+gdb_breakpoint $bp_location -locs 5 -add_locs 3
+
+delete_breakpoints
+
+set bp_location [gdb_get_line_number "Break here." $srcfile]
+
+gdb_breakpoint "$bp_location inferior 1" -locs 5 -locspec "$bp_location" \
+    -add_locs 3 -inferior 1
+
+delete_breakpoints
+
+# Now add the 2nd inferior.
+gdb_test "add-inferior" "Added inferior 2.*" "add inferior 2"
+gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
+gdb_file_cmd $binfile
+
+gdb_test_no_output "set breakpoint max-breakpoint-locations-printed 20"
+
+gdb_breakpoint "$bp_location" -locs 10 -inferior "\[12\]"
diff --git a/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp b/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp
index 93ddad84444..a02fe1f7cce 100644
--- a/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp
+++ b/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp
@@ -69,7 +69,9 @@  proc do_test { mi_version use_fix_flag expect_fixed_output } {
 	    set pattern [make_breakpoints_pattern $expect_fixed_output 2 y y]
 	    mi_gdb_test "break a_very_unique_name" \
 		[multi_line "&\"break a_very_unique_name\\\\n\"" \
-			    "~\"Breakpoint ${decimal} at.*\\(2 locations\\)\\\\n\"" \
+			    "~\"Breakpoint ${decimal} for.*at.*2 locations:\\\\n\"" \
+			    "~\"    Location ${decimal}.*\\\\n\"" \
+			    "~\"    Location ${decimal}.*\\\\n\"" \
 			    "=breakpoint-created,${pattern}" \
 			    "\\^done" ] \
 		"break a_very_unique_name"
diff --git a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
index e168a5eee45..b8d640d010a 100644
--- a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
+++ b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
@@ -293,9 +293,7 @@  proc test_continue_to_start { mode inf } {
 	with_spawn_id $gdb_main_spawn_id {
 	    # Continue to the point where we know for sure the threads are
 	    # started.
-	    gdb_test "tbreak $srcfile:$main_break_line" \
-		"Temporary breakpoint ${any}" \
-		"set breakpoint in main"
+	    gdb_breakpoint "$srcfile:$main_break_line" {temporary}
 
 	    gdb_continue_to_breakpoint "main breakpoint"
 
@@ -321,9 +319,7 @@  proc test_continue_to_start { mode inf } {
 		foreach thread { 2 3 } {
 		    gdb_test "thread $inf.$thread" ".*" "select child thread $inf.$thread"
 
-		    gdb_test "tbreak $srcfile:$thread_loop_line" \
-			"Temporary breakpoint ${any}" \
-			"set breakpoint for thread $inf.$thread"
+		    gdb_breakpoint "$srcfile:$thread_loop_line" {temporary}
 
 		    gdb_continue_to_breakpoint "continue thread $inf.$thread to infinite loop breakpoint"
 
@@ -347,12 +343,8 @@  proc test_continue_to_start { mode inf } {
 		# Put a thread-specific breakpoint for thread 2 of the current
 		# inferior.  We don't put a breakpoint for thread 3, since we
 		# want to let it run.
-		set test "set thread-specific breakpoint, thread $inf.2"
-		gdb_test_multiple "tbreak $srcfile:$thread_loop_line thread $inf.2" $test {
-		    -re "Temporary breakpoint ${any}\r\n$gdb_prompt " {
-			pass $test
-		    }
-		}
+		gdb_breakpoint "$srcfile:$thread_loop_line thread $inf.2" \
+		    {temporary}
 
 		# Confirm the stop of thread $inf.2.
 		set test "thread $inf.2 stops CLI"
diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
index 67069e1da4d..3c37c068b71 100644
--- a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
@@ -172,7 +172,7 @@  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.
-gdb_breakpoint "foo inferior 1" -locs 2 -locspec "foo"
+gdb_breakpoint "foo inferior 1" -locs 2 -locspec "foo" -inferior 1
 set bp_number [get_integer_valueof "\$bpnum" "INVALID" \
 		  "get b/p number for inferior specific breakpoint"]
 
diff --git a/gdb/testsuite/gdb.multi/multi-target-continue.exp b/gdb/testsuite/gdb.multi/multi-target-continue.exp
index d4b2fc28133..2e8dcae13bf 100644
--- a/gdb/testsuite/gdb.multi/multi-target-continue.exp
+++ b/gdb/testsuite/gdb.multi/multi-target-continue.exp
@@ -29,8 +29,7 @@  proc test_continue {non-stop} {
     }
 
     proc set_break {inf} {
-	gdb_test "break function${inf} thread ${inf}.1" \
-	    "Breakpoint ${::decimal} at ${::hex}: file .*, line ${::decimal}\\."
+	gdb_breakpoint "function${inf} thread ${inf}.1"
     }
 
     # Select inferior INF, and then run to a breakpoint on inferior
diff --git a/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp b/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
index 36f9d24a917..fe2683e9edb 100644
--- a/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
+++ b/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
@@ -51,13 +51,11 @@  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, line $line1\\."
+    gdb_breakpoint "$srcfile:$line1 thread 1.1"
 
     gdb_test "continue" "hit Breakpoint .*"
 
-    gdb_test "break $srcfile:$line2 thread 2.1" \
-	"Breakpoint .*$srcfile, line $line2\\."
+    gdb_breakpoint "$srcfile:$line2 thread 2.1"
 
     # Now block inferior 1 and issue "next".  We should stop at the
     # breakpoint for inferior 2, given schedlock off.
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 934690db2a1..e2f55f4e98e 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -765,7 +765,8 @@  proc_with_prefix test_bkpt_qualified {} {
     clean_restart ${testfile}
 
     set one_location_re "Breakpoint $decimal at $hex:.*line $decimal."
-    set two_location_re "Breakpoint $decimal at $hex:.*2 locations."
+    set two_location_re \
+	"[gdb_multi_loc_regex "Breakpoint $decimal" 2]\[^\r\n\]*"
 
     if {![runto_main]} {
 	return 0
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
index 5f0f6199894..e6f1c7b1f9f 100644
--- a/gdb/testsuite/lib/completion-support.exp
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -417,6 +417,9 @@  proc completion::_create_bp {break_command} {
 	-re "\\\(\($decimal\) locations\\\)\r\n$gdb_prompt $" {
 	    set found_locations "$expect_out(1,string)"
 	}
+	-re "[gdb_multi_loc_regex "Breakpoint $decimal" $decimal]$gdb_prompt $" {
+	    set found_locations 1
+	}
 	-re "Breakpoint $decimal at $hex: file .*, line .*$gdb_prompt $" {
 	    set found_locations 1
 	}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index bcaf061a8ce..f72b42beeb8 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -668,12 +668,25 @@  proc gdb_starti_cmd { {inferior_args {}} } {
     return -1
 }
 
+# Construct a breakpoint regex for multiple bp locations given a breakpoint
+# command BREAK_MESSAGE and a regex of expected locations LOCS.
+# This is useful for constructing multiple location regex where the
+# gdb_breakpoint proc cannot be used (for instance in completion tests).
+# It is also used in proc gdb_breakpoint.
+
+proc gdb_multi_loc_regex { break_message locs {locspec ""} { inferior ""}} {
+    set fill "\[^\r\n]"
+    return [multi_line \
+	"$break_message for $fill*$locspec at $locs locations:" \
+	"(?:    Location $::decimal at $::hex\[^\r\n\]*$inferior\.\\r\n)+" ]
+}
+
 # Set a breakpoint using LINESPEC.
 #
 # If there is an additional argument it is a list of options; the supported
 # options are allow-pending, temporary, message, no-message and qualified.
 # For multiple breakpoint locations additional options are used. These
-# are -locspec, -locs and -extra:
+# are -locspec, -locs, -extra, -add_locs and -inferior:
 # -locspec specifies a regex for the location spec output of the break command.
 #          This is only used when multiple locations matching is enabled by
 #          the -locs option. It is useful when a very specific breakpoint
@@ -685,6 +698,8 @@  proc gdb_starti_cmd { {inferior_args {}} } {
 #       treated as an error.
 # -extra allows specifying a regex for extra matches that need to appear in
 #	 the output.
+# -add_locs specifies a regex for additional locations printed.
+# -inferior specifies a regex for "in inferior" in the break command output.
 #
 # The result is 1 for success, 0 for failure.
 #
@@ -701,6 +716,7 @@  proc gdb_breakpoint { linespec args } {
     parse_some_args {
 	{locspec "\[^\r\n\]*"}
 	{locs "\[0-9\]+"}
+	{add_locs ""}
 	{extra "<nomatch>"}
 	{inferior ""}
     }
@@ -746,6 +762,20 @@  proc gdb_breakpoint { linespec args } {
     # Commonly used pattern.
     set fill "\[^\r\n]"
 
+    # If requested, expect "in inferior" in the locations output.
+    if { $inferior != ""} {
+	set inferior "in inferior $inferior"
+    }
+
+    set multiloc_msg \
+	[gdb_multi_loc_regex $break_message $locs $locspec $inferior]
+
+    set additional_locs ""
+    if { $add_locs != "" } {
+	set additional_locs \
+	    "$add_locs additional location\[s\]* not printed\.\r\n"
+    }
+
     # The extra regex is setup to not match unless the caller specifies
     # an extra match.
     gdb_test_multiple "$break_command $linespec" $test_name {
@@ -762,7 +792,7 @@  proc gdb_breakpoint { linespec args } {
 	-re -wrap "$break_message: file .*, line $decimal\\." {
 	    incr single_loc
 	}
-	-re -wrap "$break_message at $::hex$fill*$locspec$fill*\\($locs locations\\)$fill+" {
+	-re "$multiloc_msg$additional_locs$gdb_prompt $" {
 	    incr multiple_locs
 	}
 	-re -wrap "$break_message at $fill+\." {