binutils-gdb/gdb/testsuite/gdb.base/readline.exp
Andrew Burgess bb146a79c7 gdb: add test for readline handling very long commands
The test added in this commit tests for a long fixed readline issue
relating to long command lines.  A similar patch has existed in the
Fedora GDB tree for several years, but I don't see any reason why this
test would not be suitable for inclusion in upstream GDB.  I've
updated the patch to current testsuite standards.

The test is checking for an issue that was fixed by this readline
patch:

  https://lists.gnu.org/archive/html/bug-readline/2006-11/msg00002.html

Which was merged into readline 6.0 (released ~2010).  The issue was
triggered when the user enters a long command line, which wrapped over
multiple terminal lines.  The crash looks like this:

  free(): invalid pointer

  Fatal signal: Aborted
  ----- Backtrace -----
  0x4fb583 gdb_internal_backtrace_1
          ../../src/gdb/bt-utils.c:122
  0x4fb583 _Z22gdb_internal_backtracev
          ../../src/gdb/bt-utils.c:168
  0x6047b9 handle_fatal_signal
          ../../src/gdb/event-top.c:964
  0x7f26e0cc56af ???
  0x7f26e0cc5625 ???
  0x7f26e0cae8d8 ???
  0x7f26e0d094be ???
  0x7f26e0d10aab ???
  0x7f26e0d124ab ???
  0x7f26e1d32e12 rl_free_undo_list
          ../../readline-5.2/undo.c:119
  0x7f26e1d229eb readline_internal_teardown
          ../../readline-5.2/readline.c:405
  0x7f26e1d3425f rl_callback_read_char
          ../../readline-5.2/callback.c:197
  0x604c0d gdb_rl_callback_read_char_wrapper_noexcept
          ../../src/gdb/event-top.c:192
  0x60581d gdb_rl_callback_read_char_wrapper
          ../../src/gdb/event-top.c:225
  0x60492f stdin_event_handler
          ../../src/gdb/event-top.c:545
  0xa60015 gdb_wait_for_event
          ../../src/gdbsupport/event-loop.cc:694
  0xa6078d gdb_wait_for_event
          ../../src/gdbsupport/event-loop.cc:593
  0xa6078d _Z16gdb_do_one_eventi
          ../../src/gdbsupport/event-loop.cc:264
  0x6fc459 start_event_loop
          ../../src/gdb/main.c:411
  0x6fc459 captured_command_loop
          ../../src/gdb/main.c:471
  0x6fdce4 captured_main
          ../../src/gdb/main.c:1310
  0x6fdce4 _Z8gdb_mainP18captured_main_args
          ../../src/gdb/main.c:1325
  0x44f694 main
          ../../src/gdb/gdb.c:32
  ---------------------

I recreated the above crash by a little light hacking on GDB, and then
linking GDB against readline 5.2.  The above stack trace was generated
from the test included in this patch, and matches the trace that was
included in the original bug report.

It is worth acknowledging that without hacking things GDB has a
minimum requirement of readline 7.0.  This test is not about checking
whether GDB has been built against an older version of readline, it is
about checking that readline doesn't regress in this area.

Reviewed-By: Tom Tromey <tom@tromey.com>
2023-02-11 17:17:56 +00:00

250 lines
6.0 KiB
Plaintext

