gdb: defer warnings when loading separate debug files

Message ID 20221212121535.4013497-1-ahajkova@redhat.com
State New
Headers
Series gdb: defer warnings when loading separate debug files |

Commit Message

Terekhov, Mikhail via Gdb-patches Dec. 12, 2022, 12:15 p.m. UTC
  From: Alexandra Hájková <ahajkova@redhat.com>

Currently, when GDB loads debug information from a separate debug file,
there are a couple of warnings that could be produced if things go
wrong.

In find_separate_debug_file_by_buildid (build-id.c) GDB can give a
warning if the separate debug file doesn't include any actual debug
information, and in separate_debug_file_exists (symfile.c) we can warn
if the CRC checksum in the separate debug file doesn't match the
checksum in the original executable.

The problem here is that, when looking up debug information, GDB will
try several different approaches, lookup by build-id, lookup by
debug-link, and then a lookup from debuginfod.  GDB can potentially give
a warning from an earlier attempt, and then succeed with a later
attempt.  In the cases I have run into this is primarily a warning about
some out of date debug information on my machine, but then GDB finds the
correct information using debuginfod.  This can be confusing to a user,
they will see warnings from GDB when really everything is working just
fine.

For example:
warning: the debug information found in "/usr/lib/debug//lib64/ld-2.32.so.debug"
does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).

This diagnostic was printed on Fedora 33 even when the correct debuginfo
was downloaded.

In this patch I propose that we defer any warnings related to looking up
debug information from a separate debug file.  If any of the approaches
are successful then GDB will not print any of the warnings.  As far as
the user is concerned, everything "just worked".  Only if GDB completely
fails to find any suitable debug information will the warnings be
printed.

The crc_mismatch test compiles two executables: crc_mismatch and crc_mismatch-2
and then strips them of debuginfo creating separate debug files. The test then
replaces crc_mismatch-2.debug with crc_mismatch.debug to trigger "CRC mismatch"
warning. A local debuginfod server is setup to supply the correct debug file,
now when GDB looks up the debug info no warning is given.

The build-id-no-debug-warning.exp is similar to the previous test. It triggers
the "separate debug info file has no debug info" warning by replacing
the build-id based .debug file with the stripped binary and then loading it to gdb.
It then also sets up local debuginfod server with the correct debug file to download
to make sure no warnings are emmitted.
---
 gdb/build-id.c                                |   9 +-
 gdb/build-id.h                                |  10 +-
 gdb/coffread.c                                |  12 +-
 gdb/elfread.c                                 |  12 +-
 gdb/symfile.c                                 |  43 ++++--
 gdb/symfile.h                                 |   9 +-
 .../build-id-no-debug-warning.c               |  20 +++
 .../build-id-no-debug-warning.exp             | 142 ++++++++++++++++++
 gdb/testsuite/gdb.debuginfod/crc_mismatch-2.c |  22 +++
 gdb/testsuite/gdb.debuginfod/crc_mismatch.c   |  20 +++
 gdb/testsuite/gdb.debuginfod/crc_mismatch.exp | 114 ++++++++++++++
 gdb/testsuite/lib/gdb.exp                     |  38 +++--
 12 files changed, 412 insertions(+), 39 deletions(-)
 create mode 100644 gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
 create mode 100644 gdb/testsuite/gdb.debuginfod/crc_mismatch-2.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/crc_mismatch.c
 create mode 100644 gdb/testsuite/gdb.debuginfod/crc_mismatch.exp
  

Comments

Guinevere Larsen Dec. 15, 2022, 3:57 p.m. UTC | #1
On 12/12/2022 13:15, AlexandraH á jkov á ahajkova--- via Gdb-patches wrote:
> From: Alexandra Hájková <ahajkova@redhat.com>
>
> Currently, when GDB loads debug information from a separate debug file,
> there are a couple of warnings that could be produced if things go
> wrong.
>
> In find_separate_debug_file_by_buildid (build-id.c) GDB can give a
> warning if the separate debug file doesn't include any actual debug
> information, and in separate_debug_file_exists (symfile.c) we can warn
> if the CRC checksum in the separate debug file doesn't match the
> checksum in the original executable.
>
> The problem here is that, when looking up debug information, GDB will
> try several different approaches, lookup by build-id, lookup by
> debug-link, and then a lookup from debuginfod.  GDB can potentially give
> a warning from an earlier attempt, and then succeed with a later
> attempt.  In the cases I have run into this is primarily a warning about
> some out of date debug information on my machine, but then GDB finds the
> correct information using debuginfod.  This can be confusing to a user,
> they will see warnings from GDB when really everything is working just
> fine.
>
> For example:
> warning: the debug information found in "/usr/lib/debug//lib64/ld-2.32.so.debug"
> does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).
>
> This diagnostic was printed on Fedora 33 even when the correct debuginfo
> was downloaded.
>
> In this patch I propose that we defer any warnings related to looking up
> debug information from a separate debug file.  If any of the approaches
> are successful then GDB will not print any of the warnings.  As far as
> the user is concerned, everything "just worked".  Only if GDB completely
> fails to find any suitable debug information will the warnings be
> printed.
>
> The crc_mismatch test compiles two executables: crc_mismatch and crc_mismatch-2
> and then strips them of debuginfo creating separate debug files. The test then
> replaces crc_mismatch-2.debug with crc_mismatch.debug to trigger "CRC mismatch"
> warning. A local debuginfod server is setup to supply the correct debug file,
> now when GDB looks up the debug info no warning is given.
>
> The build-id-no-debug-warning.exp is similar to the previous test. It triggers
> the "separate debug info file has no debug info" warning by replacing
> the build-id based .debug file with the stripped binary and then loading it to gdb.
> It then also sets up local debuginfod server with the correct debug file to download
> to make sure no warnings are emmitted.

