From: Andrew Burgess Date: Mon, 17 Nov 2025 11:43:36 +0000 (+0000) Subject: gdb: fix 'list' for multiple source file results X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9e05bcf99ce772451d9e00eb646296c7c3170c97;p=thirdparty%2Fbinutils-gdb.git gdb: fix 'list' for multiple source file results This commit: commit c7a45b98a61451f05ff654c4fb72a9c9cb2fba36 Date: Thu Jun 12 15:37:50 2025 +0000 gdb, linespec: avoid multiple locations with same PC broke GDB's ability to list multiple source files using a 'list' command. In GDB 16 and earlier something like 'list foo.c:10' could print multiple results if there were multiple 'foo.c' files compiled into the executable. The above commit added a filter to add_sal_to_sals (linespec.c) such that multiple sals in the same program space, but with the same pc value, could not be added, only the first sal would actually be recorded. The problem with this is that add_sal_to_sals is used from decode_digits_list_mode (also linespec.c) where the pc value is forced to zero. This force to zero makes sense I think as there might not be any compiled code for the requested line (this is for 'list' after all), so there might not be a valid pc to use. I'm not a fan of using '0' as a special pc value, there are embedded targets where 0 is a valid pc value, but given we're already using 0 here, I propose to just roll with it. So, my proposal is that, if the pc is 0, add_sal_to_sals should always add the sal. This fixes the decode_digits_list_mode, but should keep the fix that c7a45b98a614 introduced. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33647 Approved-By: Tom Tromey Reviewed-By: Kevin Buettner --- diff --git a/gdb/linespec.c b/gdb/linespec.c index 277b45b4b7f..2cbfe2fcc20 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -1071,11 +1071,16 @@ add_sal_to_sals (struct linespec_state *self, const symtab_and_line &sal, const char *symname, bool literal_canonical) { - /* We don't want two SALs with the same PC from the - same program space. */ - for (const auto &s : sals) - if (sal.pc == s.pc && sal.pspace == s.pspace) - return; + /* We don't want two SALs with the same PC from the same program space. + However, for the 'list' command we force the pc value to be 0, and in + this case we do want to see all SALs. See decode_digits_list_mode + for where the 0 originates from. */ + if (sal.pc != 0) + { + for (const auto &s : sals) + if (sal.pc == s.pc && sal.pspace == s.pspace) + return; + } sals.push_back (sal); diff --git a/gdb/testsuite/gdb.base/list-multi-source.c b/gdb/testsuite/gdb.base/list-multi-source.c new file mode 100644 index 00000000000..ab2b6a01b36 --- /dev/null +++ b/gdb/testsuite/gdb.base/list-multi-source.c @@ -0,0 +1,60 @@ +/* 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 . */ + +extern int function_a (void); +extern int function_b (void); + +#ifdef MAIN +int +main (void) +{ + int res; + + res = function_a (); + + res += function_b (); + + return res; +} +#endif + +#if defined FILE_A || defined FILE_B +static int +get_value_common (void) +{ + /* NOTE: When reading this file in the source tree, the variable used in + the return statement below will be replaced by a constant value when + the file is copied into the source tree. */ + return value_to_return; /* List this line. */ +} +#endif + +#ifdef FILE_A +int +function_a (void) +{ + return get_value_common (); +} +#endif + +#ifdef FILE_B +int +function_b (void) +{ + return get_value_common (); +} +#endif diff --git a/gdb/testsuite/gdb.base/list-multi-source.exp b/gdb/testsuite/gdb.base/list-multi-source.exp new file mode 100644 index 00000000000..66f6582fdff --- /dev/null +++ b/gdb/testsuite/gdb.base/list-multi-source.exp @@ -0,0 +1,129 @@ +# 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 . + +# Test that 'list FILE:LINE' can print multiple results if FILE +# matches multiple files from the source tree. +# +# Then test that we can use 'list DIR/FILE:LINE' to restrict the +# results to a single source file. + +# With a remote host, source files are automatically copied to the +# host by dejagnu, and this drops the directory structure that is +# needed for this test to work, i.e. we need a/foo.c and b/foo.c, but +# dejagnu's automatic copying just gives us a single foo.c. Instead +# of trying to fix this, for now at least, just skip remote host +# testing. +require {!is_remote host} + +# This test uses a single source file that is copied into the build +# tree 3 times. The three copies are then copied with different +# defines set so that we see different functions in each copy. +standard_testfile .c + +# Create the source tree within the build directory. +set src_root [standard_output_file "src"] +set src_a "$src_root/a" +set src_b "$src_root/b" +set file_a "$src_a/foo.c" +set file_b "$src_b/foo.c" +set file_main "$src_root/main.c" +file mkdir "$src_root" +file mkdir "$src_a" +file mkdir "$src_b" + +# Helper proc. Copy global SRCFILE to DEST, replacing +# 'value_to_return' with VALUE during the copy. +proc copy_and_update_source_file { dest value } { + # Open the source file for reading. + set in [open "$::srcdir/$::subdir/$::srcfile" r] + + # Read in the entire contents of the file. This should be fine as + # the input file is not large. + set file_content [read $in] + + # Close the input file. + close $in + + # Perform the replacement over the entire file contents. + set updated_content [string map \ + [list "value_to_return" $value] \ + $file_content] + + # Open the destination file for writing. + set out [open $dest w] + + # Write the modified content to the destination file. + puts -nonewline $out $updated_content + + # Close the output file. + close $out +} + +# Make three copies of the single source file in the build directory +# based source tree. Two of the source files are modified slighly to +# make the output of 'list' unique for each copy. +file copy "$srcdir/$subdir/$srcfile" "$file_main" +copy_and_update_source_file $file_a "3" +copy_and_update_source_file $file_b "-3" + +# Build the executable. Use defines to make the source files +# different. +if { [prepare_for_testing_full "failed to prepare" \ + [list $testfile debug \ + $file_main [list debug additional_flags=-DMAIN] \ + $file_a [list debug additional_flags=-DFILE_A] \ + $file_b [list debug additional_flags=-DFILE_B]]]} { + return +} + +# The LINENUM we should list, and the first and last lines that should +# appear in the list output. +set linenum [gdb_get_line_number "List this line"] +set first_linenum [expr {$linenum - 5}] +set last_linenum [expr {$linenum + 4}] + +# List using FILE:LINE for a filename that is ambiguous. +gdb_test "list foo.c:$linenum" \ + [multi_line \ + "file: \"\[^\r\n\]+/a/foo.c\", line number: $linenum, symbol: \"\[^\r\n\]+\"" \ + "$first_linenum\\s+\[^\r\n\]+" \ + ".*" \ + "$linenum\\s+[string_to_regexp {return 3; /* List this line. */}]" \ + ".*" \ + "$last_linenum\\s+\[^\r\n\]+" \ + "file: \"\[^\r\n\]+/b/foo.c\", line number: $linenum, symbol: \"\[^\r\n\]+\"" \ + "$first_linenum\\s+\[^\r\n\]+" \ + ".*" \ + "$linenum\\s+[string_to_regexp {return -3; /* List this line. */}]" \ + ".*" \ + "$last_linenum\\s+\[^\r\n\]+"] + +# Now list using a more acurate filename, we should only get a single +# result. +gdb_test "list a/foo.c:$linenum" \ + [multi_line \ + "^$first_linenum\\s+\[^\r\n\]+" \ + ".*" \ + "$linenum\\s+[string_to_regexp {return 3; /* List this line. */}]" \ + ".*" \ + "$last_linenum\\s+\[^\r\n\]+"] + +gdb_test "list b/foo.c:$linenum" \ + [multi_line \ + "^$first_linenum\\s+\[^\r\n\]+" \ + ".*" \ + "$linenum\\s+[string_to_regexp {return -3; /* List this line. */}]" \ + ".*" \ + "$last_linenum\\s+\[^\r\n\]+"]