# Copyright 2002-2023 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/>.
# This file was written by Tom Tromey <tromey@redhat.com>
# This file is part of the gdb testsuite.
#
# Tests for readline operations.
#
# This function is used to test operate-and-get-next.
# NAME is the name of the test.
# ARGS is a list of alternating commands and expected results.
proc operate_and_get_next {name args} {
global gdb_prompt
set my_gdb_prompt "($gdb_prompt| >)"
set reverse {}
foreach {item result} $args {
verbose "sending $item"
sleep 1
# We can't use gdb_test here because we might see a " >" prompt.
set status 0
send_gdb "$item\n"
gdb_expect {
-re "$item" {
# Ok
}
timeout {
set status 1
}
}
if {! $status} {
gdb_expect {
-re "$result" {
# Ok.
}
timeout {
set status 1
}
}
}
if {$status} {
fail "$name - send $item"
return 0
}
pass "$name - send $item"
set reverse [linsert $reverse 0 $item $result]
}
# Now use C-p to go back to the start.
foreach {item result} $reverse {
# Actually send C-p followed by C-l. This lets us recognize the
# command when gdb prints it again.
send_gdb "\x10\x0c"
set status 0
gdb_expect {
-re "$item" {
# Ok
}
timeout {
set status 1
}
}
if {$status} {
fail "$name - C-p to $item"
return 0
}
pass "$name - C-p to $item"
}
# Now C-o through the list. Don't send the command, since it is
# already there. Strip off the first command from the list so we
# can see the next command inside the loop.
set count 0
foreach {item result} $args {
set status 0
# If this isn't the first item, make sure we see the command at
# the prompt.
if {$count > 0} {
gdb_expect {
-re ".*$item" {
# Ok
}
timeout {
set status 1
}
}
}
if {! $status} {
# For the last item, send a simple \n instead of C-o.
if {$count == [llength $args] - 2} {
send_gdb "\n"
} else {
# 15 is C-o.
send_gdb [format %c 15]
}
set status 0
gdb_expect {
-re "$result" {
# Ok
}
timeout {
set status 1
}
}
}
if {$status} {
fail "$name - C-o for $item"
return 0
}
pass "$name - C-o for $item"
set count [expr {$count + 2}]
}
# Match the prompt so the next test starts at the right place.
gdb_test "" ".*" "$name - final prompt"
return 1
}
save_vars { env(TERM) } {
# The arrow key test relies on the standard VT100 bindings, so
# make sure that an appropriate terminal is selected. The same
# bug doesn't show up if we use ^P / ^N instead.
setenv TERM vt100
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
if { ![readline_is_used] } {
unsupported "readline isn't used."
return -1
}
save_vars { timeout env(GDBHISTSIZE) env(GDBHISTFILE) } {
set timeout 30
# A simple test of operate-and-get-next.
operate_and_get_next "Simple operate-and-get-next" \
"p 1" ".* = 1" \
"p 2" ".* = 2" \
"p 3" ".* = 3"
# Test operate-and-get-next with a secondary prompt.
operate_and_get_next "operate-and-get-next with secondary prompt" \
"if 1 > 0" "" \
"p 5" "" \
"end" ".* = 5"
# Verify that arrow keys work in secondary prompts. The control
# sequence is a hard-coded VT100 up arrow.
gdb_test "print 42" "\\\$\[0-9\]* = 42"
set msg "arrow keys with secondary prompt"
gdb_test_multiple "if 1 > 0\n\033\[A\033\[A\nend" $msg {
-re ".*\\\$\[0-9\]* = 42\r\n$gdb_prompt $" {
pass $msg
}
-re ".*Undefined command:.*$gdb_prompt $" {
fail $msg
}
}
# Use the up arrow to select a previous command. Check that
# no unexpected output is added between the previously
# selected command, and the output of that command.
gdb_test "print 123" "\\\$\[0-9\]* = 123"
gdb_test_multiple "\033\[A" "use up arrow" {
-re -wrap "print 123\r\n\\\$\[0-9\]* = 123" {
pass $gdb_test_name
}
}
# Now repeat the first test with a history file that fills the entire
# history list.
set env(GDBHISTFILE) "${srcdir}/${subdir}/gdb_history"
set env(GDBHISTSIZE) "10"
clean_restart
operate_and_get_next "Simple operate-and-get-next, two" \
"p 7" ".* = 7" \
"p 8" ".* = 8" \
"p 9" ".* = 9"
# Test sending a long command to GDB, a command that requires
# multiple terminal lines. Long ago there was a readline bug
# that would cause GDB to crash in this situation. We force
# the bug by setting up a narrow terminal width, and then
# sending a long command.
clean_restart
# The number of characters to send in the command. We
# actually send a few more than this; this total is really the
# extra characters we add on after sending the command name.
set char_total 4500
set char_sent 0
# Adjust the terminal width.
gdb_test_no_output "set width 7"
# Send the command prefix, then lots of additional characters
# that create a really long command that wraps over multiple
# lines.
send_gdb "help X"
gdb_test_multiple "" "send long command to GDB" {
-re "X" {
incr char_sent
if {$char_sent <= $char_total} {
send_gdb "X"
exp_continue
}
}
-re "\[ \b\r\n\]" {
exp_continue
}
}
# Send the final newline so that GDB will process the command.
# Check GDB returns a suitable error.
send_gdb "\n"
gdb_test "" \
"Undefined command: \"X+\"\\. Try \"help\"\\." \
"All the characters transferred"
}
}