python: Add qualified parameter to gdb.Breakpoint

Message ID 1512686013-24658-1-git-send-email-simon.marchi@ericsson.com
State New, archived
Headers

Commit Message

Simon Marchi Dec. 7, 2017, 10:33 p.m. UTC
  This patch adds the possibility to pass a qualified=True|False parameter
when creating a breakpoint in Python.  It is equivalent to using
-qualified in a linespec.  The parameter actually accepts any Python
value, and converts it to boolean using Python's standard rules for
that (https://docs.python.org/3/library/stdtypes.html#truth).

Unlike the -source/-line/-function/-label parameters, it is possible to
use -qualified with a "normal" (non-explicit) linespec.  Therefore, it
is possible (unlike these other parameters) to use this new parameter
along with the spec parameter.

I updated the py-breakpoint.exp test.  To be able to test multiple
locations using a namespace, I had to switch the test case to compile as
C++.  If we really wanted to, we could run it as both C and C++, but
omit the C++-specific parts when running it as C.

gdb/ChangeLog:

	* location.h (string_to_event_location): Add match_type
	parameter.
	* location.c (string_to_event_location): Likewise.
	* python/py-breakpoint.c (bppy_init): Handle qualified
	parameter.

gdb/doc/ChangeLog:

	* python.texi (Manipulating breakpoints using Python): Document
	qualified parameter to gdb.Breakpoint.

gdb/testsuite/ChangeLog:

	* gdb.python/py-breakpoint.c (foo_ns::multiply): New function.
	* gdb.python/py-breakpoint.exp: Compile the test case as c++,
	call test_bkpt_qualified.
	(test_bkpt_qualified): New proc.
---
 gdb/doc/python.texi                        | 12 +++--
 gdb/location.c                             |  4 +-
 gdb/location.h                             | 11 +++--
 gdb/python/py-breakpoint.c                 | 16 +++++--
 gdb/testsuite/gdb.python/py-breakpoint.c   |  8 ++++
 gdb/testsuite/gdb.python/py-breakpoint.exp | 76 +++++++++++++++++++++++++++++-
 6 files changed, 113 insertions(+), 14 deletions(-)
  

Comments

Pedro Alves Dec. 8, 2017, 12:05 p.m. UTC | #1
That was fast.  :-)  Thanks!

See comments about the docs below.

The code looks fine to me, except a formatting nit.

> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -4885,7 +4885,7 @@ create both breakpoints and watchpoints.  The second accepts separate Python
>  arguments similar to @ref{Explicit Locations} and can only be used to create
>  breakpoints.
>  
> -@defun Breakpoint.__init__ (spec @r{[}, type @r{][}, wp_class @r{][}, internal @r{][}, temporary @r{]})
> +@defun Breakpoint.__init__ (spec @r{[}, type @r{][}, wp_class @r{][}, internal @r{][}, temporary @r{][}, qualified @r{]})
>  Create a new breakpoint according to @var{spec}, which is a string naming the
>  location of a breakpoint, or an expression that defines a watchpoint. The
>  contents can be any location recognized by the @code{break} command or, in the
> @@ -4909,14 +4909,20 @@ The optional @var{temporary} argument makes the breakpoint a temporary
>  breakpoint.  Temporary breakpoints are deleted after they have been hit.  Any
>  further access to the Python breakpoint after it has been hit will result in a
>  runtime error (as that breakpoint has now been automatically deleted).
> +
> +The optional @var{qualified} argument is a boolean that allows restricting the
> +breakpoint to free-functions. 

"free-functions" is incorrect.  With:

  struct A { void func (); }                  // #1
  namespace B { struct A { void func (); } }  // #2

"b -q A::func()" only matches #1 while
"b A::func()" matches both #1 and #2.

and A::func() above is not a free function.

Here's what we say for -qualified in the explicit locations part of the
manual:

~~~
@item -qualified

This flag makes @value{GDBN} interpret a function name specified with
@kbd{-function} as a complete fully-qualified name.

