[v5,gdb/testsuite] New test: gdb.base/check-errno.exp

Message ID 20240509211225.137828-2-kevinb@redhat.com
State New
Headers
Series [v5,gdb/testsuite] New test: gdb.base/check-errno.exp |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Kevin Buettner May 9, 2024, 9:10 p.m. UTC
  [This v5 version of this new test fixes failures when check-read1 is used.]

Printing the value of 'errno' from GDB is sometimes problematic.  The
situation has improved in recent years, though there are still
scenarios for which "print errno" doesn't work.

The test, gdb.base/check-errno.exp, introduced by this commit,
tests whether or not GDB can print errno using a binary compiled
in the following different ways:

- default: no switches aside from -g (and whatever else is added by the
  testing framework)
- macros: macro info is included in the debuginfo; this is enabled by
  using -g3 when using gcc or clang
- static: statically linked binary
- static-macros: statically linked binary w/ macro definitions included
  in debuginfo
- pthreads: libpthread linked binary
- pthreads-macros: libpthread linked binary w/ macro definitions included
  in debuginfo
- pthreads-static: Statically linked against libpthread
- pthreads-static-macros: Statically linked against libpthread w/ macro
  definitions

For each of these, the test also creates a corefile, then loads the
corefile and attempts to print errno again.

Additionally, the test checks that a "masking" errno declared as a
local variable will print correctly.

On Linux, if the machine is missing glibc debuginfo (or you have
debuginfod disabled), it's likely you'll see:

    (gdb) print errno
    'errno' has unknown type; cast it to its declared type

But if you add a cast, the value of errno is often available:

    (gdb) print (int) errno
    $1 = 42

The test detects this situation along with several others and does
'setup_xfail' for tests that will almost certainly fail.  It could be
argued that some of these ought to be KFAILs due to deficiencies in
GDB, but I'm not entirely certain which, if any, are fixable yet.

On Fedora 39, without glibc debuginfo, there are no failures, but
I do see the following XFAILS:

XFAIL: gdb.base/check-errno.exp: default: print errno
XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: macros: print errno
XFAIL: gdb.base/check-errno.exp: macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static: print errno
XFAIL: gdb.base/check-errno.exp: static: print (int) errno
XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static-macros: print errno
XFAIL: gdb.base/check-errno.exp: static-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads: print errno
XFAIL: gdb.base/check-errno.exp: pthreads: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-macros: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile

On Fedora 39, with glibc debug info, but without libc.a (for static
linking), there are 2 XFAILs, 2 UNSUPPORTED tests, and 4 UNTESTED
tests.

So, even when testing in less than ideal conditions, either due to lack
of glibc debuginfo or lack of a libc to link against to make a static
binary, there are no failures.

With glibc debuginfo installed, on Fedora 38, Fedora 39, Fedora 40,
Fedora rawhide (41), and Ubuntu 22.04.1 LTS, I see these XFAILs:

XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static: print errno
XFAIL: gdb.base/check-errno.exp: static: print (int) errno
XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static-macros: print errno
XFAIL: gdb.base/check-errno.exp: static-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile

On FreeBSD 13.1, the total number of XFAILs are fewer, and could be
even better still if it had debug info for glibc:

XFAIL: gdb.base/check-errno.exp: default: print errno
XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: macros: print errno
XFAIL: gdb.base/check-errno.exp: macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads: print errno
XFAIL: gdb.base/check-errno.exp: pthreads: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-macros: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile

Starting with glibc-2.34, most of the pthreads library has been
incorporated into libc, so finding thread-local variables using
libthread_db is possible for several scenarios in which it previously
wasn't.  But, prior to this, accessing errno for the default scenario
was a problem.  This is borne out by running this new test on Fedora
33, which uses glibc-2.33:

XFAIL: gdb.base/check-errno.exp: default: print errno
XFAIL: gdb.base/check-errno.exp: default: print (int) errno
XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static: print errno
XFAIL: gdb.base/check-errno.exp: static: print (int) errno
XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static-macros: print errno
XFAIL: gdb.base/check-errno.exp: static-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile

In the v3 version of this test, Tom de Vries tested on openSUSE Leap
15.5 and found a number of cases which showed a FAIL instead of an
XFAIL.  I've fixed those now.  On Leap 15.5, which uses glibc-2.31,
with glibc debug info, I now see:

XFAIL: gdb.base/check-errno.exp: default: print errno
XFAIL: gdb.base/check-errno.exp: default: print (int) errno
XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static: print errno
XFAIL: gdb.base/check-errno.exp: static: print (int) errno
XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile

On Leap 15.5, with glibc debuginfo missing, the results are a little
worse:

XFAIL: gdb.base/check-errno.exp: default: print errno
XFAIL: gdb.base/check-errno.exp: default: print (int) errno
XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: macros: print errno
XFAIL: gdb.base/check-errno.exp: macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static: print errno
XFAIL: gdb.base/check-errno.exp: static: print (int) errno
XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads: print errno
XFAIL: gdb.base/check-errno.exp: pthreads: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-macros: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile

