diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 60244ef224..0698f74b39 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,13 @@ +2014-04-10 Pedro Alves + + * mem-break.c (add_breakpoint_condition, add_breakpoint_commands): + Check if the condition or command is NULL before checking if the + breakpoint is known. On success, return true. + * mem-break.h (add_breakpoint_condition): Document return. + (add_breakpoint_commands): Add describing comment. + * server.c (skip_to_semicolon): New function. + (process_point_options): Use it. + 2014-04-09 Pedro Alves * linux-low.c (linux_read_loadmap): Pass current_inferior directly diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index b77ce100ff..5df950d348 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -799,12 +799,12 @@ add_breakpoint_condition (CORE_ADDR addr, char **condition) char *actparm = *condition; struct agent_expr *cond; - if (bp == NULL) - return 1; - if (condition == NULL) return 1; + if (bp == NULL) + return 0; + cond = gdb_parse_agent_expr (&actparm); if (cond == NULL) @@ -818,7 +818,7 @@ add_breakpoint_condition (CORE_ADDR addr, char **condition) *condition = actparm; - return 0; + return 1; } /* Evaluate condition (if any) at breakpoint BP. Return 1 if @@ -891,12 +891,12 @@ add_breakpoint_commands (CORE_ADDR addr, char **command, int persist) char *actparm = *command; struct agent_expr *cmd; - if (bp == NULL) - return 1; - if (command == NULL) return 1; + if (bp == NULL) + return 0; + cmd = gdb_parse_agent_expr (&actparm); if (cmd == NULL) @@ -910,7 +910,7 @@ add_breakpoint_commands (CORE_ADDR addr, char **command, int persist) *command = actparm; - return 0; + return 1; } /* Return true if there are no commands to run at this location, diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h index 16655383ec..4346881b5b 100644 --- a/gdb/gdbserver/mem-break.h +++ b/gdb/gdbserver/mem-break.h @@ -48,10 +48,17 @@ int breakpoint_inserted_here (CORE_ADDR addr); void clear_gdb_breakpoint_conditions (CORE_ADDR addr); -/* Set target-side condition CONDITION to the breakpoint at ADDR. */ +/* Set target-side condition CONDITION to the breakpoint at ADDR. + Returns false on failure. On success, advances CONDITION pointer + past the condition and returns true. */ int add_breakpoint_condition (CORE_ADDR addr, char **condition); +/* Set target-side commands COMMANDS to the breakpoint at ADDR. + Returns false on failure. On success, advances COMMANDS past the + commands and returns true. If PERSIST, the commands should run + even while GDB is disconnected. */ + int add_breakpoint_commands (CORE_ADDR addr, char **commands, int persist); int any_persistent_commands (void); diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 9868af628f..8317ac07d1 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -3350,6 +3350,15 @@ main (int argc, char *argv[]) } } +/* Skip PACKET until the next semi-colon (or end of string). */ + +static void +skip_to_semicolon (char **packet) +{ + while (**packet != '\0' && **packet != ';') + (*packet)++; +} + /* Process options coming from Z packets for *point at address POINT_ADDR. PACKET is the packet buffer. *PACKET is updated to point to the first char after the last processed option. */ @@ -3376,7 +3385,8 @@ process_point_options (CORE_ADDR point_addr, char **packet) /* Conditional expression. */ if (debug_threads) debug_printf ("Found breakpoint condition.\n"); - add_breakpoint_condition (point_addr, &dataptr); + if (!add_breakpoint_condition (point_addr, &dataptr)) + skip_to_semicolon (&dataptr); } else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0) { @@ -3385,15 +3395,15 @@ process_point_options (CORE_ADDR point_addr, char **packet) debug_printf ("Found breakpoint commands %s.\n", dataptr); persist = (*dataptr == '1'); dataptr += 2; - add_breakpoint_commands (point_addr, &dataptr, persist); + if (add_breakpoint_commands (point_addr, &dataptr, persist)) + skip_to_semicolon (&dataptr); } else { fprintf (stderr, "Unknown token %c, ignoring.\n", *dataptr); /* Skip tokens until we find one that we recognize. */ - while (*dataptr && *dataptr != ';') - dataptr++; + skip_to_semicolon (&dataptr); } } *packet = dataptr; diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 5fca9d2243..12ed4f9010 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2014-04-10 Pedro Alves + + * gdb.base/cond-eval-mode.c: New file. + * gdb.base/cond-eval-mode.exp: Use standard_testfile. Adjust + prepare_for_testing to build the new file. Check result of + runto_main. + (test_break, test_watch): New procedures. + (top level): Use them. + 2014-04-08 Pierre Muller * gdb.base/printcmds.exp (test_artificial_arrays): Disable diff --git a/gdb/testsuite/gdb.base/cond-eval-mode.c b/gdb/testsuite/gdb.base/cond-eval-mode.c new file mode 100644 index 0000000000..b6bbe30f16 --- /dev/null +++ b/gdb/testsuite/gdb.base/cond-eval-mode.c @@ -0,0 +1,53 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014 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 . */ + +int global; + +int cond_global; + + +int +foo (int i) +{ + global++; + return i; +} + +int +bar (int i) +{ + return i; +} + +void +test () +{ + foo (1); + foo (2); + bar (3); +} + +int +main () +{ + int i; + + for (i = 0; i < 1000; i++) + test (); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/cond-eval-mode.exp b/gdb/testsuite/gdb.base/cond-eval-mode.exp index fd45499254..dad719454a 100644 --- a/gdb/testsuite/gdb.base/cond-eval-mode.exp +++ b/gdb/testsuite/gdb.base/cond-eval-mode.exp @@ -15,11 +15,16 @@ # Test 'set breakpoint condition-evaluation' settings -if { [prepare_for_testing break.exp "break" {break.c break1.c}] } { +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} { return -1 } -runto main +if ![runto_main] then { + fail "Can't run to main" + return 0 +} set test_host "set breakpoint condition-evaluation host" set test_auto "set breakpoint condition-evaluation auto" @@ -42,3 +47,106 @@ gdb_test_multiple $test_target $test_target { pass $test_target } } + +# Test setting a condition in a breakpoint. BREAK_COMMAND is the +# break/hwatch command to test. +# +proc test_break { break_command } { + global gdb_prompt + + with_test_prefix "$break_command" { + delete_breakpoints + + gdb_test "$break_command foo" "reakpoint.* at .*" + + # A condition that evals true. + gdb_test "condition \$bpnum cond_global==0" ".*" + + set can_do_cmd 0 + + set test "continue" + gdb_test_multiple $test $test { + -re "You may have requested too many.*$gdb_prompt $" { + pass $test + } + -re "Breakpoint .*, foo .*$gdb_prompt $" { + pass $test + set can_do_cmd 1 + } + } + + if { !$can_do_cmd } { + unsupported "no target support" + return + } + + delete_breakpoints + + gdb_test "$break_command foo" ".*reakpoint .* at .*" + + # A condition that evals false. + gdb_test "condition \$bpnum cond_global==1" ".*" + + gdb_test "b bar" "Breakpoint .* at .*" + + gdb_test "continue" "Breakpoint .*, bar .*" + } +} + +# Test setting conditions in watchpoints. WATCH_COMMAND is the watch +# command to test. +# +proc test_watch { watch_command } { + global gdb_prompt + + with_test_prefix "$watch_command" { + if [target_info exists gdb,no_hardware_watchpoints] { + unsupported "no target support" + return + } + + delete_breakpoints + + gdb_test "$watch_command global" ".*atchpoint .*: global.*" + + # A condition that evals true. + gdb_test "condition \$bpnum cond_global==0" ".*" + + set can_do_cmd 0 + + set test "continue" + gdb_test_multiple $test $test { + -re "You may have requested too many.*$gdb_prompt $" { + pass $test + } + -re "atchpoint .*: global.*$gdb_prompt $" { + pass $test + set can_do_cmd 1 + } + } + + if { !$can_do_cmd } { + unsupported "no target support" + return + } + + delete_breakpoints + + gdb_test "$watch_command global" ".*atchpoint .*: global.*" + + # A condition that evals false. + gdb_test "condition \$bpnum cond_global==1" ".*" + + gdb_test "b bar" "Breakpoint .* at .*" + + gdb_test "continue" "Breakpoint .*, bar .*" + } +} + +foreach break_command { "break" "hbreak" } { + test_break $break_command +} + +foreach watch_command { "watch" "rwatch" "awatch" } { + test_watch $watch_command +}