For example, assuming a C@t{++} program with symbols named
@code{A::B::func} and @code{B::func}, the @w{@kbd{break -qualified
-function B::func}} command sets a breakpoint on @code{B::func}, only.

(Note: the @kbd{-qualified} option can precede a linespec as well
(@pxref{Linespec Locations}), so the particular example above could be
simplified as @w{@kbd{break -qualified B::func}}.)
~~~

There's similar text in the linespecs part of the manual and also
in "help break".  See:
  $ git show a20714ff39f6 -- doc/gdb.texinfo NEWS break.c

So we either need to clarify that in the Python bits too,
or some do some xref'ing.

 It is equivalent to @code{break}'s
> +@code{-qualified} flag (@pxref{Linespec Locations}).
> +
>  @end defun
>  
> -@defun Breakpoint.__init__ (@r{[} source @r{][}, function @r{][}, label @r{][}, line @r{]}, @r{][} internal @r{][}, temporary @r{][})
> +@defun Breakpoint.__init__ (@r{[} source @r{][}, function @r{][}, label @r{][}, line @r{]}, @r{][} internal @r{][}, temporary  @r{][}, qualified @r{]})
>  Create a new explicit location breakpoint (@pxref{Explicit Locations})
>  according to the specifications contained in the key words @var{source},
>  @var{function}, @var{label} and @var{line}.

Should @var{qualified} be added to this list too?


(BTW, noticed a typo in this paragraph:

  "create a new a explicit location"

the second "a" is spurious.)

>  
> -@var{internal} and @var{temporary} have the same usage as explained previously.
> +@var{internal}, @var{temporary} and @var{qualified} have the same usage as
> +explained previously.
>  @end defun
>  
>  The available types are represented by constants defined in the @code{gdb}

