about summary refs log tree commit diff
diff options
context:
space:
mode:
authorZack Weinberg <zackw@panix.com>2017-06-22 18:28:45 -0400
committerZack Weinberg <zackw@panix.com>2017-06-22 18:28:45 -0400
commit09e65f085e8491db0ada73c558ffe8dc296ade47 (patch)
treec31ff4f8f871bab4762248bffc9c3c508037ef61
parentbf7aa82eab0f1f9c78d39cf57a3f6b25eefc47c1 (diff)
downloadglibc-zack/errno-prettyprint.tar.gz
glibc-zack/errno-prettyprint.tar.xz
glibc-zack/errno-prettyprint.zip
Add pretty-printer for errno. zack/errno-prettyprint
This patch adds the actual pretty-printer for errno.  I could have
used Python's built-in errno module to get the symbolic names for the
constants, but it seemed better to do something entirely under our
control, so there's a .pysym file generated from errnos.texi, with a
hook that allows the Hurd to add additional constants.  Then a .py
module is generated from that plus errno.h in the usual manner; many
thanks to the authors of the .pysym mechanism.

There is also a test which verifies that the .py file (not the .pysym
file) covers all of the constants defined in errno.h.

hurd-add-errno-constants.awk has been manually tested, but the
makefile logic that runs it has not been tested.

	* stdlib/errno-printer.py: New pretty-printer.
	* stdlib/test-errno-constants.py: New special test.
	* stdlib/test-errno-printer.c, stdlib/test-errno-printer.py:
	New pretty-printer test.
	* stdlib/make-errno-constants.awk: New script to generate the
	.pysym file needed by errno-printer.py.
	* stdlib/Makefile: Install, run, and test all of the above, as
	appropriate.

	* sysdeps/mach/hurd/hurd-add-errno-constants.awk: New script to
	add Mach/Hurd-specific errno constants to the .pysym file used by
	stdlib/errno-printer.py.
	* sysdeps/mach/hurd/Makefile: Hook hurd-add-errno-constants.awk
	into the generation of that .pysym file.
-rw-r--r--stdlib/Makefile38
-rw-r--r--stdlib/errno-printer.py105
-rw-r--r--stdlib/make-errno-constants.awk66
-rw-r--r--stdlib/test-errno-constants.py58
-rw-r--r--stdlib/test-errno-printer.c43
-rw-r--r--stdlib/test-errno-printer.py71
-rw-r--r--sysdeps/mach/hurd/Makefile10
-rw-r--r--sysdeps/mach/hurd/hurd-add-errno-constants.awk80
8 files changed, 471 insertions, 0 deletions
diff --git a/stdlib/Makefile b/stdlib/Makefile
index 0314d5926b..31025465cb 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -149,6 +149,34 @@ ifeq ($(run-built-tests),yes)
 tests-special += $(objpfx)tst-fmtmsg.out
 endif
 
+ifdef PYTHON
+# Pretty-printer for errno.  The .pysym file is itself generated from
+# errnos.texi, and can be augmented by sysdeps Makefiles, primarily for
+# the sake of the Hurd, which has a bunch of extra error constants.
+pretty-printers := errno-printer.py
+tests-printers := test-errno-printer
+gen-py-const-headers := errno_constants.pysym
+vpath %.pysym $(objpfx)
+
+define sysd-add-errno-constants
+:
+endef
+$(objpfx)errno_constants.pysym: make-errno-constants.awk \
+				$(..)manual/errno.texi
+	($(AWK) -f make-errno-constants.awk $(..)manual/errno.texi; \
+	$(sysd-add-errno-constants)) > $@T
+	mv -f $@T $@
+
+# We must specify both CFLAGS and CPPFLAGS to override any
+# compiler options the user might have provided that conflict
+# with what we need e.g. user specifies CPPFLAGS with -O2 and
+# we need -O0.
+CFLAGS-test-errno-printer.c := $(CFLAGS-printers-tests)
+CPPFLAGS-test-errno-printer.c := $(CFLAGS-printers-tests)
+
+tests-special += $(objpfx)test-errno-constants.out
+endif
+
 include ../Rules
 
 ifeq ($(run-built-tests),yes)
@@ -220,3 +248,13 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3
 	$(evaluate-test)
 
 $(objpfx)tst-makecontext: $(libdl)
