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

Message ID 20240502013934.389445-1-kevinb@redhat.com
State New
Headers
Series [v3,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 fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed

Commit Message

Kevin Buettner May 2, 2024, 1:38 a.m. UTC
  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 with a binary compiled
in the following different ways:

- default: no switches aside from -g (and whatever else is added by the
  testing framework)
- macros: macros 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 are likely to fail.  Also, on Linux,
doing 'print (int) errno' for a statically linked binary (currently)
doesn't work.  I've XFAILed these cases explicitly.

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

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
XPASS: 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
XPASS: 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

(It's likely that this also happens on other versions of Fedora, but I
haven't checked this.)

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 and XPASSes

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
XPASS: 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
XPASS: 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, I see the same XFAILs, but the XPASSes are simply PASSes.

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 | 237 +++++++++++++++++++++++++
 2 files changed, 274 insertions(+)
 create mode 100644 gdb/testsuite/gdb.base/check-errno.c
 create mode 100644 gdb/testsuite/gdb.base/check-errno.exp
  

Comments

Kevin Buettner May 2, 2024, 1:45 a.m. UTC | #1
I had intended for the following text to be included just before the
main body of the commit log:

[ This v3 patch fixes the binfile names for the pthreads-static and
  pthreads-static-macros scenarios.  Aside from documenting the
  potential for XPASSes in check-errno.exp, I haven't figured out what
  to do about the XPASSes flagged by Linaro CI.  ]

Kevin

On Wed,  1 May 2024 18:38:03 -0700
Kevin Buettner <kevinb@redhat.com> wrote:

> 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 with a binary compiled
> in the following different ways:
> 
> - default: no switches aside from -g (and whatever else is added by the
>   testing framework)
> - macros: macros 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 are likely to fail.  Also, on Linux,
> doing 'print (int) errno' for a statically linked binary (currently)
> doesn't work.  I've XFAILed these cases explicitly.
> 
> On Fedora 39, without glibc debuginfo, there are no failures, but
> I do see the following XFAILS and XPASSES:
> 
> 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
> XPASS: 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
> XPASS: 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
> 
> (It's likely that this also happens on other versions of Fedora, but I
> haven't checked this.)
> 
> 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 and XPASSes
> 
> 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
> XPASS: 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
> XPASS: 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, I see the same XFAILs, but the XPASSes are simply PASSes.
> 
> 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 | 237 +++++++++++++++++++++++++
>  2 files changed, 274 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..af7d204f56e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/check-errno.exp
> @@ -0,0 +1,237 @@
> +# 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, some of these tests could fail.
> +#
> +# Another possibility (which is tested by the "macro" tests below) is
> +# that errno is a macro and the debuginfo includes a macro expression
> +# for errno.  In the past, this often helped with being able to print
> +# errno, but at the time that this test was written, it seems to not
> +# always work correctly.
> +#
> +# When the binary is compiled statically, GDB's access to thread local
> +# storage may be compromised, perhaps leading to failures with those
> +# tests.
> +#
> +# To complicate things even more, access to errno might not work when
> +# examining a core file.  E.g. if it's necessary to call a function,
> +# perhaps named '__errno_location', this won't be possible without
> +# a running inferior.
> +#
> +# 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.
> +
> +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".  We pass these ptype tests, even for
> +    # less than ideal outputs, because the point is to set up the
> +    # xfail.  A failure can still occur for output which doesn't match
> +    # any of those listed.
> +    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
> +	}
> +	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
> +	    pass $test
> +	    set do_xfail 1
> +	    set do_xfail_core_test 1
> +
> +	    # 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.
> +	    #
> +	    # For the test "print (int) errno", we're casting the
> +	    # result of the expression, which includes both the call
> +	    # along with a dereferencing operation.
> +	    #
> +	    # This will sometimes produce the right answer, but it's
> +	    # also just as likely to fail.  E.g.  on x86_64, if the
> +	    # address being returned as a 32-bit int is the same as
> +	    # that which would have been returned as a 64-bit pointer,
> +	    # then the test might pass.  Otherwise, it will almost
> +	    # certainly fail, which is why we XFAIL it here.  But do
> +	    # expect to see the occasional XPASS for this case.
> +	    set do_xfail_cast 1
> +	}
> +    }
> +
> +    # If errno is defined as a macro that contains an obvious function
> +    # call, it's likely that 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
> +	}
> +    }
> +
> +    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 {
> +	set xfail_cast_test 0
> +	if {[istarget *-linux*]} {
> +	    set xfail_cast_test 1
> +	}
> +	do_tests $xfail_cast_test
> +    }
> +}
> +
> +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 {
> +	set xfail_cast_test 0
> +	if {[istarget *-linux*]} {
> +	    set xfail_cast_test 1
> +	}
> +	do_tests $xfail_cast_test
> +    }
> +}
> +
> +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
> +    }
> +}
> -- 
> 2.44.0
>
  
