]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Conditional Z1 breakpoint hangs GDBserver.
authorPedro Alves <palves@redhat.com>
Thu, 10 Apr 2014 18:22:23 +0000 (19:22 +0100)
committerPedro Alves <palves@redhat.com>
Thu, 10 Apr 2014 16:14:12 +0000 (17:14 +0100)
While trying to fix hbreak2.exp against GDBserver I noticed this...

 (gdb) hbreak main if 1
 Sending packet: $m400580,40#2e...Packet received: e8d2ffffff5dc3554889e54883ec10c745fc00000000eb0eb800000000e8c1ffffff8345fc01817dfce70300007ee9b800000000c9c3662e0f1f840000000000
 Sending packet: $m40058f,1#31...Packet received: c7
 Hardware assisted breakpoint 1 at 0x40058f: file ../../../src/gdb/testsuite/gdb.base/break-idempotent.c, line 46.
 Sending packet: $Z1,40058f,1;X3,220127#9b...
 *hangs forever*

The issue is that nothing advances the packet pointer if
add_breakpoint_condition either fails to parse the agent expression,
or fails to find the breakpoint, resulting in an infinite loop in
process_point_options.  The latter case should really be fixed by
GDBserver tracking GDB Z1 breakpoints in its breakpoint structures
like Z0 breakpoints are, but the latter case still needs handling.
add_breakpoint_commands has the same issue, though at present I don't
know any way to trigger it other than sending a manually cooked
packet.

Unbelievably, it doesn't look like we have any test that tries setting
a conditional hardware breakpoint.  Looking at cond-eval-mode.exp, it
looks like the file was meant to actually test something, but it's
mostly empty today.  This patch adds tests that tries all sorts of
conditional breakpoints and watchpoints.  The test hangs/fails without
the GDBserver fix.

Tested on x86_64 Fedora 17.

gdb/gdbserver/
2014-04-10  Pedro Alves  <palves@redhat.com>

* 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.

gdb/testsuite/
2014-04-10  Pedro Alves  <palves@redhat.com>

* 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.

gdb/gdbserver/ChangeLog
gdb/gdbserver/mem-break.c
gdb/gdbserver/mem-break.h
gdb/gdbserver/server.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/cond-eval-mode.c [new file with mode: 0644]
gdb/testsuite/gdb.base/cond-eval-mode.exp

index 60244ef224e5f505060826ced62a73d90b9f1bc8..0698f74b39c0375d4e62e54ff91c69b91a12952a 100644 (file)
@@ -1,3 +1,13 @@
+2014-04-10  Pedro Alves  <palves@redhat.com>
+
+       * 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  <palves@redhat.com>
 
        * linux-low.c (linux_read_loadmap): Pass current_inferior directly
index b77ce100ff499aa249ed8d2391ca4f25c6c5f0b2..5df950d34809148c042a8f6bd1dbe937c3aef4d9 100644 (file)
@@ -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,
index 16655383eced143d67a79d5fc2e959445596403c..4346881b5b7f3141e169661aad59d3f1d9fbf47a 100644 (file)
@@ -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);
index 9868af628fc2ff0bc2a2420fe0ff3d2f65afc423..8317ac07d126e6faf5f4d8ef2ead747002c0996f 100644 (file)
@@ -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;
index 5fca9d2243b48db9aa6215255859a6f9897f5558..12ed4f9010427f012aa9cf5dc205f43e0110f017 100644 (file)
@@ -1,3 +1,12 @@
+2014-04-10  Pedro Alves  <palves@redhat.com>
+
+       * 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  <muller@sourceware.org>
 
        * 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 (file)
index 0000000..b6bbe30
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.  */
+
+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;
+}
index fd45499254ef6ac8e5add86e765864311784123b..dad719454afaa5625df243d198a1ee05d907942d 100644 (file)
 
 # 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
+}