> @@ -759,6 +761,9 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
>  	case bp_breakpoint:
>  	  {
>  	    event_location_up location;
> +	    symbol_name_match_type func_name_match_type
> +	      = qualified ? symbol_name_match_type::FULL
> +		  : symbol_name_match_type::WILD;

Should wrap the multi-line expression in ()s:

  symbol_name_match_type func_name_match_type
    = (qualified ? symbol_name_match_type::FULL
       : symbol_name_match_type::WILD);

Though I prefer breaking ternary operators like this:

  symbol_name_match_type func_name_match_type
    = (qualified
       ? symbol_name_match_type::FULL
       : symbol_name_match_type::WILD);

because it makes it look more obviously like

 if COND
   THEN
   ELSE

Thanks,
Pedro Alves
  
Eli Zaretskii Dec. 8, 2017, 2:15 p.m. UTC | #2
> From: Simon Marchi <simon.marchi@ericsson.com>
> CC: Simon Marchi <simon.marchi@ericsson.com>
> Date: Thu, 7 Dec 2017 17:33:33 -0500
> 
> This patch adds the possibility to pass a qualified=True|False parameter
> when creating a breakpoint in Python.  It is equivalent to using
> -qualified in a linespec.  The parameter actually accepts any Python
> value, and converts it to boolean using Python's standard rules for
> that (https://docs.python.org/3/library/stdtypes.html#truth).
> 
> Unlike the -source/-line/-function/-label parameters, it is possible to
> use -qualified with a "normal" (non-explicit) linespec.  Therefore, it
> is possible (unlike these other parameters) to use this new parameter
> along with the spec parameter.
> 
> I updated the py-breakpoint.exp test.  To be able to test multiple
> locations using a namespace, I had to switch the test case to compile as
> C++.  If we really wanted to, we could run it as both C and C++, but
> omit the C++-specific parts when running it as C.
> 
> gdb/ChangeLog:
> 
> 	* location.h (string_to_event_location): Add match_type
> 	parameter.
> 	* location.c (string_to_event_location): Likewise.
> 	* python/py-breakpoint.c (bppy_init): Handle qualified
> 	parameter.
> 
> gdb/doc/ChangeLog:
> 
> 	* python.texi (Manipulating breakpoints using Python): Document
> 	qualified parameter to gdb.Breakpoint.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.python/py-breakpoint.c (foo_ns::multiply): New function.
> 	* gdb.python/py-breakpoint.exp: Compile the test case as c++,
> 	call test_bkpt_qualified.
> 	(test_bkpt_qualified): New proc.

OK for the documentation part.  (Do we need a NEWS entry?)
  
Simon Marchi Dec. 8, 2017, 6:42 p.m. UTC | #3
On 2017-12-08 07:05, Pedro Alves wrote:
> That was fast.  :-)  Thanks!
> 
> See comments about the docs below.
> 
> The code looks fine to me, except a formatting nit.
> 
>> --- a/gdb/doc/python.texi
>> +++ b/gdb/doc/python.texi
>> @@ -4885,7 +4885,7 @@ create both breakpoints and watchpoints.  The 
>> second accepts separate Python
>>  arguments similar to @ref{Explicit Locations} and can only be used to 
>> create
>>  breakpoints.
>> 
>> -@defun Breakpoint.__init__ (spec @r{[}, type @r{][}, wp_class @r{][}, 
>> internal @r{][}, temporary @r{]})
>> +@defun Breakpoint.__init__ (spec @r{[}, type @r{][}, wp_class @r{][}, 
>> internal @r{][}, temporary @r{][}, qualified @r{]})
>>  Create a new breakpoint according to @var{spec}, which is a string 
>> naming the
>>  location of a breakpoint, or an expression that defines a watchpoint. 
>> The
>>  contents can be any location recognized by the @code{break} command 
>> or, in the
>> @@ -4909,14 +4909,20 @@ The optional @var{temporary} argument makes 
>> the breakpoint a temporary
>>  breakpoint.  Temporary breakpoints are deleted after they have been 
>> hit.  Any
>>  further access to the Python breakpoint after it has been hit will 
>> result in a
>>  runtime error (as that breakpoint has now been automatically 
>> deleted).
>> +
>> +The optional @var{qualified} argument is a boolean that allows 
>> restricting the
>> +breakpoint to free-functions.
> 
> "free-functions" is incorrect.  With:
> 
>   struct A { void func (); }                  // #1
>   namespace B { struct A { void func (); } }  // #2
> 
> "b -q A::func()" only matches #1 while
> "b A::func()" matches both #1 and #2.
> 
> and A::func() above is not a free function.
> 
> Here's what we say for -qualified in the explicit locations part of the
> manual:
> 
> ~~~
> @item -qualified
> 
> This flag makes @value{GDBN} interpret a function name specified with
> @kbd{-function} as a complete fully-qualified name.
> 
> For example, assuming a C@t{++} program with symbols named
> @code{A::B::func} and @code{B::func}, the @w{@kbd{break -qualified
> -function B::func}} command sets a breakpoint on @code{B::func}, only.
> 
> (Note: the @kbd{-qualified} option can precede a linespec as well
> (@pxref{Linespec Locations}), so the particular example above could be
> simplified as @w{@kbd{break -qualified B::func}}.)
> ~~~
> 
> There's similar text in the linespecs part of the manual and also
> in "help break".  See:
>   $ git show a20714ff39f6 -- doc/gdb.texinfo NEWS break.c
> 
> So we either need to clarify that in the Python bits too,
> or some do some xref'ing.

Agreed, I didn't like that wording either.  I had only found the 
Linespec Locations page, which doesn't specifically say "fully-qualified 
name", but mentions free-function (though it's in an example, not a 
formal definition).  How is this?

The optional @var{qualified} argument is a boolean that allows 
interpreting
the function passed in @code{spec} as a fully-qualified name.  It is 
equivalent
to @code{break}'s @code{-qualified} flag (@pxref{Linespec Locations} and
@ref{Explicit Locations}).

