[v9,1/2] gdb: extend gdb_breakpoint for multiple locations

Message ID 20250324111646.3201607-2-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

Gerlicher, Klaus March 24, 2025, 11:16 a.m. UTC
  From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>

Many tests have gdb_test "break..." style testing which
could be more generically handled by gdb_breakpoint.

This, for example

gdb_test "break func2" \
    "Breakpoint.*at.*func2.*\\(2 locations\\)"

becomes

gdb_breakpoint "func2" -locs 2

where -locs specifies that gdb_breakpoint should try to match
to 2 locations in the "break" output.

Extend gdb_breakpoint with options for matching multiple
breakpoint locations.

Also remove one historical stale comment and trailing space
and indent issues.
---
 gdb/testsuite/gdb.ada/bp_inlined_func.exp     |  4 +-
 gdb/testsuite/gdb.ada/homonym.exp             |  8 +-
 gdb/testsuite/gdb.ada/operator_bp.exp         | 18 +---
 .../gdb.base/condbreak-multi-context.exp      | 30 +++---
 gdb/testsuite/gdb.base/dtrace-probe.exp       |  4 +-
 gdb/testsuite/gdb.base/foll-fork.exp          |  7 +-
 gdb/testsuite/gdb.base/msym-bp-shl.exp        |  4 +-
 gdb/testsuite/gdb.base/msym-bp.exp            |  2 +-
 gdb/testsuite/gdb.base/solib-symbol.exp       |  4 +-
 gdb/testsuite/gdb.base/stap-probe.exp         |  8 +-
 gdb/testsuite/gdb.base/step-over-exit.exp     |  2 +-
 gdb/testsuite/gdb.cp/breakpoint-locs.exp      |  2 +-
 gdb/testsuite/gdb.cp/ena-dis-br-range.exp     |  4 +-
 gdb/testsuite/gdb.cp/mb-ctor.exp              |  8 +-
 gdb/testsuite/gdb.cp/mb-inline.exp            |  8 +-
 gdb/testsuite/gdb.cp/mb-templates.exp         | 13 +--
 gdb/testsuite/gdb.cp/meth-typedefs.exp        |  2 +-
 gdb/testsuite/gdb.cp/ovldbreak.exp            |  4 +-
 gdb/testsuite/gdb.cp/paramless.exp            |  4 +-
 gdb/testsuite/gdb.cp/templates.exp            | 22 ++---
 gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp | 20 ++--
 .../gdb.dwarf2/dw2-skip-prologue.exp          |  2 +-
 gdb/testsuite/gdb.linespec/break-asm-file.exp |  4 +-
 gdb/testsuite/gdb.linespec/linespec.exp       | 20 +---
 .../gdb.multi/bp-thread-specific.exp          |  6 +-
 .../gdb.multi/inferior-specific-bp.exp        |  3 +-
 gdb/testsuite/gdb.opt/inline-break.exp        | 23 ++---
 gdb/testsuite/gdb.python/py-bp-locations.exp  |  2 +-
 gdb/testsuite/lib/gdb.exp                     | 99 +++++++++++++++----
 29 files changed, 165 insertions(+), 172 deletions(-)
  

Comments

Andrew Burgess May 14, 2025, 3:33 p.m. UTC | #1
Klaus Gerlicher <klaus.gerlicher@intel.com> writes:

> From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>
>
> Many tests have gdb_test "break..." style testing which
> could be more generically handled by gdb_breakpoint.
>
> This, for example
>
> gdb_test "break func2" \
>     "Breakpoint.*at.*func2.*\\(2 locations\\)"
>
> becomes
>
> gdb_breakpoint "func2" -locs 2
>
> where -locs specifies that gdb_breakpoint should try to match
> to 2 locations in the "break" output.
>
> Extend gdb_breakpoint with options for matching multiple
> breakpoint locations.
>
> Also remove one historical stale comment and trailing space
> and indent issues.

<snip>

