aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Burgess <aburgess@redhat.com>2025-08-13 15:29:38 +0100
committerAndrew Burgess <aburgess@redhat.com>2025-08-15 11:45:51 +0100
commite53b88b40ed38651b50f954dfe76066822094c15 (patch)
tree152297bd411eb9e6a93734547271ff8ef7e1bd15
parent1321c777e975a280d92e8aa577164f9bf9f81b8e (diff)
downloadbinutils-e53b88b40ed38651b50f954dfe76066822094c15.zip
binutils-e53b88b40ed38651b50f954dfe76066822094c15.tar.gz
binutils-e53b88b40ed38651b50f954dfe76066822094c15.tar.bz2
gdb: fix forward/reverse search, when no lines are printed
This commit continues the theme of the previous commit, restoring the behaviour of forward-search and reverse-search to what it was before GDB 10, but adds tests to guard the behaviour. I believe this restored behaviour is inline with the documentation of the two search commands. This time, I'm looking at what happens when no source lines end up being printed. This can happen for two reasons, a request is made to list a line number outside the file, or, a request is made to list a reverse series of lines (e.g. line 100 to line 50). Before GDB 10, a 'list' command that resulted in no lines being printed would not change the notion of the last line listed. As a result a future forward or reverse search would continue from the previous last line. As an example, here's current master branch behaviour: (gdb) list 50 45 /* Line 45 */ 46 /* Line 46 */ 47 /* Line 47 */ 48 /* Line 48 */ 49 /* Line 49 */ 50 /* Line 50 */ 51 /* Line 51 */ 52 /* Line 52 */ 53 /* Line 53 */ 54 /* Line 54 */ (gdb) list 1000 Line number 995 out of range; long-file.c has 101 lines. (gdb) forward-search Line Expression not found (gdb) But on GDB 9, the forward-search instead acts like this: (gdb) forward-search Line 55 /* Line 55 */ (gdb) Similarly, reverse-search reports "Expression not found" on master, but would return line 53 on GDB 9. The reverse-search result for master might be a little surprising, surely, if we tried to list line 1000, then every line before that should be searched. The problem is that last_line_listed ends up being set to 995 (which is 1000 minus half the listsize). Then, when the reverse-search kicks in GDB tried to read line 994, fails, and abandons the search. This problem was introduced with commit: commit 8b7fcda2744145f2380af01c9db8e11986f7af6d Date: Sun Dec 22 14:58:22 2019 +0100 Fix search in TUI This commit wants last_line_listed updated so that the search functions would work correctly (see notes below) when GDB is in TUI mode. Without this commit last_line_listed would never be updated in TUI mode, and so every search would effectively start from the beginning of the file. The fix I propose is to delay updating the current_source_location until the source code has been printed successfully. That is, just before we leave print_source_lines_base, after a successful print. This update happens inside the 'if (noprint)' block, a the return here isn't really considered an error condition, this is a specific choice _not_ to print the source code. However, in the 'if (stopline <= line)' block, the current_source_location is not updated, that's because this 'if' represents an error condition. The last_line_listed is also updated at the end of the function still, which is the path taken in CLI mode when source lines are actually printed. I've added some CLI tests to confirm the forward/reverse search behaviour. And I've also added some TUI tests to check search works within the TUI. The TUI tests cover more than just the fix for this issue. There is one slight issue with the TUI. At this point you should definitively go read the previous commit. OK, so, the forward and reverse searches are supposed to search from the last line listed. The previous commit fixed this for CLI mode, but for the TUI the last_line_listed is _still_ being set to the first line displayed. The problem is that print_source_lines_base doesn't know the size of the TUI src window, and so, doesn't know what the last line listed will be, and so cannot set last_line_listed correctly. This indicates to me that setting last_line_listed from the core GDB code is likely the wrong approach, or, at least, the way it is done now is the wrong approach. Currently the 'list' command converts the arguments into a set of lines to be printed based on the CLI environment, e.g. using the 'listsize' if necessary. But the 'listsize' doesn't make sense for the TUI, where the src window height really overrides the 'listsize'. The list command then calls the core GDB print_source_lines function to do the printing, but for the TUI we don't actually print anything, we just update the internal state (including last_line_listed) and are done. I think that the current interpreter, CLI or TUI, needs to get involved in the 'list' command implementation much sooner. This would allow, for example, the CLI to use 'listsize' and the TUI to use the src window height. It might then be up to the interpreter to track the last line listed maybe? I mention all this only to acknowledge this issue. The problem existed before this commit, and continues to exist after this commit. I'm not proposing to fix this problem right now. The TUI forward/reverse search continue to work as well as they have since commit 8b7fcda2744. Approved-By: Tom Tromey <tom@tromey.com>
-rw-r--r--gdb/source.c17
-rw-r--r--gdb/testsuite/gdb.base/source-search.exp25
-rw-r--r--gdb/testsuite/gdb.tui/source-search.c127
-rw-r--r--gdb/testsuite/gdb.tui/source-search.exp71
4 files changed, 233 insertions, 7 deletions
diff --git a/gdb/source.c b/gdb/source.c
index b207e0d..8cb5c36 100644
--- a/gdb/source.c
+++ b/gdb/source.c
@@ -1312,14 +1312,12 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
int nlines = stopline - line;
struct ui_out *uiout = current_uiout;
- /* Regardless of whether we can open the file, set current_source_symtab. */
+ /* Regardless of whether we can open the file, we'll want to set
+ current_source_symtab, but not if throw an error, or return without
+ printing any source lines. */
current_source_location *loc
= get_source_location (current_program_space);
- loc->set (s, line);
- first_line_listed = line;
- last_line_listed = line;
-
/* If printing of source lines is disabled, just print file and line
number. */
if (uiout->test_flags (ui_source_list) && source_open)
@@ -1380,6 +1378,10 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
uiout->text ("\n");
}
+ loc->set (s, line);
+ first_line_listed = line;
+ last_line_listed = line;
+
return;
}
@@ -1399,7 +1401,7 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
}
const char *iter = lines.c_str ();
- int new_lineno = loc->line ();
+ int new_lineno = line;
for (; nlines-- > 0 && *iter != '\0'; ++new_lineno)
{
if (flags & PRINT_SOURCE_LINES_FILENAME)
@@ -1470,8 +1472,9 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
/* As NEW_LINENO was incremented after displaying the last source line,
the last line shown was the one before NEW_LINENO. */
+ first_line_listed = line;
last_line_listed = new_lineno - 1;
- loc->set (loc->symtab (), new_lineno);
+ loc->set (s, new_lineno);
}
diff --git a/gdb/testsuite/gdb.base/source-search.exp b/gdb/testsuite/gdb.base/source-search.exp
index 474d429..559c500 100644
--- a/gdb/testsuite/gdb.base/source-search.exp
+++ b/gdb/testsuite/gdb.base/source-search.exp
@@ -79,3 +79,28 @@ gdb_test "list 127"
gdb_test "reverse-search Last line" \
"Expression not found" \
"reverse search for the last line fails"
+
+# List some lines from the middle of the file. Then try an invalid
+# 'list' command. Finally, check searches pick up from the middle of
+# the file where the first 'list' successfully completed.
+foreach_with_prefix search_direction { forward reverse } {
+ foreach_with_prefix bad_list { out-of-range backwards } {
+ gdb_test "list 50"
+
+ if { $bad_list eq "out-of-range" } {
+ gdb_test "list 1000" \
+ "Line number 995 out of range; \[^\r\n\]+ has 127 lines\\."
+ } else {
+ gdb_test_no_output "list 60,50"
+ }
+
+ if { $search_direction eq "forward" } {
+ set line 55
+ } else {
+ set line 53
+ }
+
+ gdb_test "${search_direction}-search Line" \
+ "$line\\s+/\\* Line $line \\*/"
+ }
+}
diff --git a/gdb/testsuite/gdb.tui/source-search.c b/gdb/testsuite/gdb.tui/source-search.c
new file mode 100644
index 0000000..2320c5c
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/source-search.c
@@ -0,0 +1,127 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2025 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/>. */
+
+int
+main (void)
+{
+ /* Line 21 */
+ /* Line 22 */
+ /* Line 23 */
+ /* Line 24 */
+ /* Line 25 */
+ /* Line 26 */
+ /* Line 27 */
+ /* Line 28 */
+ /* Line 29 */
+ /* Line 30 */
+ /* Line 31 */
+ /* Line 32 */
+ /* Line 33 */
+ /* Line 34 */
+ /* Line 35 */
+ /* Line 36 */
+ /* Line 37 */
+ /* Line 38 */
+ /* Line 39 */
+ /* Line 40 */
+ /* Line 41 */
+ /* Line 42 */
+ /* Line 43 */
+ /* Line 44 */
+ /* Line 45 */
+ /* Line 46 */
+ /* Line 47 */
+ /* Line 48 */
+ /* Line 49 */
+ /* Line 50 */
+ /* Line 51 */
+ /* Line 52 */
+ /* Line 53 */
+ /* Line 54 */
+ /* Line 55 */
+ /* Line 56 */
+ /* Line 57 */
+ /* Line 58 */
+ /* Line 59 */
+ /* Line 60 */
+ /* Line 61 */
+ /* Line 62 */
+ /* Line 63 */
+ /* Line 64 */
+ /* Line 65 */
+ /* Line 66 */
+ /* Line 67 */
+ /* Line 68 */
+ /* Line 69 */
+ /* Line 70 */
+ /* Line 71 */
+ /* Line 72 */
+ /* Line 73 */
+ /* Line 74 */
+ /* Line 75 */
+ /* Line 76 */
+ /* Line 77 */
+ /* Line 78 */
+ /* Line 79 */
+ /* Line 80 */
+ /* Line 81 */
+ /* Line 82 */
+ /* Line 83 */
+ /* Line 84 */
+ /* Line 85 */
+ /* Line 86 */
+ /* Line 87 */
+ /* Line 88 */
+ /* Line 89 */
+ /* Line 90 */
+ /* Line 91 */
+ /* Line 92 */
+ /* Line 93 */
+ /* Line 94 */
+ /* Line 95 */
+ /* Line 96 */
+ /* Line 97 */
+ /* Line 98 */
+ /* Line 99 */
+ /* Line 100 */
+ /* Line 101 */
+ /* Line 102 */
+ /* Line 103 */
+ /* Line 104 */
+ /* Line 105 */
+ /* Line 106 */
+ /* Line 107 */
+ /* Line 108 */
+ /* Line 109 */
+ /* Line 110 */
+ /* Line 111 */
+ /* Line 112 */
+ /* Line 113 */
+ /* Line 114 */
+ /* Line 115 */
+ /* Line 116 */
+ /* Line 117 */
+ /* Line 118 */
+ /* Line 119 */
+ /* Line 120 */
+ /* Line 121 */
+ /* Line 122 */
+ /* Line 123 */
+ /* Line 124 */
+ /* Line 125 */
+ return 0;
+} /* Last line. */
diff --git a/gdb/testsuite/gdb.tui/source-search.exp b/gdb/testsuite/gdb.tui/source-search.exp
new file mode 100644
index 0000000..6865db4
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/source-search.exp
@@ -0,0 +1,71 @@
+# Copyright 2025 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/>.
+
+# Test forward-search and reverse-search within the TUI src window.
+
+tuiterm_env
+
+standard_testfile .c
+
+if {[build_executable "failed to build" ${testfile} ${srcfile}] == -1} {
+ return
+}
+
+Term::clean_restart 24 80 $testfile
+if {![Term::enter_tui]} {
+ unsupported "TUI not supported"
+ return
+}
+
+proc check_src_window { testname first_line } {
+ set last_line [expr $first_line + 12]
+ Term::check_box_contents $testname 0 0 80 15 \
+ "^\\s+${first_line}\\s+.*\\s+${last_line}\\s+/\\* Line ${last_line} \\*/\\s+$"
+
+}
+
+set first_line 15
+
+# Check that the window contents are as expected.
+check_src_window "initial src contents" $first_line
+
+# Search forward. Searches are from the last line displayed, so this
+# will move the next source line onto the screen each time.
+for { set i 1 } { $i < 4 } { incr i } {
+ incr first_line
+ Term::command "forward-search Line"
+ check_src_window "src windows after forward-search $i" $first_line
+}
+
+# Reverse search. Like forward-search, but move backward through the
+# source.
+for { set i 1 } { $i < 3 } { incr i } {
+ incr first_line -1
+ Term::command "reverse-search Line"
+ check_src_window "src windows after reverse-search $i" $first_line
+}
+
+# Until there are no matching lines left.
+Term::command "reverse-search Line"
+gdb_assert {[regexp -- "^Expression not found\\s+$" [Term::get_line 22]]} \
+ "check start of source was reached"
+
+Term::command "forward-search Last line"
+Term::check_box_contents "forward-search to end of file" 0 0 80 15 \
+ "^\\s+122\\s+.*/\\* Last line\\. \\*/\\s+$"
+
+Term::command "reverse-search This testcase is part"
+Term::check_box_contents "reverse-search to start of file" 0 0 80 15 \
+ "^\\s+1\\s+.*\\s+13\\s+GNU General Public License for more details\\.\\s+$"