Tom de Vries May 2, 2024, 10:37 a.m. UTC | #2
On 5/2/24 03:38, Kevin Buettner wrote:
> 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 with a binary compiled
> in the following different ways:
> 
> - default: no switches aside from -g (and whatever else is added by the
>    testing framework)
> - macros: macros 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

Hi,

I applied the patch, and ran the test-case on leap 15.5.

With glibc debuginfo installed, I get:
...
FAIL: gdb.base/check-errno.exp: default: print errno
FAIL: gdb.base/check-errno.exp: default: print (int) errno
FAIL: gdb.base/check-errno.exp: default: check errno value from corefile
FAIL: gdb.base/check-errno.exp: static: print errno
FAIL: gdb.base/check-errno.exp: static: check errno value from corefile
FAIL: gdb.base/check-errno.exp: pthreads-static: print errno
FAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from 
corefile
...

Without glibc debuginfo installed, I get:
...
FAIL: gdb.base/check-errno.exp: default: print (int) errno
FAIL: gdb.base/check-errno.exp: static: print errno
FAIL: gdb.base/check-errno.exp: static: check errno value from corefile
FAIL: gdb.base/check-errno.exp: pthreads-static: print errno
FAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from 
corefile
...

Then I ran it on tumbleweed.

With and without glibc debuginfo installed, I get:
...
FAIL: gdb.base/check-errno.exp: static: print errno
FAIL: gdb.base/check-errno.exp: static: check errno value from corefile
FAIL: gdb.base/check-errno.exp: pthreads-static: print errno
FAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from 
corefile
...

Looking in more detail at the first two FAILs of leap 15.5 with glibc 
debug info installed, we have:
...
(gdb) print errno^M
Cannot find thread-local storage for process 2259, shared library 
/lib64/libc.so.6:^M
Cannot find thread-local variables on this target^M
(gdb) FAIL: gdb.base/check-errno.exp: default: print errno
print (int) errno^M
Cannot find thread-local storage for process 2259, shared library 
/lib64/libc.so.6:^M
Cannot find thread-local variables on this target^M
(gdb) FAIL: gdb.base/check-errno.exp: default: print (int) errno
...

I think some of these are FAILs due to using a glibc version that 
doesn't have libpthreads merged.  Leap 15.5 uses glibc 2.31. 
libpthreads was merged into libc in version 2.34.  Current tumbleweed 
uses 2.39.

FWIW, in a fedora rawhide container I get:
...
XPASS: gdb.base/check-errno.exp: static-macros: print (int) errno
XPASS: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
...

That matches your description, but I think these xpasses shouldn't be there.

Thanks,
- Tom
  
Kevin Buettner May 2, 2024, 4:02 p.m. UTC | #3
On Thu, 2 May 2024 12:37:23 +0200
Tom de Vries <tdevries@suse.de> wrote:

> On 5/2/24 03:38, Kevin Buettner wrote:
> > 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 with a binary compiled
> > in the following different ways:
> > 
> > - default: no switches aside from -g (and whatever else is added by the
> >    testing framework)
> > - macros: macros 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  
> 
> Hi,
> 
> I applied the patch, and ran the test-case on leap 15.5.
> 
> With glibc debuginfo installed, I get:
> ...
> FAIL: gdb.base/check-errno.exp: default: print errno
> FAIL: gdb.base/check-errno.exp: default: print (int) errno
> FAIL: gdb.base/check-errno.exp: default: check errno value from corefile
> FAIL: gdb.base/check-errno.exp: static: print errno
> FAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> FAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> FAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from 
> corefile
> ...
> 
> Without glibc debuginfo installed, I get:
> ...
> FAIL: gdb.base/check-errno.exp: default: print (int) errno
> FAIL: gdb.base/check-errno.exp: static: print errno
> FAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> FAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> FAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from 
> corefile
> ...
> 
> Then I ran it on tumbleweed.
> 
> With and without glibc debuginfo installed, I get:
> ...
> FAIL: gdb.base/check-errno.exp: static: print errno
> FAIL: gdb.base/check-errno.exp: static: check errno value from corefile
> FAIL: gdb.base/check-errno.exp: pthreads-static: print errno
> FAIL: gdb.base/check-errno.exp: pthreads-static: check errno value from 
> corefile
> ...
> 
> Looking in more detail at the first two FAILs of leap 15.5 with glibc 
> debug info installed, we have:
> ...
> (gdb) print errno^M
> Cannot find thread-local storage for process 2259, shared library 
> /lib64/libc.so.6:^M
> Cannot find thread-local variables on this target^M
> (gdb) FAIL: gdb.base/check-errno.exp: default: print errno
> print (int) errno^M
> Cannot find thread-local storage for process 2259, shared library 
> /lib64/libc.so.6:^M
> Cannot find thread-local variables on this target^M
> (gdb) FAIL: gdb.base/check-errno.exp: default: print (int) errno
> ...
> 
> I think some of these are FAILs due to using a glibc version that 
> doesn't have libpthreads merged.  Leap 15.5 uses glibc 2.31. 
> libpthreads was merged into libc in version 2.34.  Current tumbleweed 
> uses 2.39.

