--- /dev/null
+# Copyright 2003 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+# This is a regression test for the following bug, as of 2003-12-12:
+#
+# Set a breakpoint which will be hit many times. Attach a complex set
+# of commands to it, including a "continue" command. Run the program,
+# so that the breakpoint is hit, its commands get executed, and the
+# program continues and hits the breakpoint again. You will see
+# messages like "warning: Invalid control type in command structure.",
+# or maybe GDB will crash.
+#
+# When the breakpoint is hit, bpstat_stop_status copies the
+# breakpoint's command tree to the bpstat. bpstat_do_actions then
+# calls execute_control_command to run the commands. The 'continue'
+# command invokes the following chain of calls:
+#
+# continue_command
+# -> clear_proceed_status
+# -> bpstat_clear
+# -> free_command_lines
+# -> frees the commands we are currently running.
+#
+# When control does eventually return to execute_control_command, GDB
+# continues to walk the tree of freed command nodes, resulting in the
+# error messages and / or crashes.
+#
+# Since this bug depends on storage being reused between the time that
+# we continue and the time that we fall back to bpstat_do_actions, the
+# reproduction recipe is more delicate than I would like. I welcome
+# suggestions for improving this.
+
+set prms_id 0
+set bug_id 0
+
+set testfile "freebpcmd"
+set srcfile ${testfile}.c
+set srcfile1 ${testfile}1.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ gdb_suppress_entire_file "Testcase compile failed, so all tests in this file will automatically fail."
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+gdb_test "break [gdb_get_line_number "euphonium"]" "" "set breakpoint"
+
+# The goal of all this is to make sure that there's plenty of memory
+# churn, and different amounts of it each time the inferior stops;
+# this seems to make GDB crash more reliably.
+set lines {{if (i%2) == 0}
+ {echo "even "}
+ {print i}
+ {else}
+ {echo "odd "}
+ {print i}
+ {end}
+ {set variable $foo = 0}
+ {set variable $j = 0}
+ {while $j < i}
+ {set variable $foo += $j}
+ {set variable $j++}
+ {end}
+ {print $foo}
+ {if i != 40}
+ {c}
+ {end}
+ {end}}
+
+send_gdb "commands\n"
+for {set i 0} {$i < [llength $lines]} {incr i} {
+ gdb_expect {
+ -re ".*>" {
+ send_gdb "[lindex $lines $i]\n"
+ }
+ -re "$gdb_prompt $" {
+ set reason "got top-level prompt early"
+ break
+ }
+ timeout {
+ set reason "timeout"
+ break
+ }
+ }
+}
+if {$i >= [llength $lines]} {
+ pass "send breakpoint commands"
+} else {
+ fail "send breakpoint commands ($reason)"
+}
+
+gdb_run_cmd
+gdb_test_multiple "" "run program with breakpoint commands" {
+ -re "warning: Invalid control type in command structure" {
+ fail "run program with breakpoint commands"
+ }
+ -re "$gdb_prompt $" {
+ pass "run program with breakpoint commands"
+ }
+ eof {
+ fail "run program with breakpoint commands (GDB died)"
+ }
+}