From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id AHyDHiT1BWZSCRoAWB0awg (envelope-from ) for ; Thu, 28 Mar 2024 18:54:28 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=CUSRpJBQ; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 72B8E1E0C0; Thu, 28 Mar 2024 18:54:28 -0400 (EDT) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 4A2DE1E08C for ; Thu, 28 Mar 2024 18:54:26 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CCB51385828B for ; Thu, 28 Mar 2024 22:54:25 +0000 (GMT) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 56F783858D32 for ; Thu, 28 Mar 2024 22:54:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 56F783858D32 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 56F783858D32 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711666444; cv=none; b=F2I1atAku8ZwPbHzPVUHujcEaJdQedTIajIosvYUThP8/1biRyYxKBLyjxtoTEjlaHOyiqbwlecwFDmkBfWR222/jiRpOpycXyDV0lcZVuNRB5i5t8pPiCrU4XIq7O2LMn+UC1c2OGu4ifapuuhvfz4RryK80oZQrERkkaMQCDs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711666444; c=relaxed/simple; bh=rXjqUO5OINA0g6S3s2pb7ZDc+Phb+SwLgJU9hh2RMs0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=kb/1ngTLGjdyx+3K3SZ/DIFnzF8ZK3BH3Ei301xsf5ywvalESn3WOER5Ch+3YVwxQl3GKAomjc5pKkuvt9rioCQTm5USTuoZP0pyRkmJlq1hNRY6M4ZlIisdADZPICfozI4BJ00bwIWfmluyij9j22YNiX252bdM/hUydUwgSq8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711666441; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qYV6rdLxwRrRfAMHkKtmlHss6GeuObMygTwKdhM8bpo=; b=CUSRpJBQywB9cRw7UBuo//9GxjUDghxUAu/Tom//meN8DTN4leEdwQ2XPWaxacnwY276Vy A0JJBj59YPjSOY3gBCs12Hl9Z1CMC4vcvjnkBVMBPSpY8esaTyLPWcChs7Qms0f9sqW01T TcJnLLTlGMv/qO9fBQnaKO9H/S4EwFw= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-408-DdoalMRnOsKe9JOjfPKJuA-1; Thu, 28 Mar 2024 18:53:59 -0400 X-MC-Unique: DdoalMRnOsKe9JOjfPKJuA-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 21705800265 for ; Thu, 28 Mar 2024 22:53:59 +0000 (UTC) Received: from f39-1.lan (unknown [10.22.10.134]) by smtp.corp.redhat.com (Postfix) with ESMTP id A24F740C6CB1; Thu, 28 Mar 2024 22:53:58 +0000 (UTC) From: Kevin Buettner To: gdb-patches@sourceware.org Cc: Kevin Buettner Subject: [PATCH] [gdb/testsuite] New test: gdb.base/check-errno.exp Date: Thu, 28 Mar 2024 15:53:14 -0700 Message-ID: <20240328225314.41524-1-kevinb@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.2 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-10.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+public-inbox=simark.ca@sourceware.org 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 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. As described later on, GDB using a hack like the one employed by Fedora GDB will fail these tests. 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 this case explicitly. On Fedora 40, without glibc debuginfo, I see the following FAILS and 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 FAIL: 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: 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 FAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile (It's likely that this also happens on earlier versions of Fedora, but I haven't checked this.) The lesson here is that if you want errno to work better, you should make sure that glibc debuginfo is available, perhaps via debuginfod. On FreeBSD 13.1, I see the following FAILs and 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 FAIL: 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 FAIL: gdb.base/check-errno.exp: pthreads-macros: print (int) errno XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile 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 no FAILs): 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: check errno value from corefile XFAIL: gdb.base/check-errno.exp: pthreads-macros: check errno value from corefile Each of these in this latter batch could be considered bugs, so lets look at them more closely: XFAIL: gdb.base/check-errno.exp: macros: check errno value from corefile: print errno You can't do that without a process to debug. In this case, errno is a macro defined as follows: #define errno (*__errno_location ()) In this case, GDB is debugging a core file, and there's no process in which to make a suitable inferior function call. The only way that I can see to fix this is to ignore the macro and fall back to using the errno symbol. XFAIL: gdb.base/check-errno.exp: static: print errno: print errno Cannot find thread-local storage for process 35973, executable file .../outputs/gdb.base/check-errno/check-errno-static: Cannot find thread-local variables on this target The message is self-explanatory, but I hope we can find a way to fix this. I have filed a bug, assigned to me, which mentions this case: https://sourceware.org/bugzilla/show_bug.cgi?id=31563 XFAIL: gdb.base/check-errno.exp: static: print (int) errno: print (int) errno Cannot find thread-local storage for process 35973, executable file .../outputs/gdb.base/check-errno/check-errno-static: Cannot find thread-local variables on this target Same as the static "print errno" case, above. XFAIL: gdb.base/check-errno.exp: static: check errno value from corefile: print errno Cannot find thread-local storage for LWP 35973, executable file .../outputs/gdb.base/check-errno/check-errno-static: Cannot find thread-local variables on this target Despite being for a core file, this is the same as above. XFAIL: gdb.base/check-errno.exp: static-macros: print errno: print errno '__errno_location' has unknown return type; cast the call to its declared return type In this case, errno is a macro defined as follows: #define errno (*__errno_location ()) Doing 'print (int) errno' (which is one of the tests) works though. I think there should be some way to fix this one too. 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: print errno You can't do that without a process to debug. This case was discussed earlier - errno is a macro and GDB would need a running inferior (not a core file) in order to evaluate the function. Finally, I'll note that Fedora GDB has a local patch which has a hack that intercepts "errno" in process_print_command_args(), and replaces it with "*(*(int *(*)(void)) __errno_location) ()". Running Fedora GDB against the new test in this commit results in 6 unexpected failures, 2 unexpected successes, and 5 expected failures. These results are considerably worse than running GDB without this hack. The 2 unexpected successes were: XPASS: gdb.base/check-errno.exp: static: print errno XPASS: gdb.base/check-errno.exp: static-macros: print errno The 6 unexpected failures were: FAIL: gdb.base/check-errno.exp: default: print masking errno FAIL: gdb.base/check-errno.exp: macros: print masking errno FAIL: gdb.base/check-errno.exp: static: print masking errno FAIL: gdb.base/check-errno.exp: static-macros: print masking errno FAIL: gdb.base/check-errno.exp: pthreads: print masking errno FAIL: gdb.base/check-errno.exp: pthreads-macros: print masking errno Co-Authored-By: Jan Kratochvil --- gdb/testsuite/gdb.base/check-errno.c | 37 +++++ gdb/testsuite/gdb.base/check-errno.exp | 191 +++++++++++++++++++++++++ 2 files changed, 228 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 . */ + +#include + +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..14cda745a5b --- /dev/null +++ b/gdb/testsuite/gdb.base/check-errno.exp @@ -0,0 +1,191 @@ +# 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 + } + } + + # 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 + } +} -- 2.44.0