>  It is equivalent to @code{break}'s
>> +@code{-qualified} flag (@pxref{Linespec Locations}).
>> +
>>  @end defun
>> 
>> -@defun Breakpoint.__init__ (@r{[} source @r{][}, function @r{][}, 
>> label @r{][}, line @r{]}, @r{][} internal @r{][}, temporary @r{][})
>> +@defun Breakpoint.__init__ (@r{[} source @r{][}, function @r{][}, 
>> label @r{][}, line @r{]}, @r{][} internal @r{][}, temporary  @r{][}, 
>> qualified @r{]})
>>  Create a new explicit location breakpoint (@pxref{Explicit 
>> Locations})
>>  according to the specifications contained in the key words 
>> @var{source},
>>  @var{function}, @var{label} and @var{line}.
> 
> Should @var{qualified} be added to this list too?

It is mentioned in the paragraph below.

> (BTW, noticed a typo in this paragraph:
> 
>   "create a new a explicit location"
> 
> the second "a" is spurious.)

That text changed based on Eli's comments, but thanks anyway.

>> 
>> -@var{internal} and @var{temporary} have the same usage as explained 
>> previously.
>> +@var{internal}, @var{temporary} and @var{qualified} have the same 
>> usage as
>> +explained previously.
>>  @end defun
>> 
>>  The available types are represented by constants defined in the 
>> @code{gdb}
> 
>> @@ -759,6 +761,9 @@ bppy_init (PyObject *self, PyObject *args, 
>> PyObject *kwargs)
>>  	case bp_breakpoint:
>>  	  {
>>  	    event_location_up location;
>> +	    symbol_name_match_type func_name_match_type
>> +	      = qualified ? symbol_name_match_type::FULL
>> +		  : symbol_name_match_type::WILD;
> 
> Should wrap the multi-line expression in ()s:
> 
>   symbol_name_match_type func_name_match_type
>     = (qualified ? symbol_name_match_type::FULL
>        : symbol_name_match_type::WILD);
> 
> Though I prefer breaking ternary operators like this:
> 
>   symbol_name_match_type func_name_match_type
>     = (qualified
>        ? symbol_name_match_type::FULL
>        : symbol_name_match_type::WILD);
> 
> because it makes it look more obviously like
> 
>  if COND
>    THEN
>    ELSE

Done.

Thanks,

Simon
  
Pedro Alves Dec. 13, 2017, 1:17 p.m. UTC | #4
On 12/08/2017 06:42 PM, Simon Marchi wrote:

>> So we either need to clarify that in the Python bits too,
>> or some do some xref'ing.
> 
> Agreed, I didn't like that wording either.  I had only found the
> Linespec Locations page, which doesn't specifically say "fully-qualified
> name", but mentions free-function (though it's in an example, not a
> formal definition).  How is this?
> 
> The optional @var{qualified} argument is a boolean that allows interpreting
> the function passed in @code{spec} as a fully-qualified name.  It is
> equivalent
> to @code{break}'s @code{-qualified} flag (@pxref{Linespec Locations} and
> @ref{Explicit Locations}).

Thanks, that looks fine.

(I thought I had replied earlier, but looks like I dreamt it.)

Thanks,
Pedro Alves
  
Simon Marchi Dec. 13, 2017, 4:45 p.m. UTC | #5
On 2017-12-13 08:17, Pedro Alves wrote:
> On 12/08/2017 06:42 PM, Simon Marchi wrote:
> 
>>> So we either need to clarify that in the Python bits too,
>>> or some do some xref'ing.
>> 
>> Agreed, I didn't like that wording either.  I had only found the
>> Linespec Locations page, which doesn't specifically say 
>> "fully-qualified
>> name", but mentions free-function (though it's in an example, not a
>> formal definition).  How is this?
>> 
>> The optional @var{qualified} argument is a boolean that allows 
>> interpreting
>> the function passed in @code{spec} as a fully-qualified name.  It is
>> equivalent
>> to @code{break}'s @code{-qualified} flag (@pxref{Linespec Locations} 
>> and
>> @ref{Explicit Locations}).
> 
> Thanks, that looks fine.
> 
> (I thought I had replied earlier, but looks like I dreamt it.)
> 
> Thanks,
> Pedro Alves

Thanks, it is now pushed.

Simon
  

Patch

diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 47f4e07..78280d0 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4885,7 +4885,7 @@  create both breakpoints and watchpoints.  The second accepts separate Python
 arguments similar to @ref{Explicit Locations} and can only be used to create
 breakpoints.
 
-@defun Breakpoint.__init__ (spec @r{[}, type @r{][}, wp_class @r{][}, internal @r{][}, temporary @r{]})
+@defun Breakpoint.__init__ (spec @r{[}, type @r{][}, wp_class @r{][}, internal @r{][}, temporary @r{][}, qualified @r{]})
 Create a new breakpoint according to @var{spec}, which is a string naming the
 location of a breakpoint, or an expression that defines a watchpoint. The
 contents can be any location recognized by the @code{break} command or, in the
@@ -4909,14 +4909,20 @@  The optional @var{temporary} argument makes the breakpoint a temporary
 breakpoint.  Temporary breakpoints are deleted after they have been hit.  Any
 further access to the Python breakpoint after it has been hit will result in a
 runtime error (as that breakpoint has now been automatically deleted).
+
+The optional @var{qualified} argument is a boolean that allows restricting the
+breakpoint to free-functions.  It is equivalent to @code{break}'s
+@code{-qualified} flag (@pxref{Linespec Locations}).
+
 @end defun
 
-@defun Breakpoint.__init__ (@r{[} source @r{][}, function @r{][}, label @r{][}, line @r{]}, @r{][} internal @r{][}, temporary @r{][})
+@defun Breakpoint.__init__ (@r{[} source @r{][}, function @r{][}, label @r{][}, line @r{]}, @r{][} internal @r{][}, temporary  @r{][}, qualified @r{]})
 Create a new explicit location breakpoint (@pxref{Explicit Locations})
 according to the specifications contained in the key words @var{source},
 @var{function}, @var{label} and @var{line}.
 
-@var{internal} and @var{temporary} have the same usage as explained previously.
+@var{internal}, @var{temporary} and @var{qualified} have the same usage as
+explained previously.
 @end defun
 
 The available types are represented by constants defined in the @code{gdb}