+
+# Note: errno_constants.py depends on errno.h and everything it
+# includes, as well as on errno_constants.pysym, so we don't need to
+# specify that this test also depends on both.
+$(objpfx)test-errno-constants.out: \
+		test-errno-constants.py $(objpfx)errno_constants.py
+	$(PYTHON) $< $(objpfx) $(CC) $(CFLAGS) $(CPPFLAGS) > $@; \
+	$(evaluate-test)
+
+libof-test-errno-constants = testsuite
diff --git a/stdlib/errno-printer.py b/stdlib/errno-printer.py
new file mode 100644
index 0000000000..aa09c78235
--- /dev/null
+++ b/stdlib/errno-printer.py
@@ -0,0 +1,105 @@
+# Pretty printer for errno.
+# Copyright (C) 2016-2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+"""This file contains the gdb pretty printers for the following types:
+
+    * __error_t (the type of 'errno')
+    * error_t   (cast any 'int' to 'error_t' to print it like an errno value)
+
+You can check which printers are registered and enabled by issuing the
+'info pretty-printer' gdb command.  Printers should trigger automatically when
+trying to print a variable of one of the types mentioned above.
+"""
+
+
+import gdb
+import gdb.printing
+import errno_constants
+
+
+def make_errno_reverse_mapping():
+    """Construct a reverse mapping from errno values to symbolic names.
+       The result is a dictionary indexed by integers, not a list,
+       because errno values are not necessarily contiguous.
+    """
+
+    # Certain errno symbols are allowed to have the same numeric value.
+    # If they do, one of them (whichever one is in POSIX, or if both or
+    # neither are, the shortest) is selected as the preferred name.
+    # This map goes from non-preferred name(s) to preferred name.
+    permitted_collisions = {
+        "EDEADLOCK":   "EDEADLK",
+        "EOPNOTSUPP":  "ENOTSUP",
+        "EWOULDBLOCK": "EAGAIN",
+    }
+
+    errno_names = { 0: "Success" }
+    for name in dir(errno_constants):
+        if name[0] == 'E':
+            number = getattr(errno_constants, name)
+            other = errno_names.get(number)
+            if other is None:
+                errno_names[number] = name
+            else:
+                p1 = permitted_collisions.get(name)
+                p2 = permitted_collisions.get(other)
+                if p1 is not None and p1 == other:
+                    pass # the value in errno_names is already what we want
+                elif p2 is not None and p2 == name:
+                    errno_names[number] = name
+                else:
+                    raise RuntimeError(
+                        "errno value collision: {} = {}, {}"
+                        .format(number, name, errno_names[number]))
+
+    return errno_names
+
+
+errno_names = make_errno_reverse_mapping()
+
+
+class ErrnoPrinter(object):
+    """Pretty printer for errno values."""
+
+    def __init__(self, val):
+        self._val = int(val)
+
+    def to_string(self):
+        """gdb API function.
+
+        This is called from gdb when we try to print an error_t.
+        """
+        if self._val in errno_names:
+            return "{:d} ({})".format(self._val, errno_names[self._val])
+        else:
+            return "{:d}".format(self._val)
+
+
+def register(objfile):
+    """Register pretty printers for the current objfile."""
+
+    printer = gdb.printing.RegexpCollectionPrettyPrinter("glibc-errno")
+    printer.add_printer('error_t', r'^(?:__)?error_t', ErrnoPrinter)
+
+    if objfile == None:
+        objfile = gdb
+
+    gdb.printing.register_pretty_printer(objfile, printer)
+
+
+register(gdb.current_objfile())
diff --git a/stdlib/make-errno-constants.awk b/stdlib/make-errno-constants.awk
new file mode 100644
index 0000000000..32344dca95
--- /dev/null
+++ b/stdlib/make-errno-constants.awk
@@ -0,0 +1,66 @@
+# Copyright (C) 2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library 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
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# Generate errno_constants.pysym from errno.texi.
+# errno.texi contains lines like:
+# @errno{ENOSYS, 123, Function not implemented}
+# The number is only relevant for the Hurd.
+
+BEGIN {
+    print "#include <errno.h>"
+    print ""
+    print "-- Errno constants"
+
+    # Some error constants do not exist on all supported operating systems.
+    # FIXME: Encode this information in errno.texi.
+    ## (Sometimes) two names for the same number
+    optional["EDEADLOCK"]	= 1
+    optional["EDEADLK"]		= 1
+    optional["EOPNOTSUPP"]	= 1
+    optional["ENOTSUP"]		= 1
+    optional["EWOULDBLOCK"]	= 1
+    optional["EAGAIN"]		= 1
+    ## BSD-specific
+    optional["EAUTH"]		= 1
+    optional["EBADRPC"]		= 1
+    optional["EFTYPE"]		= 1
+    optional["ENEEDAUTH"]	= 1
+    optional["EPROCLIM"]	= 1
+    optional["EPROCUNAVAIL"]	= 1
+    optional["EPROGMISMATCH"]	= 1
+    optional["EPROGUNAVAIL"]	= 1
+    optional["ERPCMISMATCH"]	= 1
+    ## GNU-specific
+    optional["EBACKGROUND"]	= 1
+    optional["ED"]		= 1
+    optional["EDIED"]		= 1
+    optional["EGRATUITOUS"]	= 1
+    optional["EGREGIOUS"]	= 1
+    optional["EIEIO"]		= 1
+}
+
+/^@errno\{/ {
+    e = substr($1, 8, length($1)-8)
+    if (e in optional)
+      {
+	print "#ifdef", e
+	print e
+	print "#endif"
+      }
+    else
+	print e
+}
diff --git a/stdlib/test-errno-constants.py b/stdlib/test-errno-constants.py
new file mode 100644
index 0000000000..a79df97625
--- /dev/null
+++ b/stdlib/test-errno-constants.py
@@ -0,0 +1,58 @@
+# Test that errno_constants.py includes every error number defined by errno.h.
+# Copyright (C) 2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# Usage: test-errno-constants.py $(common-objpfx)stdlib $(CC) $(CFLAGS) $(CPPFLAGS)
+
+import os
+import sys
+import subprocess
+
+sys.path.append(sys.argv[1])
+import errno_constants
+
+def main():
+    cc_cmd = sys.argv[2:]
+    cc_cmd.extend(["-E", "-dM", "-xc", "-"])
+    cc_proc = subprocess.Popen(cc_cmd,
+                               stdin=subprocess.PIPE,
+                               stdout=subprocess.PIPE)
+    cc_proc.stdin.write(b"#define _GNU_SOURCE\n"
+                       b"#include <errno.h>\n")
+    cc_proc.stdin.close()
+
+    cc_output = cc_proc.stdout.read()
+    status = cc_proc.wait()
+    if status:
+        sys.stderr.write("{}\nunsuccessful exit, status {:04x}"
+                         .format(" ".join(cc_cmd), status))
+        sys.exit(1)
+
+    ok = True
+    for line in cc_output.decode("utf-8").splitlines():
+        if not line.startswith("#define E"):
+            continue
+        emacro = line.split()[1]
+        if not hasattr(errno_constants, emacro):
+            if ok:
+                sys.stderr.write("*** Missing constants:\n")
+                ok = False
+            sys.stderr.write(emacro + "\n")
+
+    sys.exit(0 if ok else 1)
+
+main()
diff --git a/stdlib/test-errno-printer.c b/stdlib/test-errno-printer.c
new file mode 100644
index 0000000000..da2345f188
--- /dev/null
+++ b/stdlib/test-errno-printer.c
@@ -0,0 +1,43 @@
+/* Helper program for testing the errno pretty-printer.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#define PASS 0
+#define FAIL 1
+
+const error_t array_of_error_t[3] = { 0, ERANGE, -2 };
+
+__thread int ensure_gdb_can_read_thread_variables = 0;
+
+int
+main (void)
+{
+  int result = PASS;
+  errno = array_of_error_t[0];
+  unsigned long x = strtoul("9999999999999999999999999999999999999", 0, 10);
+  if (x != ULONG_MAX)
+    result = FAIL;
+  if (errno != ERANGE)
+    result = FAIL;
+  errno = -2; /* Break: test errno 2 */
+  return result;
+}
diff --git a/stdlib/test-errno-printer.py b/stdlib/test-errno-printer.py
new file mode 100644
index 0000000000..b55ee08b08
--- /dev/null
+++ b/stdlib/test-errno-printer.py
@@ -0,0 +1,71 @@
+# Test for the errno pretty-printer.
+# Copyright (C) 2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+import sys
+
+from test_printers_common import *
+
+test_source = sys.argv[1]
+test_bin = sys.argv[2]
+printer_files = sys.argv[3:]
+printer_names = ['global glibc-errno']
+
+try:
+    init_test(test_bin, printer_files, printer_names)
+    go_to_main()
+
+    # All supported versions of gdb do run the pretty-printer on an
+    # _array_ of error_t.
+    test_printer('array_of_error_t',
+                 r'= \{0 \(Success\), \d+ \(ERANGE\), -2\}', is_ptr=False)
+
+    # Some versions of gdb don't run the pretty-printer on a _scalar_
+    # whose type is error_t.  If we have such a gdb, the test is
+    # unsupported.
+    test('print (error_t) 0',
+         pattern             = r'= 0 \(Success\)$',
+         unsupported_pattern = r'= 0$')
+
+    # Some versions of gdb don't support reading thread-specific variables;
+    # these versions may also have trouble reading errno.
+    test('print ensure_gdb_can_read_thread_variables',
+         pattern             = r'= 0$',
+         unsupported_pattern = r'Cannot find thread-local')
+
+    next_cmd()
+    next_cmd()
+    test_printer('errno', r'0 (Success)', is_ptr=False)
+    next_cmd()
+    test_printer('errno', r'\d+ (ERANGE)', is_ptr=False)
+
+    break_at(test_source, 'test errno 2', is_ptr=False)
+    continue_cmd()
+    next_cmd()
+    test_printer('errno', r'-2', is_ptr=False)
+
+    continue_cmd() # Exit
+
+except (NoLineError, pexpect.TIMEOUT) as exception:
+    print('Error: {0}'.format(exception))
+    result = FAIL
+
+else:
+    print('Test succeeded.')
+    result = PASS
+
+exit(result)
diff --git a/sysdeps/mach/hurd/Makefile b/sysdeps/mach/hurd/Makefile
index 13bdf5c7c9..5171425650 100644
--- a/sysdeps/mach/hurd/Makefile
+++ b/sysdeps/mach/hurd/Makefile
@@ -98,6 +98,16 @@ $(common-objpfx)stamp-errnos: $(hurd)/errnos.awk $(errno.texinfo) \
 	touch $@
 
 common-generated += errnos.d stamp-errnos
+
+# Augmentations to the errno pretty-printer.
+ifeq ($(subdir),stdlib)
+define sysd-add-errno-constants
+$(AWK) -f $(hurd)/hurd-add-errno-constants.awk $(mach-errnos-deps)
+endef
+$(objpfx)errno_constants.pysym: \
+    $(hurd)/hurd-add-errno-constants.awk $(mach-errnos-deps)
+endif
+
 
 # We install the real libc.a as libcrt.a and as libc.a we install a linker
 # script which does -( -lcrt -lmachuser -lhurduser -).
diff --git a/sysdeps/mach/hurd/hurd-add-errno-constants.awk b/sysdeps/mach/hurd/hurd-add-errno-constants.awk
new file mode 100644
index 0000000000..0d51766c97
--- /dev/null
+++ b/sysdeps/mach/hurd/hurd-add-errno-constants.awk
@@ -0,0 +1,80 @@
+# Add Hurd-specific constants to errno_constants.pysym.
+# Copyright (C) 2017 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library 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
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <http://www.gnu.org/licenses/>.
+
+# On the Hurd, errno.h defines E-constants corresponding to a number of
+# Mach low-level errors.  Add these to the set of values recognized by
+# stdlib/errno-printer.py.  This script must be kept in sync with errnos.awk.
+
+BEGIN {
+  in_mach_errors = "";
+  in_mig_errors = 0;
+  in_device_errors = 0;
+}
+
+function emit_subhead()
+{
+  header = FILENAME;
+  sub(/.*include\//, "", header);
+  printf("\n-- Errors from <%s>\n", header);
+}
+
+NF == 3 && $1 == "#define" && $2 == "MACH_SEND_IN_PROGRESS" \
+  {
+    in_mach_errors = FILENAME;
+    emit_subhead();
+  }
+NF == 3 && $1 == "#define" && $2 == "KERN_SUCCESS" \
+  {
+    in_mach_errors = FILENAME;
+    emit_subhead();
+    next;
+  }
+in_mach_errors != "" && $2 == "MACH_IPC_COMPAT" \
+  {
+    in_mach_errors = "";
+    next;
+  }
+
+$1 == "#define" && $2 == "_MACH_MIG_ERRORS_H_" \
+  {
+    in_mig_errors = 1;
+    emit_subhead();
+    next;
+  }
+in_mig_errors && $1 == "#endif" && $3 == "_MACH_MIG_ERRORS_H_" \
+  {
+    in_mig_errors = 0;
+  }
+
+$1 == "#define" && $2 == "D_SUCCESS" \
+  {
+    in_device_errors = 1;
+    emit_subhead();
+    next;
+  }
+in_device_errors && $1 == "#endif" \
+  {
+    in_device_errors = 0;
+  }
+
+(in_mach_errors == FILENAME && NF == 3 && $1 == "#define") || \
+(in_mig_errors && $1 == "#define" && $3 <= -300) || \
+(in_device_errors && $1 == "#define" && $2 ~ /D_/ && NF > 3) \
+  {
+    print "E" $2;
+  }