aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom de Vries <tdevries@suse.de>2025-01-29 11:21:02 +0100
committerAndrew Burgess <aburgess@redhat.com>2025-02-25 10:56:36 +0000
commit1e9bd86ccda976c0f7ef513cb5e9ee3eff460768 (patch)
tree60bd79ce4c792df44514d8691a0e59e6a09e34c7
parentf40b59b91240886003b679ff4a3fd51b6b60696a (diff)
downloadbinutils-1e9bd86ccda976c0f7ef513cb5e9ee3eff460768.zip
binutils-1e9bd86ccda976c0f7ef513cb5e9ee3eff460768.tar.gz
binutils-1e9bd86ccda976c0f7ef513cb5e9ee3eff460768.tar.bz2
gdb: don't show incorrect source file in source window
Consider the test-case sources main.c and foo.c: $ cat main.c extern int foo (void); int main (void) { return foo (); } $ cat foo.c extern int foo (void); int foo (void) { return 0; } and main.c compiled with debug info, and foo.c without: $ gcc -g main.c -c $ gcc foo.c -c $ gcc -g main.o foo.o In TUI mode, if we run to foo: $ gdb -q a.out -tui -ex "b foo" -ex run it gets us "[ No Source Available ]": ┌─main.c─────────────────────────────────────────┐ │ │ │ │ │ │ │ [ No Source Available ] │ │ │ │ │ └────────────────────────────────────────────────┘ (src) In: foo L?? PC: 0x400566 ... Breakpoint 1, 0x0000000000400566 in foo () (gdb) But after resizing (pressing ctrl-<minus> in the gnome-terminal), we get instead the source for main.c: ┌─main.c─────────────────────────────────────────┐ │ 3 int │ │ 4 main (void) │ │ 5 { │ │ 6 return foo (); │ │ 7 } │ │ │ │ │ └────────────────────────────────────────────────┘ (src) In: foo L?? PC: 0x400566 ... Breakpoint 1, 0x0000000000400566 in foo () (gdb) which is inappropriate because we're stopped in function foo, which is not in main.c. The problem is that, when the window is resized, GDB ends up calling tui_source_window_base::rerender. The rerender function has three cases, one for when the window already has some source code content (which is not the case here), a case for when the inferior is active, and we have a selected frame (which is the case that applies here), and a final case for when the inferior is not running. For the case which we end up in, the source code window has no content, but the inferior is running, so we have a selected frame, GDB calls the get_current_source_symtab_and_line() function to get the symtab_and_line for the current location. The get_current_source_symtab_and_line() will actually return the last recorded symtab and line location, not the current symtab and line location. What this means, is that, if the current location has no debug information, get_current_source_symtab_and_line() will return any previously recorded location, or failing that, the default (main) location. This behaviour of get_current_source_symtab_and_line() also causes problems for the 'list' command. Consider this pure CLI session: (gdb) break foo Breakpoint 1 at 0x40110a (gdb) run Starting program: /tmp/a.out Breakpoint 1, 0x000000000040110a in foo () (gdb) list 1 extern int foo (void); 2 3 int 4 main (void) 5 { 6 return foo (); 7 } (gdb) list . Insufficient debug info for showing source lines at current PC (0x40110a). (gdb) However, if we look at how GDB's TUI updates the source window during a normal stop, we see that GDB does a better job of displaying the expected contents. Going back to our original example, when we start GDB with: $ gdb -q a.out -tui -ex "b foo" -ex run we do get the "[ No Source Available ]" message as expected. Why is that? The answer is that, in this case GDB uses tui_show_frame_info to update the source window, tui_show_frame_info is called each time a prompt is displayed, like this: #0 tui_show_frame_info (fi=...) at ../../src/gdb/tui/tui-status.c:269 #1 0x0000000000f55975 in tui_refresh_frame_and_register_information () at ../../src/gdb/tui/tui-hooks.c:118 #2 0x0000000000f55ae8 in tui_before_prompt (current_gdb_prompt=0x31ef930 <top_prompt+16> "(gdb) ") at ../../src/gdb/tui/tui-hooks.c:165 #3 0x000000000090ea45 in std::_Function_handler<void(char const*), void (*)(char const*)>::_M_invoke (__functor=..., __args#0=@0x7ffc955106b0: 0x31ef930 <top_prompt+16> "(gdb) ") at /usr/include/c++/9/bits/std_function.h:300 #4 0x00000000009020df in std::function<void(char const*)>::operator() (this=0x5281260, __args#0=0x31ef930 <top_prompt+16> "(gdb) ") at /usr/include/c++/9/bits/std_function.h:688 #5 0x0000000000901c35 in gdb::observers::observable<char const*>::notify (this=0x31dda00 <gdb::observers::before_prompt>, args#0=0x31ef930 <top_prompt+16> "(gdb) ") at ../../src/gdb/../gdbsupport/observable.h:166 #6 0x00000000008ffed8 in notify_before_prompt (prompt=0x31ef930 <top_prompt+16> "(gdb) ") at ../../src/gdb/event-top.c:518 #7 0x00000000008fff08 in top_level_prompt () at ../../src/gdb/event-top.c:534 #8 0x00000000008ffdeb in display_gdb_prompt (new_prompt=0x0) at ../../src/gdb/event-top.c:487 If we look at how tui_show_frame_info figures out what source to display, it doesn't use get_current_source_symtab_and_line(), instead, it finds a symtab_and_line directly from a frame_info_pt. This means we are not dependent on get_current_source_symtab_and_line() returning the current location (which it does not). I propose that we change tui_source_window_base::rerender() so that, for the case we are discussing here (the inferior has a selected frame, but the source window has no contents), we move away from using get_current_source_symtab_and_line(), and instead use find_frame_sal instead, like tui_show_frame_info does. This means that we will always use the inferior's current location. Tested on x86_64-linux. Reviewed-By: Tom de Vries <tdevries@suse.de> Reported-By: Andrew Burgess <aburgess@redhat.com> Co-Authored-By: Andrew Burgess <aburgess@redhat.com> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32614
-rw-r--r--gdb/testsuite/gdb.tui/resize-3-foo.c24
-rw-r--r--gdb/testsuite/gdb.tui/resize-3-main.c24
-rw-r--r--gdb/testsuite/gdb.tui/resize-3.exp73
-rw-r--r--gdb/tui/tui-winsource.c26
4 files changed, 129 insertions, 18 deletions
diff --git a/gdb/testsuite/gdb.tui/resize-3-foo.c b/gdb/testsuite/gdb.tui/resize-3-foo.c
new file mode 100644
index 0000000..653b24b
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/resize-3-foo.c
@@ -0,0 +1,24 @@
+/* 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/>. */
+
+extern int foo (void);
+
+int
+foo (void)
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.tui/resize-3-main.c b/gdb/testsuite/gdb.tui/resize-3-main.c
new file mode 100644
index 0000000..95fdd3b
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/resize-3-main.c
@@ -0,0 +1,24 @@
+/* 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/>. */
+
+extern int foo (void);
+
+int
+main (void)
+{
+ return foo ();
+}
diff --git a/gdb/testsuite/gdb.tui/resize-3.exp b/gdb/testsuite/gdb.tui/resize-3.exp
new file mode 100644
index 0000000..fcda407c
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/resize-3.exp
@@ -0,0 +1,73 @@
+# 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 TUI resizing while showing "No Source Available".
+
+require allow_tui_tests
+
+standard_testfile -main.c -foo.c
+
+if { [build_executable_from_specs "failed to prepare" \
+ $testfile {debug} \
+ $srcfile {debug} \
+ $srcfile2 {nodebug}] == -1 } {
+ return -1
+}
+
+tuiterm_env
+
+Term::clean_restart 24 80 $testfile
+
+# It would be simpler to run directly to foo and then enter TUI, but that
+# fails to trigger PR32614. So instead, we first run to main, enter TUI and
+# then run to foo.
+if {![runto_main]} {
+ perror "test suppressed"
+ return
+}
+
+# Set a breakpoint on foo, easier to do before entering TUI.
+gdb_breakpoint foo
+
+if {![Term::enter_tui]} {
+ unsupported "TUI not supported"
+ return
+}
+
+# Continue to foo.
+Term::command continue
+
+with_test_prefix "before resize" {
+ Term::check_contents "Source window empty" \
+ "No Source Available"
+}
+
+Term::resize 40 90
+
+with_test_prefix "after resize" {
+ # Regression test for PR32614.
+ Term::check_contents "Source window empty" \
+ "No Source Available"
+
+ Term::command "layout asm"
+
+ # Check that, when we switch to the assembler window, the
+ # disassembly is still shown.
+ Term::check_contents_not \
+ "No Assembly message is not displayed" \
+ "No Assembly Available"
+ Term::check_contents "Assembler for foo is shown" \
+ "$hex\\s+<foo"
+}
diff --git a/gdb/tui/tui-winsource.c b/gdb/tui/tui-winsource.c
index 1cf2bae..b5889a8 100644
--- a/gdb/tui/tui-winsource.c
+++ b/gdb/tui/tui-winsource.c
@@ -456,25 +456,15 @@ tui_source_window_base::rerender ()
}
else if (deprecated_safe_get_selected_frame () != NULL)
{
- symtab_and_line cursal
- = get_current_source_symtab_and_line (current_program_space);
frame_info_ptr frame = deprecated_safe_get_selected_frame ();
- struct gdbarch *gdbarch = get_frame_arch (frame);
-
- struct symtab *s = find_pc_line_symtab (get_frame_pc (frame));
- if (this != tui_src_win ())
- find_line_pc (s, cursal.line, &cursal.pc);
-
- /* This centering code is copied from tui_source_window::maybe_update.
- It would be nice to do centering more often, and do it in just one
- location. But since this is a regression fix, handle this
- conservatively for now. */
- int start_line = (cursal.line - ((height - box_size ()) / 2)) + 1;
- if (start_line <= 0)
- start_line = 1;
- cursal.line = start_line;
-
- update_source_window (gdbarch, cursal);
+ symtab_and_line sal = find_frame_sal (frame);
+
+ /* find_frame_sal does not always set SAL.PC, but we want to ensure
+ that it is available in the SAL before updating the window. */
+ get_frame_pc_if_available (frame, &sal.pc);
+
+ maybe_update (get_frame_arch (frame), sal);
+ update_exec_info (false);
}
else
{