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

Message ID 20240930060102.752079-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 success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Gerlicher, Klaus Sept. 30, 2024, 6:01 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 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 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                                      |   5 +
 gdb/breakpoint.c                              | 125 ++++++++++++++----
 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            |   4 +-
 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  |  56 ++++++++
 .../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    |   2 +-
 gdb/testsuite/lib/completion-support.exp      |   3 +
 gdb/testsuite/lib/gdb.exp                     |  33 ++++-
 18 files changed, 295 insertions(+), 60 deletions(-)
 create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.cc
 create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.exp
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index f9b24758ea8..55d7e0d5504 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,11 @@ 
 
 *** Changes since GDB 15
 
+* Printing multiple breakpoint locations when setting a breakpoint
+
+  Setting a breakpoint that is instantiated for multiple locations now prints
+  locations limited by "set 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 d161e24097a..674a37922ac 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 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
@@ -11785,38 +11798,85 @@  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 %u", 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)
+		    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_printf (
+		    " in file %ps, line %d",
+		    styled_string (file_name_style.style (), filename),
+		    bl.line_number);
+		}
+
+	      if (all_inferiors ().size () > 1)
+		{
+		  for (const auto &inf : all_inferiors ())
+		    if (inf->pspace == bl.pspace)
+		      gdb_printf (" on inferior %d", inf->num);
+		}
+	      else if (bl.owner->inferior != -1)
+		gdb_printf (" on 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 %d.",
-			  styled_string (file_name_style.style (),
-					 filename),
-			  bl.line_number);
+	      int omitted_locs = n - loc_idx;
+	      gdb_printf ("%u additional location%s not printed.",
+			  omitted_locs, omitted_locs > 1 ? "s" : "");
 	    }
-	  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 %d.",
+			    styled_string (file_name_style.style (), filename),
+			    bl.line_number);
+	    }
 	}
-    }
+      }
 }
 
 /* See breakpoint.h.  */
@@ -15245,6 +15305,17 @@  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_support,
+			   &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,
+			   &setlist, &showlist);
+
   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 86f25e999d8..acf3325f92a 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 max-breakpoint-locations-printed}:
+
+@smallexample
+(gdb) set 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 max-breakpoint-locations-printed
+@item set max-breakpoint-locations-printed @var{count}
+@itemx set 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 max-breakpoint-locations-printed
+@item show 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..573b47f0423 100644
--- a/gdb/testsuite/gdb.cp/ovldbreak.exp
+++ b/gdb/testsuite/gdb.cp/ovldbreak.exp
@@ -332,6 +332,8 @@  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 max-breakpoint-locations-printed 0"
+
 incr bpnum
 send_gdb "break foo::overload1arg\n" 
 gdb_expect {
@@ -340,7 +342,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 "[gdb_multi_loc_regex Breakpoint 12]$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 480e03406f2..81e56f4b6ac 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 max-breakpoint-locations-printed 0"
+
 test_driver
diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
index 981cd4155c0..e99a6ddaa46 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 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..5dc3ee5ff8a
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/multiple-locs.exp
@@ -0,0 +1,56 @@ 
+# 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 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 -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 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 28d0d90d056..0103b8985d3 100644
--- a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
@@ -105,7 +105,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
+gdb_breakpoint "foo inferior 1" -locs 2 -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..210a3d35160 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -765,7 +765,7 @@  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 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..92207c350e0 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 \[0-9\]+]$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 ede85ec8504..5a0fe1196c2 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -668,15 +668,30 @@  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 { inferior ""}} {
+    return [multi_line \
+	"$break_message \[0-9\]+ for \[^\r\n\]+ at\[^\r\n\]* $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 -locs and -extra:
+# are -locs, -extra, -add_locs and -inferior:
 # -locs specifies how many locations to expect the breakpoint set output
 #       and enforces that a single location match is an error.
 # -extra allows specifying extra matches that need to appear in the output.
+# -add_locs specifies a regex for  additional locations printed.
+# -inferior specifies a regex for "on inferior" output locations are in
+#           different inferiors.
 #
 # The result is 1 for success, 0 for failure.
 #
@@ -692,6 +707,7 @@  proc gdb_breakpoint { linespec args } {
 
     parse_some_args {
 	{locs "\[0-9\]+"}
+	{add_locs ""}
 	{extra "<nomatch>"}
 	{inferior ""}
     }
@@ -734,6 +750,19 @@  proc gdb_breakpoint { linespec args } {
 
     set test_name "gdb_breakpoint: set breakpoint at $linespec"
 
+    # If requested, expect "on inferior" in the locations output.
+    if { $inferior != ""} {
+	set inferior "on inferior $inferior"
+    }
+
+    set multiloc_msg [gdb_multi_loc_regex $break_message $locs $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 {
@@ -747,7 +776,7 @@  proc gdb_breakpoint { linespec args } {
 	-re "$break_message \[0-9\]*: file .*, line $decimal.\r\n$gdb_prompt $" {
 	    incr single_loc
 	}
-	-re "$break_message \[0-9\]+ at .*\($locs locations\).*\r\n$gdb_prompt $" {
+	-re "$multiloc_msg$additional_locs$gdb_prompt $" {
 	    incr multiple_locs
 	}
 	-re "$break_message \[0-9\]+ at .*\.\r\n$gdb_prompt $" {