* Mike Gulick <mgulick@mathworks.com> [2019-09-05 22:40:48 +0000]:
> Hi,
>
> I'm having trouble getting gdb to find the corresponding source for a
> binary whose compilation directory was rewritten using
> -fdebug-prefix-map. I am doing this in order to make builds
> reproducible. I am using this gcc switch to remove a portion of the
> compile directory, e.g.
>
> $ cd $HOME/test/src/
> $ gcc -g -o test -fdebug-prefix-map=$HOME= test.c
> $ dwarfdump test
> ...
> DW_AT_name test.c
> DW_AT_comp_dir /test/src
> ...
>
> When attempting to debug the resulting binary, gdb is unable to find the
> source file even if I add the removed path $HOME to the gdb source
> path:
>
> $ cd $HOME
> $ gdb --nh ~/test/src/test
> GNU gdb (GDB) 8.3
> ...
> (gdb) directory /home/mgulick
> Source directories searched: /home/mgulick:$cdir:$cwd
> (gdb) info sources
> Source files for which symbols have been read in:
>
> /test/src/test.c
>
> Source files for which symbols will be read in on demand:
>
>
> (gdb) b test.c:3
> Breakpoint 1 at 0x664: file test.c, line 3.
> (gdb) r
> Starting program: /mathworks/home/mgulick/test/src/test
>
> Breakpoint 1, main () at test.c:3
> 3 test.c: No such file or directory.
>
>
> It turns out that only the source file, i.e. DW_AT_name, is used when
> searching the source path. This is surprising given the example with
> '/usr/src/foo-1.0/lib/foo.c' and '/mnt/cross' in the gdb documentation
> here: https://sourceware.org/gdb/onlinedocs/gdb/Source-Path.html. It
> seems uncommon for DW_AT_name to be the full path to the source file in
> most programs I have come across. In that example from the
> documentation, it is likely that DW_AT_name would be 'lib/foo.c' and
> DW_AT_comp_dir would be '/usr/src/foo-1.0'.
That depends on how you compile your source:
gcc -o my.exe /usr/src/foo-1.0/lib/foo.c
would result in DW_AT_name being '/usr/src/foo-1.0/lib/foo.c' I
believe.
So I think the example is fine, though I don't think it really goes
into enough details.
>
> I have implemented two different approaches to handle this, both of
> which work, and I wanted feedback on a) whether you think this is a
> legitimate bug/feature, and b) which approach you prefer.
>
> Approach 1 is to include another pass in find_and_open_source where the
> compilation directory is prepended to the filename before searching for
> the file with openp. This allows the example I provided above to work
> as-is.
>
> Approach 2 is to allow '$cdir' to appear as part of an entry in the
> source_path. Currently it is only allowed to exist as a standalone
> entry. This would allow me to say 'directory /home/mgulick/$cdir', and
> find_and_open_source would expand the $cdir part of this string to the
> compilation directory.
>
> I prefer approach 1 for a couple of reasons:
>
> 1) It is simpler for users to understand. It doesn't really require
> understanding the difference between DW_AT_comp_dir and DW_AT_name. It
> will match the source file in /home/mgulick/test.c, /test/src/test.c, or
> /home/mgulick/test/src/test.c. Of course, since '$cdir' is still in
> source_path, it will also look in /test/src/test/src/test.c, which is a
> little confusing, but shouldn't be a problem (and we could explicitly
> remove '$cdir' from the path when doing this search).
>
> 2) It seems to match the existing gdb documentation.
>
> I dislike approach 2 because it makes the directory strings more
> complicated, and it also means I'm unable to have a directory named
> '/home/mgulick/$cdir' (bizarre, I know, but such things are
> possible).
I agree with your approach, I think it's nicer if things can "just
work" for the user, and this seems like a good approach.
>
> Here is a preliminary patch for the first approach. I will go ahead and
> add a test-case and re-send a formal patch review request if this seems
> like an acceptable solution.
Do you have a copyright assignment in place? Any patch over a couple
of trivial lines requires an assignment. Others might correct me, but
this patch would seem to be big enough to me.
>
> Thanks,
> Mike
>
> diff --git a/gdb/source.c b/gdb/source.c
> index b27f210802..b7b5741028 100644
> --- a/gdb/source.c
> +++ b/gdb/source.c
> @@ -1033,7 +1033,35 @@ find_and_open_source (const char *filename,
> openp_flags flags = OPF_SEARCH_IN_PATH;
> if (basenames_may_differ)
> flags |= OPF_RETURN_REALPATH;
> +
> + /* Try to locate file using filename */
Comments need to end with '.', and there should always be two spaces
after a '.' in comments.
> result = openp (path, flags, filename, OPEN_MODE, fullname);
> + if (result < 0 && dirname != NULL)
> + {
> + /* Try to locate file using compilation dir + filename. This is helpful
> + if part of the compilation directory was removed, e.g. using gcc's
> + -fdebug-prefix-map, and we have added the missing prefix to
> + source_path. */
> + /* Allocate space for dirname, possibly an extra slash, the filename,
> + and terminating null */
You need two spaces at the end of your comments, and can you fold
these two comments into one.
> + char * cdir_filename = (char *)
> + alloca (strlen (dirname) + strlen(SLASH_STRING) + strlen (filename) + 1);
Whitespace between function name and opening parenthesis.
> +
> + cdir_filename = strcpy (cdir_filename, dirname);
> + int len = strlen (cdir_filename); /* last char in cdir_filename */
Comments should be formatted as sentences, so capital 'L' here, and
'. ' and the end.
> +
> + /* Add directory separator if not provided by dirname or filename */
> + if (!(IS_DIR_SEPARATOR (cdir_filename[len]) ||
> + IS_DIR_SEPARATOR (filename[0])))
Break the line before the '||'.
> + strcat (cdir_filename, SLASH_STRING);
> + else if (IS_DIR_SEPARATOR (cdir_filename[len]) &&
> + IS_DIR_SEPARATOR (filename[0]))
Same, line break before '&&'.
> + /* Both provide a slash, use only one */
Comment formatting.
> + cdir_filename[len] = '\0';
> + strcat(cdir_filename,filename);
Whitespace before opening parenthesis.
> +
> + result = openp(path, flags, cdir_filename, OPEN_MODE, fullname);
And again.
> + }
> if (result < 0)
> {
> /* Didn't work. Try using just the basename. */
You'll also need to write a short ChangeLog entry for the patch. I
also wonder if the function could be simplified by using std::string
to build the new string rather than all of the strlen/strcpy/strcat
calls? Though I don't think that would be a requirement for
acceptance.
You might also consider extending the documentation relating to source
path searching to better describe the process - as you discuss above,
the existing documentation is not that clear on the subject.
Finally, you'd probably want to write a test.... however, I was
wondering how a test for this might be written, and ended up writing
one myself. Feel free to use this as a starting point, or just
include this as is if it helps.
Thanks,
Andrew
---
commit c10a25ca9a1eb8f7e2cbab9b8fd652fe9feb16de
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date: Sun Sep 8 00:19:48 2019 +0100
gdb/testsuite: New test for mapped compilation directory
Ensure GDB can find source files when compiling using the
-fdebug-prefix-map GCC option. The problem is that the compilation
directory is not tried against other directories in the directory
search path.
gdb/testsuite/ChangeLog:
* gdb.base/source-dir.c: New file.
* gdb.base/source-dir.exp: Add extra test for mapped compilation
directory.
new file mode 100644
@@ -0,0 +1,5 @@
+int
+main ()
+{
+ return 0;
+}
@@ -15,9 +15,127 @@
standard_testfile
-gdb_start
+proc search_dir_list { dirs } {
+ set output "\r\nSource directories searched: "
+ append output [join $dirs "\[:;\]"]
-set foo "/nOtExStInG"
+ return ${output}
+}
-gdb_test "directory $foo/a $foo/b $foo/c" "\r\nSource directories searched: $foo/a\[:;\]$foo/b\[:;\]$foo/c\[:;\]\\\$cdir\[:;\]\\\$cwd"
-gdb_test "directory $foo/b $foo/d $foo/c" "\r\nSource directories searched: $foo/b\[:;\]$foo/d\[:;\]$foo/c\[:;\]$foo/a\[:;\]\\\$cdir\[:;\]\\\$cwd"
+
+# Check that adding directories to the search path changes the order
+# in which directories are searched.
+proc test_changing_search_directory {} {
+ gdb_start
+
+ set foo "/nOtExStInG"
+
+ gdb_test "directory $foo/a $foo/b $foo/c" \
+ [search_dir_list [list \
+ "$foo/a" \
+ "$foo/b" \
+ "$foo/c" \
+ "\\\$cdir" \
+ "\\\$cwd"]]
+ gdb_test "directory $foo/b $foo/d $foo/c" \
+ [search_dir_list [list \
+ "$foo/b" \
+ "$foo/d" \
+ "$foo/c" \
+ "$foo/a" \
+ "\\\$cdir" \
+ "\\\$cwd"]]
+ gdb_exit
+}
+
+# Test that the compilation directory can also be extended with a
+# prefix from the directory search path in order to find source files.
+proc test_truncated_comp_dir {} {
+ global srcfile srcdir subdir binfile
+
+ # When we run this test the current directory will be something
+ # like this:
+ # /some/path/to/gdb/build/testsuite/
+ # We are going to copy the source file out of the source tree into
+ # a location like this:
+ # /some/path/to/gdb/build/testsuite/output/gdb.base/soure-dir/
+ #
+ # We will then switch to this directory and compile the source
+ # file, however, we will ask GCC to remove this prefix from the
+ # compilation directory in the debug info:
+ # /some/path/to/gdb/build/testsuite/output/
+ #
+ # As a result the debug information will look like this:
+ #
+ # DW_AT_name : source-dir.c
+ # DW_AT_comp_dir : /gdb.base/source-dir
+ #
+ # Finally we switch back to this directory:
+ # /some/path/to/gdb/build/testsuite/
+ #
+ # and start GDB. There was a time when GDB would be unable to
+ # find the source file no matter what we added to the directory
+ # search path, this should now be fixed.
+
+ set original_dir [pwd]
+ set working_dir [standard_output_file ""]
+ cd ${working_dir}
+
+ set strip_dir [file normalize "${working_dir}/../.."]
+
+ set new_srcfile [standard_output_file ${srcfile}]
+ remote_exec host "cp ${srcdir}/${subdir}/${srcfile} ${new_srcfile}"
+
+ set options \
+ "debug additional_flags=-fdebug-prefix-map=${strip_dir}="
+ if { [gdb_compile "${srcfile}" "${binfile}" \
+ executable ${options}] != "" } {
+ untested "failed to compile"
+ return -1
+ }
+
+ cd ${original_dir}
+
+ clean_restart ${binfile}
+
+ gdb_test_no_output "set directories \$cdir:\$cwd"
+ gdb_test "show directories" \
+ "\r\nSource directories searched: \\\$cdir\[:;\]\\\$cwd"
+
+ if ![runto_main] then {
+ fail "can't run to main"
+ return 0
+ }
+
+ gdb_test "info source" \
+ [multi_line \
+ "Current source file is ${srcfile}" \
+ "Compilation directory is \[^\n\r\]+" \
+ "${srcfile}: No such file or directory."]
+
+ gdb_test "dir $strip_dir" \
+ [search_dir_list [list \
+ "$strip_dir" \
+ "\\\$cdir" \
+ "\\\$cwd"]]
+ gdb_test "list" [multi_line \
+ "1\[ \t\]+int" \
+ "2\[ \t\]+main \\(\\)" \
+ "3\[ \t\]+\\{" \
+ "4\[ \t\]+return 0;" \
+ "5\[ \t\]+\\}" ]
+
+ gdb_test "info source" \
+ [multi_line \
+ "Current source file is ${srcfile}" \
+ "Compilation directory is \[^\n\r\]+" \
+ "Located in ${new_srcfile}" \
+ "Contains 5 lines." \
+ "Source language is c." \
+ "Producer is \[^\n\r\]+" \
+ "\[^\n\r\]+" \
+ "\[^\n\r\]+" ]
+}
+
+test_changing_search_directory
+test_truncated_comp_dir