gdb/breakpoint: make a copy of the "commands" command's argument
authorTankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Fri, 11 Sep 2020 13:04:01 +0000 (15:04 +0200)
committerTankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Wed, 16 Sep 2020 14:22:01 +0000 (16:22 +0200)
When GDB reads commands from the input, its internal buffer is re-used
for each line.  This is usually just fine because commands are
executed in order; by the time we read the next line, we are already
done with the current line.  However, a problematic case is breakpoint
commands that are input from a script.  The header (e.g. commands 1 2)
is overwritten with the next line before the breakpoint numbers are
processed completely.

For example, suppose we have the following script:

  break main
  break main
  commands 1 2
    print 100123
  end

and source this script:

  (gdb) source script.gdb
  Breakpoint 1 at 0x1245: file main.cpp, line 27.
  Breakpoint 2 at 0x1245: file main.cpp, line 27.
  No breakpoint number 123.

Note the "No breakpoint number 123." error message.  This happens
because GDB first reads "commands 1 2" into its internal buffer

  buffer -> "commands 1 2"

and then starts parsing the breakpoint numbers.  After parsing the first
token, the "next token" pointer is as below:

  buffer -> "commands 1 2"
  next-token -----------^

So, if we continue parsing, we would tokenize "2" correctly.  However,
before parsing the next number, GDB reads the commands to attach them
to breakpoint 1.  Reading the commands causes the buffer to be
overwritten:

  buffer -> "  print 100123"
  next-token -----------^

So, the next time we parse the breakpoint number, we read "123".

To fix, simply create a copy of the arguments of the header.

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

* breakpoint.c (commands_command_1): Make a copy of the 'arg'
argument.

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

* gdb.base/bp-cmds-sourced-script.c: New file.
* gdb.base/bp-cmds-sourced-script.exp: New test.
* gdb.base/bp-cmds-sourced-script.gdb: New file.

gdb/ChangeLog
gdb/breakpoint.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/bp-cmds-sourced-script.c [new file with mode: 0644]
gdb/testsuite/gdb.base/bp-cmds-sourced-script.exp [new file with mode: 0644]
gdb/testsuite/gdb.base/bp-cmds-sourced-script.gdb [new file with mode: 0644]

index bf73755c477eede5acec9d68ce5a71845109c225..85c733e203fe968d77181a61236471616a98a4e1 100644 (file)
@@ -1,3 +1,8 @@
+2020-09-16  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
+
+       * breakpoint.c (commands_command_1): Make a copy of the 'arg'
+       argument.
+
 2020-09-16  Andrew Burgess  <andrew.burgess@embecosm.com>
 
        * ada-lang.c (ada_language_data): Delete.
index fff80ff907e39d0d7eb5ece3b886ef39af40d217..3fb259ff40964f1442d69121536957ae40b6fe5b 100644 (file)
@@ -1229,13 +1229,23 @@ commands_command_1 (const char *arg, int from_tty,
 
   if (arg == NULL || !*arg)
     {
+      /* Argument not explicitly given.  Synthesize it.  */
       if (breakpoint_count - prev_breakpoint_count > 1)
        new_arg = string_printf ("%d-%d", prev_breakpoint_count + 1,
                                 breakpoint_count);
       else if (breakpoint_count > 0)
        new_arg = string_printf ("%d", breakpoint_count);
-      arg = new_arg.c_str ();
     }
+  else
+    {
+      /* Create a copy of ARG.  This is needed because the "commands"
+        command may be coming from a script.  In that case, the read
+        line buffer is going to be overwritten in the lambda of
+        'map_breakpoint_numbers' below when reading the next line
+        before we are are done parsing the breakpoint numbers.  */
+      new_arg = arg;
+    }
+  arg = new_arg.c_str ();
 
   map_breakpoint_numbers
     (arg, [&] (breakpoint *b)
index d368b2dfc1ac5b34bf3656c9db2f676c431115b3..848f35ff7ec4e2e2e3255fb80a28c5b04e23f188 100644 (file)
@@ -1,3 +1,9 @@
+2020-09-16  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
+
+       * gdb.base/bp-cmds-sourced-script.c: New file.
+       * gdb.base/bp-cmds-sourced-script.exp: New test.
+       * gdb.base/bp-cmds-sourced-script.gdb: New file.
+
 2020-09-16  Tom de Vries  <tdevries@suse.de>
 
        * lib/gdbserver-support.exp (gdbserver_exit): Make sure we
diff --git a/gdb/testsuite/gdb.base/bp-cmds-sourced-script.c b/gdb/testsuite/gdb.base/bp-cmds-sourced-script.c
new file mode 100644 (file)
index 0000000..f4825c8
--- /dev/null
@@ -0,0 +1,22 @@
+/* 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/>.  */
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/bp-cmds-sourced-script.exp b/gdb/testsuite/gdb.base/bp-cmds-sourced-script.exp
new file mode 100644 (file)
index 0000000..db551f1
--- /dev/null
@@ -0,0 +1,39 @@
+# 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/>.  */
+
+# Test that breakpoint commands entered in a GDB script work as
+# expected when the commands are defined for multiple breakpoints.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+    return -1
+}
+
+set script_file ${srcdir}/${subdir}/$testfile.gdb
+
+gdb_test "source $script_file" \
+    "Breakpoint 1\[^\r\n\]*\r\nBreakpoint 2\[^\r\n\]*" \
+    "source the script"
+
+gdb_run_cmd
+
+gdb_test_multiple "" "commands executed twice" {
+    -re "\\$${decimal} = 100123\r\n\\$${decimal} = 100123\r\n$gdb_prompt $" {
+       pass $gdb_test_name
+    }
+}
+
+gdb_continue_to_end
diff --git a/gdb/testsuite/gdb.base/bp-cmds-sourced-script.gdb b/gdb/testsuite/gdb.base/bp-cmds-sourced-script.gdb
new file mode 100644 (file)
index 0000000..228fa38
--- /dev/null
@@ -0,0 +1,20 @@
+# 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/>.  */
+
+break main
+break main
+commands 1 2
+  print 100123
+end