binutils-gdb/gdb/testsuite/gdb.tui/scroll.exp
Andrew Burgess cd074e0415 gdb/tui: fix issue with handling the return character
My initial goal was to fix our gdb/testsuite/lib/tuiterm.exp such that
it would correctly support (some limited) scrolling of the command
window.

What I observe is that when sending commands to the tui command window
in a test script with:

  Term::command "p 1"

The command window would be left looking like this:

  (gdb)
  (gdb) p 1$1 = 1
  (gdb)

When I would have expected it to look like this:

  (gdb) p 1
  $1 = 1
  (gdb)

Obviously a bug in our tuiterm.exp library, right???

Wrong!

Turns out there's a bug in GDB.

If in GDB I enable the tui and then type (slowly) the 'p 1\r' (the \r
is pressing the return key at the end of the string), then you do
indeed get the "expected" terminal output.

However, if instead I copy the 'p 1\r' string and paste it into the
tui in one go then I now see the same corrupted output as we do when
using tuiterm.exp.

It turns out the problem is that GDB fails when handling lots of input
arriving quickly with a \r (or \n) on the end.

The reason for this bug is as follows:

When the tui is active the terminal is in no-echo mode, so characters
sent to the terminal are not echoed out again.  This means that when
the user types \r, this is not echoed to the terminal.

The characters read in are passed to readline and \r indicates that
the command line is complete and ready to be processed.  However, the
\r is not included in readlines command buffer, and is NOT printed by
readline when is displays its buffer to the screen.

So, in GDB we have to manually spot the \r when it is read in and
update the display.  Printing a newline character to the output and
moving the cursor to the next line.  This is done in tui_getc_1.

Now readline tries to reduce the number of write calls.  So if we very
quickly (as in paste in one go) the text 'p 1' to readline (this time
with no \r on the end), then readline will fetch the fist character
and add it to its internal buffer.  But before printing the character
out readline checks to see if there's more input incoming.  As we
pasted multiple characters, then yes, readline sees the ' ' and adds
this to its buffer, and finally the '1', this too is added to the
buffer.

Now if at this point we take a break, readline sees there is no more
input available, and so prints its buffer out.

Now when we press \r the code in tui_getc_1 kicks in, adds a \n to the
output and moves the cursor to the next line.

But, if instead we paste 'p 1\r' in one go then readline adds 'p 1' to
its buffer as before, but now it sees that there is still more input
available.  Now it fetches the '\r', but this triggers the newline
behaviour, we print '\n' and move to the next line - however readline
has not printed its buffer yet!

So finally we end up on the next line.  There's no more input
available so readline prints its buffer, then GDB gets passed the
buffer, handles it, and prints the result.

The solution I think is to put of our special newline insertion code
until we know that readline has finished printing its buffer.  Handily
we know when this is - the next thing readline does is pass us the
command line buffer for processing.  So all we need to do is hook in
to the command line processing, and before we pass the command line to
GDB's internals we do all of the magic print a newline and move the
cursor to the next line stuff.

Luckily, GDB's interpreter mechanism already provides the hooks we
need to do this.  So all I do here is move the newline printing code
from tui_getc_1 into a new function, setup a new input_handler hook
for the tui, and call my new newline printing function.

After this I can enable the tui and paste in 'p 1\r' and see the
correct output.

Also the tuiterm.exp library will now see non-corrupted output.

gdb/ChangeLog:

	* tui/tui-interp.c (tui_command_line_handler): New function.
	(tui_interp::resume): Register tui_command_line_handler as the
	input_handler.
	* tui/tui-io.c (tui_inject_newline_into_command_window): New
	function.
	(tui_getc_1): Delete handling of '\n' and '\r'.
	* tui-io.h (tui_inject_newline_into_command_window): Declare.

gdb/testsuite/ChangeLog:

	* gdb.tui/scroll.exp: Tighten expected results.  Remove comment
	about bug in GDB, update expected results, and add more tests.
2021-02-08 09:51:46 +00:00

73 lines
2.2 KiB
Plaintext

# Copyright 2021 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/>.
# Check scrolling in the command window. This test only covers the
# case where scrolling in the command window is caused by issuing many
# non-inferior related commands, as once the inferior is given control
# the terminal settings are modified and our tuiterm library really
# gets confused.
tuiterm_env
standard_testfile tui-layout.c
if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} {
return -1
}
Term::clean_restart 24 80 $testfile
if {![Term::enter_tui]} {
unsupported "TUI not supported"
return
}
for {set i 0} {$i < 10} {incr i 1} {
Term::command "p $i"
}
# Now check that the contents of the command window are as expected.
Term::check_region_contents "check cmd window" 0 16 80 8 \
[multi_line \
"\\\$7 = 6\\s+" \
"\\(gdb\\) p 7\\s+" \
"\\\$8 = 7\\s+" \
"\\(gdb\\) p 8\\s+" \
"\\\$9 = 8\\s+" \
"\\(gdb\\) p 9\\s+" \
"\\\$10 = 9\\s+" \
"\\(gdb\\)"]
# Now create a new layout where the CMD window is at the top of the
# screen. Sitch to this layout and ensure that scrolling still works
# as expected.
Term::command "tui new-layout flip cmd 1 src 1"
Term::command "layout flip"
for {set i 10} {$i < 20} {incr i 1} {
Term::command "p $i"
}
# Now check that the contents of the command window are as expected.
Term::check_region_contents "check cmd window in flip layout" 0 0 80 8 \
[multi_line \
"\\\$17 = 16\\s+" \
"\\(gdb\\) p 17\\s+" \
"\\\$18 = 17\\s+" \
"\\(gdb\\) p 18\\s+" \
"\\\$19 = 18\\s+" \
"\\(gdb\\) p 19\\s+" \
"\\\$20 = 19\\s+" \
"\\(gdb\\)"]