I can reproduce some of the above using old versions of Fedora.  I'll
attempt to xfail those cases too.

> FWIW, in a fedora rawhide container I get:
> ...
> XPASS: gdb.base/check-errno.exp: static-macros: print (int) errno
> XPASS: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
> ...
> 
> That matches your description, but I think these xpasses shouldn't be there.

Based on bug 31693, which you recently filed, I agree.  Those should 
be FAILs.  I'll update it so that those tests fail instead.

Thanks!

Kevin
  
Tom de Vries May 2, 2024, 4:45 p.m. UTC | #4
On 5/2/24 18:02, Kevin Buettner wrote:
>> FWIW, in a fedora rawhide container I get:
>> ...
>> XPASS: gdb.base/check-errno.exp: static-macros: print (int) errno
>> XPASS: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
>> ...
>>
>> That matches your description, but I think these xpasses shouldn't be there.
> 
> Based on bug 31693, which you recently filed, I agree.  Those should
> be FAILs.  I'll update it so that those tests fail instead.
> 

Well, what I meant (and didn't clearly state) is that there's no good 
reason to commit a test-case that produces an xpass.  It usually means 
that the xfail scope is too big.

Thanks,
- Tom
  
Kevin Buettner May 2, 2024, 5:39 p.m. UTC | #5
On Thu, 2 May 2024 18:45:40 +0200
Tom de Vries <tdevries@suse.de> wrote:

> On 5/2/24 18:02, Kevin Buettner wrote:
> >> FWIW, in a fedora rawhide container I get:
> >> ...
> >> XPASS: gdb.base/check-errno.exp: static-macros: print (int) errno
> >> XPASS: gdb.base/check-errno.exp: pthreads-static-macros: print (int) errno
> >> ...
> >>
> >> That matches your description, but I think these xpasses shouldn't be there.  
> > 
> > Based on bug 31693, which you recently filed, I agree.  Those should
> > be FAILs.  I'll update it so that those tests fail instead.
> >   
> 
> Well, what I meant (and didn't clearly state) is that there's no good 
> reason to commit a test-case that produces an xpass.  It usually means 
> that the xfail scope is too big.

Yeah, that makes sense.

But in this case, as you've demonstrated, there's a bug in GDB, so it
doesn't make sense to set up an xfail for them.

I'm looking at your "Fix cast handling for indirection" patch now and
will wait for it to land before posting a new version of my
check-errno patch.

Kevin
  

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..af7d204f56e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/check-errno.exp
@@ -0,0 +1,237 @@ 
+# 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, some of these tests could fail.
+#
+# Another possibility (which is tested by the "macro" tests below) is
+# that errno is a macro and the debuginfo includes a macro expression
+# for errno.  In the past, this often helped with being able to print
+# errno, but at the time that this test was written, it seems to not
+# always work correctly.
+#
+# When the binary is compiled statically, GDB's access to thread local
+# storage may be compromised, perhaps leading to failures with those
+# tests.
+#
+# To complicate things even more, access to errno might not work when
+# examining a core file.  E.g. if it's necessary to call a function,
+# perhaps named '__errno_location', this won't be possible without
+# a running inferior.
+#
+# 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.
+
+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".  We pass these ptype tests, even for
+    # less than ideal outputs, because the point is to set up the
+    # xfail.  A failure can still occur for output which doesn't match
+    # any of those listed.
+    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
+	}
+	-re ".*has unknown return type; cast the call to its declared return type.*$::gdb_prompt $" {
+	    pass $test
+	    set do_xfail 1
+	    set do_xfail_core_test 1
+
+	    # 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.
+	    #
+	    # For the test "print (int) errno", we're casting the
+	    # result of the expression, which includes both the call
+	    # along with a dereferencing operation.
+	    #
+	    # This will sometimes produce the right answer, but it's
+	    # also just as likely to fail.  E.g.  on x86_64, if the
+	    # address being returned as a 32-bit int is the same as
+	    # that which would have been returned as a 64-bit pointer,
+	    # then the test might pass.  Otherwise, it will almost
+	    # certainly fail, which is why we XFAIL it here.  But do
+	    # expect to see the occasional XPASS for this case.
+	    set do_xfail_cast 1
+	}
+    }
+
+    # If errno is defined as a macro that contains an obvious function
+    # call, it's likely that 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
+	}
+    }
+
+    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 {
+	set xfail_cast_test 0
+	if {[istarget *-linux*]} {
+	    set xfail_cast_test 1
+	}
+	do_tests $xfail_cast_test
+    }
+}
+
+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 {
+	set xfail_cast_test 0
+	if {[istarget *-linux*]} {
+	    set xfail_cast_test 1
+	}
+	do_tests $xfail_cast_test
+    }
+}
+
+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
+    }
+}