Summary: | Ctrl-D distorts GDB prompt | ||
---|---|---|---|
Product: | gdb | Reporter: | Baris Aktemur <tankut.baris.aktemur> |
Component: | cli | Assignee: | Not yet assigned to anyone <unassigned> |
Status: | RESOLVED FIXED | ||
Severity: | normal | CC: | aburgess, brobecker, guinevere, tromey |
Priority: | P2 | ||
Version: | 11.1 | ||
Target Milestone: | 12.1 | ||
Host: | Target: | ||
Build: | Last reconfirmed: | 2022-01-28 00:00:00 |
Description
Baris Aktemur
2022-01-28 09:32:04 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? 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. 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. 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 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. That's what readline is sending explicitly. I think it is used to realign the cursor position. #define BRACK_PASTE_FINI "\033[?2004l\r" 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. 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. Andrew, thanks for all the investigation you're doing for this bug. 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. 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 :). 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. 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 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 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 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 This issue should now be fully resolved in the master branch. |