So, bottom line, this test does not introduce any new failures on the
platforms on which I've tested, but the XFAILs are certainly unfortunate.
Some aren't fixable - e.g. when attempting to make a function call while
debugging a core file - but I think that some of them are.  I'm using
this new test case as a starting point for investigating problems with
printing errno.

Co-Authored-By: Jan Kratochvil
---
 gdb/testsuite/gdb.base/check-errno.c   |  37 ++++
 gdb/testsuite/gdb.base/check-errno.exp | 249 +++++++++++++++++++++++++
 2 files changed, 286 insertions(+)
 create mode 100644 gdb/testsuite/gdb.base/check-errno.c
 create mode 100644 gdb/testsuite/gdb.base/check-errno.exp
  

Comments

Tom de Vries May 10, 2024, 8:40 a.m. UTC | #1
On 5/9/24 23:10, Kevin Buettner wrote:
> [This v5 version of this new test fixes failures when check-read1 is used.]
> 
> Printing the value of 'errno' from GDB is sometimes problematic.  The
> situation has improved in recent years, though there are still
> scenarios for which "print errno" doesn't work.
> 
> The test, gdb.base/check-errno.exp, introduced by this commit,
> tests whether or not GDB can print errno using a binary compiled
> in the following different ways:
> 
> - default: no switches aside from -g (and whatever else is added by the
>    testing framework)
> - macros: macro info is included in the debuginfo; this is enabled by
>    using -g3 when using gcc or clang
> - static: statically linked binary
> - static-macros: statically linked binary w/ macro definitions included
>    in debuginfo
> - pthreads: libpthread linked binary
> - pthreads-macros: libpthread linked binary w/ macro definitions included
>    in debuginfo
> - pthreads-static: Statically linked against libpthread
> - pthreads-static-macros: Statically linked against libpthread w/ macro
>    definitions
> 
> For each of these, the test also creates a corefile, then loads the
> corefile and attempts to print errno again.
> 
> Additionally, the test checks that a "masking" errno declared as a
> local variable will print correctly.
> 
> On Linux, if the machine is missing glibc debuginfo (or you have
> debuginfod disabled), it's likely you'll see:
> 
>      (gdb) print errno
>      'errno' has unknown type; cast it to its declared type
> 
> But if you add a cast, the value of errno is often available:
> 
>      (gdb) print (int) errno
>      $1 = 42
> 
> The test detects this situation along with several others and does
> 'setup_xfail' for tests that will almost certainly fail.  It could be
> argued that some of these ought to be KFAILs due to deficiencies in
> GDB, but I'm not entirely certain which, if any, are fixable yet.
> 
> On Fedora 39, without glibc debuginfo, there are no failures, but
> I do see the following XFAILS:
> 
> XFAIL: gdb.base/check-errno.exp: default: print errno
> XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: macros: print errno
> XFAIL: gdb.base/check-errno.exp: macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static: print errno
> XFAIL: gdb.base/check-errno.exp: static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static-macros: print errno
> XFAIL: gdb.base/check-errno.exp: static-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile
> 
> On Fedora 39, with glibc debug info, but without libc.a (for static
> linking), there are 2 XFAILs, 2 UNSUPPORTED tests, and 4 UNTESTED
> tests.
> 
> So, even when testing in less than ideal conditions, either due to lack
> of glibc debuginfo or lack of a libc to link against to make a static
> binary, there are no failures.
> 
> With glibc debuginfo installed, on Fedora 38, Fedora 39, Fedora 40,
> Fedora rawhide (41), and Ubuntu 22.04.1 LTS, I see these XFAILs:
> 
> XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static: print errno
> XFAIL: gdb.base/check-errno.exp: static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static-macros: print errno
> XFAIL: gdb.base/check-errno.exp: static-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile
> 
> On FreeBSD 13.1, the total number of XFAILs are fewer, and could be
> even better still if it had debug info for glibc:
> 
> XFAIL: gdb.base/check-errno.exp: default: print errno
> XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: macros: print errno
> XFAIL: gdb.base/check-errno.exp: macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile
> 
> Starting with glibc-2.34, most of the pthreads library has been
> incorporated into libc, so finding thread-local variables using
> libthread_db is possible for several scenarios in which it previously
> wasn't.  But, prior to this, accessing errno for the default scenario
> was a problem.  This is borne out by running this new test on Fedora
> 33, which uses glibc-2.33:
> 
> XFAIL: gdb.base/check-errno.exp: default: print errno
> XFAIL: gdb.base/check-errno.exp: default: print (int) errno
> XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static: print errno
> XFAIL: gdb.base/check-errno.exp: static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static-macros: print errno
> XFAIL: gdb.base/check-errno.exp: static-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile
> 
> In the v3 version of this test, Tom de Vries tested on openSUSE Leap
> 15.5 and found a number of cases which showed a FAIL instead of an
> XFAIL.  I've fixed those now.  On Leap 15.5, which uses glibc-2.31,
> with glibc debug info, I now see:
> 
> XFAIL: gdb.base/check-errno.exp: default: print errno
> XFAIL: gdb.base/check-errno.exp: default: print (int) errno
> XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static: print errno
> XFAIL: gdb.base/check-errno.exp: static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile
> 
> On Leap 15.5, with glibc debuginfo missing, the results are a little
> worse:
> 
> XFAIL: gdb.base/check-errno.exp: default: print errno
> XFAIL: gdb.base/check-errno.exp: default: print (int) errno
> XFAIL: gdb.base/check-errno.exp: default: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: macros: print errno
> XFAIL: gdb.base/check-errno.exp: macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static: print errno
> XFAIL: gdb.base/check-errno.exp: static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: static-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: print (int) errno
> XFAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from corefile
> XFAIL: gdb.base/check-errno.exp: pthreads-static-macros: check errno value from corefile
> 
> So, bottom line, this test does not introduce any new failures on the
> platforms on which I've tested, but the XFAILs are certainly unfortunate.
> Some aren't fixable - e.g. when attempting to make a function call while
> debugging a core file - but I think that some of them are.  I'm using
> this new test case as a starting point for investigating problems with
> printing errno.
> 

