diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 6b0a7c192ca..7cf96aa255f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2020-10-27 Tankut Baris Aktemur + + * breakpoint.h (set_breakpoint_condition): Add a new bool parameter. + * breakpoint.c: Update the help text of the 'condition' and 'break' + commands. + (set_breakpoint_condition): Take a new bool parameter + to control whether condition definition should be forced even when + the condition expression is invalid in all of the current locations. + (condition_command): Update the call to 'set_breakpoint_condition'. + (find_condition_and_thread): Take the "-force-condition" flag into + account. + * linespec.c (linespec_keywords): Add "-force-condition" as an + element. + (FORCE_KEYWORD_INDEX): New #define. + (linespec_lexer_lex_keyword): Update to consider "-force-condition" + as a keyword. + * ada-lang.c (create_ada_exception_catchpoint): Ditto. + * guile/scm-breakpoint.c (gdbscm_set_breakpoint_condition_x): Ditto. + * python/py-breakpoint.c (bppy_set_condition): Ditto. + * NEWS: Mention the changes to the 'break' and 'condition' commands. + 2020-10-27 Tankut Baris Aktemur * breakpoint.h (class bp_location) : New field. diff --git a/gdb/NEWS b/gdb/NEWS index 1789cf31356..c99d3181a8b 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -21,6 +21,28 @@ set debug event-loop show debug event-loop Control the display of debug output about GDB's event loop. +* Changed commands + +break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] + [-force-condition] [if CONDITION] + This command would previously refuse setting a breakpoint if the + CONDITION expression is invalid at a location. It now accepts and + defines the breakpoint if there is at least one location at which + the CONDITION is valid. The locations for which the CONDITION is + invalid, are automatically disabled. If CONDITION is invalid at all + of the locations, setting the breakpoint is still rejected. However, + the '-force-condition' flag can be used in this case for forcing GDB to + define the breakpoint, making all the current locations automatically + disabled. This may be useful if the user knows the condition will + become meaningful at a future location, e.g. due to a shared library + load. + +condition [-force] N COND + The behavior of this command is changed the same way for the 'break' + command as explained above. The '-force' flag can be used to force + GDB into defining the condition even when COND is invalid for all the + current locations of breakpoint N. + *** Changes in GDB 10 * There are new feature names for ARC targets: "org.gnu.gdb.arc.core" diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 0a5b93f5e29..9aba81449d4 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -12684,7 +12684,7 @@ create_ada_exception_catchpoint (struct gdbarch *gdbarch, c->excep_string = excep_string; create_excep_cond_exprs (c.get (), ex_kind); if (!cond_string.empty ()) - set_breakpoint_condition (c.get (), cond_string.c_str (), from_tty); + set_breakpoint_condition (c.get (), cond_string.c_str (), from_tty, false); install_breakpoint (0, std::move (c), 1); } diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 0d3fd0c3d7e..152f4066b48 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -882,7 +882,7 @@ set_breakpoint_location_condition (const char *cond_string, bp_location *loc, void set_breakpoint_condition (struct breakpoint *b, const char *exp, - int from_tty) + int from_tty, bool force) { if (*exp == 0) { @@ -950,8 +950,9 @@ set_breakpoint_condition (struct breakpoint *b, const char *exp, catch (const gdb_exception_error &e) { /* Condition string is invalid. If this happens to - be the last loc, abandon. */ - if (loc->next == nullptr) + be the last loc, abandon (if not forced) or continue + (if forced). */ + if (loc->next == nullptr && !force) throw; } } @@ -1032,6 +1033,18 @@ condition_command (const char *arg, int from_tty) error_no_arg (_("breakpoint number")); p = arg; + + /* Check if the "-force" flag was passed. */ + bool force = false; + const char *tok = skip_spaces (p); + const char *end_tok = skip_to_space (tok); + int toklen = end_tok - tok; + if (toklen >= 1 && strncmp (tok, "-force", toklen) == 0) + { + force = true; + p = end_tok + 1; + } + bnum = get_number (&p); if (bnum == 0) error (_("Bad breakpoint argument: '%s'"), arg); @@ -1051,7 +1064,7 @@ condition_command (const char *arg, int from_tty) " a %s stop condition defined for this breakpoint."), ext_lang_capitalized_name (extlang)); } - set_breakpoint_condition (b, p, from_tty); + set_breakpoint_condition (b, p, from_tty, force); if (is_breakpoint (b)) update_global_location_list (UGLL_MAY_INSERT); @@ -9172,6 +9185,7 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc, *thread = -1; *task = 0; *rest = NULL; + bool force = false; while (tok && *tok) { @@ -9195,10 +9209,25 @@ find_condition_and_thread (const char *tok, CORE_ADDR pc, if (toklen >= 1 && strncmp (tok, "if", toklen) == 0) { tok = cond_start = end_tok + 1; - parse_exp_1 (&tok, pc, block_for_pc (pc), 0); + try + { + parse_exp_1 (&tok, pc, block_for_pc (pc), 0); + } + catch (const gdb_exception_error &) + { + if (!force) + throw; + else + tok = tok + strlen (tok); + } cond_end = tok; *cond_string = savestring (cond_start, cond_end - cond_start); } + else if (toklen >= 1 && strncmp (tok, "-force-condition", toklen) == 0) + { + tok = cond_start = end_tok + 1; + force = true; + } else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0) { const char *tmptok; @@ -15252,7 +15281,8 @@ specified name as a complete fully-qualified name instead." command. */ #define BREAK_ARGS_HELP(command) \ -command" [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]\n\ +command" [PROBE_MODIFIER] [LOCATION] [thread THREADNUM]\n\ +\t[-force-condition] [if CONDITION]\n\ PROBE_MODIFIER shall be present if the command is to be placed in a\n\ probe point. Accepted values are `-probe' (for a generic, automatically\n\ guessed probe type), `-probe-stap' (for a SystemTap probe) or \n\ @@ -15265,6 +15295,9 @@ stack frame. This is useful for breaking on return to a stack frame.\n\ \n\ THREADNUM is the number from \"info threads\".\n\ CONDITION is a boolean expression.\n\ +\n\ +With the \"-force-condition\" flag, the condition is defined even when\n\ +it is invalid for all current locations.\n\ \n" LOCATION_HELP_STRING "\n\n\ Multiple breakpoints at one place are permitted, and useful if their\n\ conditions are different.\n\ @@ -15586,8 +15619,10 @@ then no output is printed when it is hit, except what the commands print.")); c = add_com ("condition", class_breakpoint, condition_command, _("\ Specify breakpoint number N to break only if COND is true.\n\ -Usage is `condition N COND', where N is an integer and COND is an\n\ -expression to be evaluated whenever breakpoint N is reached.")); +Usage is `condition [-force] N COND', where N is an integer and COND\n\ +is an expression to be evaluated whenever breakpoint N is reached.\n\ +With the \"-force\" flag, the condition is defined even when it is\n\ +invalid for all current locations.")); set_cmd_completer (c, condition_completer); c = add_com ("tbreak", class_breakpoint, tbreak_command, _("\ diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 7d02cedf2fa..7845dd7f88f 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -1627,9 +1627,11 @@ extern int breakpoints_should_be_inserted_now (void); in our opinion won't ever trigger. */ extern void breakpoint_retire_moribund (void); -/* Set break condition of breakpoint B to EXP. */ +/* Set break condition of breakpoint B to EXP. + If FORCE, define the condition even if it is invalid in + all of the breakpoint locations. */ extern void set_breakpoint_condition (struct breakpoint *b, const char *exp, - int from_tty); + int from_tty, bool force); /* Checks if we are catching syscalls or not. Returns 0 if not, greater than 0 if we are. */ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 84c173b94a3..b8a4429b661 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2020-10-27 Tankut Baris Aktemur + + * gdb.texinfo (Set Breaks): Document the '-force-condition' flag + of the 'break'command. + * gdb.texinfo (Conditions): Document the '-force' flag of the + 'condition' command. + 2020-10-27 Tankut Baris Aktemur * gdb.texinfo (Set Breaks): Document disabling of breakpoint diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 9da79ed5cde..d779d4a84f1 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -4318,6 +4318,30 @@ undefined variable: No symbol "foo" in current context. @end smallexample +@item break @dots{} -force-condition if @var{cond} +There may be cases where the condition @var{cond} is invalid at all +the current locations, but the user knows that it will be valid at a +future location; for example, because of a library load. In such +cases, by using the @code{-force-condition} keyword before @samp{if}, +@value{GDBN} can be forced to define the breakpoint with the given +condition expression instead of refusing it. + +@smallexample +(@value{GDBP}) break func -force-condition if foo +warning: failed to validate condition at location 1, disabling: + No symbol "foo" in current context. +warning: failed to validate condition at location 2, disabling: + No symbol "foo" in current context. +warning: failed to validate condition at location 3, disabling: + No symbol "foo" in current context. +Breakpoint 1 at 0x1158: test.c:18. (3 locations) +@end smallexample + +This causes all the present locations where the breakpoint would +otherwise be inserted, to be disabled, as seen in the example above. +However, if there exist locations at which the condition is valid, the +@code{-force-condition} keyword has no effect. + @kindex tbreak @item tbreak @var{args} Set a breakpoint enabled only for one stop. The @var{args} are the @@ -5503,6 +5527,12 @@ not actually evaluate @var{expression} at the time the @code{condition} command (or a command that sets a breakpoint with a condition, like @code{break if @dots{}}) is given, however. @xref{Expressions, ,Expressions}. +@item condition -force @var{bnum} @var{expression} +When the @code{-force} flag is used, define the condition even if +@var{expression} is invalid at all the current locations of breakpoint +@var{bnum}. This is similar to the @code{-force-condition} option +of the @code{break} command. + @item condition @var{bnum} Remove the condition from breakpoint number @var{bnum}. It becomes an ordinary unconditional breakpoint. diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c index 96b6ca91f8d..7c9707235ec 100644 --- a/gdb/guile/scm-breakpoint.c +++ b/gdb/guile/scm-breakpoint.c @@ -905,7 +905,7 @@ gdbscm_set_breakpoint_condition_x (SCM self, SCM newvalue) ? nullptr : gdbscm_scm_to_c_string (newvalue)); - set_breakpoint_condition (bp_smob->bp, exp ? exp.get () : "", 0); + set_breakpoint_condition (bp_smob->bp, exp ? exp.get () : "", 0, false); return SCM_UNSPECIFIED; }); diff --git a/gdb/linespec.c b/gdb/linespec.c index a5fd3af1e30..34161931167 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -72,7 +72,7 @@ enum class linespec_complete_what /* An expression. E.g., "break foo if EXPR", or "break *EXPR". */ EXPRESSION, - /* A linespec keyword ("if"/"thread"/"task"). + /* A linespec keyword ("if"/"thread"/"task"/"-force-condition"). E.g., "break func threa". */ KEYWORD, }; @@ -254,8 +254,9 @@ typedef enum ls_token_type linespec_token_type; /* List of keywords. This is NULL-terminated so that it can be used as enum completer. */ -const char * const linespec_keywords[] = { "if", "thread", "task", NULL }; +const char * const linespec_keywords[] = { "if", "thread", "task", "-force-condition", NULL }; #define IF_KEYWORD_INDEX 0 +#define FORCE_KEYWORD_INDEX 3 /* A token of the linespec lexer */ @@ -486,11 +487,22 @@ linespec_lexer_lex_keyword (const char *p) { int j; + /* Special case: "-force" is always followed by an "if". */ + if (i == FORCE_KEYWORD_INDEX) + { + p += len; + p = skip_spaces (p); + int nextlen = strlen (linespec_keywords[IF_KEYWORD_INDEX]); + if (!(strncmp (p, linespec_keywords[IF_KEYWORD_INDEX], nextlen) == 0 + && isspace (p[nextlen]))) + return NULL; + } + /* Special case: "if" ALWAYS stops the lexer, since it is not possible to predict what is going to appear in the condition, which can only be parsed after SaLs have been found. */ - if (i != IF_KEYWORD_INDEX) + else if (i != IF_KEYWORD_INDEX) { p += len; p = skip_spaces (p); diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index 7369c91ad90..8099b06531b 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -461,7 +461,7 @@ bppy_set_condition (PyObject *self, PyObject *newvalue, void *closure) try { - set_breakpoint_condition (self_bp->bp, exp, 0); + set_breakpoint_condition (self_bp->bp, exp, 0, false); } catch (gdb_exception &ex) { diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 4e5f5dcbe4c..cf646c7838d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2020-10-27 Tankut Baris Aktemur + + * gdb.base/condbreak-multi-context.exp: Expand to test forcing + the condition. + * gdb.linespec/cpcompletion.exp: Update to consider the + '-force-condition' keyword. + * gdb.linespec/explicit.exp: Ditto. + * lib/completion-support.exp: Ditto. + 2020-10-27 Tankut Baris Aktemur * gdb.base/condbreak-multi-context.cc: New file. diff --git a/gdb/testsuite/gdb.base/condbreak-multi-context.exp b/gdb/testsuite/gdb.base/condbreak-multi-context.exp index 4e56d36fb43..cef4280a81c 100644 --- a/gdb/testsuite/gdb.base/condbreak-multi-context.exp +++ b/gdb/testsuite/gdb.base/condbreak-multi-context.exp @@ -228,3 +228,28 @@ with_test_prefix "scenario 3" { # The second BP's locations are all disabled. No more hits! gdb_continue_to_end } + +# Scenario 4: Test the '-force'/'-force-condition' flag. + +with_test_prefix "force" { + clean_restart ${binfile} + + gdb_breakpoint "func" + # Pick a condition that is invalid at every location. + set bpnum1 [get_integer_valueof "\$bpnum" 0 "get bpnum1"] + gdb_test "cond -force $bpnum1 foo" \ + [multi_line \ + "${warning} at location ${bpnum1}.1, disabling:" \ + " No symbol \"foo\" in current context." \ + "${warning} at location ${bpnum1}.2, disabling:" \ + " No symbol \"foo\" in current context." \ + "${warning} at location ${bpnum1}.3, disabling:" \ + " No symbol \"foo\" in current context."] \ + "force the condition of bp 1" + check_bp_locations $bpnum1 {N* N* N*} "after forcing the condition" + + # Now with the 'break' command. + gdb_breakpoint "func -force-condition if baz" + set bpnum2 [get_integer_valueof "\$bpnum" 0 "get bpnum2"] + check_bp_locations $bpnum2 {N* N* N*} "set using the break command" +} diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp index 2c95e2b4048..de96556f22d 100644 --- a/gdb/testsuite/gdb.linespec/cpcompletion.exp +++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp @@ -836,12 +836,14 @@ proc_with_prefix function-labels {} { } # Test that completion after a function name offers keyword -# (if/task/thread) matches in linespec mode, and also the explicit -# location options in explicit locations mode. +# (if/task/thread/-force-condition) matches in linespec mode, and also +# the explicit location options in explicit locations mode. proc_with_prefix keywords-after-function {} { set explicit_list \ - [concat $completion::explicit_opts_list $completion::keyword_list] + [lsort [concat \ + $completion::explicit_opts_list \ + $completion::keyword_list]] # Test without a source file, with a known source file, and with # and unknown source file. @@ -865,7 +867,9 @@ proc_with_prefix keywords-after-function {} { proc_with_prefix keywords-after-label {} { set explicit_list \ - [concat $completion::explicit_opts_list $completion::keyword_list] + [lsort [concat \ + $completion::explicit_opts_list \ + $completion::keyword_list]] foreach_location_labels \ { "" "cpls.cc" } \ diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp index e9ccbbb7403..52a1fce5371 100644 --- a/gdb/testsuite/gdb.linespec/explicit.exp +++ b/gdb/testsuite/gdb.linespec/explicit.exp @@ -405,6 +405,7 @@ namespace eval $testfile { # completion matches both the explicit location options and # the linespec keywords. set completions_list { + "-force-condition" "-function" "-label" "-line" diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp index 51436cc6713..dbb958157ed 100644 --- a/gdb/testsuite/lib/completion-support.exp +++ b/gdb/testsuite/lib/completion-support.exp @@ -27,7 +27,7 @@ namespace eval completion { # List of all quote chars, including no-quote at all. variable maybe_quoted_list {"" "'" "\""} - variable keyword_list {"if" "task" "thread"} + variable keyword_list {"-force-condition" "if" "task" "thread"} variable explicit_opts_list \ {"-function" "-label" "-line" "-qualified" "-source"}