diff --git a/gdb/location.c b/gdb/location.c
index 6752462..0f79764 100644
--- a/gdb/location.c
+++ b/gdb/location.c
@@ -915,10 +915,10 @@  string_to_event_location_basic (const char **stringp,
 
 event_location_up
 string_to_event_location (const char **stringp,
-			  const struct language_defn *language)
+			  const struct language_defn *language,
+			  symbol_name_match_type match_type)
 {
   const char *arg, *orig;
-  symbol_name_match_type match_type = symbol_name_match_type::WILD;
 
   /* Try an explicit location.  */
   orig = arg = *stringp;
diff --git a/gdb/location.h b/gdb/location.h
index fcfa8fb..d5d879d 100644
--- a/gdb/location.h
+++ b/gdb/location.h
@@ -220,11 +220,14 @@  extern event_location_up
    This function is intended to be used by CLI commands and will parse
    explicit locations in a CLI-centric way.  Other interfaces should use
    string_to_event_location_basic if they want to maintain support for
-   legacy specifications of probe, address, and linespec locations.  */
+   legacy specifications of probe, address, and linespec locations.
 
-extern event_location_up
-  string_to_event_location (const char **argp,
-			    const struct language_defn *langauge);
+   MATCH_TYPE should be either WILD or FULL.  If -q/--qualified is specified
+   in the input string, it will take precedence over this parameter.  */
+
+extern event_location_up string_to_event_location
+  (const char **argp, const struct language_defn *langauge,
+   symbol_name_match_type match_type = symbol_name_match_type::WILD);
 
 /* Like string_to_event_location, but does not attempt to parse
    explicit locations.  MATCH_TYPE indicates how function names should
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index f865317..5239713 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -694,7 +694,7 @@  bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 {
   static const char *keywords[] = { "spec", "type", "wp_class", "internal",
 				    "temporary","source", "function",
-				    "label", "line", NULL };
+				    "label", "line", "qualified", NULL };
   const char *spec = NULL;
   enum bptype type = bp_breakpoint;
   int access_type = hw_write;
@@ -707,12 +707,14 @@  bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
   char *label = NULL;
   char *source = NULL;
   char *function = NULL;
+  int qualified = 0;
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "|siiOOsssO", keywords,
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "|siiOOsssOp", keywords,
 					&spec, &type, &access_type,
 					&internal,
 					&temporary, &source,
-					&function, &label, &lineobj))
+					&function, &label, &lineobj,
+					&qualified))
     return -1;
 
 
@@ -759,6 +761,9 @@  bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 	case bp_breakpoint:
 	  {
 	    event_location_up location;
+	    symbol_name_match_type func_name_match_type
+	      = qualified ? symbol_name_match_type::FULL
+		  : symbol_name_match_type::WILD;
 
 	    if (spec != NULL)
 	      {
@@ -767,7 +772,8 @@  bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 		const char *copy = copy_holder.get ();
 
 		location  = string_to_event_location (&copy,
-						      current_language);
+						      current_language,
+						      func_name_match_type);
 	      }
 	    else
 	      {
@@ -782,6 +788,8 @@  bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 		  explicit_loc.line_offset =
 		    linespec_parse_line_offset (line.get ());
 
+		explicit_loc.func_name_match_type = func_name_match_type;
+
 		location = new_explicit_location (&explicit_loc);
 	      }
 
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.c b/gdb/testsuite/gdb.python/py-breakpoint.c
index 562cab3..c9f124c 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.c
+++ b/gdb/testsuite/gdb.python/py-breakpoint.c
@@ -17,6 +17,14 @@ 
 
 int result = 0;
 
+namespace foo_ns
+{
+  int multiply (int i)
+  {
+    return i * i;
+  }
+}
+
 int multiply (int i)
 {
   return i * i;
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e89b9b8..7b4cfaa 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -20,7 +20,9 @@  load_lib gdb-python.exp
 
 standard_testfile
 
-if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+set options {debug c++}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} ${options}] } {
     return -1
 }
 
@@ -610,6 +612,77 @@  proc test_bkpt_explicit_loc {} {
     }
 }
 
+proc_with_prefix test_bkpt_qualified {} {
+    global decimal hex testfile
+
+    # Start with a fresh gdb.
+    clean_restart ${testfile}
+
+    set one_location_re "Breakpoint $decimal at $hex:.*line $decimal."
+    set two_location_re "Breakpoint $decimal at $hex:.*2 locations."
+
+    if ![runto_main] then {
+	fail "cannot run to main."
+	return 0
+    }
+
+    # Test the default value of "qualified".
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(\"multiply\")" \
+	$two_location_re \
+	"qualified implicitly false"
+
+    # Test qualified=False.
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(\"multiply\", qualified=False)" \
+	$two_location_re \
+	"qualified false"
+
+    # Test qualified=True.
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(\"multiply\", qualified=True)" \
+	$one_location_re \
+	"qualified true"
+
+    # Test qualified=True with an explicit function.
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(function=\"multiply\", qualified=True)" \
+	$one_location_re \
+	"qualified true and explicit"
+
+    # Test qualified=False with an explicit function.
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(function=\"multiply\", qualified=False)" \
+	$two_location_re \
+	"qualified false and explicit"
+
+    # Test -q in the spec string.
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(\"-q multiply\")" \
+	$one_location_re \
+	"-q in spec string"
+
+    # Test -q in the spec string with explicit location.
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(\"-q -function multiply\")" \
+	$one_location_re \
+	"-q in spec string with explicit location"
+
+    # Test -q in the spec string and qualified=False (-q should win).
+    delete_breakpoints
+    gdb_test \
+	"python gdb.Breakpoint(\"-q multiply\", qualified=False)" \
+	$one_location_re \
+	"-q in spec string and qualified false"
+}
+
 test_bkpt_basic
 test_bkpt_deletion
 test_bkpt_cond_and_cmds
@@ -622,3 +695,4 @@  test_bkpt_address
 test_bkpt_pending
 test_bkpt_events
 test_bkpt_explicit_loc
+test_bkpt_qualified