Hi Kevin,

I applied the patch and tested without and with debug info on leap 15.5, 
and only got passes and xfails, so that looks good.  Likewise on tumbleweed.

One nit:
...
$ git show --check --pretty=%s
New test: gdb.base/check-errno.exp

gdb/testsuite/gdb.base/check-errno.exp:38: trailing whitespace.
+# there were always scenarios in which printing errno was problematic.
...

I tested this using make-check-all.sh, and got:
...
(gdb) PASS: gdb.base/check-errno.exp: default: continue to breakpoint: 
main-breakpoint
ptype errno^M
Cannot find thread-local storage for Thread 14233.14233, shared library 
/lib64/libc.so.6:^M
Remote target failed to process qGetTLSAddr request^M
(gdb) FAIL: gdb.base/check-errno.exp: default: ptype errno
...
with f.i. target board native-extended-gdbserver/native-gdbserver, I'm 
hoping that's easy to fix in the test-case.

I also ran into trouble with target board fission, I've filed a PR for 
this ( https://sourceware.org/bugzilla/show_bug.cgi?id=31723 ), I don't 
think we want to change anything about the test-case for this.

Thanks,
- Tom

> Co-Authored-By: Jan Kratochvil
> ---
>   gdb/testsuite/gdb.base/check-errno.c   |  37 ++++
>   gdb/testsuite/gdb.base/check-errno.exp | 249 +++++++++++++++++++++++++
>   2 files changed, 286 insertions(+)
>   create mode 100644 gdb/testsuite/gdb.base/check-errno.c
>   create mode 100644 gdb/testsuite/gdb.base/check-errno.exp
> 
> diff --git a/gdb/testsuite/gdb.base/check-errno.c b/gdb/testsuite/gdb.base/check-errno.c
> new file mode 100644
> index 00000000000..c6835a866e8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/check-errno.c
> @@ -0,0 +1,37 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2024 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/>.  */
> +
> +#include <errno.h>
> +
> +static void shadow_errno ();
> +
> +int main ()
> +{
> +  errno = 42;
> +
> +  shadow_errno ();	/* main-breakpoint */
> +  return 0;
> +}
> +
> +#undef errno
> +static void
> +shadow_errno ()
> +{
> +  int errno = 36;
> +
> +  return;		/* shadow_errno-breakpoint */
> +}
> diff --git a/gdb/testsuite/gdb.base/check-errno.exp b/gdb/testsuite/gdb.base/check-errno.exp
> new file mode 100644
> index 00000000000..4f87681a34b
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/check-errno.exp
> @@ -0,0 +1,249 @@
> +# Copyright 2024 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.
> +
> +# Check that errno can be accessed by GDB under a variety of
> +# circumstances.
> +#
> +# The challenge with GDB accessing errno is that, on modern systems,
> +# errno is a variable in thread-local storage.  So, if GDB's access to
> +# thread local storage is broken or unavailable, some of these tests
> +# could fail.  On Linux, this is/was known to happen on systems with
> +# older versions of glibc as well as when debugging statically linked
> +# binaries.
> +#
> +# Another possibility is that the environment lacks sufficient
> +# type information to print errno.  This can happen for the errno
> +# variable itself or when the debuginfo contains a macro for errno
> +# which refers to a function lacking type information.
> +#
> +# When debugging core files, access to errno might not be possible
> +# both due to the situations described earlier along with the fact
> +# that inferior function calls are not possible (for the cases in
> +# which errno is a macro which calls a function returning errno's
> +# address).
> +#
> +# It's also possible for a program to declare errno in an inner scope
> +# causing the thread-local errno to be shadowed.  GDB should still
> +# correctly print the masking errno for this case.
> +#
> +# At the time that this test was written, on GNU/Linux and on FreeBSD,
> +# there were always scenarios in which printing errno was problematic.
> +# This test attempts to identify the problem cases and set up xfails
> +# for them.  So, hopefully, there should be no actual failures.  But
> +# the "expected" failures encountered by by running this test do
> +# genuinely illustrate problems that a user might encounter while
> +# attempting to print errno.
> +
> +standard_testfile
> +
> +proc do_tests {{do_xfail_cast 0} {do_xfail 0} {do_xfail_core_test 0}} {
> +    clean_restart $::binfile
> +    runto_main
> +
> +    gdb_breakpoint [gdb_get_line_number "main-breakpoint"]
> +    gdb_continue_to_breakpoint "main-breakpoint"
> +
> +    # Whether or not "print errno" will work often depends on the
> +    # debuginfo available.  We can make some inferences about whether
> +    # some of the tests should have xfail set-up by looking at the
> +    # output of "ptype errno".  This test is set up to always pass
> +    # even for less than ideal outputs, because the point is to set up
> +    # the xfail(s).
> +    set test "ptype errno"
> +    gdb_test_multiple $test $test {
> +	-re "type = int\r\n$::gdb_prompt $" {
> +	    pass $test
> +	}
> +	-re "type = .*no debug info.*$::gdb_prompt $" {
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	}
> +	-re ".*Cannot find thread-local variables on this target.*$::gdb_prompt $" {
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	    set do_xfail_cast 1
> +	}
> +	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
> +
> +	    # On Linux, using -g3, which causes macro information to
> +	    # be included in the debuginfo, errno might be defined as
> +	    # follows:
> +	    #
> +	    #   #define errno (*__errno_location ())
> +	    #
> +	    # So, when we do "ptype errno", due to macro expansion,
> +	    # this ends up being "ptype (*__errno_location ())".  So
> +	    # the call to __errno_location (or something similar on
> +	    # other OSes) is the call mentioned in the error message.
> +
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	    set do_xfail_cast 1
> +	}
> +    }
> +
> +    # If errno is defined as a macro that contains an obvious function
> +    # call, it won't work when debugging a core file.
> +    set test "info macro errno"
> +    gdb_test_multiple $test $test {
> +	-re "Defined at.*\[\r\n\]#define.*\\\(\\\).*$::gdb_prompt $" {
> +	    set do_xfail_core_test 1
> +	    pass $test
> +	}
> +	-re "Defined at.*\[\r\n\]#define.*$::gdb_prompt $" {
> +	    pass $test
> +	}
> +	-re "The symbol .errno. has no definition.*$::gdb_prompt $" {
> +	    pass $test
> +	}
> +    }
> +
> +    # Sometimes, "ptype errno" will ferret out that thread local
> +    # variables aren't accessible, but sometimes it won't.  Dig deeper
> +    # by trying to access memory using the "x/d" command.  Again, the
> +    # point here is to set up an xfail for the later tests, so we pass
> +    # this test for other known outputs.
> +    set test "x/d &errno"
> +    gdb_test_multiple $test $test {
> +	-re ".*Cannot find thread-local variables on this target.*$::gdb_prompt $" {
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	    set do_xfail_cast 1
> +	}
> +	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	    set do_xfail_cast 1
> +	    pass $test
> +	}
> +	-re "$::hex.*?:\[\t \]$::decimal\[\r\n\]+$::gdb_prompt $" {
> +	    pass $test
> +	}
> +    }
> +
> +    if $do_xfail {
> +	setup_xfail *-*-*
> +    }
> +    gdb_test "print errno" ".* = 42"
> +
> +    if $do_xfail_cast {
> +	setup_xfail *-*-*
> +    }
> +    gdb_test "print (int) errno" ".* = 42"
> +
> +    set corefile ${::binfile}.core
> +    set core_supported [gdb_gcore_cmd $corefile "save corefile"]
> +    # Normally, we'd check core_supported here and return if it's
> +    # not, but we'll defer that until after the shadow test.
> +
> +    gdb_breakpoint [gdb_get_line_number "shadow_errno-breakpoint"]
> +    gdb_continue_to_breakpoint "shadow_errno-breakpoint"
> +
> +    # This test demonstrates why a simple hack to GDB for printing
> +    # errno is a bad idea.  (The hack was to intercept the string
> +    # "errno" in process_print_command_args() and replace it with
> +    # "*(*(int *(*)(void)) __errno_location) ()".)
> +    gdb_test "print errno" ".* = 36" "print masking errno"
> +
> +    # Finish test early if no core file was made.
> +    if !$core_supported {
> +	return
> +    }
> +
> +    clean_restart $::binfile
> +
> +    set core_loaded [gdb_core_cmd $corefile "load corefile"]
> +    if { $core_loaded == -1 } {
> +	return
> +    }
> +    if $do_xfail_core_test {
> +	setup_xfail *-*-*
> +    }
> +    gdb_test "print errno" ".* = 42" "check errno value from corefile"
> +}
> +
> +set binprefix $binfile
> +
> +with_test_prefix "default" {
> +    set binfile $binprefix-default
> +    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "macros" {
> +    set binfile $binprefix-macros
> +    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "static" {
> +    set binfile $binprefix-static
> +    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "static-macros" {
> +    set binfile $binprefix-static-macros
> +    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros "additional_flags=-static"}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "pthreads" {
> +    set binfile $binprefix-pthreads
> +    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "pthreads-macros" {
> +    set binfile $binprefix-pthreads-macros
> +    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "pthreads-static" {
> +    set binfile $binprefix-pthreads-static
> +    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
> +
> +with_test_prefix "pthreads-static-macros" {
> +    set binfile $binprefix-pthreads-static-macros
> +    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros "additional_flags=-static"}] != "" } {
> +	untested "failed to compile"
> +    } else {
> +	do_tests
> +    }
> +}
  
Pedro Alves May 10, 2024, 1:08 p.m. UTC | #2
Hi Kevin,

Just a few nits I noticed while reading this.

On 2024-05-09 22:10, Kevin Buettner wrote:

> diff --git a/gdb/testsuite/gdb.base/check-errno.exp b/gdb/testsuite/gdb.base/check-errno.exp
> new file mode 100644
> index 00000000000..4f87681a34b
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/check-errno.exp
> @@ -0,0 +1,249 @@
> +# Copyright 2024 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

Seems like you got a tab instead of a double space after "PURPOSE.".

> +# GNU General Public License for more details.
> +
> +# Check that errno can be accessed by GDB under a variety of
> +# circumstances.
> +#
> +# The challenge with GDB accessing errno is that, on modern systems,
> +# errno is a variable in thread-local storage.  So, if GDB's access to
> +# thread local storage is broken or unavailable, some of these tests
> +# could fail.  On Linux, this is/was known to happen on systems with
> +# older versions of glibc as well as when debugging statically linked
> +# binaries.
> +#
> +# Another possibility is that the environment lacks sufficient
> +# type information to print errno.  This can happen for the errno
> +# variable itself or when the debuginfo contains a macro for errno
> +# which refers to a function lacking type information.
> +#
> +# When debugging core files, access to errno might not be possible
> +# both due to the situations described earlier along with the fact
> +# that inferior function calls are not possible (for the cases in
> +# which errno is a macro which calls a function returning errno's
> +# address).
> +#
> +# It's also possible for a program to declare errno in an inner scope
> +# causing the thread-local errno to be shadowed.  GDB should still
> +# correctly print the masking errno for this case.
> +#
> +# At the time that this test was written, on GNU/Linux and on FreeBSD,
> +# there were always scenarios in which printing errno was problematic. 
> +# This test attempts to identify the problem cases and set up xfails
> +# for them.  So, hopefully, there should be no actual failures.  But
> +# the "expected" failures encountered by by running this test do

"by by" -> "by"

> +# genuinely illustrate problems that a user might encounter while
> +# attempting to print errno.
> +
> +standard_testfile
> +
> +proc do_tests {{do_xfail_cast 0} {do_xfail 0} {do_xfail_core_test 0}} {
> +    clean_restart $::binfile
> +    runto_main

Return early if runto_main fails.

> +
> +    gdb_breakpoint [gdb_get_line_number "main-breakpoint"]
> +    gdb_continue_to_breakpoint "main-breakpoint"
> +
> +    # Whether or not "print errno" will work often depends on the
> +    # debuginfo available.  We can make some inferences about whether
> +    # some of the tests should have xfail set-up by looking at the
> +    # output of "ptype errno".  This test is set up to always pass
> +    # even for less than ideal outputs, because the point is to set up
> +    # the xfail(s).
> +    set test "ptype errno"
> +    gdb_test_multiple $test $test {
> +	-re "type = int\r\n$::gdb_prompt $" {
> +	    pass $test
> +	}

You don't need this $test variable throughout.  You can write:

    gdb_test_multiple "ptype errno" "" {
	-re "type = int\r\n$::gdb_prompt $" {
	    pass $gdb_test_name
	}

etc.

Actually, you can also use -wrap to simplify further:

    gdb_test_multiple "ptype errno" "" {
	-re -wrap "type = int" {
	    pass $gdb_test_name
	}


> +	-re "type = .*no debug info.*$::gdb_prompt $" {
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	}
> +	-re ".*Cannot find thread-local variables on this target.*$::gdb_prompt $" {
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	    set do_xfail_cast 1
> +	}
> +	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
> +
> +	    # On Linux, using -g3, which causes macro information to

This is glibc-specific, not Linux-specific.

> +	    # be included in the debuginfo, errno might be defined as
> +	    # follows:
> +	    #
> +	    #   #define errno (*__errno_location ())
> +	    #
> +	    # So, when we do "ptype errno", due to macro expansion,
> +	    # this ends up being "ptype (*__errno_location ())".  So
> +	    # the call to __errno_location (or something similar on
> +	    # other OSes) is the call mentioned in the error message.
> +
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +	    set do_xfail_cast 1


Otherwise LGTM.  I was going to ask about gdbserver testing, but Tom already pointed
out a problem there.
  
Pedro Alves May 10, 2024, 1:12 p.m. UTC | #3
Also, why call this "check-errno.exp" instead of just "errno.exp" ?  Every testcase is checking something,
so "check" is redundant, IMHO.  Calling is errno.exp would make it slightly more discoverable, I think.
  
Kevin Buettner May 11, 2024, 7:30 p.m. UTC | #4
On Fri, 10 May 2024 10:40:42 +0200
Tom de Vries <tdevries@suse.de> wrote:

> Hi Kevin,
> 
> I applied the patch and tested without and with debug info on leap 15.5, 
> and only got passes and xfails, so that looks good.  Likewise on tumbleweed.
> 
> One nit:
> ...
> $ git show --check --pretty=%s
> New test: gdb.base/check-errno.exp
> 
> gdb/testsuite/gdb.base/check-errno.exp:38: trailing whitespace.
> +# there were always scenarios in which printing errno was problematic.
> ...

Fixed (in my local tree).

> I tested this using make-check-all.sh, and got:
> ...
> (gdb) PASS: gdb.base/check-errno.exp: default: continue to breakpoint: 
> main-breakpoint
> ptype errno^M
> Cannot find thread-local storage for Thread 14233.14233, shared library 
> /lib64/libc.so.6:^M
> Remote target failed to process qGetTLSAddr request^M
> (gdb) FAIL: gdb.base/check-errno.exp: default: ptype errno
> ...
> with f.i. target board native-extended-gdbserver/native-gdbserver, I'm 
> hoping that's easy to fix in the test-case.

Fixed - and yes, it was easy.

Thanks for the review.

Kevin
  
Kevin Buettner May 11, 2024, 9:57 p.m. UTC | #5
Hi Pedro,

Thanks for your review!

On Fri, 10 May 2024 14:08:24 +0100
Pedro Alves <pedro@palves.net> wrote:

> Actually, you can also use -wrap to simplify further:
> 
>     gdb_test_multiple "ptype errno" "" {
> 	-re -wrap "type = int" {
> 	    pass $gdb_test_name
> 	}

I've made all of your suggested fixes, except for the one above.

While I can use -wrap for the above RE, if I also use it for the next
RE, I see failures when using check-read1 when testing on a machine
without glibc debuginfo.  For the moment, I've decided against using
-wrap, but if I get time, I'll revisit it and try to understand why
it's failing.

This isn't working for me:

    gdb_test_multiple "ptype errno" "check errno type availability" {
	-re -wrap "type = int" {
	    pass $gdb_test_name
	}
	-re -wrap "type = .*no debug info" {
	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	}
	-re -wrap ".*Cannot find thread-local variables on this target" {
	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	    set do_xfail_cast 1
	}
	-re -wrap ".*Cannot find thread-local storage" {
	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	    set do_xfail_cast 1
	}
	-re -wrap ".*has unknown return type; cast the call to its declared return type" {

	    # On systems which glibc as the C library, using -g3,
	    # which causes macro information to be included in the
	    # debuginfo, errno might be defined as follows:
	    #
	    #   #define errno (*__errno_location ())
	    #
	    # So, when we do "ptype errno", due to macro expansion,
	    # this ends up being "ptype (*__errno_location ())".  So
	    # the call to __errno_location (or something similar on
	    # other OSes) is the call mentioned in the error message.

	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	    set do_xfail_cast 1
	}
    }

But this is:

    gdb_test_multiple "ptype errno" "check errno type availability" {
	-re "type = int\r\n$::gdb_prompt $" {
	    pass $gdb_test_name
	}
	-re "type = .*no debug info.*$::gdb_prompt $" {
	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	}
	-re ".*Cannot find thread-local variables on this target.*$::gdb_prompt $" {
	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	    set do_xfail_cast 1
	}
	-re ".*Cannot find thread-local storage.*$::gdb_prompt $" {
	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	    set do_xfail_cast 1
	}
	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {

	    # On systems which glibc as the C library, using -g3,
	    # which causes macro information to be included in the
	    # debuginfo, errno might be defined as follows:
	    #
	    #   #define errno (*__errno_location ())
	    #
	    # So, when we do "ptype errno", due to macro expansion,
	    # this ends up being "ptype (*__errno_location ())".  So
	    # the call to __errno_location (or something similar on
	    # other OSes) is the call mentioned in the error message.

	    pass $gdb_test_name
	    set do_xfail 1
	    set do_xfail_core_test 1
	    set do_xfail_cast 1
	}
    }

Kevin
  
Pedro Alves May 13, 2024, 2:34 p.m. UTC | #6
Whoops, I notice now that I sent it off list by mistake.  Replying again to add it to the list.
While at it, since I'm back on the PC, commenting in line too -- see further below.

On 2024-05-12 11:24, Pedro Alves wrote:
> Sent from phone sorry for top reply.
> 
> It's because you are missing a .* at the end of the patterns.  Same as you would need if you used gdb_test. 
> 
> Also .* at the beginning of the pattern is unnecessary, it's already implicit.
> 
> 
> A 11 de maio de 2024 22:57:41 WEST, Kevin Buettner <kevinb@redhat.com> escreveu:
> 
>     Hi Pedro,
> 
>     Thanks for your review!
> 
>     On Fri, 10 May 2024 14:08:24 +0100
>     Pedro Alves <pedro@palves.net> wrote:
> 
>         Actually, you can also use -wrap to simplify further:
> 
>         gdb_test_multiple "ptype errno" "" {
>         -re -wrap "type = int" {
>         pass $gdb_test_name
>         }
> 
> 
>     I've made all of your suggested fixes, except for the one above.
> 
>     While I can use -wrap for the above RE, if I also use it for the next
>     RE, I see failures when using check-read1 when testing on a machine
>     without glibc debuginfo. For the moment, I've decided against using
>     -wrap, but if I get time, I'll revisit it and try to understand why
>     it's failing.
> 
>     This isn't working for me:
> 
>     gdb_test_multiple "ptype errno" "check errno type availability" {
>     -re -wrap "type = int" {
>     pass $gdb_test_name
>     }
>     -re -wrap "type = .*no debug info" {

This should be:

      -re -wrap "type = .*no debug info.*" {

Note you had the .* in the original version too:

      -re "type = .*no debug info.*$::gdb_prompt $" {

It's the same transformation you would do if you used gdb_test instead.

The .* wasn't needed for the "type = int" case because you didn't have it
in the original version either:

      -re "type = int\r\n$::gdb_prompt $" {

This one:

      -re -wrap ".*Cannot find thread-local variables on this target" {

should be

      -re -wrap ".*Cannot find thread-local variables on this target.*" {

for the same reason -- the original had a .* :

      -re ".*Cannot find thread-local storage.*$::gdb_prompt $" {

and really, it should be:

     -re -wrap "Cannot find thread-local variables on this target.*" {

... as the leading ".*" doesn't add anything.  IMHO, it can misleads
newcomers into thinking that not adding a ".*" at the start gives you
a tighter match.

Pedro Alves
  

Patch

diff --git a/gdb/testsuite/gdb.base/check-errno.c b/gdb/testsuite/gdb.base/check-errno.c
new file mode 100644
index 00000000000..c6835a866e8
--- /dev/null
+++ b/gdb/testsuite/gdb.base/check-errno.c
@@ -0,0 +1,37 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 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/>.  */
+
+#include <errno.h>
+
+static void shadow_errno ();
+
+int main ()
+{
+  errno = 42;
+
+  shadow_errno ();	/* main-breakpoint */
+  return 0;
+}
+
+#undef errno
+static void
+shadow_errno ()
+{
+  int errno = 36;
+
+  return;		/* shadow_errno-breakpoint */
+}
diff --git a/gdb/testsuite/gdb.base/check-errno.exp b/gdb/testsuite/gdb.base/check-errno.exp
new file mode 100644
index 00000000000..4f87681a34b
--- /dev/null
+++ b/gdb/testsuite/gdb.base/check-errno.exp
@@ -0,0 +1,249 @@ 
+# Copyright 2024 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.
+
+# Check that errno can be accessed by GDB under a variety of
+# circumstances.
+#
+# The challenge with GDB accessing errno is that, on modern systems,
+# errno is a variable in thread-local storage.  So, if GDB's access to
+# thread local storage is broken or unavailable, some of these tests
+# could fail.  On Linux, this is/was known to happen on systems with
+# older versions of glibc as well as when debugging statically linked
+# binaries.
+#
+# Another possibility is that the environment lacks sufficient
+# type information to print errno.  This can happen for the errno
+# variable itself or when the debuginfo contains a macro for errno
+# which refers to a function lacking type information.
+#
+# When debugging core files, access to errno might not be possible
+# both due to the situations described earlier along with the fact
+# that inferior function calls are not possible (for the cases in
+# which errno is a macro which calls a function returning errno's
+# address).
+#
+# It's also possible for a program to declare errno in an inner scope
+# causing the thread-local errno to be shadowed.  GDB should still
+# correctly print the masking errno for this case.
+#
+# At the time that this test was written, on GNU/Linux and on FreeBSD,
+# there were always scenarios in which printing errno was problematic. 
+# This test attempts to identify the problem cases and set up xfails
+# for them.  So, hopefully, there should be no actual failures.  But
+# the "expected" failures encountered by by running this test do
+# genuinely illustrate problems that a user might encounter while
+# attempting to print errno.
+
+standard_testfile
+
+proc do_tests {{do_xfail_cast 0} {do_xfail 0} {do_xfail_core_test 0}} {
+    clean_restart $::binfile
+    runto_main
+
+    gdb_breakpoint [gdb_get_line_number "main-breakpoint"]
+    gdb_continue_to_breakpoint "main-breakpoint"
+
+    # Whether or not "print errno" will work often depends on the
+    # debuginfo available.  We can make some inferences about whether
+    # some of the tests should have xfail set-up by looking at the
+    # output of "ptype errno".  This test is set up to always pass
+    # even for less than ideal outputs, because the point is to set up
+    # the xfail(s).
+    set test "ptype errno"
+    gdb_test_multiple $test $test {
+	-re "type = int\r\n$::gdb_prompt $" {
+	    pass $test
+	}
+	-re "type = .*no debug info.*$::gdb_prompt $" {
+	    pass $test
+	    set do_xfail 1
+	    set do_xfail_core_test 1
+	}
+	-re ".*Cannot find thread-local variables on this target.*$::gdb_prompt $" {
+	    pass $test
+	    set do_xfail 1
+	    set do_xfail_core_test 1
+	    set do_xfail_cast 1
+	}
+	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
+
+	    # On Linux, using -g3, which causes macro information to
+	    # be included in the debuginfo, errno might be defined as
+	    # follows:
+	    #
+	    #   #define errno (*__errno_location ())
+	    #
+	    # So, when we do "ptype errno", due to macro expansion,
+	    # this ends up being "ptype (*__errno_location ())".  So
+	    # the call to __errno_location (or something similar on
+	    # other OSes) is the call mentioned in the error message.
+
+	    pass $test
+	    set do_xfail 1
+	    set do_xfail_core_test 1
+	    set do_xfail_cast 1
+	}
+    }
+
+    # If errno is defined as a macro that contains an obvious function
+    # call, it won't work when debugging a core file.
+    set test "info macro errno"
+    gdb_test_multiple $test $test {
+	-re "Defined at.*\[\r\n\]#define.*\\\(\\\).*$::gdb_prompt $" {
+	    set do_xfail_core_test 1
+	    pass $test
+	}
+	-re "Defined at.*\[\r\n\]#define.*$::gdb_prompt $" {
+	    pass $test
+	}
+	-re "The symbol .errno. has no definition.*$::gdb_prompt $" {
+	    pass $test
+	}
+    }
+
+    # Sometimes, "ptype errno" will ferret out that thread local
+    # variables aren't accessible, but sometimes it won't.  Dig deeper
+    # by trying to access memory using the "x/d" command.  Again, the
+    # point here is to set up an xfail for the later tests, so we pass
+    # this test for other known outputs.
+    set test "x/d &errno"
+    gdb_test_multiple $test $test {
+	-re ".*Cannot find thread-local variables on this target.*$::gdb_prompt $" {
+	    pass $test
+	    set do_xfail 1
+	    set do_xfail_core_test 1
+	    set do_xfail_cast 1
+	}
+	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
+	    set do_xfail 1
+	    set do_xfail_core_test 1
+	    set do_xfail_cast 1
+	    pass $test
+	}
+	-re "$::hex.*?:\[\t \]$::decimal\[\r\n\]+$::gdb_prompt $" {
+	    pass $test
+	}
+    }
+
+    if $do_xfail {
+	setup_xfail *-*-*
+    }
+    gdb_test "print errno" ".* = 42"
+
+    if $do_xfail_cast {
+	setup_xfail *-*-*
+    }
+    gdb_test "print (int) errno" ".* = 42"
+
+    set corefile ${::binfile}.core
+    set core_supported [gdb_gcore_cmd $corefile "save corefile"]
+    # Normally, we'd check core_supported here and return if it's
+    # not, but we'll defer that until after the shadow test.
+
+    gdb_breakpoint [gdb_get_line_number "shadow_errno-breakpoint"]
+    gdb_continue_to_breakpoint "shadow_errno-breakpoint"
+
+    # This test demonstrates why a simple hack to GDB for printing
+    # errno is a bad idea.  (The hack was to intercept the string
+    # "errno" in process_print_command_args() and replace it with
+    # "*(*(int *(*)(void)) __errno_location) ()".)
+    gdb_test "print errno" ".* = 36" "print masking errno"
+
+    # Finish test early if no core file was made.
+    if !$core_supported {
+	return
+    }
+
+    clean_restart $::binfile
+
+    set core_loaded [gdb_core_cmd $corefile "load corefile"]
+    if { $core_loaded == -1 } {
+	return
+    }
+    if $do_xfail_core_test {
+	setup_xfail *-*-*
+    }
+    gdb_test "print errno" ".* = 42" "check errno value from corefile"
+}
+
+set binprefix $binfile
+
+with_test_prefix "default" {
+    set binfile $binprefix-default
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "macros" {
+    set binfile $binprefix-macros
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "static" {
+    set binfile $binprefix-static
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "static-macros" {
+    set binfile $binprefix-static-macros
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros "additional_flags=-static"}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "pthreads" {
+    set binfile $binprefix-pthreads
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "pthreads-macros" {
+    set binfile $binprefix-pthreads-macros
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "pthreads-static" {
+    set binfile $binprefix-pthreads-static
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}
+
+with_test_prefix "pthreads-static-macros" {
+    set binfile $binprefix-pthreads-static-macros
+    if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug macros "additional_flags=-static"}] != "" } {
+	untested "failed to compile"
+    } else {
+	do_tests
+    }
+}