Hello Sasha,

I am not very familiar with the area of the code, but I see no issues 
with it and it introduces no regressions, so:

Reviewed-By: Bruno Larsen <blarsen@redhat.com>
  
Simon Marchi Dec. 15, 2022, 4:35 p.m. UTC | #2
On 12/12/22 07:15, AlexandraH á jkov á ahajkova--- via Gdb-patches wrote:
> From: Alexandra Hájková <ahajkova@redhat.com>
> 
> Currently, when GDB loads debug information from a separate debug file,
> there are a couple of warnings that could be produced if things go
> wrong.
> 
> In find_separate_debug_file_by_buildid (build-id.c) GDB can give a
> warning if the separate debug file doesn't include any actual debug
> information, and in separate_debug_file_exists (symfile.c) we can warn
> if the CRC checksum in the separate debug file doesn't match the
> checksum in the original executable.
> 
> The problem here is that, when looking up debug information, GDB will
> try several different approaches, lookup by build-id, lookup by
> debug-link, and then a lookup from debuginfod.  GDB can potentially give
> a warning from an earlier attempt, and then succeed with a later
> attempt.  In the cases I have run into this is primarily a warning about
> some out of date debug information on my machine, but then GDB finds the
> correct information using debuginfod.  This can be confusing to a user,
> they will see warnings from GDB when really everything is working just
> fine.
> 
> For example:
> warning: the debug information found in "/usr/lib/debug//lib64/ld-2.32.so.debug"
> does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).
> 
> This diagnostic was printed on Fedora 33 even when the correct debuginfo
> was downloaded.
> 
> In this patch I propose that we defer any warnings related to looking up
> debug information from a separate debug file.  If any of the approaches
> are successful then GDB will not print any of the warnings.  As far as
> the user is concerned, everything "just worked".  Only if GDB completely
> fails to find any suitable debug information will the warnings be
> printed.

I don't really like this behavior change.  The situations you described:

 - a separate debug file without any actual debug information
 - CRC checksum that doesn't match

... are abnormal situations which still deserve being warned about, I
think.  If the files are there, it's because they are meant to be used,
so if something prevents GDB from using them, I want to know.  Silencing
the warning just makes investigating "why doesn't GDB read my separate
debug file" harder.

I can understand why this can be a bit confusing to the user, but
the warning is still factually correct.  For instance, the one you
quoted:

    warning: the debug information found in "/usr/lib/debug//lib64/ld-2.32.so.debug"
    does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).

This doesn't say that GDB didn't end up finding *some* debug info for
the shared object, just that this particular one is broken.  Maybe it's
an old one I installed by hand, that I should delete, maybe the package
from the distro is broken.  In any case it's good for the user to know
so they can fix the situation.

Simon
  
Alexandra Petlanova Hajkova Dec. 19, 2022, 12:28 p.m. UTC | #3
>
> I don't really like this behavior change.  The situations you described:
>
>  - a separate debug file without any actual debug information
>  - CRC checksum that doesn't match
>
> ... are abnormal situations which still deserve being warned about, I
> think.  If the files are there, it's because they are meant to be used,
> so if something prevents GDB from using them, I want to know.  Silencing
> the warning just makes investigating "why doesn't GDB read my separate
> debug file" harder.
>
> I can understand why this can be a bit confusing to the user, but
> the warning is still factually correct.  For instance, the one you
> quoted:
>
>     warning: the debug information found in
> "/usr/lib/debug//lib64/ld-2.32.so.debug"
>     does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).
>
> This doesn't say that GDB didn't end up finding *some* debug info for
> the shared object, just that this particular one is broken.  Maybe it's
> an old one I installed by hand, that I should delete, maybe the package
> from the distro is broken.  In any case it's good for the user to know
> so they can fix the situation.
>
> Simon
>
> Yes, it's a good point. It might be confusing to see the warnings if the
correct debuginfo was retrieved but it might be useful to see all the
warnings
on the other hand. I would love to have more opinions on this matter.

Thank you,
Alexandra
  
Frank Ch. Eigler Dec. 19, 2022, 1:04 p.m. UTC | #4
Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

> [...]
> I don't really like this behavior change.  The situations you described:
>
>  - a separate debug file without any actual debug information
>  - CRC checksum that doesn't match
>
> ... are abnormal situations which still deserve being warned about, I
> think.  If the files are there, it's because they are meant to be
> used,

That's a bit of a leap.  They can happen totally accidentally, when a
sysadmin or cron job does an automated update of the system, and for
whatever reason, the debug packages got missed.  On some distros, this
can easily happen.  Then the remnants are not "meant to be used" at all,
they are just fossils.

> so if something prevents GDB from using them, I want to know.
> Silencing the warning just makes investigating "why doesn't GDB read
> my separate debug file" harder.

If GDB did find the correct debug file one way or the other, then why
would you care, never mind bother investigate?  It's a non-problem.


> I can understand why this can be a bit confusing to the user, but
> the warning is still factually correct.  For instance, the one you
> quoted:
>
>     warning: the debug information found in
>     "/usr/lib/debug//lib64/ld-2.32.so.debug" does not match
>     "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).