> diff --git a/gdb/testsuite/gdb.base/stap-probe.exp b/gdb/testsuite/gdb.base/stap-probe.exp
> index 40e8c5e06f3..6bbbe837f84 100644
> --- a/gdb/testsuite/gdb.base/stap-probe.exp
> +++ b/gdb/testsuite/gdb.base/stap-probe.exp
> @@ -181,9 +181,7 @@ proc stap_test {exec_name {args ""}} {
>      	"check \$_probe_arg1 for probe user"
>  
>      # Set a breakpoint with multiple probe locations.
> -    gdb_test "break -pstap test:two" \
> -	"Breakpoint \[0-9\]+ at $hex.*2 locations.*" \
> -	"set multi-location probe breakpoint (probe two)"
> +    gdb_breakpoint "-pstap test:two" -locs 2 -locspec ""
>  
>      # Reinit GDB, set a breakpoint on probe m4.
>      delete_breakpoints
> @@ -273,9 +271,7 @@ proc stap_test_no_debuginfo {exec_name {args ""}} {
>      # Set a breakpoint with multiple probe locations.
>      # In this scenario, we may expect more than 2 locations because of
>      # the optimizations (inlining, loop unrolling, etc).
> -    gdb_test "break -pstap test:two" \
> -	"Breakpoint .* at $hex.*\[0-9\]+ locations.*" \
> -	"set multi-location probe breakpoint (probe two)"
> +    gdb_breakpoint "-pstap test:two"  -locs "\[0-9\]+"

Use $decimal here maybe?

>  
>      # Reinit GDB, set a breakpoint on probe m4.
>      delete_breakpoints
> diff --git a/gdb/testsuite/gdb.base/step-over-exit.exp b/gdb/testsuite/gdb.base/step-over-exit.exp
> index d373b1a28ad..7c657c7f0f8 100644
> --- a/gdb/testsuite/gdb.base/step-over-exit.exp
> +++ b/gdb/testsuite/gdb.base/step-over-exit.exp
> @@ -44,7 +44,7 @@ gdb_test "set detach-on-fork off"
>  
>  # Step 1, find the syscall instruction address.
>  
> -gdb_test "break _exit" "Breakpoint $decimal at .*"
> +gdb_breakpoint "_exit"
>  
>  # Hit the breakpoint on _exit.  The address of syscall insn is recorded.
>  
> diff --git a/gdb/testsuite/gdb.cp/breakpoint-locs.exp b/gdb/testsuite/gdb.cp/breakpoint-locs.exp
> index 088a7660276..e873a97356e 100644
> --- a/gdb/testsuite/gdb.cp/breakpoint-locs.exp
> +++ b/gdb/testsuite/gdb.cp/breakpoint-locs.exp
> @@ -26,4 +26,4 @@ if { [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2"\
>      return -1
>  }
>  
> -gdb_test "break N1::C1::baz" "\\(2 locations\\)"
> +gdb_breakpoint "N1::C1::baz" -locs 2
> diff --git a/gdb/testsuite/gdb.cp/ena-dis-br-range.exp b/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
> index 103d38baa2f..0bd1d2e1c0d 100644
> --- a/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
> +++ b/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
> @@ -51,9 +51,7 @@ proc make_info_breakpoint_reply_re {b1 b2 b21 b22 b23 b24} {
>  	       ]
>  }
>  
> -gdb_test "break foo::overload" \
> -    "Breakpoint \[0-9\]+ at $hex: foo::overload. .4 locations." \
> -    "set breakpoint at overload"
> +gdb_breakpoint "foo::overload" -locs 4
>  
>  gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
>      "breakpoint info"
> diff --git a/gdb/testsuite/gdb.cp/mb-ctor.exp b/gdb/testsuite/gdb.cp/mb-ctor.exp
> index 1a84b7990f4..f628ea5e997 100644
> --- a/gdb/testsuite/gdb.cp/mb-ctor.exp
> +++ b/gdb/testsuite/gdb.cp/mb-ctor.exp
> @@ -38,15 +38,11 @@ if {![runto_main]} {
>  # Set a breakpoint with multiple locations
>  # and a condition.
>  
> -gdb_test "break 'Derived::Derived(int)'" \
> -    "Breakpoint.*at.*: Derived::Derived.int.. \\(2 locations\\).*" \
> -    "set-breakpoint at ctor"
> +gdb_breakpoint "Derived::Derived(int)" -locs 2
>  
>  gdb_breakpoint [gdb_get_line_number "set breakpoint here"]
>  
> -gdb_test "break 'Derived::~Derived()'" \
> -    "Breakpoint.*at.*: Derived::~Derived... \\(2 locations\\).*" \
> -    "set-breakpoint at dtor"
> +gdb_breakpoint "Derived::~Derived()" -locs 2
>  
>  gdb_test "continue" \
>      ".*Breakpoint.*Derived.*i=7.*" \
> diff --git a/gdb/testsuite/gdb.cp/mb-inline.exp b/gdb/testsuite/gdb.cp/mb-inline.exp
> index db80bb48e40..f34a12e4953 100644
> --- a/gdb/testsuite/gdb.cp/mb-inline.exp
> +++ b/gdb/testsuite/gdb.cp/mb-inline.exp
> @@ -33,9 +33,7 @@ set bp_location [gdb_get_line_number "set breakpoint here" $hdrfile]
>  
>  # Set a breakpoint with multiple locations.
>  
> -gdb_test "break $hdrfile:$bp_location" \
> -    "Breakpoint.*at.*: $hdrfile:$bp_location. \\(2 locations\\).*" \
> -    "set breakpoint"
> +gdb_breakpoint "$hdrfile:$bp_location" -locs 2
>  
>  # Do "info break" now so we can easily compare it with the later "info break"
>  # if problems arise.
> @@ -83,9 +81,7 @@ if { ![runto_main] } {
>      return 0
>  }
>  
> -gdb_test "break $hdrfile:$bp_location" \
> -    "Breakpoint.*at.*: $hdrfile:$bp_location. \\(2 locations\\).*" \
> -    "set multi_line_foo breakpoint"
> +gdb_breakpoint "$hdrfile:$bp_location" -locs 2
>  gdb_test "continue" \
>      ".*Breakpoint.*multi_line_foo \\(i=0\\).*" \
>      "run to multi_line_foo breakpoint 4 afn"
> diff --git a/gdb/testsuite/gdb.cp/mb-templates.exp b/gdb/testsuite/gdb.cp/mb-templates.exp
> index 2df88a322bb..f707220caac 100644
> --- a/gdb/testsuite/gdb.cp/mb-templates.exp
> +++ b/gdb/testsuite/gdb.cp/mb-templates.exp
> @@ -30,9 +30,8 @@ set bp_location [gdb_get_line_number "set breakpoint here"]
>  # Set a breakpoint with multiple locations
>  # and a condition.
>  
> -gdb_test "break $srcfile:$bp_location if i==1" \
> -    "Breakpoint.*at.*: $srcfile:$bp_location. \\(2 locations\\).*" \
> -    "initial condition: set breakpoint"
> +gdb_breakpoint "$srcfile:$bp_location if i==1" -locs 2 \
> +    -locspec "$srcfile:$bp_location"
>  
>  gdb_run_cmd
>  
> @@ -55,9 +54,7 @@ delete_breakpoints
>  gdb_test "kill" "" "kill" \
>           {Kill the program being debugged\? \(y or n\) } "y"
>  
> -gdb_test "break $srcfile:$bp_location" \
> -    "Breakpoint.*at.*: $srcfile:$bp_location. \\(2 locations\\).*" \
> -    "separate condition: set breakpoint"
> +gdb_breakpoint "$srcfile:$bp_location" -locs 2
>  
>  gdb_test_no_output {condition $bpnum i==1} \
>      "separate condition: set condition"
> @@ -111,9 +108,7 @@ if { ![runto_main] } {
>      return 0
>  }
>  
> -gdb_test "break $srcfile:$bp_location" \
> -    "Breakpoint.*at.*: $srcfile:$bp_location. \\(2 locations\\).*" \
> -    "set multi_line_foo breakpoint"
> +gdb_breakpoint "$srcfile:$bp_location" -locs 2
>  gdb_test "continue" \
>      ".*Breakpoint.*multi_line_foo<int> \\(i=0\\).*" \
>      "run to multi_line_foo breakpoint 2 <int>"
> diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp
> index 149f44c6c17..03163121df2 100644
> --- a/gdb/testsuite/gdb.cp/meth-typedefs.exp
> +++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp
> @@ -181,7 +181,7 @@ foreach f [list "$func" "'$func'"] {
>  	     "$line3${any}// test${any}"]
>  
>      delete_breakpoints
> -    gdb_test "break $f" "\\(3 locations\\)"
> +    gdb_breakpoint "$f" -locs 3 -locspec ""
>  }
>  
>  gdb_exit
> diff --git a/gdb/testsuite/gdb.cp/ovldbreak.exp b/gdb/testsuite/gdb.cp/ovldbreak.exp
> index 99d0a388adb..84efb37b8db 100644
> --- a/gdb/testsuite/gdb.cp/ovldbreak.exp
> +++ b/gdb/testsuite/gdb.cp/ovldbreak.exp
> @@ -443,9 +443,7 @@ gdb_test "break foo::foofunc" \
>  # Test breaking on an overloaded function when multiple-symbols
>  # is set to "all"
>  gdb_test_no_output "set multiple-symbols all"
> -gdb_test "break foo::foofunc" \
> -    "Breakpoint \[0-9\]+ at ${hex}: foo::foofunc. .2 locations..*" \
> -    "break on ambiguous symbol when multiple-symbols is set to all"
> +gdb_breakpoint "foo::foofunc" -locs 2
>  
>  # That's all, folks.
>  
> diff --git a/gdb/testsuite/gdb.cp/paramless.exp b/gdb/testsuite/gdb.cp/paramless.exp
> index 68f66441f80..3658ef7107e 100644
> --- a/gdb/testsuite/gdb.cp/paramless.exp
> +++ b/gdb/testsuite/gdb.cp/paramless.exp
> @@ -35,7 +35,7 @@ foreach ordinary {"outer<int>::fn" "outer<char>::fn<short>" "toplev<char>"} {
>      clean_restart $testfile
>  }
>  
> -gdb_test "break outer::fn" "Breakpoint $decimal at .*2 locations."
> +gdb_breakpoint "outer::fn" -locs 2
>  
>  clean_restart $testfile
> -gdb_test "break toplev" "Breakpoint $decimal at .*2 locations."
> +gdb_breakpoint "toplev" -locs 2
> diff --git a/gdb/testsuite/gdb.cp/templates.exp b/gdb/testsuite/gdb.cp/templates.exp
> index 8dd0cdc70ef..3ca8b20d844 100644
> --- a/gdb/testsuite/gdb.cp/templates.exp
> +++ b/gdb/testsuite/gdb.cp/templates.exp
> @@ -620,25 +620,25 @@ gdb_test_no_output "set multiple-symbols all"
>  
>  # Test setting breakpoints in a method of all class template instantiations,
>  # including overloads.
> -gdb_test "break Foo::foo"  "Breakpoint.*at.* \\(3 locations\\)"
> +gdb_breakpoint "Foo::foo" -locs 3
>  foreach t [list "int" "char" "volatile char *"] {
>      gdb_breakpoint "Foo<$t>::foo (int, $t)"
>      gdb_breakpoint "Foo::foo (int, $t)"
>  }
>  
> -gdb_test "break Bar::bar" "Breakpoint.*at.* \\(2 locations\\)"
> -gdb_test "break Bar::bar (int, int)" "Breakpoint.*at.* \\(2 locations\\)"
> +gdb_breakpoint "Bar::bar" -locs 2
> +gdb_breakpoint "Bar::bar (int, int)" -locs 2
>  foreach val [list 1 33] {
>      gdb_breakpoint "Bar<int, $val>::bar (int, int)"
>  }
>  
>  # Test setting breakpoints in a member function template of a class template,
>  # including overloads.
> -gdb_test "break Foozle::fogey" "Breakpoint.*at.* \\(9 locations\\)" \
> -    "break at template method fogey"
> +gdb_breakpoint "Foozle::fogey" -locs 9
> +
>  foreach t [list "int" "char" "Empty<int>"] {
> -    gdb_test "break Foozle::fogey ($t)" "Breakpoint.*at.* \\(3 locations\\)"
> -    gdb_test "break Foozle::fogey<$t>" "Breakpoint.*at.* \\(3 locations\\)"
> +    gdb_breakpoint "Foozle::fogey ($t)" -locs 3
> +    gdb_breakpoint "Foozle::fogey<$t>" -locs 3
>      foreach u [list "int" "char" "Empty<int>"] {
>  	gdb_breakpoint "Foozle<$t>::fogey<$u>" message
>  	gdb_assert { [gdb_breakpoint "Foozle<$t>::fogey<$u> ($u)" no-message] } \
> @@ -657,10 +657,10 @@ foreach t [list "int" "char" "Empty<int>"] {
>  # operator<<:
>  #   1. operator<< <Empty<int>>
>  #   2. operator<< <Foozle<int>>
> -gdb_test "break -source $srcfile -func operator<" \
> -    "Breakpoint.*at.* \\(4 locations\\)"
> -gdb_test "break -source $srcfile -func operator<<" \
> -    "Breakpoint.*at.* \\(2 locations\\)"
> +gdb_breakpoint "-source $srcfile -func operator<" -locs 4 -locspec \
> +    "-source $srcfile -function operator<"
> +gdb_breakpoint "-source $srcfile -func operator<<" -locs 2 -locspec \
> +   "-source $srcfile -function operator<<"
>  foreach t [list "Empty" "Foozle"] {
>      set tt "$t<int>"
>      gdb_breakpoint "operator< <$tt>" message
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp
> index 2e6f7114c33..6371b629d9b 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp
> @@ -44,8 +44,8 @@ gdb_test "break func1" \
>  # The result should be a breakpoint with two locations: the
>  # out-of-line function and the single inlined instance.
>  #
> -gdb_test "break func2" \
> -    "Breakpoint.*at.*func2.*\\(2 locations\\)"
> +gdb_breakpoint "func2" -locs 2
> +
>  
>  #
>  # func3b is a static inlined function that is called once from
> @@ -62,8 +62,7 @@ gdb_test "break func3b" \
>  # the inlined call to func4a in main, and the inlined instance
>  # within the out-of-line func4a.
>  #
> -gdb_test "break func4b" \
> -    "Breakpoint.*at.*func4b.*\\(2 locations\\)"
> +gdb_breakpoint "func4b" -locs 2
>  
>  #
>  # func5b is a non-static inlined function that is called once
> @@ -71,8 +70,8 @@ gdb_test "break func4b" \
>  # breakpoint with two locations: the out-of-line function and the
>  # inlined instance within the inlined call to func5a in main.
>  #
> -gdb_test "break func5b" \
> -    "Breakpoint.*at.*func5b.*\\(2 locations\\)"
> +gdb_breakpoint "func5b" -locs 2
> +
>  #
>  # func6b is a non-static inlined function that is called once from
>  # within another non-static inlined function.  The result should be
> @@ -80,8 +79,7 @@ gdb_test "break func5b" \
>  # inlined instance within the out-of-line func6a, and the inlined
>  # instance within the inlined call to func6a in main,
>  #
> -gdb_test "break func6b" \
> -    "Breakpoint.*at.*func6b.*\\(3 locations\\)"
> +gdb_breakpoint "func6b" -locs 3
>  
>  #
>  # func7b is a static inlined function that is called twice: once from
> @@ -89,8 +87,7 @@ gdb_test "break func6b" \
>  # two locations: the inlined instance within the inlined instance of
>  # func7a, and the inlined instance within main.
>  #
> -gdb_test "break func7b" \
> -    "Breakpoint.*at.*func7b.*\\(2 locations\\)"
> +gdb_breakpoint "func7b" -locs 2
>  
>  #
>  # func8b is a non-static inlined function that is called twice: once
> @@ -99,8 +96,7 @@ gdb_test "break func7b" \
>  # within the inlined instance of func7a, and the inlined instance
>  # within main.
>  #
> -gdb_test "break func8b" \
> -    "Breakpoint.*at.*func8b.*\\(3 locations\\)"
> +gdb_breakpoint "func8b" -locs 3
>  
>  #
>  # func1 is a static inlined function.  The result should be that no
> diff --git a/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp b/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
> index 78ac57439f3..49fc2293dc4 100644
> --- a/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
> +++ b/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
> @@ -71,7 +71,7 @@ if ![runto_main] {
>      return -1
>  }
>  
> -gdb_breakpoint "func"
> +gdb_breakpoint "func" -locs 2 -locspec ""
>  gdb_continue_to_breakpoint "func"
>  
>  # Sanity check GDB has really found 2 locations
> diff --git a/gdb/testsuite/gdb.linespec/break-asm-file.exp b/gdb/testsuite/gdb.linespec/break-asm-file.exp
> index 1044fc79a26..3977e6aac85 100644
> --- a/gdb/testsuite/gdb.linespec/break-asm-file.exp
> +++ b/gdb/testsuite/gdb.linespec/break-asm-file.exp
> @@ -60,7 +60,5 @@ gdb_test "break b/$asm_file0:func" \
>  
>  gdb_test "delete 2"
>  
> -gdb_test "break $asm_file0:func" \
> -    "Breakpoint 3 at 0x\[0-9a-fA-F\]+: .*$asm_file0.*(2 locations).*" \
> -    "set a break-point at function in all instances for a selected ASM file."
> +gdb_breakpoint "$asm_file0:func" -locs 2
>  
> diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
> index 576d788cae0..6398309f7e4 100644
> --- a/gdb/testsuite/gdb.linespec/linespec.exp
> +++ b/gdb/testsuite/gdb.linespec/linespec.exp
> @@ -144,17 +144,11 @@ if { [is_remote host] } {
>      }
>  }
>  
> -gdb_test "break thefile.cc:$l1" \
> -    "Breakpoint $decimal at $hex: thefile.cc:$l1. \[(\]2 locations\[)\]" \
> -    "multi-location break using file:line"
> +gdb_breakpoint "thefile.cc:$l1" -locs 2
>  
> -gdb_test "break dupname" \
> -    "Breakpoint $decimal at $hex: dupname. \[(\]2 locations\[)\]" \
> -    "multi-location break using duplicate function name"
> +gdb_breakpoint "dupname" -locs 2
>  
> -gdb_test "break dupname:label" \
> -    "Breakpoint $decimal at $hex: dupname:label. \[(\]2 locations\[)\]" \
> -    "multi-location break using duplicate function name and label"
> +gdb_breakpoint "dupname:label" -locs 2
>  
>  # Testing if the "condition" command completes only the breakpoints,
>  # not the locations.
> @@ -165,9 +159,7 @@ gdb_test "break lspec.cc:nosuchfunction" \
>      "Function \"nosuchfunction\" not defined in \"lspec.cc\"." \
>      "set breakpoint on non-existent function"
>  
> -gdb_test "break NameSpace::overload" \
> -    "Breakpoint \[0-9\]+ at $hex: NameSpace::overload. \[(\]3 locations\[)\]" \
> -    "set breakpoint at all instances of NameSpace::overload"
> +gdb_breakpoint "NameSpace::overload" -locs 3
>  
>  gdb_test "break lspec.cc:NameSpace::overload" \
>      "Breakpoint \[0-9\]+ at $hex: file .*lspec.cc, line 7." \
> @@ -210,6 +202,4 @@ gdb_test "file $binfile" \
>      "Reading symbols from .*" \
>      "set the new inferior file for linespec tests"
>  
> -gdb_test "break -q main" \
> -    "Breakpoint \[0-9\]+ at $hex: -qualified main. .2 locations." \
> -    "set breakpoint at main in both inferiors"
> +gdb_breakpoint "-qualified main" -locs 2
> diff --git a/gdb/testsuite/gdb.multi/bp-thread-specific.exp b/gdb/testsuite/gdb.multi/bp-thread-specific.exp
> index 11dc2483624..0e289790d7d 100644
> --- a/gdb/testsuite/gdb.multi/bp-thread-specific.exp
> +++ b/gdb/testsuite/gdb.multi/bp-thread-specific.exp
> @@ -73,16 +73,14 @@ gdb_test "info threads" \
>  # Set the first breakpoint.  Currently this is going to insert at two
>  # 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: file \[^\r\n\]+$srcfile, line $decimal\\."
> +gdb_breakpoint "foo thread 2.1"
>  
>  set bpnum [get_integer_valueof "\$bpnum" "INVALID"]
>  
>  # Now set another breakpoint that will be at the same location as the
>  # earlier breakpoint.  Check that the thread-id used when describing
>  # the earlier breakpoints is correct.
> -gdb_test "break foo thread 1.1" \
> -    "Breakpoint $decimal at $hex: file \[^\r\n\]+$srcfile, line $decimal\\."
> +gdb_breakpoint "foo thread 1.1"
>  
>  # Save the breakpoints into a file.
>  if {[is_remote host]} {
> diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
> index 82cc9240e50..67069e1da4d 100644
> --- a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
> +++ b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
> @@ -172,8 +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_test "break foo inferior 1" \
> -    "Breakpoint $decimal at $hex: foo\\. \\(2 locations\\)"
> +gdb_breakpoint "foo inferior 1" -locs 2 -locspec "foo"
>  set bp_number [get_integer_valueof "\$bpnum" "INVALID" \
>  		  "get b/p number for inferior specific breakpoint"]
>  
> diff --git a/gdb/testsuite/gdb.opt/inline-break.exp b/gdb/testsuite/gdb.opt/inline-break.exp
> index 9c57d22404b..70549301964 100644
> --- a/gdb/testsuite/gdb.opt/inline-break.exp
> +++ b/gdb/testsuite/gdb.opt/inline-break.exp
> @@ -96,8 +96,7 @@ gdb_test "break func1" \
>  # The result should be a breakpoint with two locations: the
>  # out-of-line function and the single inlined instance.
>  #
> -gdb_test "break func2" \
> -    "Breakpoint.*at.*func2.*\\(2 locations\\)"
> +gdb_breakpoint "func2" -locs 2
>  
>  #
>  # func3b is a static inlined function that is called once from
> @@ -114,8 +113,8 @@ gdb_test "break func3b" \
>  # the inlined call to func4a in main, and the inlined instance
>  # within the out-of-line func4a.
>  #
> -gdb_test "break func4b" \
> -    "Breakpoint.*at.*func4b.*\\(2 locations\\)"
> +gdb_breakpoint "func4b" -locs 2
> +
>  
>  #
>  # func5b is a non-static inlined function that is called once
> @@ -123,8 +122,8 @@ gdb_test "break func4b" \
>  # breakpoint with two locations: the out-of-line function and the
>  # inlined instance within the inlined call to func5a in main.
>  #
> -gdb_test "break func5b" \
> -    "Breakpoint.*at.*func5b.*\\(2 locations\\)"
> +gdb_breakpoint "func5b" -locs 2
> +
>  #
>  # func6b is a non-static inlined function that is called once from
>  # within another non-static inlined function.  The result should be
> @@ -132,8 +131,7 @@ gdb_test "break func5b" \
>  # inlined instance within the out-of-line func6a, and the inlined
>  # instance within the inlined call to func6a in main,
>  #
> -gdb_test "break func6b" \
> -    "Breakpoint.*at.*func6b.*\\(3 locations\\)"
> +gdb_breakpoint "func6b" -locs 3
>  
>  #
>  # func7b is a static inlined function that is called twice: once from
> @@ -141,8 +139,7 @@ gdb_test "break func6b" \
>  # two locations: the inlined instance within the inlined instance of
>  # func7a, and the inlined instance within main.
>  #
> -gdb_test "break func7b" \
> -    "Breakpoint.*at.*func7b.*\\(2 locations\\)"
> +gdb_breakpoint "func7b" -locs 2
>  
>  #
>  # func8b is a non-static inlined function that is called twice: once
> @@ -151,8 +148,7 @@ gdb_test "break func7b" \
>  # within the inlined instance of func7a, and the inlined instance
>  # within main.
>  #
> -gdb_test "break func8b" \
> -    "Breakpoint.*at.*func8b.*\\(3 locations\\)"
> +gdb_breakpoint "func8b" -locs 3
>  
>  #
>  # func1 is a static inlined function.  The result should be that no
> @@ -314,8 +310,7 @@ with_test_prefix "check alignment" {
>  	continue
>      }
>  
> -    gdb_test "break func4b" \
> -	"Breakpoint.*at.*func4b.*\\(2 locations\\)"
> +    gdb_breakpoint "func4b" -locs 2
>  
>      set expected_line_length -1
>      gdb_test_multiple "info break \$bpnum" "xxxx" {
> diff --git a/gdb/testsuite/gdb.python/py-bp-locations.exp b/gdb/testsuite/gdb.python/py-bp-locations.exp
> index 4892947ca55..a2bff8a655e 100644
> --- a/gdb/testsuite/gdb.python/py-bp-locations.exp
> +++ b/gdb/testsuite/gdb.python/py-bp-locations.exp
> @@ -56,7 +56,7 @@ proc build_bpl_regexp { args } {
>  }
>  
>  # Set breakpoint with 2 locations.
> -gdb_breakpoint "add"
> +gdb_breakpoint "add" -locs 2
>  
>  set expected_line_a [gdb_get_line_number "a + b"]
>  set expected_line_b [gdb_get_line_number "c + d"]
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 3349da7a263..4a5f1e0df89 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -672,6 +672,19 @@ proc gdb_starti_cmd { {inferior_args {}} } {
>  #
>  # 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:
> +# -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
> +#          output is expected. For example, "func if a == 0" and the caller
> +#          wants to match "func" in the breakpoint command output. When
> +#          not set the content of LINESPEC is matched, regardless of -locs.
> +# -locs specifies a regex for the number of locations that are printed by the
> +#	break command. This also enforces that a single location match is
> +#       treated as an error.
> +# -extra allows specifying a regex for extra matches that need to appear before
> +#	 the breakpoint command output.
>  #
>  # The result is 1 for success, 0 for failure.
>  #
> @@ -685,17 +698,32 @@ proc gdb_breakpoint { linespec args } {
>      global gdb_prompt
>      global decimal
>  
> +    parse_some_args {
> +	{locspec "\[^\r\n\]*"}
> +	{locs "\[0-9\]+"}
> +	{extra ""}
> +    }
> +
> +    if { $locspec eq "\[^\r\n\]*" && $locs ne "\[0-9\]+"} {

Consider gdb.ada/operator_bp.exp.  After your patch we now do this:

  gdb_breakpoint "\"$op\"" -locs $::decimal

And decimal is '[0-9]+'.  I think using '[0-9]+' as the default for locs
is a mistake.  Given that locs is the "switch", when it's set it
indicates the user expects multiple locations, I'd change the default
for locs to be the empty string, and then check for non-empty.

> +	set locspec [string_to_regexp $linespec]
> +    }
> +
>      set pending_response n
>      if {[lsearch -exact $args allow-pending] != -1} {
>  	set pending_response y
>      }
>  
>      set break_command "break"
> -    set break_message "Breakpoint"
> +    # Expecting some output before breakmessage, append it before it.
> +    if { $extra ne "" } {
> +	append extra "\r\n"
> +    }
> +    set break_message "Breakpoint $decimal"
>      if {[lsearch -exact $args temporary] != -1} {
>  	set break_command "tbreak"
> -	set break_message "Temporary breakpoint"
> +	set break_message "Temporary breakpoint $decimal"
>      }
> +    set break_message "$extra$break_message"
>  
>      if {[lsearch -exact $args qualified] != -1} {
>  	append break_command " -qualified"
> @@ -712,36 +740,73 @@ proc gdb_breakpoint { linespec args } {
>  	set print_pass 1
>      }
>  
> +    # Counters for single and multiple locations
> +    set multiple_locs 0
> +    set single_loc 0
> +
>      set test_name "gdb_breakpoint: set breakpoint at $linespec"
> -    # The first two regexps are what we get with -g, the third is without -g.
> +    set fill "\[^\r\n]"

Typo, missing '\', should be "\[^\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 {
> -	-re "$break_message \[0-9\]* at .*: file .*, line $decimal.\r\n$gdb_prompt $" {}
> -	-re "$break_message \[0-9\]*: file .*, line $decimal.\r\n$gdb_prompt $" {}
> -	-re "$break_message \[0-9\]* at .*$gdb_prompt $" {}
> -	-re "$break_message \[0-9\]* \\(.*\\) pending.*$gdb_prompt $" {
> -		if {$pending_response == "n"} {
> -			if { $print_fail } {
> -				fail $gdb_test_name
> -			}
> -			return 0
> +	-re -wrap "$break_message at $fill+: file $fill+, line $decimal\\." {
> +	    incr single_loc
> +	}
> +	-re -wrap "$break_message: file .*, line $decimal\\." {
> +	    incr single_loc
> +	}
> +	-re -wrap "$break_message at $::hex$fill*$locspec$fill*\($locs locations\)$fill+" {

The '\(' and '\)' should be '\\(' and '\\)'.  The single '\' removes any
special meaning from the next character, in this case '(', before
passing the character to TCL.  However, '(' doesn't have any special
meaning to TCL, so the '\' has no effect and is silently dropped.

What you want is for the regexp to contain '\('.  To achieve this we
need to remove the special meaning from the '\' character, which we do
by escaping with backslash, hence '\\('.

With that fixed, the final '$fill+' will no longer match anything.
Right now, the regexp ends up containing '($locs locations)', the
'(...)' act as a capture group.  The final '$fill+' then matches the
trailing ')' from the actual output.

Once you switch to using '\\($locs locations\\)' the final ')' in the
output will be matched by '\\)', and this leaves nothing for the
trailing '$fill+'.

I'd change the full regexp here to this:

  -re -wrap "$break_message at $::hex: $fill*$locspec$fill*\\. \\($locs locations\\)" {

I've added some more of the punctuation back in too, which unfortunately
means that this regexp doesn't match all cases, so I'd then add this
additional case:

	-re -wrap "$break_message at $::hex \\($locs locations\\)" {
	    incr multiple_locs
	}

We already have multiple cases for the single location case, so having
multiple cases for the multi-location case seems OK to me.  I haven't
checked, but I wonder if adding this second case would allow you to
remove the `-locspec ""` that you had to add in some cases?

> +	    incr multiple_locs
> +	}
> +	-re -wrap "$break_message at $fill+\." {

As above, I suspect '\.' should really be '\\.' in order to match a
literal '.', rather than being a regexp any single character matcher?

> +	    incr single_loc
> +	}
> +	-re -wrap "$break_message \\(.*\\) pending$fill+" {

As you're changing this line anyway, I'd drop the '.*' and replace it
with '$fill*' or probably '$fill+'.  Also, the trailing '$fill+' feels
like it's only necessary to match the trailing '.'.  Could this be
replaced with '\\.'?

> +	    if {$pending_response == "n"} {
> +		if { $print_fail } {
> +		    fail $gdb_test_name
>  		}
> +		return 0
> +	    }
>  	}
> -	-re "Make breakpoint pending.*y or \\\[n\\\]. $" { 
> -		send_gdb "$pending_response\n"
> -		exp_continue
> +	-re "Make breakpoint pending.*y or \\\[n\\\]. $" {
> +	    send_gdb "$pending_response\n"
> +	    exp_continue
>  	}
> -	-re "$gdb_prompt $" {
> +	-re -wrap "" {
>  	    if { $print_fail } {
>  		fail $test_name
>  	    }
>  	    return 0
>  	}
>      }
> +
> +    # If multiple locations where requested, assert we didn't
> +    # see a single location match.
> +    if { $locs ne "\[0-9\]+"

Here's another place where passing '-locs $decimal' is going to skip
something that should be done.

> +	 && $locs ne 1
> +	 && $single_loc != 0 } {
> +	if { $print_fail } {
> +	    fail $test_name

Maybe:

  fail "$test_name (single location matched)"

This will make this case easier to distinguish from the others.

> +	}
> +	return 0
> +    }
> +
> +    # Special case to enforce single location matches only. If -LOCS is set to
> +    # 1 fail if we get multi location matches.
> +    if { $locs eq 1 && $multiple_locs != 0 } {
> +	if { $print_fail } {
> +	    fail $test_name

And here:

  fail "$test_name (multiple locations matched)"

Thanks,
Andrew
  

Patch

diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func.exp b/gdb/testsuite/gdb.ada/bp_inlined_func.exp
index db56a11d12a..028863af3fc 100644
--- a/gdb/testsuite/gdb.ada/bp_inlined_func.exp
+++ b/gdb/testsuite/gdb.ada/bp_inlined_func.exp
@@ -32,9 +32,7 @@  if {![runto_main]} {
 # Check that inserting breakpoint on read_small inlined function inserts
 # 4 breakpoints (or possibly 5, including the read_small function itself).
 
-gdb_test "break read_small" \
-         "Breakpoint $decimal at $hex: read_small\\. \\(\[45\] locations\\)" \
-         "set breakpoint at read_small"
+gdb_breakpoint "read_small" -locs "\[45\]"
 
 # We do not verify each breakpoint info, but use continue commands instead
 # to verify that we properly stop on each expected breakpoint.
diff --git a/gdb/testsuite/gdb.ada/homonym.exp b/gdb/testsuite/gdb.ada/homonym.exp
index 2fef3c6fbce..15920866daa 100644
--- a/gdb/testsuite/gdb.ada/homonym.exp
+++ b/gdb/testsuite/gdb.ada/homonym.exp
@@ -29,13 +29,9 @@  clean_restart ${testfile}
 # Do these tests before running, so we are operating in a known
 # environment.
 
-gdb_test "break Get_Value" \
-    "Breakpoint \[0-9\]+ at $hex: Get_Value. .2 locations." \
-    "set breakpoint at Get_Value"
+gdb_breakpoint "Get_Value" -locs 2
 
-gdb_test "break homonym.adb:Get_Value" \
-    "Breakpoint \[0-9\]+ at $hex: homonym.adb:Get_Value. .2 locations." \
-    "set breakpoint at homonym.adb:Get_Value"
+gdb_breakpoint "homonym.adb:Get_Value" -locs 2
 
 gdb_test "break <homonym__get_value>" \
     ".*Breakpoint \[0-9\]+ at $hex: (file .*homonym\\.adb, line $decimal\\.|<homonym__get_value>\\. .2 locations.)" \
diff --git a/gdb/testsuite/gdb.ada/operator_bp.exp b/gdb/testsuite/gdb.ada/operator_bp.exp
index 2335d492798..d3f78ee6fe6 100644
--- a/gdb/testsuite/gdb.ada/operator_bp.exp
+++ b/gdb/testsuite/gdb.ada/operator_bp.exp
@@ -33,22 +33,12 @@  runto "ops_test.adb:$bp_location"
 
 # Set breakpoints for all operators, using just the operator name in quotes.
 
-set bp_re "Breakpoint $decimal at $hex"
 foreach op { "+" "-" } {
-    set op_re [string_to_regexp $op]
-    gdb_test "break \"$op\"" "$bp_re: \"$op_re\"\\. \\($decimal locations\\).*"
+    gdb_breakpoint "\"$op\"" -locs $::decimal
 }
 
 foreach op { "*" "/" "mod" "rem" "**" "<" "<=" ">" ">=" "=" "and" "or" "xor" "&" "abs" "not"} {
-    set op_re [string_to_regexp $op]
-    gdb_test_multiple "break \"$op\"" "" {
-	-re -wrap "$bp_re: file .*ops.adb, line $decimal." {
-	    pass $gdb_test_name
-	}
-	-re -wrap "$bp_re: \"$op_re\"\\. \\($decimal locations\\).*" {
-	    pass $gdb_test_name
-	}
-    }
+    gdb_breakpoint "\"$op\""
 }
 
 # Make sure we stop correctly in each operator function.
@@ -70,9 +60,7 @@  runto "ops_test.adb:$bp_location"
 # Set breakpoints for all operators, using just the operator name in quotes.
 
 foreach op { "+" "-" } {
-    set op_re [string_to_regexp $op]
-    gdb_test "break ops.\"$op\"" \
-             "Breakpoint $decimal at $hex: ops\\.\"$op_re\"\\. \\(2 locations\\)"
+    gdb_breakpoint "ops.\"$op\"" -locs 2
 }
 
 foreach op { "*" "/" "mod" "rem" "**" "<" "<=" ">" ">=" "=" "and" "or" "xor" "&" "abs" "not"} {
diff --git a/gdb/testsuite/gdb.base/condbreak-multi-context.exp b/gdb/testsuite/gdb.base/condbreak-multi-context.exp
index 024f7689355..7db8acbaa53 100644
--- a/gdb/testsuite/gdb.base/condbreak-multi-context.exp
+++ b/gdb/testsuite/gdb.base/condbreak-multi-context.exp
@@ -142,24 +142,22 @@  proc_with_prefix scenario_1 { start_before } {
     # and C::func) should be disabled.  We do not test location
     # indices strictly at this moment, because we don't know them,
     # yet.  We have strict location index tests below.
-    gdb_test "break func if aaa == 10" \
-	[multi_line \
-	     "${warning} at location $decimal, disabling:" \
-	     "  No symbol \"aaa\" in current context." \
-	     "${warning} at location $decimal, disabling:" \
-	     "  No symbol \"aaa\" in current context." \
-	     "Breakpoint $decimal at $fill .3 locations."] \
-	"define bp with condition aaa == 10"
+    set extra [multi_line \
+		 "${warning} at location $decimal, disabling:" \
+		 "  No symbol \"aaa\" in current context." \
+		 "${warning} at location $decimal, disabling:" \
+		 "  No symbol \"aaa\" in current context."]
+    gdb_breakpoint "func if aaa == 10" -locs 3 -extra $extra -locspec "func"
+
     set bpnum1 [get_integer_valueof "\$bpnum" 0 "get bpnum1"]
 
-    gdb_test "break func if ccc == 30" \
-	[multi_line \
-	     ".*${warning} at location $decimal, disabling:" \
-	     "  No symbol \"ccc\" in current context." \
-	     ".*${warning} at location $decimal, disabling:" \
-	     "  No symbol \"ccc\" in current context." \
-	     ".*Breakpoint $decimal at $fill .3 locations."] \
-	"define bp with condition ccc == 30"
+    set extra [multi_line \
+		 "${warning} at location $decimal, disabling:" \
+		 "  No symbol \"ccc\" in current context." \
+		 "${warning} at location $decimal, disabling:" \
+		 "  No symbol \"ccc\" in current context."]
+    gdb_breakpoint "func if ccc == 30" -locs 3 -extra $extra -locspec "func"
+
     set bpnum2 [get_integer_valueof "\$bpnum" 0 "get bpnum2"]
 
     with_test_prefix "after creating b/p" {
diff --git a/gdb/testsuite/gdb.base/dtrace-probe.exp b/gdb/testsuite/gdb.base/dtrace-probe.exp
index a8c38f91fb6..a8338fb4f02 100644
--- a/gdb/testsuite/gdb.base/dtrace-probe.exp
+++ b/gdb/testsuite/gdb.base/dtrace-probe.exp
@@ -92,9 +92,7 @@  proc dtrace_test {} {
         "print \$_probe_arg1 for probe progress-counter"
 
     # Set a breakpoint with multiple probe locations.
-    gdb_test "break -pdtrace test:two-locations" \
-        "Breakpoint \[0-9\]+ at $hex.*2 locations.*" \
-        "set multi-location probe breakpoint (probe two-locations)"
+    gdb_breakpoint "-pdtrace test:two-locations" -locs 2 -locspec ""
 
     return 0
 }
diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp
index 0d801f004e4..177bc71f9bc 100644
--- a/gdb/testsuite/gdb.base/foll-fork.exp
+++ b/gdb/testsuite/gdb.base/foll-fork.exp
@@ -183,11 +183,8 @@  proc_with_prefix test_follow_fork { follow-fork-mode detach-on-fork cmd } {
     # locations it means there are two program spaces.
     if {${detach-on-fork} == "off" || ${follow-fork-mode} == "child"} {
 	set bpnum "<unset>"
-	gdb_test_multiple "break callee" "break callee" {
-	    -re -wrap "Breakpoint ($::decimal) at $::hex: callee\\. \\(2 locations\\)" {
-		set bpnum $expect_out(1,string)
-		pass $gdb_test_name
-	    }
+	if { [gdb_breakpoint "callee" -locs 2] } {
+	    set bpnum [get_integer_valueof "\$bpnum" 0 "get bpnum"]
 	}
 
 	set any {[^\r\n]+}
diff --git a/gdb/testsuite/gdb.base/msym-bp-shl.exp b/gdb/testsuite/gdb.base/msym-bp-shl.exp
index 31af1f90f31..ab178a18f9e 100644
--- a/gdb/testsuite/gdb.base/msym-bp-shl.exp
+++ b/gdb/testsuite/gdb.base/msym-bp-shl.exp
@@ -61,7 +61,7 @@  proc test {debug} {
     # Should find two locations: the static foo in the
     # msym-bp-shl-main-2 file, and <foo@plt>, both in the main binary.
     with_test_prefix "before run" {
-	gdb_test "break foo" "\\(2 locations\\)"
+	gdb_breakpoint "foo" -locs 2 -locspec ""
 
 	if {$debug} {
 	    test_info_break_2 \
@@ -85,7 +85,7 @@  proc test {debug} {
     # foo in the msym-bp-shl-main-2 file, and the extern foo in the
     # shared library.
     with_test_prefix "at main" {
-	gdb_test "break foo" "\\(2 locations\\)"
+	gdb_breakpoint "foo" -locs 2 -locspec ""
 
 	if {$debug} {
 	    test_info_break_2 \
diff --git a/gdb/testsuite/gdb.base/msym-bp.exp b/gdb/testsuite/gdb.base/msym-bp.exp
index 84d29020900..e154ea6da16 100644
--- a/gdb/testsuite/gdb.base/msym-bp.exp
+++ b/gdb/testsuite/gdb.base/msym-bp.exp
@@ -52,7 +52,7 @@  proc test {debug} {
 	upvar debug debug
 
 	with_test_prefix $prefix {
-	    gdb_test "break foo" "\\(2 locations\\)"
+	    gdb_breakpoint "foo" -locs 2 -locspec ""
 
 	    if {$debug} {
 		test_info_break_2 \
diff --git a/gdb/testsuite/gdb.base/solib-symbol.exp b/gdb/testsuite/gdb.base/solib-symbol.exp
index 1ec386703cf..6759156ef83 100644
--- a/gdb/testsuite/gdb.base/solib-symbol.exp
+++ b/gdb/testsuite/gdb.base/solib-symbol.exp
@@ -57,8 +57,6 @@  gdb_test "continue" \
 	 "Continuing.*"
 
 # This symbol is now looked up in the ELF library and the binary.
-gdb_test "br foo2" \
-	 "Breakpoint.*: foo2. .2 locations..*" \
-	 "foo2 in mdlib"
+gdb_breakpoint "foo2" -locs 2
 
 gdb_exit
diff --git a/gdb/testsuite/gdb.base/stap-probe.exp b/gdb/testsuite/gdb.base/stap-probe.exp
index 40e8c5e06f3..6bbbe837f84 100644
--- a/gdb/testsuite/gdb.base/stap-probe.exp
+++ b/gdb/testsuite/gdb.base/stap-probe.exp
@@ -181,9 +181,7 @@  proc stap_test {exec_name {args ""}} {
     	"check \$_probe_arg1 for probe user"
 
     # Set a breakpoint with multiple probe locations.
-    gdb_test "break -pstap test:two" \
-	"Breakpoint \[0-9\]+ at $hex.*2 locations.*" \
-	"set multi-location probe breakpoint (probe two)"
+    gdb_breakpoint "-pstap test:two" -locs 2 -locspec ""
 
     # Reinit GDB, set a breakpoint on probe m4.
     delete_breakpoints
@@ -273,9 +271,7 @@  proc stap_test_no_debuginfo {exec_name {args ""}} {
     # Set a breakpoint with multiple probe locations.
     # In this scenario, we may expect more than 2 locations because of
     # the optimizations (inlining, loop unrolling, etc).
-    gdb_test "break -pstap test:two" \
-	"Breakpoint .* at $hex.*\[0-9\]+ locations.*" \
-	"set multi-location probe breakpoint (probe two)"
+    gdb_breakpoint "-pstap test:two"  -locs "\[0-9\]+"
 
     # Reinit GDB, set a breakpoint on probe m4.
     delete_breakpoints
diff --git a/gdb/testsuite/gdb.base/step-over-exit.exp b/gdb/testsuite/gdb.base/step-over-exit.exp
index d373b1a28ad..7c657c7f0f8 100644
--- a/gdb/testsuite/gdb.base/step-over-exit.exp
+++ b/gdb/testsuite/gdb.base/step-over-exit.exp
@@ -44,7 +44,7 @@  gdb_test "set detach-on-fork off"
 
 # Step 1, find the syscall instruction address.
 
-gdb_test "break _exit" "Breakpoint $decimal at .*"
+gdb_breakpoint "_exit"
 
 # Hit the breakpoint on _exit.  The address of syscall insn is recorded.
 
diff --git a/gdb/testsuite/gdb.cp/breakpoint-locs.exp b/gdb/testsuite/gdb.cp/breakpoint-locs.exp
index 088a7660276..e873a97356e 100644
--- a/gdb/testsuite/gdb.cp/breakpoint-locs.exp
+++ b/gdb/testsuite/gdb.cp/breakpoint-locs.exp
@@ -26,4 +26,4 @@  if { [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2"\
     return -1
 }
 
-gdb_test "break N1::C1::baz" "\\(2 locations\\)"
+gdb_breakpoint "N1::C1::baz" -locs 2
diff --git a/gdb/testsuite/gdb.cp/ena-dis-br-range.exp b/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
index 103d38baa2f..0bd1d2e1c0d 100644
--- a/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
+++ b/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
@@ -51,9 +51,7 @@  proc make_info_breakpoint_reply_re {b1 b2 b21 b22 b23 b24} {
 	       ]
 }
 
-gdb_test "break foo::overload" \
-    "Breakpoint \[0-9\]+ at $hex: foo::overload. .4 locations." \
-    "set breakpoint at overload"
+gdb_breakpoint "foo::overload" -locs 4
 
 gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
     "breakpoint info"
diff --git a/gdb/testsuite/gdb.cp/mb-ctor.exp b/gdb/testsuite/gdb.cp/mb-ctor.exp
index 1a84b7990f4..f628ea5e997 100644
--- a/gdb/testsuite/gdb.cp/mb-ctor.exp
+++ b/gdb/testsuite/gdb.cp/mb-ctor.exp
@@ -38,15 +38,11 @@  if {![runto_main]} {
 # Set a breakpoint with multiple locations
 # and a condition.
 
-gdb_test "break 'Derived::Derived(int)'" \
-    "Breakpoint.*at.*: Derived::Derived.int.. \\(2 locations\\).*" \
-    "set-breakpoint at ctor"
+gdb_breakpoint "Derived::Derived(int)" -locs 2
 
 gdb_breakpoint [gdb_get_line_number "set breakpoint here"]
 
-gdb_test "break 'Derived::~Derived()'" \
-    "Breakpoint.*at.*: Derived::~Derived... \\(2 locations\\).*" \
-    "set-breakpoint at dtor"
+gdb_breakpoint "Derived::~Derived()" -locs 2
 
 gdb_test "continue" \
     ".*Breakpoint.*Derived.*i=7.*" \
diff --git a/gdb/testsuite/gdb.cp/mb-inline.exp b/gdb/testsuite/gdb.cp/mb-inline.exp
index db80bb48e40..f34a12e4953 100644
--- a/gdb/testsuite/gdb.cp/mb-inline.exp
+++ b/gdb/testsuite/gdb.cp/mb-inline.exp
@@ -33,9 +33,7 @@  set bp_location [gdb_get_line_number "set breakpoint here" $hdrfile]
 
 # Set a breakpoint with multiple locations.
 
-gdb_test "break $hdrfile:$bp_location" \
-    "Breakpoint.*at.*: $hdrfile:$bp_location. \\(2 locations\\).*" \
-    "set breakpoint"
+gdb_breakpoint "$hdrfile:$bp_location" -locs 2
 
 # Do "info break" now so we can easily compare it with the later "info break"
 # if problems arise.
@@ -83,9 +81,7 @@  if { ![runto_main] } {
     return 0
 }
 
-gdb_test "break $hdrfile:$bp_location" \
-    "Breakpoint.*at.*: $hdrfile:$bp_location. \\(2 locations\\).*" \
-    "set multi_line_foo breakpoint"
+gdb_breakpoint "$hdrfile:$bp_location" -locs 2
 gdb_test "continue" \
     ".*Breakpoint.*multi_line_foo \\(i=0\\).*" \
     "run to multi_line_foo breakpoint 4 afn"
diff --git a/gdb/testsuite/gdb.cp/mb-templates.exp b/gdb/testsuite/gdb.cp/mb-templates.exp
index 2df88a322bb..f707220caac 100644
--- a/gdb/testsuite/gdb.cp/mb-templates.exp
+++ b/gdb/testsuite/gdb.cp/mb-templates.exp
@@ -30,9 +30,8 @@  set bp_location [gdb_get_line_number "set breakpoint here"]
 # Set a breakpoint with multiple locations
 # and a condition.
 
-gdb_test "break $srcfile:$bp_location if i==1" \
-    "Breakpoint.*at.*: $srcfile:$bp_location. \\(2 locations\\).*" \
-    "initial condition: set breakpoint"
+gdb_breakpoint "$srcfile:$bp_location if i==1" -locs 2 \
+    -locspec "$srcfile:$bp_location"
 
 gdb_run_cmd
 
@@ -55,9 +54,7 @@  delete_breakpoints
 gdb_test "kill" "" "kill" \
          {Kill the program being debugged\? \(y or n\) } "y"
 
-gdb_test "break $srcfile:$bp_location" \
-    "Breakpoint.*at.*: $srcfile:$bp_location. \\(2 locations\\).*" \
-    "separate condition: set breakpoint"
+gdb_breakpoint "$srcfile:$bp_location" -locs 2
 
 gdb_test_no_output {condition $bpnum i==1} \
     "separate condition: set condition"
@@ -111,9 +108,7 @@  if { ![runto_main] } {
     return 0
 }
 
-gdb_test "break $srcfile:$bp_location" \
-    "Breakpoint.*at.*: $srcfile:$bp_location. \\(2 locations\\).*" \
-    "set multi_line_foo breakpoint"
+gdb_breakpoint "$srcfile:$bp_location" -locs 2
 gdb_test "continue" \
     ".*Breakpoint.*multi_line_foo<int> \\(i=0\\).*" \
     "run to multi_line_foo breakpoint 2 <int>"
diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp
index 149f44c6c17..03163121df2 100644
--- a/gdb/testsuite/gdb.cp/meth-typedefs.exp
+++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp
@@ -181,7 +181,7 @@  foreach f [list "$func" "'$func'"] {
 	     "$line3${any}// test${any}"]
 
     delete_breakpoints
-    gdb_test "break $f" "\\(3 locations\\)"
+    gdb_breakpoint "$f" -locs 3 -locspec ""
 }
 
 gdb_exit
diff --git a/gdb/testsuite/gdb.cp/ovldbreak.exp b/gdb/testsuite/gdb.cp/ovldbreak.exp
index 99d0a388adb..84efb37b8db 100644
--- a/gdb/testsuite/gdb.cp/ovldbreak.exp
+++ b/gdb/testsuite/gdb.cp/ovldbreak.exp
@@ -443,9 +443,7 @@  gdb_test "break foo::foofunc" \
 # Test breaking on an overloaded function when multiple-symbols
 # is set to "all"
 gdb_test_no_output "set multiple-symbols all"
-gdb_test "break foo::foofunc" \
-    "Breakpoint \[0-9\]+ at ${hex}: foo::foofunc. .2 locations..*" \
-    "break on ambiguous symbol when multiple-symbols is set to all"
+gdb_breakpoint "foo::foofunc" -locs 2
 
 # That's all, folks.
 
diff --git a/gdb/testsuite/gdb.cp/paramless.exp b/gdb/testsuite/gdb.cp/paramless.exp
index 68f66441f80..3658ef7107e 100644
--- a/gdb/testsuite/gdb.cp/paramless.exp
+++ b/gdb/testsuite/gdb.cp/paramless.exp
@@ -35,7 +35,7 @@  foreach ordinary {"outer<int>::fn" "outer<char>::fn<short>" "toplev<char>"} {
     clean_restart $testfile
 }
 
-gdb_test "break outer::fn" "Breakpoint $decimal at .*2 locations."
+gdb_breakpoint "outer::fn" -locs 2
 
 clean_restart $testfile
-gdb_test "break toplev" "Breakpoint $decimal at .*2 locations."
+gdb_breakpoint "toplev" -locs 2
diff --git a/gdb/testsuite/gdb.cp/templates.exp b/gdb/testsuite/gdb.cp/templates.exp
index 8dd0cdc70ef..3ca8b20d844 100644
--- a/gdb/testsuite/gdb.cp/templates.exp
+++ b/gdb/testsuite/gdb.cp/templates.exp
@@ -620,25 +620,25 @@  gdb_test_no_output "set multiple-symbols all"
 
 # Test setting breakpoints in a method of all class template instantiations,
 # including overloads.
-gdb_test "break Foo::foo"  "Breakpoint.*at.* \\(3 locations\\)"
+gdb_breakpoint "Foo::foo" -locs 3
 foreach t [list "int" "char" "volatile char *"] {
     gdb_breakpoint "Foo<$t>::foo (int, $t)"
     gdb_breakpoint "Foo::foo (int, $t)"
 }
 
-gdb_test "break Bar::bar" "Breakpoint.*at.* \\(2 locations\\)"
-gdb_test "break Bar::bar (int, int)" "Breakpoint.*at.* \\(2 locations\\)"
+gdb_breakpoint "Bar::bar" -locs 2
+gdb_breakpoint "Bar::bar (int, int)" -locs 2
 foreach val [list 1 33] {
     gdb_breakpoint "Bar<int, $val>::bar (int, int)"
 }
 
 # Test setting breakpoints in a member function template of a class template,
 # including overloads.
-gdb_test "break Foozle::fogey" "Breakpoint.*at.* \\(9 locations\\)" \
-    "break at template method fogey"
+gdb_breakpoint "Foozle::fogey" -locs 9
+
 foreach t [list "int" "char" "Empty<int>"] {
-    gdb_test "break Foozle::fogey ($t)" "Breakpoint.*at.* \\(3 locations\\)"
-    gdb_test "break Foozle::fogey<$t>" "Breakpoint.*at.* \\(3 locations\\)"
+    gdb_breakpoint "Foozle::fogey ($t)" -locs 3
+    gdb_breakpoint "Foozle::fogey<$t>" -locs 3
     foreach u [list "int" "char" "Empty<int>"] {
 	gdb_breakpoint "Foozle<$t>::fogey<$u>" message
 	gdb_assert { [gdb_breakpoint "Foozle<$t>::fogey<$u> ($u)" no-message] } \
@@ -657,10 +657,10 @@  foreach t [list "int" "char" "Empty<int>"] {
 # operator<<:
 #   1. operator<< <Empty<int>>
 #   2. operator<< <Foozle<int>>
-gdb_test "break -source $srcfile -func operator<" \
-    "Breakpoint.*at.* \\(4 locations\\)"
-gdb_test "break -source $srcfile -func operator<<" \
-    "Breakpoint.*at.* \\(2 locations\\)"
+gdb_breakpoint "-source $srcfile -func operator<" -locs 4 -locspec \
+    "-source $srcfile -function operator<"
+gdb_breakpoint "-source $srcfile -func operator<<" -locs 2 -locspec \
+   "-source $srcfile -function operator<<"
 foreach t [list "Empty" "Foozle"] {
     set tt "$t<int>"
     gdb_breakpoint "operator< <$tt>" message
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp b/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp
index 2e6f7114c33..6371b629d9b 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-inline-break.exp
@@ -44,8 +44,8 @@  gdb_test "break func1" \
 # The result should be a breakpoint with two locations: the
 # out-of-line function and the single inlined instance.
 #
-gdb_test "break func2" \
-    "Breakpoint.*at.*func2.*\\(2 locations\\)"
+gdb_breakpoint "func2" -locs 2
+
 
 #
 # func3b is a static inlined function that is called once from
@@ -62,8 +62,7 @@  gdb_test "break func3b" \
 # the inlined call to func4a in main, and the inlined instance
 # within the out-of-line func4a.
 #
-gdb_test "break func4b" \
-    "Breakpoint.*at.*func4b.*\\(2 locations\\)"
+gdb_breakpoint "func4b" -locs 2
 
 #
 # func5b is a non-static inlined function that is called once
@@ -71,8 +70,8 @@  gdb_test "break func4b" \
 # breakpoint with two locations: the out-of-line function and the
 # inlined instance within the inlined call to func5a in main.
 #
-gdb_test "break func5b" \
-    "Breakpoint.*at.*func5b.*\\(2 locations\\)"
+gdb_breakpoint "func5b" -locs 2
+
 #
 # func6b is a non-static inlined function that is called once from
 # within another non-static inlined function.  The result should be
@@ -80,8 +79,7 @@  gdb_test "break func5b" \
 # inlined instance within the out-of-line func6a, and the inlined
 # instance within the inlined call to func6a in main,
 #
-gdb_test "break func6b" \
-    "Breakpoint.*at.*func6b.*\\(3 locations\\)"
+gdb_breakpoint "func6b" -locs 3
 
 #
 # func7b is a static inlined function that is called twice: once from
@@ -89,8 +87,7 @@  gdb_test "break func6b" \
 # two locations: the inlined instance within the inlined instance of
 # func7a, and the inlined instance within main.
 #
-gdb_test "break func7b" \
-    "Breakpoint.*at.*func7b.*\\(2 locations\\)"
+gdb_breakpoint "func7b" -locs 2
 
 #
 # func8b is a non-static inlined function that is called twice: once
@@ -99,8 +96,7 @@  gdb_test "break func7b" \
 # within the inlined instance of func7a, and the inlined instance
 # within main.
 #
-gdb_test "break func8b" \
-    "Breakpoint.*at.*func8b.*\\(3 locations\\)"
+gdb_breakpoint "func8b" -locs 3
 
 #
 # func1 is a static inlined function.  The result should be that no
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp b/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
index 78ac57439f3..49fc2293dc4 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-skip-prologue.exp
@@ -71,7 +71,7 @@  if ![runto_main] {
     return -1
 }
 
-gdb_breakpoint "func"
+gdb_breakpoint "func" -locs 2 -locspec ""
 gdb_continue_to_breakpoint "func"
 
 # Sanity check GDB has really found 2 locations
diff --git a/gdb/testsuite/gdb.linespec/break-asm-file.exp b/gdb/testsuite/gdb.linespec/break-asm-file.exp
index 1044fc79a26..3977e6aac85 100644
--- a/gdb/testsuite/gdb.linespec/break-asm-file.exp
+++ b/gdb/testsuite/gdb.linespec/break-asm-file.exp
@@ -60,7 +60,5 @@  gdb_test "break b/$asm_file0:func" \
 
 gdb_test "delete 2"
 
-gdb_test "break $asm_file0:func" \
-    "Breakpoint 3 at 0x\[0-9a-fA-F\]+: .*$asm_file0.*(2 locations).*" \
-    "set a break-point at function in all instances for a selected ASM file."
+gdb_breakpoint "$asm_file0:func" -locs 2
 
diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
index 576d788cae0..6398309f7e4 100644
--- a/gdb/testsuite/gdb.linespec/linespec.exp
+++ b/gdb/testsuite/gdb.linespec/linespec.exp
@@ -144,17 +144,11 @@  if { [is_remote host] } {
     }
 }
 
-gdb_test "break thefile.cc:$l1" \
-    "Breakpoint $decimal at $hex: thefile.cc:$l1. \[(\]2 locations\[)\]" \
-    "multi-location break using file:line"
+gdb_breakpoint "thefile.cc:$l1" -locs 2
 
-gdb_test "break dupname" \
-    "Breakpoint $decimal at $hex: dupname. \[(\]2 locations\[)\]" \
-    "multi-location break using duplicate function name"
+gdb_breakpoint "dupname" -locs 2
 
-gdb_test "break dupname:label" \
-    "Breakpoint $decimal at $hex: dupname:label. \[(\]2 locations\[)\]" \
-    "multi-location break using duplicate function name and label"
+gdb_breakpoint "dupname:label" -locs 2
 
 # Testing if the "condition" command completes only the breakpoints,
 # not the locations.
@@ -165,9 +159,7 @@  gdb_test "break lspec.cc:nosuchfunction" \
     "Function \"nosuchfunction\" not defined in \"lspec.cc\"." \
     "set breakpoint on non-existent function"
 
-gdb_test "break NameSpace::overload" \
-    "Breakpoint \[0-9\]+ at $hex: NameSpace::overload. \[(\]3 locations\[)\]" \
-    "set breakpoint at all instances of NameSpace::overload"
+gdb_breakpoint "NameSpace::overload" -locs 3
 
 gdb_test "break lspec.cc:NameSpace::overload" \
     "Breakpoint \[0-9\]+ at $hex: file .*lspec.cc, line 7." \
@@ -210,6 +202,4 @@  gdb_test "file $binfile" \
     "Reading symbols from .*" \
     "set the new inferior file for linespec tests"
 
-gdb_test "break -q main" \
-    "Breakpoint \[0-9\]+ at $hex: -qualified main. .2 locations." \
-    "set breakpoint at main in both inferiors"
+gdb_breakpoint "-qualified main" -locs 2
diff --git a/gdb/testsuite/gdb.multi/bp-thread-specific.exp b/gdb/testsuite/gdb.multi/bp-thread-specific.exp
index 11dc2483624..0e289790d7d 100644
--- a/gdb/testsuite/gdb.multi/bp-thread-specific.exp
+++ b/gdb/testsuite/gdb.multi/bp-thread-specific.exp
@@ -73,16 +73,14 @@  gdb_test "info threads" \
 # Set the first breakpoint.  Currently this is going to insert at two
 # 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: file \[^\r\n\]+$srcfile, line $decimal\\."
+gdb_breakpoint "foo thread 2.1"
 
 set bpnum [get_integer_valueof "\$bpnum" "INVALID"]
 
 # Now set another breakpoint that will be at the same location as the
 # earlier breakpoint.  Check that the thread-id used when describing
 # the earlier breakpoints is correct.
-gdb_test "break foo thread 1.1" \
-    "Breakpoint $decimal at $hex: file \[^\r\n\]+$srcfile, line $decimal\\."
+gdb_breakpoint "foo thread 1.1"
 
 # Save the breakpoints into a file.
 if {[is_remote host]} {
diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
index 82cc9240e50..67069e1da4d 100644
--- a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
@@ -172,8 +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_test "break foo inferior 1" \
-    "Breakpoint $decimal at $hex: foo\\. \\(2 locations\\)"
+gdb_breakpoint "foo inferior 1" -locs 2 -locspec "foo"
 set bp_number [get_integer_valueof "\$bpnum" "INVALID" \
 		  "get b/p number for inferior specific breakpoint"]
 
diff --git a/gdb/testsuite/gdb.opt/inline-break.exp b/gdb/testsuite/gdb.opt/inline-break.exp
index 9c57d22404b..70549301964 100644
--- a/gdb/testsuite/gdb.opt/inline-break.exp
+++ b/gdb/testsuite/gdb.opt/inline-break.exp
@@ -96,8 +96,7 @@  gdb_test "break func1" \
 # The result should be a breakpoint with two locations: the
 # out-of-line function and the single inlined instance.
 #
-gdb_test "break func2" \
-    "Breakpoint.*at.*func2.*\\(2 locations\\)"
+gdb_breakpoint "func2" -locs 2
 
 #
 # func3b is a static inlined function that is called once from
@@ -114,8 +113,8 @@  gdb_test "break func3b" \
 # the inlined call to func4a in main, and the inlined instance
 # within the out-of-line func4a.
 #
-gdb_test "break func4b" \
-    "Breakpoint.*at.*func4b.*\\(2 locations\\)"
+gdb_breakpoint "func4b" -locs 2
+
 
 #
 # func5b is a non-static inlined function that is called once
@@ -123,8 +122,8 @@  gdb_test "break func4b" \
 # breakpoint with two locations: the out-of-line function and the
 # inlined instance within the inlined call to func5a in main.
 #
-gdb_test "break func5b" \
-    "Breakpoint.*at.*func5b.*\\(2 locations\\)"
+gdb_breakpoint "func5b" -locs 2
+
 #
 # func6b is a non-static inlined function that is called once from
 # within another non-static inlined function.  The result should be
@@ -132,8 +131,7 @@  gdb_test "break func5b" \
 # inlined instance within the out-of-line func6a, and the inlined
 # instance within the inlined call to func6a in main,
 #
-gdb_test "break func6b" \
-    "Breakpoint.*at.*func6b.*\\(3 locations\\)"
+gdb_breakpoint "func6b" -locs 3
 
 #
 # func7b is a static inlined function that is called twice: once from
@@ -141,8 +139,7 @@  gdb_test "break func6b" \
 # two locations: the inlined instance within the inlined instance of
 # func7a, and the inlined instance within main.
 #
-gdb_test "break func7b" \
-    "Breakpoint.*at.*func7b.*\\(2 locations\\)"
+gdb_breakpoint "func7b" -locs 2
 
 #
 # func8b is a non-static inlined function that is called twice: once
@@ -151,8 +148,7 @@  gdb_test "break func7b" \
 # within the inlined instance of func7a, and the inlined instance
 # within main.
 #
-gdb_test "break func8b" \
-    "Breakpoint.*at.*func8b.*\\(3 locations\\)"
+gdb_breakpoint "func8b" -locs 3
 
 #
 # func1 is a static inlined function.  The result should be that no
@@ -314,8 +310,7 @@  with_test_prefix "check alignment" {
 	continue
     }
 
-    gdb_test "break func4b" \
-	"Breakpoint.*at.*func4b.*\\(2 locations\\)"
+    gdb_breakpoint "func4b" -locs 2
 
     set expected_line_length -1
     gdb_test_multiple "info break \$bpnum" "xxxx" {
diff --git a/gdb/testsuite/gdb.python/py-bp-locations.exp b/gdb/testsuite/gdb.python/py-bp-locations.exp
index 4892947ca55..a2bff8a655e 100644
--- a/gdb/testsuite/gdb.python/py-bp-locations.exp
+++ b/gdb/testsuite/gdb.python/py-bp-locations.exp
@@ -56,7 +56,7 @@  proc build_bpl_regexp { args } {
 }
 
 # Set breakpoint with 2 locations.
-gdb_breakpoint "add"
+gdb_breakpoint "add" -locs 2
 
 set expected_line_a [gdb_get_line_number "a + b"]
 set expected_line_b [gdb_get_line_number "c + d"]
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 3349da7a263..4a5f1e0df89 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -672,6 +672,19 @@  proc gdb_starti_cmd { {inferior_args {}} } {
 #
 # 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:
+# -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
+#          output is expected. For example, "func if a == 0" and the caller
+#          wants to match "func" in the breakpoint command output. When
+#          not set the content of LINESPEC is matched, regardless of -locs.
+# -locs specifies a regex for the number of locations that are printed by the
+#	break command. This also enforces that a single location match is
+#       treated as an error.
+# -extra allows specifying a regex for extra matches that need to appear before
+#	 the breakpoint command output.
 #
 # The result is 1 for success, 0 for failure.
 #
@@ -685,17 +698,32 @@  proc gdb_breakpoint { linespec args } {
     global gdb_prompt
     global decimal
 
+    parse_some_args {
+	{locspec "\[^\r\n\]*"}
+	{locs "\[0-9\]+"}
+	{extra ""}
+    }
+
+    if { $locspec eq "\[^\r\n\]*" && $locs ne "\[0-9\]+"} {
+	set locspec [string_to_regexp $linespec]
+    }
+
     set pending_response n
     if {[lsearch -exact $args allow-pending] != -1} {
 	set pending_response y
     }
 
     set break_command "break"
-    set break_message "Breakpoint"
+    # Expecting some output before breakmessage, append it before it.
+    if { $extra ne "" } {
+	append extra "\r\n"
+    }
+    set break_message "Breakpoint $decimal"
     if {[lsearch -exact $args temporary] != -1} {
 	set break_command "tbreak"
-	set break_message "Temporary breakpoint"
+	set break_message "Temporary breakpoint $decimal"
     }
+    set break_message "$extra$break_message"
 
     if {[lsearch -exact $args qualified] != -1} {
 	append break_command " -qualified"
@@ -712,36 +740,73 @@  proc gdb_breakpoint { linespec args } {
 	set print_pass 1
     }
 
+    # Counters for single and multiple locations
+    set multiple_locs 0
+    set single_loc 0
+
     set test_name "gdb_breakpoint: set breakpoint at $linespec"
-    # The first two regexps are what we get with -g, the third is without -g.
+    set fill "\[^\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 {
-	-re "$break_message \[0-9\]* at .*: file .*, line $decimal.\r\n$gdb_prompt $" {}
-	-re "$break_message \[0-9\]*: file .*, line $decimal.\r\n$gdb_prompt $" {}
-	-re "$break_message \[0-9\]* at .*$gdb_prompt $" {}
-	-re "$break_message \[0-9\]* \\(.*\\) pending.*$gdb_prompt $" {
-		if {$pending_response == "n"} {
-			if { $print_fail } {
-				fail $gdb_test_name
-			}
-			return 0
+	-re -wrap "$break_message at $fill+: file $fill+, line $decimal\\." {
+	    incr single_loc
+	}
+	-re -wrap "$break_message: file .*, line $decimal\\." {
+	    incr single_loc
+	}
+	-re -wrap "$break_message at $::hex$fill*$locspec$fill*\($locs locations\)$fill+" {
+	    incr multiple_locs
+	}
+	-re -wrap "$break_message at $fill+\." {
+	    incr single_loc
+	}
+	-re -wrap "$break_message \\(.*\\) pending$fill+" {
+	    if {$pending_response == "n"} {
+		if { $print_fail } {
+		    fail $gdb_test_name
 		}
+		return 0
+	    }
 	}
-	-re "Make breakpoint pending.*y or \\\[n\\\]. $" { 
-		send_gdb "$pending_response\n"
-		exp_continue
+	-re "Make breakpoint pending.*y or \\\[n\\\]. $" {
+	    send_gdb "$pending_response\n"
+	    exp_continue
 	}
-	-re "$gdb_prompt $" {
+	-re -wrap "" {
 	    if { $print_fail } {
 		fail $test_name
 	    }
 	    return 0
 	}
     }
+
+    # If multiple locations where requested, assert we didn't
+    # see a single location match.
+    if { $locs ne "\[0-9\]+"
+	 && $locs ne 1
+	 && $single_loc != 0 } {
+	if { $print_fail } {
+	    fail $test_name
+	}
+	return 0
+    }
+
+    # Special case to enforce single location matches only. If -LOCS is set to
+    # 1 fail if we get multi location matches.
+    if { $locs eq 1 && $multiple_locs != 0 } {
+	if { $print_fail } {
+	    fail $test_name
+	}
+	return 0
+    }
+
     if { $print_pass } {
 	pass $test_name
     }
     return 1
-}    
+}
 
 # Set breakpoint at function and run gdb until it breaks there.
 # Since this is the only breakpoint that will be set, if it stops