Bug 28833 - Ctrl-D distorts GDB prompt
Summary: Ctrl-D distorts GDB prompt
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: cli (show other bugs)
Version: 11.1
: P2 normal
Target Milestone: 12.1
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-01-28 09:32 UTC by Baris Aktemur
Modified: 2022-04-22 17:53 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2022-01-28 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Baris Aktemur 2022-01-28 09:32:04 UTC
With GDB-10, Ctrl-D quits GDB with the following output:

  $ gdb-10 -q
  (gdb) quit

But with GDB-11 or with the current upstream, the prompt is distorted.
Start GDB-11:

  $ gdb-11 -q
  (gdb)

and now hit Ctrl-D:

  quit)
  $

This seems like a regression.
Comment 1 Andrew Burgess 2022-02-06 12:14:45 UTC
I've tried reproducing this using gdb 11.1, 10.2, 10.1, 10.2, and master branch on commit 94e57f287f9.  I see the working behaviour (i.e. no prompt corruption) in all cases.

Baris, could you try starting gdb with `-q -nx -nh` flag, just in case the problem is something in an init file?
Comment 2 Baris Aktemur 2022-02-07 10:28:47 UTC
Hi Andrew, 

Using `-q -nx -nh` did not change the behavior for me.  I did bisection and found that the problem appears starting with


commit b4f26d541aa7224b70d363932e816e6e1a857633
Author: Tom Tromey <tom@tromey.com>
Date:   Tue Mar 2 13:42:37 2021 -0700

    Import GNU Readline 8.1

    This imports readline 8.1.  I did this via various hackery in a
    readline git repository to make a version of readline identical to
    gdb's, then did a git merge.

    readline/ChangeLog
    2021-03-02  Tom Tromey  <tom@tromey.com>

            * Import readline 8.1.
Comment 3 Andrew Burgess 2022-02-08 10:15:22 UTC
Baris,

Thanks for chasing this down some more.  I know this doesn't help you, but is another data point:

I can confirm I definitely have commit b4f26d541aa7224b70d in my tree, and I am definitely linking against the in-tree version of readline (not my system readline), but I still don't see the prompt corruption you see.

I tried a couple of different terminals too, in case that revealed the issue - but still nothing bad on my side.
Comment 4 Guinevere Larsen 2022-02-14 12:14:24 UTC
Andrew, I see this corruption when testing in Fedora 35, both using system GDB and upstream GDB compiled with system readline.

Tested through an ssh to the machine, fedora 35's terminal was bash. In case it makes a difference, I was using a tmux + zsh setup to ssh into the fedora 35
Comment 5 Andrew Burgess 2022-02-15 11:48:01 UTC
Thanks for the pointers Bruno.  I did manage to reproduce this in the end, used script to grab the output of gdb, including any escape sequences, and now understand why I couldn't reproduce this.

Summary: I had 'set enable-bracketed-paste off' in my ~/.xinputrc file.  With this in place, the problem goes away (not proposing this as a solution, just giving all the details).

With 'set enable-bracketed-paste on' I can reproduce the problem on my local machine.

So, when this goes "right", all I see from GDB is:

  (gdb) quit

with no additional escape sequences.

