binutils-gdb/gdb/testsuite/gdb.base/condbreak-multi-context.cc
Tankut Baris Aktemur b5fa468fef gdb/breakpoint: disable a bp location if condition is invalid at that location
Currently, for a conditional breakpoint, GDB checks if the condition
can be evaluated in the context of the first symtab and line (SAL).
In case of an error, defining the conditional breakpoint is aborted.
This prevents having a conditional breakpoint whose condition may
actually be meaningful for some of the location contexts.  This patch
makes it possible to define conditional BPs by checking all location
contexts.  If the condition is meaningful for even one context, the
breakpoint is defined.  The locations for which the condition gives
errors are disabled.

The bp_location struct is introduced a new field, 'disabled_by_cond'.
This field denotes whether the location is disabled automatically
because the condition was non-evaluatable.  Disabled-by-cond locations
cannot be enabled by the user.  But locations that are not
disabled-by-cond can be enabled/disabled by the user manually as
before.

For a concrete example, consider 3 contexts of a function 'func'.

  class Base
  {
  public:
    int b = 20;

    void func () {}
  };

  class A : public Base
  {
  public:
    int a = 10;

    void func () {}
  };

  class C : public Base
  {
  public:
    int c = 30;

    void func () {}
  };

Note that

* the variable 'a' is defined only in the context of A::func.
* the variable 'c' is defined only in the context of C::func.
* the variable 'b' is defined in all the three contexts.

With the existing GDB, it's not possible to define a conditional
breakpoint at 'func' if the condition refers to 'a' or 'c':

  (gdb) break func if a == 10
  No symbol "a" in current context.
  (gdb) break func if c == 30
  No symbol "c" in current context.
  (gdb) info breakpoints
  No breakpoints or watchpoints.

With this patch, it becomes possible:

  (gdb) break func if a == 10
  warning: failed to validate condition at location 1, disabling:
    No symbol "a" in current context.
  warning: failed to validate condition at location 3, disabling:
    No symbol "a" in current context.
  Breakpoint 1 at 0x11b6: func. (3 locations)
  (gdb) break func if c == 30
  Note: breakpoint 1 also set at pc 0x11ce.
  Note: breakpoint 1 also set at pc 0x11c2.
  Note: breakpoint 1 also set at pc 0x11b6.
  warning: failed to validate condition at location 1, disabling:
    No symbol "c" in current context.
  warning: failed to validate condition at location 2, disabling:
    No symbol "c" in current context.
  Breakpoint 2 at 0x11b6: func. (3 locations)
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
          stop only if a == 10
  1.1                         N*  0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  1.2                         y   0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  1.3                         N*  0x00000000000011ce in C::func() at condbreak-multi-context.cc:39
  2       breakpoint     keep y   <MULTIPLE>
          stop only if c == 30
  2.1                         N*  0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  2.2                         N*  0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  2.3                         y   0x00000000000011ce in C::func() at condbreak-multi-context.cc:39
  (*): Breakpoint condition is invalid at this location.

Here, uppercase 'N' denotes that the location is disabled because of
the invalid condition, as mentioned with a footnote in the legend of
the table.  Locations that are disabled by the user are still denoted
with lowercase 'n'.  Executing the code hits the breakpoints 1.2 and
2.3 as expected.

Defining a condition on an unconditional breakpoint gives the same
behavior above:

  (gdb) break func
  Breakpoint 1 at 0x11b6: func. (3 locations)
  (gdb) cond 1 a == 10
  warning: failed to validate condition at location 1.1, disabling:
    No symbol "a" in current context.
  warning: failed to validate condition at location 1.3, disabling:
    No symbol "a" in current context.
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
          stop only if a == 10
  1.1                         N*  0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  1.2                         y   0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  1.3                         N*  0x00000000000011ce in C::func() at condbreak-multi-context.cc:39
  (*): Breakpoint condition is invalid at this location.

Locations that are disabled because of a condition cannot be enabled
by the user:

  ...
  (gdb) enable 1.1
  Breakpoint 1's condition is invalid at location 1, cannot enable.