Ideally, users should really only be warned about things that they can
do something about.  Being warned about the same thing that only a
sysadmin can possibly fix, and repeatedly, seems more like noise.

- FChE
  
Andrew Burgess Dec. 19, 2022, 2:43 p.m. UTC | #5
Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

> On 12/12/22 07:15, AlexandraH á jkov á ahajkova--- via Gdb-patches wrote:
>> From: Alexandra Hájková <ahajkova@redhat.com>
>> 
>> Currently, when GDB loads debug information from a separate debug file,
>> there are a couple of warnings that could be produced if things go
>> wrong.
>> 
>> In find_separate_debug_file_by_buildid (build-id.c) GDB can give a
>> warning if the separate debug file doesn't include any actual debug
>> information, and in separate_debug_file_exists (symfile.c) we can warn
>> if the CRC checksum in the separate debug file doesn't match the
>> checksum in the original executable.
>> 
>> The problem here is that, when looking up debug information, GDB will
>> try several different approaches, lookup by build-id, lookup by
>> debug-link, and then a lookup from debuginfod.  GDB can potentially give
>> a warning from an earlier attempt, and then succeed with a later
>> attempt.  In the cases I have run into this is primarily a warning about
>> some out of date debug information on my machine, but then GDB finds the
>> correct information using debuginfod.  This can be confusing to a user,
>> they will see warnings from GDB when really everything is working just
>> fine.
>> 
>> For example:
>> warning: the debug information found in "/usr/lib/debug//lib64/ld-2.32.so.debug"
>> does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).
>> 
>> This diagnostic was printed on Fedora 33 even when the correct debuginfo
>> was downloaded.
>> 
>> In this patch I propose that we defer any warnings related to looking up
>> debug information from a separate debug file.  If any of the approaches
>> are successful then GDB will not print any of the warnings.  As far as
>> the user is concerned, everything "just worked".  Only if GDB completely
>> fails to find any suitable debug information will the warnings be
>> printed.
>
> I don't really like this behavior change.  The situations you described:
>
>  - a separate debug file without any actual debug information
>  - CRC checksum that doesn't match
>
> ... are abnormal situations which still deserve being warned about, I
> think.  If the files are there, it's because they are meant to be used,
> so if something prevents GDB from using them, I want to know.

In my experience, most users don't care where the debug info comes from,
so long as GDB can find it somehow.

>                                                                Silencing
> the warning just makes investigating "why doesn't GDB read my separate
> debug file" harder.
>
> I can understand why this can be a bit confusing to the user, but
> the warning is still factually correct.  For instance, the one you
> quoted:
>
>     warning: the debug information found in "/usr/lib/debug//lib64/ld-2.32.so.debug"
>     does not match "/lib64/ld-linux-x86-64.so.2" (CRC mismatch).
>
> This doesn't say that GDB didn't end up finding *some* debug info for
> the shared object, just that this particular one is broken.

I think we should not underestimate the concern a warning can cause.
Sure, it's not an error, but there are some users who, upon seeing a
warning, will stop and seek help until they have resolved the issue for
fear that it's important.

Maybe if we extended the warning to say "... but don't worry I found
some debug information elsewhere." then this would stop the worry?