However, with enable-bracketed-paste in on, I now see this:

  \033[?2004h(gdb) \033[?2004l\rquit

Now the '\033[?2004h' and '\033[?2004l' are, I guess, expected. These are the bracketed paste markers.  However, notice after the second sequence there is a sneaky \r.  This is what sends the 'quit' to the start of the line and causes the corruption.

I'm going to try and figure out where the \r is coming from.
Comment 6 Andreas Schwab 2022-02-15 12:34:06 UTC
That's what readline is sending explicitly.  I think it is used to realign the cursor position.

#define BRACK_PASTE_FINI       "\033[?2004l\r"
Comment 7 Andrew Burgess 2022-02-15 14:01:53 UTC
Indeed.  The problem seems to be that we are not correctly using readline's callback interface.

In event-top.c:command_line_handler, we print "quit\n" when the 'cmd' variable is EOF.  This happens when the incoming rl is nullptr.

This is what tries to print the 'quit' on the same line as the prompt, when the user presses ctrl-d.

If we look at the readline manual, they have an example of using the callback interface:

  https://tiswww.case.edu/php/chet/readline/readline.html#SEC43

In here, we see, in cb_linehandler, that when the incoming line is nullptr, they explicitly print a `\n` character.  This isn't an arbitrary choice made for this example, this is required, as, after seeing the ctrl-d readline emits the BRACK_PASTE_FINI, which includes the '\r'.  Failing to move to the new line will lead to any further output overwriting the prompt.

I'm currently trying to figure out if there's a better place where we can print the 'quit' to work around this problem.
Comment 8 Andrew Burgess 2022-02-16 17:06:22 UTC
I understand what the problem is, and I have a patch that could fix this issue.  The problem is that the patch requires changes to our readline code.  Before we push any local readline changes I think it's worth engaging with the readline devs, which I've done here:

  https://lists.gnu.org/archive/html/bug-readline/2022-02/msg00021.html

I suggest we see how that conversation goes before deciding the best way to fix this issue.
Comment 9 Baris Aktemur 2022-02-17 07:01:18 UTC
Andrew, thanks for all the investigation you're doing for this bug.
Comment 10 Joel Brobecker 2022-02-27 11:21:13 UTC
Setting the target milestone to 12.1, to confirm the fact that we'd like to fix this before we release GDB 12.1, if possible.
Comment 11 Joel Brobecker 2022-03-06 10:05:01 UTC
For the record, Andrew posted this patch to the readline community...

    https://lists.gnu.org/archive/html/bug-readline/2022-03/msg00005.html

... and it looks like the patch was accepted, or already in (Chet's answer was a bit confusing in that regard). The important part is that this seems fixed upstream, paving the way for us to backport it.

Well done, Andrew :).
Comment 12 Andrew Burgess 2022-03-07 15:14:26 UTC
I've posted this to the gdb mailing list:

  https://sourceware.org/pipermail/gdb-patches/2022-March/186391.html

this includes a back-port from upstream readline, and a GDB patch to make use of the readline fix.
Comment 13 Sourceware Commits 2022-03-16 20:44:15 UTC
The master branch has been updated by Andrew Burgess <aburgess@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a6b413d24ccc5d76179bab866834e11fd6fec294

commit a6b413d24ccc5d76179bab866834e11fd6fec294
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Fri Mar 11 14:44:03 2022 +0000

    gdb: work around prompt corruption caused by bracketed-paste-mode
    
    In this commit:
    
      commit b4f26d541aa7224b70d363932e816e6e1a857633
      Date:   Tue Mar 2 13:42:37 2021 -0700
    
          Import GNU Readline 8.1
    
    We imported readline 8.1 into GDB.  As a consequence bug PR cli/28833
    was reported.  This bug spotted that, when the user terminated GDB by
    sending EOF (usually bound to Ctrl+d), the last prompt would become
    corrupted.  Here's what happens, the user is sat at a prompt like
    this:
    
      (gdb)
    
    And then the user sends EOF (Ctrl+d), we now see this:
    
      quit)
      ... gdb terminates, and we return to the shell ...
    
    Notice the 'quit' was printed over the prompt.
    
    This problem is a result of readline 8.1 enabling bracketed paste mode
    by default.  This problem is present in readline 8.0 too, but in that
    version of readline bracketed paste mode is off by default, so a user
    will not see the bug unless they specifically enable the feature.
    
    Bracketed paste mode is available in readline 7.0 too, but the bug
    is not present in this version of readline, see below for why.
    
    What causes this problem is how readline disables bracketed paste
    mode.  Bracketed paste mode is a terminal feature that is enabled and
    disabled by readline emitting a specific escape sequence.  The problem
    for GDB is that the escape sequence to disable bracketed paste mode
    includes a '\r' character at the end, see this thread for more
    details:
    
      https://lists.gnu.org/archive/html/bug-bash/2018-01/msg00097.html
    
    The change to add the '\r' character to the escape sequence used to
    disable bracketed paste mode was introduced between readline 7.0 and
    readline 8.0, this is why the bug would not occur when using older
    versions of readline (note: I don't know if its even possible to build
    GDB using readline 7.0.  That really isn't important, I'm just
    documenting the history of this issue).
    
    So, the escape sequence to disable bracketed paste mode is emitted
    from the readline function rl_deprep_terminal, this is called after
    the user has entered a complete command and pressed return, or, if the
    user sends EOF.
    
    However, these two cases are slightly different.  In the first case,
    when the user has entered a command and pressed return, the cursor
    will have moved to the next, empty, line, before readline emits the
    escape sequence to leave bracketed paste mode.  The final '\r'
    character moves the cursor back to the beginning of this empty line,
    which is harmless.
    
    For the EOF case though, this is not what happens.  Instead, the
    escape sequence to leave bracketed paste mode is emitted on the same
    line as the prompt.  The final '\r' moves the cursor back to the start
    of the prompt line.  This leaves us ready to override the prompt.
    
    It is worth noting, that this is not the intended behaviour of
    readline, in rl_deprep_terminal, readline should emit a '\n' character
    when EOF is seen.  However, due to a bug in readline this does not
    happen (the _rl_eof_found flag is never set).  This is the first
    readline bug that effects GDB.
    
    GDB prints the 'quit' message from command_line_handler (in
    event-top.c), this function is called (indirectly) from readline to
    process the complete command line, but also in the EOF case (in which
    case the command line is set to nullptr).  As this is part of the
    callback to process a complete command, this is called after readline
    has disabled bracketed paste mode (by calling rl_deprep_terminal).
    
    And so, when bracketed paste mode is in use, rl_deprep_terminal leaves
    the cursor at the start of the prompt line (in the EOF case), and
    command_line_handler then prints 'quit', which overwrites the prompt.
    
    The solution to this problem is to print the 'quit' message earlier,
    before rl_deprep_terminal is called.  This is easy to do by using the
    rl_deprep_term_function hook.  It is this hook that usually calls
    rl_deprep_terminal, however, if we replace this with a new function,
    we can print the 'quit' string, and then call rl_deprep_terminal
    ourselves.  This allows the 'quit' to be printed before
    rl_deprep_terminal is called.
    
    The problem here is that there is no way in rl_deprep_terminal to know
    if readline is processing EOF or not, and as a result, we don't know
    when we should print 'quit'.  This is the second readline bug that
    effects GDB.
    
    Both of these readline issues are discussed in this thread:
    
      https://lists.gnu.org/archive/html/bug-readline/2022-02/msg00021.html
    
    The result of that thread was that readline was patched to address
    both of these issues.
    
    Now it should be easy to backport the readline fix to GDB's in tree
    copy of readline, and then change GDB to make use of these fixes to
    correctly print the 'quit' string.
    
    However, we are just about to branch GDB 12, and there is concern from
    some that changing readline this close to a new release is a risky
    idea, see this thread:
    
      https://sourceware.org/pipermail/gdb-patches/2022-March/186391.html
    
    So, this commit doesn't change readline at all.  Instead, this commit
    is the smallest possible GDB change in order to avoid the prompt
    corruption.
    
    In this commit I change GDB to print the 'quit' string on the line
    after the prompt, but only when bracketed paste mode is on.  This
    avoids the overwriting issue, the user sees this:
    
      (gdb)
      quit
      ... gdb terminates, and returns to the shell ...
    
    This isn't ideal, but is better than the existing behaviour.  After
    GDB 12 has branched, we can backport the readline fix, and apply a
    real fix to GDB.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28833
Comment 14 Sourceware Commits 2022-04-22 17:51:35 UTC
The master branch has been updated by Andrew Burgess <aburgess@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b913bd98ce81a1600463f9a8a30db04d44b64a47

commit b913bd98ce81a1600463f9a8a30db04d44b64a47
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Tue Mar 29 15:07:04 2022 +0100

    gdb: improved EOF handling when using readline 7
    
    In this commit:
    
      commit a6b413d24ccc5d76179bab866834e11fd6fec294
      Date:   Fri Mar 11 14:44:03 2022 +0000
    
          gdb: work around prompt corruption caused by bracketed-paste-mode
    
    a change was made to GDB to work around bug PR gdb/28833.  The
    consequence of this work around is that, when bracketed paste mode is
    enabled in readline, and GDB is quit by sending EOF, then the output
    will look like this:
    
      (gdb)
      quit
    
    The ideal output, which is what we get when bracketed paste mode is
    off, is this:
    
      (gdb) quit
    
    The reason we need to make this change is explained in the original
    commit referenced above.  What isn't mentioned in the above commit, is
    that the change that motivated this work around was only added in
    readline 8, older versions of readline don't require the change.
    
    In later commits in this series I will add a fix to GDB's in-tree copy
    of readline (this fix is back-ported from upstream readline), and then
    I will change GDB so that, when using the (patched) in-tree readline,
    we can have the ideal output in all cases.
    
    However, GDB can be built against the system readline.  When this is
    done, and the system readline is version 8, then we will still have to
    use the work around (two line) style output.
    
    But, if GDB is built against the system readline, and the system
    readline is an older version 7 readline, then there's no reason why we
    can't have the ideal output, after all, readline 7 doesn't include the
    change that we need to work around.
    
    This commit changes GDB so that, when using readline 7 we get the
    ideal output in all cases.  This change is trivial (a simple check
    against the readline version number) so I think this should be fine to
    include.
    
    For testing this commit, you need to configure GDB including the
    '--with-system-readline' flag, and build GDB on a system that uses
    readline 7, for example 'Ubuntu 18.04'.  Then run the test
    'gdb.base/eof-exit.exp', you should expect everything to PASS.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28833
Comment 15 Sourceware Commits 2022-04-22 17:51:40 UTC
The master branch has been updated by Andrew Burgess <aburgess@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4fb7bc4b147fd30b781ea2dad533956d0362295a

commit 4fb7bc4b147fd30b781ea2dad533956d0362295a
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Mon Mar 7 13:49:21 2022 +0000

    readline: back-port changes needed to properly detect EOF
    
    This commit is a partial back-port of this upstream readline commit:
    
      commit 002d31aa5f5929eb32d0e0e2e8b8d35d99e59961
      Author: Chet Ramey <chet.ramey@case.edu>
      Date:   Thu Mar 3 11:11:47 2022 -0500
    
          add rl_eof_found to public API; fix pointer aliasing problems  \
                with history-search-backward; fix a display problem with \
                runs of invisible characters at the end of a physical    \
                screen line
    
    I have only pulled in the parts of this commit that relate to the new
    rl_eof_found global, and the RL_STATE_EOF state flag.  These changes
    are needed in order to fix PR cli/28833, and are discussed in this
    thread to the bug-readline mailing list:
    
      https://lists.gnu.org/archive/html/bug-readline/2022-02/msg00021.html
    
    The above commit is not yet in any official readline release, but my
    hope is that now it has been merged into the readline tree it should
    be safe enough to back port this fix to GDB's tree.
    
    At some point in the future we will inevitably want to roll forward
    the version of readline that we maintain in the binutils-gdb
    repository.  When that day comes the changes in this commit can be
    replaced with the latest upstream readline code, as I have not changed
    the meaning of this code at all from what is in upstream readline.
    
    This commit alone does not fix the PR cli/28833 issue, for that see
    the next commit, which changes GDB itself.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28833
Comment 16 Sourceware Commits 2022-04-22 17:51:45 UTC
The master branch has been updated by Andrew Burgess <aburgess@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=91395d97d905c31ac38513e4aaedecb3b25e818f

commit 91395d97d905c31ac38513e4aaedecb3b25e818f
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Tue Feb 15 17:28:03 2022 +0000

    gdb: handle bracketed-paste-mode and EOF correctly
    
    This commit replaces an earlier commit that worked around the issues
    reported in bug PR gdb/28833.
    
    The previous commit just implemented a work around in order to avoid
    the worst results of the bug, but was not a complete solution.  The
    full solution was considered too risky to merge close to branching GDB
    12.  This improved fix has been applied after GDB 12 branched.  See
    this thread for more details:
    
      https://sourceware.org/pipermail/gdb-patches/2022-March/186391.html
    
    This commit replaces this earlier commit:
    
      commit 74a159a420d4b466cc81061c16d444568e36740c
      Date:   Fri Mar 11 14:44:03 2022 +0000
    
          gdb: work around prompt corruption caused by bracketed-paste-mode
    
    Please read that commit for a full description of the bug, and why is
    occurs.
    
    In this commit I extend GDB to use readline's rl_deprep_term_function
    hook to call a new function gdb_rl_deprep_term_function.  From this
    new function we can now print the 'quit' message, this replaces the
    old printing of 'quit' in command_line_handler.  Of course, we only
    print 'quit' in gdb_rl_deprep_term_function when we are handling EOF,
    but thanks to the previous commit (to readline) we now know when this
    is.
    
    There are two aspects of this commit that are worth further
    discussion, the first is in the new gdb_rl_deprep_term_function
    function.  In here I have used a scoped_restore_tmpl to disable the
    readline global variable rl_eof_found.
    
    The reason for this is that, in rl_deprep_terminal, readline will
    print an extra '\n' character before printing the escape sequence to
    leave bracketed paste mode.  You might then think that in the
    gdb_rl_deprep_term_function function, we could simply print "quit" and
    rely on rl_deprep_terminal to print the trailing '\n'.  However,
    rl_deprep_terminal only prints the '\n' when bracketed paste mode is
    on.  If the user has turned this feature off, no '\n' is printed.
    This means that in gdb_rl_deprep_term_function we need to print
    "quit" when bracketed paste mode is on, and "quit\n" when bracketed
    paste mode is off.
    
    We could absolutely do that, no problem, but given we know how
    rl_deprep_terminal is implemented, it's easier (I think) to just
    temporarily clear rl_eof_found, this prevents the '\n' being printed
    from rl_deprep_terminal, and so in gdb_rl_deprep_term_function, we can
    now always print "quit\n" and this works for all cases.
    
    The second issue that should be discussed is backwards compatibility
    with older versions of readline.  GDB can be built against the system
    readline, which might be older than the version contained within GDB's
    tree.  If this is the case then the system readline might not contain
    the fixes needed to support correctly printing the 'quit' string.
    
    To handle this situation I have retained the existing code in
    command_line_handler for printing 'quit', however, this code is only
    used now if the version of readline we are using doesn't not include
    the required fixes.  And so, if a user is using an older version of
    readline, and they have bracketed paste mode on, then they will see
    the 'quit' sting printed on the line below the prompt, like this:
    
      (gdb)
      quit
    
    I think this is the best we can do when someone builds GDB against an
    older version of readline.
    
    Using a newer version of readline, or the patched version of readline
    that is in-tree, will now give a result like this in all cases:
    
      (gdb) quit
    
    Which is what we want.
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28833
Comment 17 Andrew Burgess 2022-04-22 17:53:41 UTC
This issue should now be fully resolved in the master branch.