Resetting the condition enables the locations back:

  ...
  (gdb) cond 1
  Breakpoint 1's condition is now valid at location 1, enabling.
  Breakpoint 1's condition is now valid at location 3, enabling.
  Breakpoint 1 now unconditional.
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
  1.1                         y   0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  1.2                         y   0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  1.3                         y   0x00000000000011ce in C::func() at condbreak-multi-context.cc:39

If a location is disabled by the user, a condition can still be defined
but the location will remain disabled even if the condition is meaningful
for the disabled location:

  ...
  (gdb) disable 1.2
  (gdb) cond 1 a == 10
  warning: failed to validate condition at location 1.1, disabling:
    No symbol "a" in current context.
  warning: failed to validate condition at location 1.3, disabling:
    No symbol "a" in current context.
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
          stop only if a == 10
  1.1                         N*  0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  1.2                         n   0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  1.3                         N*  0x00000000000011ce in C::func() at condbreak-multi-context.cc:39
  (*): Breakpoint condition is invalid at this location.

The condition of a breakpoint can be changed.  Locations'
enable/disable states are updated accordingly.

  ...
  (gdb) cond 1 c == 30
  warning: failed to validate condition at location 1.1, disabling:
    No symbol "c" in current context.
  Breakpoint 1's condition is now valid at location 3, enabling.
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
          stop only if c == 30
  1.1                         N*  0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  1.2                         N*  0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  1.3                         y   0x00000000000011ce in C::func() at condbreak-multi-context.cc:39
  (*): Breakpoint condition is invalid at this location.

  (gdb) cond 1 b == 20
  Breakpoint 1's condition is now valid at location 1, enabling.
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
          stop only if b == 20
  1.1                         y   0x00000000000011b6 in Base::func() at condbreak-multi-context.cc:23
  1.2                         n   0x00000000000011c2 in A::func() at condbreak-multi-context.cc:31
  1.3                         y   0x00000000000011ce in C::func() at condbreak-multi-context.cc:39
  # Note that location 1.2 was disabled by the user previously.

If the condition expression is bad for all the locations, it will be
rejected.

  (gdb) cond 1 garbage
  No symbol "garbage" in current context.

For conditions that are invalid or valid for all the locations of a
breakpoint, the existing behavior is preserved.

Regression-tested on X86_64 Linux.

gdb/ChangeLog:
2020-10-27  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

	* breakpoint.h (class bp_location) <disabled_by_cond>: New field.
	* breakpoint.c (set_breakpoint_location_condition): New function.
	(set_breakpoint_condition): Disable a breakpoint location if parsing
	the condition string gives an error.
	(should_be_inserted): Update to consider the 'disabled_by_cond' field.
	(build_target_condition_list): Ditto.
	(build_target_command_list): Ditto.
	(build_bpstat_chain): Ditto.
	(print_one_breakpoint_location): Ditto.
	(print_one_breakpoint): Ditto.
	(breakpoint_1): Ditto.
	(bp_location::bp_location): Ditto.
	(locations_are_equal): Ditto.
	(update_breakpoint_locations): Ditto.
	(enable_disable_bp_num_loc): Ditto.
	(init_breakpoint_sal): Use set_breakpoint_location_condition.
	(find_condition_and_thread_for_sals): New static function.
	(create_breakpoint): Call find_condition_and_thread_for_sals.
	(location_to_sals): Call find_condition_and_thread_for_sals instead
	of find_condition_and_thread.

gdb/testsuite/ChangeLog:
2020-10-27  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

	* gdb.base/condbreak-multi-context.cc: New file.
	* gdb.base/condbreak-multi-context.exp: New file.

gdb/doc/ChangeLog:
2020-10-27  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

	* gdb.texinfo (Set Breaks): Document disabling of breakpoint
	locations for which the breakpoint condition is invalid.
2020-10-27 10:58:45 +01:00

55 lines
1.0 KiB
C++

/* This testcase is part of GDB, the GNU debugger.
Copyright 2020 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/>. */
class Base
{
public:
int b = 20;
void func () {}
};
class A : public Base
{
public:
int a = 10;
void func () {}
};
class C : public Base
{
public:
int c = 30;
void func () {}
};
int
main ()
{
Base bobj;
A aobj;
C cobj;
aobj.func ();
bobj.func ();
cobj.func ();
return 0;
}