But, I would suggest that the above message could fall into a category
below warning.  In my mind the "levels" are:

  error - something went wrong and GDB can't continue,

  warning - something went wrong, GDB can continue, but your experience
  is not going to be what you expect in some way,

  info? (this doesn't exist right now) - just some useful information,
  but everything is just fine.

The message Alexandra is changing could be an 'info' message when GDB is
able to find debug elsewhere, but could be promoted to 'warning' when
GDB is unable to find any alternative debug information.

Right now, some 'info' messages are just printed to the console by GDB,
e.g. when we print 'Reading symbols from /tmp/test...', but there are
plenty of cases where GDB doesn't print out all the intermediate steps
that it tries when looking for a result, e.g. if I do 'file ls' then GDB
will find /usr/bin/ls, but GDB doesn't tell me where it looked and
didn't find 'ls'.  I know this isn't exactly the same, but, it isn't
unrelated I think.

>                                                              Maybe it's
> an old one I installed by hand, that I should delete, maybe the package
> from the distro is broken.  In any case it's good for the user to know
> so they can fix the situation.

In my experience, in most cases, the user doesn't care about fixing the
problem, unless it is somehow stopping them debug their issue.  If GDB
can find what it needs, then I think most users don't care, things just
work, and they're fine with that.

And it's not like GDB is hiding what its doing, there are info messages
printed that GDB is downloading a file, then that GDB is reading debug
from the freshly downloaded file, so if the user does have some
expectations about which debug file should be used, this should inform
them that something isn't going quite right.

Maybe one thing we could do, which might help in this area, would be, at
the point where the (potential) warning is added to the warning list we
could emit a debug print guarded by separate_debug_file_debug.  That
way, if a user asks the question, "why is GDB failing to find the
expected debug file?", they can 'set debug separate-debug-file' and the
answer would be there?

Thanks,
Andrew
  
Simon Marchi Dec. 20, 2022, 2:29 a.m. UTC | #6
> I think we should not underestimate the concern a warning can cause.
> Sure, it's not an error, but there are some users who, upon seeing a
> warning, will stop and seek help until they have resolved the issue for
> fear that it's important.
> 
> Maybe if we extended the warning to say "... but don't worry I found
> some debug information elsewhere." then this would stop the worry?
> 
> But, I would suggest that the above message could fall into a category
> below warning.  In my mind the "levels" are:
> 
>   error - something went wrong and GDB can't continue,
> 
>   warning - something went wrong, GDB can continue, but your experience
>   is not going to be what you expect in some way,
> 
>   info? (this doesn't exist right now) - just some useful information,
>   but everything is just fine.
> 
> The message Alexandra is changing could be an 'info' message when GDB is
> able to find debug elsewhere, but could be promoted to 'warning' when
> GDB is unable to find any alternative debug information.

Yeah that makes sense.  But given that my view appears to be the
exception, I'm fine with going with no message at all as well, if we
think regular users don't care.  I'm not a regular GDB user, that's
probably why I want to be warned about everything that can possibly make
GDB misbehave, so I can fix it.

> Right now, some 'info' messages are just printed to the console by GDB,
> e.g. when we print 'Reading symbols from /tmp/test...', but there are
> plenty of cases where GDB doesn't print out all the intermediate steps
> that it tries when looking for a result, e.g. if I do 'file ls' then GDB
> will find /usr/bin/ls, but GDB doesn't tell me where it looked and
> didn't find 'ls'.  I know this isn't exactly the same, but, it isn't
> unrelated I think.
> 
>>                                                              Maybe it's
>> an old one I installed by hand, that I should delete, maybe the package
>> from the distro is broken.  In any case it's good for the user to know
>> so they can fix the situation.
> 
> In my experience, in most cases, the user doesn't care about fixing the
> problem, unless it is somehow stopping them debug their issue.  If GDB
> can find what it needs, then I think most users don't care, things just
> work, and they're fine with that.

I personally would like to be warned, but you're probably right about
the average user.

> And it's not like GDB is hiding what its doing, there are info messages
> printed that GDB is downloading a file, then that GDB is reading debug
> from the freshly downloaded file, so if the user does have some
> expectations about which debug file should be used, this should inform
> them that something isn't going quite right.
> 
> Maybe one thing we could do, which might help in this area, would be, at
> the point where the (potential) warning is added to the warning list we
> could emit a debug print guarded by separate_debug_file_debug.  That
> way, if a user asks the question, "why is GDB failing to find the
> expected debug file?", they can 'set debug separate-debug-file' and the
> answer would be there?

Agreed, it's so difficult to figure out why GDB doesn't find separate
debug info sometimes, we need good debug messages.

Simon
  

Patch

diff --git a/gdb/build-id.c b/gdb/build-id.c
index 0c5d65d02ac..4c6e44560f0 100644
--- a/gdb/build-id.c
+++ b/gdb/build-id.c
@@ -202,7 +202,8 @@  build_id_to_exec_bfd (size_t build_id_len, const bfd_byte *build_id)
 /* See build-id.h.  */
 
 std::string
-find_separate_debug_file_by_buildid (struct objfile *objfile)
+find_separate_debug_file_by_buildid (struct objfile *objfile,
+				     std::vector<std::string> *warnings_vector)
 {
   const struct bfd_build_id *build_id;
 
@@ -220,8 +221,10 @@  find_separate_debug_file_by_buildid (struct objfile *objfile)
       if (abfd != NULL
 	  && filename_cmp (bfd_get_filename (abfd.get ()),
 			   objfile_name (objfile)) == 0)
-	warning (_("\"%s\": separate debug info file has no debug info"),
-		 bfd_get_filename (abfd.get ()));
+	warnings_vector->emplace_back
+	  (string_printf
+	   (_("\"%s\": separate debug info file has no debug info"),
+	    bfd_get_filename (abfd.get ())));
       else if (abfd != NULL)
 	return std::string (bfd_get_filename (abfd.get ()));
     }
diff --git a/gdb/build-id.h b/gdb/build-id.h
index 7629427c504..5d5c21845f2 100644
--- a/gdb/build-id.h
+++ b/gdb/build-id.h
@@ -49,10 +49,16 @@  extern gdb_bfd_ref_ptr build_id_to_exec_bfd (size_t build_id_len,
 
 /* Find the separate debug file for OBJFILE, by using the build-id
    associated with OBJFILE's BFD.  If successful, returns the file name for the
-   separate debug file, otherwise, return an empty string.  */
+   separate debug file, otherwise, return an empty string.
+
+   Any warnings that are generated by the lookup process should be added to
+   WARNINGS_VECTOR, one std::string per warning.  If some other mechanism can
+   be used to lookup the debug information then the warning will not be shown,
+   however, if GDB fails to find suitable debug information using any
+   approach, then any warnings will be printed.  */
 
 extern std::string find_separate_debug_file_by_buildid
-  (struct objfile *objfile);
+  (struct objfile *objfile, std::vector<std::string> *warnings_vector);
 
 /* Return an hex-string representation of BUILD_ID.  */
 
diff --git a/gdb/coffread.c b/gdb/coffread.c
index f29627d9797..6377b08d246 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -734,10 +734,13 @@  coff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
   /* Try to add separate debug file if no symbols table found.   */
   if (!objfile->has_partial_symbols ())
     {
-      std::string debugfile = find_separate_debug_file_by_buildid (objfile);
+      std::vector<std::string> warnings_vector;
+      std::string debugfile
+	= find_separate_debug_file_by_buildid (objfile, &warnings_vector);
 
       if (debugfile.empty ())
-	debugfile = find_separate_debug_file_by_debuglink (objfile);
+	debugfile
+	  = find_separate_debug_file_by_debuglink (objfile, &warnings_vector);
 
       if (!debugfile.empty ())
 	{
@@ -746,6 +749,11 @@  coff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags)
 	  symbol_file_add_separate (debug_bfd, debugfile.c_str (),
 				    symfile_flags, objfile);
 	}
+      /* If all the methods to collect the debuginfo failed, print any
+	 warnings that were collected.  */
+      if (debugfile.empty () && !warnings_vector.empty ())
+	for (const std::string &w : warnings_vector)
+	  warning ("%s", w.c_str ());
     }
 }
 
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 64aeb239670..ae159408faf 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -1194,6 +1194,7 @@  elf_symfile_read_dwarf2 (struct objfile *objfile,
 			 symfile_add_flags symfile_flags)
 {
   bool has_dwarf2 = true;
+  std::vector<std::string> warnings_vector;
 
   if (dwarf2_has_info (objfile, NULL, true))
     dwarf2_initialize_objfile (objfile);
@@ -1213,10 +1214,12 @@  elf_symfile_read_dwarf2 (struct objfile *objfile,
 	   && objfile->separate_debug_objfile == NULL
 	   && objfile->separate_debug_objfile_backlink == NULL)
     {
-      std::string debugfile = find_separate_debug_file_by_buildid (objfile);
+      std::string debugfile
+	= find_separate_debug_file_by_buildid (objfile, &warnings_vector);
 
       if (debugfile.empty ())
-	debugfile = find_separate_debug_file_by_debuglink (objfile);
+	debugfile = find_separate_debug_file_by_debuglink (objfile,
+							   &warnings_vector);
 
       if (!debugfile.empty ())
 	{
@@ -1258,6 +1261,11 @@  elf_symfile_read_dwarf2 (struct objfile *objfile,
 		}
 	    }
 	}
+      /* If all the methods to collect the debuginfo failed, print
+	 the warnings, if there're any. */
+      if (debugfile.empty () && !has_dwarf2 && !warnings_vector.empty ())
+	for (const std::string &w : warnings_vector)
+	  warning ("%s", w.c_str ());
     }
 
   return has_dwarf2;
diff --git a/gdb/symfile.c b/gdb/symfile.c
index eb27668f9d3..9d63d80e100 100644
--- a/gdb/symfile.c
+++ b/gdb/symfile.c
@@ -1244,7 +1244,8 @@  bool separate_debug_file_debug = false;
 
 static int
 separate_debug_file_exists (const std::string &name, unsigned long crc,
-			    struct objfile *parent_objfile)
+			    struct objfile *parent_objfile,
+			    std::vector<std::string> *warnings_vector)
 {
   unsigned long file_crc;
   int file_crc_p;
@@ -1336,9 +1337,10 @@  separate_debug_file_exists (const std::string &name, unsigned long crc,
 	}
 
       if (verified_as_different || parent_crc != file_crc)
-	warning (_("the debug information found in \"%s\""
-		   " does not match \"%s\" (CRC mismatch).\n"),
-		 name.c_str (), objfile_name (parent_objfile));
+	warnings_vector->emplace_back
+	  (string_printf (_("the debug information found in \"%s\""
+			    " does not match \"%s\" (CRC mismatch).\n"),
+			  name.c_str (), objfile_name (parent_objfile)));
 
       if (separate_debug_file_debug)
 	gdb_printf (gdb_stdlog, _(" no, CRC doesn't match.\n"));
@@ -1373,13 +1375,17 @@  show_debug_file_directory (struct ui_file *file, int from_tty,
    looking for.  CANON_DIR is the "realpath" form of DIR.
    DIR must contain a trailing '/'.
    Returns the path of the file with separate debug info, or an empty
-   string.  */
+   string.
+
+   Any warnings generated as part of the lookup process are added to
+   WARNINGS_VECTOR, one std::string per warning.  */
 
 static std::string
 find_separate_debug_file (const char *dir,
 			  const char *canon_dir,
 			  const char *debuglink,
-			  unsigned long crc32, struct objfile *objfile)
+			  unsigned long crc32, struct objfile *objfile,
+			  std::vector<std::string> *warnings_vector)
 {
   if (separate_debug_file_debug)
     gdb_printf (gdb_stdlog,
@@ -1390,7 +1396,7 @@  find_separate_debug_file (const char *dir,
   std::string debugfile = dir;
   debugfile += debuglink;
 
-  if (separate_debug_file_exists (debugfile, crc32, objfile))
+  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings_vector))
     return debugfile;
 
   /* Then try in the subdirectory named DEBUG_SUBDIRECTORY.  */
@@ -1399,7 +1405,7 @@  find_separate_debug_file (const char *dir,
   debugfile += "/";
   debugfile += debuglink;
 
-  if (separate_debug_file_exists (debugfile, crc32, objfile))
+  if (separate_debug_file_exists (debugfile, crc32, objfile, warnings_vector))
     return debugfile;
 
   /* Then try in the global debugfile directories.
@@ -1444,7 +1450,8 @@  find_separate_debug_file (const char *dir,
       debugfile += dir_notarget;
       debugfile += debuglink;
 
-      if (separate_debug_file_exists (debugfile, crc32, objfile))
+      if (separate_debug_file_exists (debugfile, crc32, objfile,
+				      warnings_vector))
 	return debugfile;
 
       const char *base_path = NULL;
@@ -1466,7 +1473,8 @@  find_separate_debug_file (const char *dir,
 	  debugfile += "/";
 	  debugfile += debuglink;
 
-	  if (separate_debug_file_exists (debugfile, crc32, objfile))
+	  if (separate_debug_file_exists (debugfile, crc32, objfile,
+					  warnings_vector))
 	    return debugfile;
 
 	  /* If the file is in the sysroot, try using its base path in
@@ -1479,7 +1487,8 @@  find_separate_debug_file (const char *dir,
 	  debugfile += "/";
 	  debugfile += debuglink;
 
-	  if (separate_debug_file_exists (debugfile, crc32, objfile))
+	  if (separate_debug_file_exists (debugfile, crc32, objfile,
+					  warnings_vector))
 	    return debugfile;
 	}
 
@@ -1507,11 +1516,11 @@  terminate_after_last_dir_separator (char *path)
   path[i + 1] = '\0';
 }
 
-/* Find separate debuginfo for OBJFILE (using .gnu_debuglink section).
-   Returns pathname, or an empty string.  */
+/* See symtab.h.  */
 
 std::string
-find_separate_debug_file_by_debuglink (struct objfile *objfile)
+find_separate_debug_file_by_debuglink
+  (struct objfile *objfile, std::vector<std::string> *warnings_vector)
 {
   unsigned long crc32;
 
@@ -1531,7 +1540,8 @@  find_separate_debug_file_by_debuglink (struct objfile *objfile)
 
   std::string debugfile
     = find_separate_debug_file (dir.c_str (), canon_dir.get (),
-				debuglink.get (), crc32, objfile);
+				debuglink.get (), crc32, objfile,
+				warnings_vector);
 
   if (debugfile.empty ())
     {
@@ -1555,7 +1565,8 @@  find_separate_debug_file_by_debuglink (struct objfile *objfile)
 							symlink_dir.get (),
 							debuglink.get (),
 							crc32,
-							objfile);
+							objfile,
+							warnings_vector);
 		}
 	    }
 	}
diff --git a/gdb/symfile.h b/gdb/symfile.h
index ffd1acddfdb..e21c08ce027 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -241,7 +241,14 @@  extern struct objfile *symbol_file_add_from_bfd (const gdb_bfd_ref_ptr &,
 extern void symbol_file_add_separate (const gdb_bfd_ref_ptr &, const char *,
 				      symfile_add_flags, struct objfile *);
 
-extern std::string find_separate_debug_file_by_debuglink (struct objfile *);
+/* Find separate debuginfo for OBJFILE (using .gnu_debuglink section).
+   Returns pathname, or an empty string.
+
+   Any warnings generated as part of this lookup are added to
+   WARNINGS_VECTOR, one std::string per warning.  */
+
+extern std::string find_separate_debug_file_by_debuglink
+  (struct objfile *objfile, std::vector<std::string> *warnings_vector);
 
 /* Build (allocate and populate) a section_addr_info struct from an
    existing section table.  */
diff --git a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.c b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.c
new file mode 100644
index 00000000000..223a703bb4f
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.c
@@ -0,0 +1,20 @@ 
+/* Copyright 2022 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)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
new file mode 100644
index 00000000000..b328e19f438
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
@@ -0,0 +1,142 @@ 
+# Copyright 2022 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/>.
+#
+# This test triggers the "separate debug info file has no debug info" warning by replacing
+# the build-id based .debug file with the stripped binary and then loading it to gdb.
+# It then also sets up local debuginfod server with the correct debug file to download
+# to make sure no warnings are emmitted.
+
+
+standard_testfile
+
+load_lib debuginfod-support.exp
+
+if { [skip_debuginfod_tests] } { return -1 }
+
+if {[build_executable "build executable" ${testfile} ${srcfile} \
+	 {debug build-id}] == -1} {
+    return -1
+}
+
+# Split BINFILE into BINFILE.stripped and BINFILE.debug, the first is
+# the executable with the debug information removed, and the second is
+# the debug information.
+#
+# However, by passing the "no-debuglink" flag we prevent this proc
+# from adding a .gnu_debuglink section to the executable.  Any lookup
+# of the debug information by GDB will need to be done based on the
+# build-id.
+if [gdb_gnu_strip_debug $binfile no-debuglink] {
+    unsupported "cannot produce separate debug info files"
+    return -1
+}
+
+# Get the .build-id/PREFIX/SUFFIX.debug file name, and convert it to
+# an absolute path, this is where we will place the debug information.
+set build_id_debug_file \
+    [standard_output_file [build_id_debug_filename_get $binfile]]
+
+# Get the BINFILE.debug filename.  This is the file we should be
+# moving to the BUILD_ID_DEBUG_FILE location, but we wont, we're going
+# to move something else there instead.
+set debugfile [standard_output_file "${binfile}.debug"]
+
+# Move debugfile to the directory to be used by the debuginfod
+# server.
+set debuginfod_debugdir [standard_output_file "debug"]
+remote_exec build "mkdir $debuginfod_debugdir"
+remote_exec build "mv $debugfile $debuginfod_debugdir"
+
+# This is BINFILE with the debug information removed.  We are going to
+# place this in the BUILD_ID_DEBUG_FILE location, this would usually
+# represent a mistake by the user, and will trigger a warning from
+# GDB, this is the warning we are checking for.
+set stripped_binfile [standard_output_file "${binfile}.stripped"]
+
+# Create the .build-id/PREFIX directory name from
+# .build-id/PREFIX/SUFFIX.debug filename.
+set debugdir [file dirname ${build_id_debug_file}]
+remote_exec build "mkdir -p $debugdir"
+
+# Now move the stripped executable into the .build-id directory
+# instead of the debug information.  Later on we're going to try and
+# load this into GDB.  GDB will then try to find the separate debug
+# information, which will point back at this file, which also doesn't
+# have debug information, which could cause a loop.  But GDB will spot
+# this and give a warning.
+remote_exec build "mv ${stripped_binfile} ${build_id_debug_file}"
+
+# Now start GDB.
+clean_restart
+
+# Tell GDB where to look for the .build-id directory.
+set debug_file_directory [standard_output_file ""]
+gdb_test_no_output "set debug-file-directory ${debug_file_directory}" \
+    "set debug-file-directory"
+
+# Now load the file into GDB, and look for the warning.
+gdb_test "file ${build_id_debug_file}" \
+    [multi_line \
+    ".*Reading symbols from.*debuginfo.*" \
+    ".*separate debug info file has no debug info.*"] \
+    "load test file, expect a warning"
+
+# Now we should close GDB.
+gdb_exit
+
+# Create CACHE and DB directories ready for debuginfod to use.
+prepare_for_debuginfod cache db
+
+# Start debuginfod server and test debuginfo is downloaded from
+# it and we can se no warnings anymore.
+proc_with_prefix local_debuginfod { } {
+    global db debuginfod_debugdir cache build_id_debug_file
+
+    set url [start_debuginfod $db $debuginfod_debugdir]
+    if { $url == "" } {
+	unresolved "failed to start debuginfod server"
+	return
+    }
+
+    # Point the client to the server.
+    setenv DEBUGINFOD_URLS $url
+
+    # GDB should now find the symbol and source files.
+    clean_restart
+
+    # Enable debuginfod and fetch the debuginfo.
+    gdb_test_no_output "set debuginfod enabled on"
+
+    # "separate debug info file has no debug info" warning should not be
+    # reported now because the correct debuginfo should be fetched from
+    # debuginfod.
+    gdb_test "file ${build_id_debug_file}" \
+	[multi_line \
+	     "Reading symbols from ${build_id_debug_file}\\.\\.\\." \
+	     "Downloading separate debug info for ${build_id_debug_file}\\.\\.\\." \
+	     "Reading symbols from ${cache}/\[^\r\n\]+\\.\\.\\.(?:\r\nExpanding full symbols from \[^\r\n\]+)*"] \
+	"debuginfod running, info downloaded, no warnings"
+}
+
+# Restart GDB, and load the file, this time we should correctly get
+# the debug symbols from the server, and should not see the warning.
+with_debuginfod_env $cache {
+    local_debuginfod
+}
+
+stop_debuginfod
+# Spare debug files may confuse testsuite runs in the future.
+remote_exec build "rm -f $debugfile"
+
diff --git a/gdb/testsuite/gdb.debuginfod/crc_mismatch-2.c b/gdb/testsuite/gdb.debuginfod/crc_mismatch-2.c
new file mode 100644
index 00000000000..6b3984dc7d2
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/crc_mismatch-2.c
@@ -0,0 +1,22 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/crc_mismatch.c b/gdb/testsuite/gdb.debuginfod/crc_mismatch.c
new file mode 100644
index 00000000000..1801a9c953a
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/crc_mismatch.c
@@ -0,0 +1,20 @@ 
+/* Copyright 2022 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 (int argc, char **argv, char **envp)
+{
+  return argc;
+}
diff --git a/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp b/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp
new file mode 100644
index 00000000000..76cc771814b
--- /dev/null
+++ b/gdb/testsuite/gdb.debuginfod/crc_mismatch.exp
@@ -0,0 +1,114 @@ 
+# Copyright 2022 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/>.
+#
+# This test compiles two executables: crc_mismatch and crc_mismatch-2
+# and then strips them of debuginfo creating separate debug files. The test
+# then replaces crc_mismatch-2.debug with crc_mismatch.debug to trigger
+# "CRC mismatch" warning. A local debuginfod server is setup to supply
+# the correct debug file, now when GDB looks up the debug info no warning
+# is given.
+
+standard_testfile .c -2.c
+
+load_lib debuginfod-support.exp
+
+if { [skip_debuginfod_tests] } { return -1 }
+
+if  {[build_executable "build executable" $testfile $srcfile debug] == -1 } {
+    untested "failed to compile"
+    return -1
+}
+
+# The procedure gdb_gnu_strip_debug will produce an executable called
+# ${binfile}, which is just like the executable ($binfile) but without
+# the debuginfo. Instead $binfile has a .gnu_debuglink section which
+# contains the name of a debuginfo only file.
+if [gdb_gnu_strip_debug $binfile] {
+    # Check that you have a recent version of strip and objcopy installed.
+    unsupported "cannot produce separate debug info files"
+    return -1
+}
+
+set debugfile "$[standard_output_file ${testfile}.debug]"
+set debugdir [standard_output_file "debug"]
+remote_exec build "mkdir $debugdir"
+remote_exec build "mkdir -p [file dirname $debugfile]"
+remote_exec build "mv -f [standard_output_file ${testfile}.debug] $debugfile"
+
+# Test CRC mismatch is reported.
+if {[build_executable crc_mismatch.exp crc_mismatch-2 crc_mismatch-2.c debug] != -1
+    && ![gdb_gnu_strip_debug [standard_output_file crc_mismatch-2]]} {
+
+    # Copy the correct debug file for crc_mismatch-2 to the debugdir
+    # which is going to be used by local debuginfod.
+    remote_exec build "cp [standard_output_file crc_mismatch-2.debug] ${debugdir}"
+    # Move the unmatching debug file for crc_mismatch-2 instead of its real one
+    # to trigger the "CRC mismatch" warning.
+    remote_exec build "mv ${debugfile} [standard_output_file crc_mismatch-2.debug]"
+
+    gdb_exit
+    gdb_start
+
+    set escapedobjdirsubdir [string_to_regexp [standard_output_file {}]]
+
+    gdb_test "file [standard_output_file crc_mismatch-2]" "warning: the debug information found in \"${escapedobjdirsubdir}/crc_mismatch-2\\.debug\" does not match \"${escapedobjdirsubdir}/crc_mismatch-2\" \\(CRC mismatch\\)\\..*\\(No debugging symbols found in .*\\).*" "CRC mismatch is reported"
+}
+
+# Create CACHE and DB directories ready for debuginfod to use.
+prepare_for_debuginfod cache db
+
+# Start debuginfod server, test the correct debuginfo was fetched
+# from the server so there're not warnings anymore.
+proc_with_prefix local_debuginfod { } {
+    global binfile db debugdir cache
+    set escapedobjdirsubdir [string_to_regexp [standard_output_file {}]]
+
+    set url [start_debuginfod $db $debugdir]
+    if { $url == "" } {
+	unresolved "failed to start debuginfod server"
+	return
+    }
+
+    # Point the client to the server.
+    setenv DEBUGINFOD_URLS $url
+
+    # GDB should now find the symbol and source files.
+    clean_restart
+
+    # Enable debuginfod and fetch the debuginfo.
+    gdb_test_no_output "set debuginfod enabled on"
+    gdb_test "file $binfile" ".*Reading symbols from.*debuginfo.*" \
+	"file [file tail $binfile] cmd on"
+
+    # CRC mismatch should not be reported now because the correct debuginfo
+    # should be fetched from debuginfod.
+    gdb_test "file [standard_output_file crc_mismatch-2]" \
+	[multi_line \
+	     "Reading symbols from ${escapedobjdirsubdir}/crc_mismatch-2\\.\\.\\." \
+	     "Downloading separate debug info for ${escapedobjdirsubdir}/crc_mismatch-2\\.\\.\\." \
+	     "Reading symbols from ${cache}/\[^\r\n\]+\\.\\.\\.(?:\r\nExpanding full symbols from \[^\r\n\]+)*"] \
+	 "debuginfod running, info downloaded, no CRC mismatch"
+
+    #
+
+}
+
+with_debuginfod_env $cache {
+    local_debuginfod
+}
+
+stop_debuginfod
+# Spare debug files may confuse testsuite runs in the future.
+remote_exec build "rm -f $debugfile"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 008f59b9f30..34c0d1a4e02 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -7153,9 +7153,19 @@  proc build_id_debug_filename_get { filename } {
     return ".build-id/${data}.debug"
 }
 
-# Create stripped files for DEST, replacing it.  If ARGS is passed, it is a
-# list of optional flags.  The only currently supported flag is no-main,
-# which removes the symbol entry for main from the separate debug file.
+# DEST should be a file compiled with debug information.  This proc
+# creates two new files DEST.debug which contains the debug
+# information extracted from DEST, and DEST.stripped, which is a copy
+# of DEST with the debug information removed.  A '.gnu_debuglink'
+# section will be added to DEST.stripped that points to DEST.debug.
+#
+# If ARGS is passed, it is a list of optional flags.  The currently
+# supported flags are:
+#
+#   - no-main : remove the symbol entry for main from the separate
+#               debug file DEST.debug,
+#   - no-debuglink : don't add the '.gnu_debuglink' section to
+#                    DEST.stripped.
 #
 # Function returns zero on success.  Function will return non-zero failure code
 # on some targets not supporting separate debug info (such as i386-msdos).
@@ -7200,7 +7210,7 @@  proc gdb_gnu_strip_debug { dest args } {
     # leaves the symtab in the original file only.  There's no way to get
     # objcopy or strip to remove the symbol table without also removing the
     # debugging sections, so this is as close as we can get.
-    if { [llength $args] == 1 && [lindex $args 0] == "no-main" } {
+    if {[lsearch -exact $args "no-main"] != -1} {
 	set result [catch "exec $objcopy_program -N main ${debug_file} ${debug_file}-tmp" output]
 	verbose "result is $result"
 	verbose "output is $output"
@@ -7211,15 +7221,17 @@  proc gdb_gnu_strip_debug { dest args } {
 	file rename "${debug_file}-tmp" "${debug_file}"
     }
 
-    # Link the two previous output files together, adding the .gnu_debuglink
-    # section to the stripped_file, containing a pointer to the debug_file,
-    # save the new file in dest.
-    # This will be the regular executable filename, in the usual location.
-    set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${dest}" output]
-    verbose "result is $result"
-    verbose "output is $output"
-    if {$result == 1} {
-      return 1
+    # Unless the "no-debuglink" flag is passed, then link the two
+    # previous output files together, adding the .gnu_debuglink
+    # section to the stripped_file, containing a pointer to the
+    # debug_file, save the new file in dest.
+    if {[lsearch -exact $args "no-debuglink"] == -1} {
+	set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${dest}" output]
+	verbose "result is $result"
+	verbose "output is $output"
+	if {$result == 1} {
+	    return 1
+	}
     }
 
     # Workaround PR binutils/10802: