[PATCH 3/3] Add pretty-printer for errno.

Zack Weinberg <[email protected]>
Newsgroups gmane.comp.gdb.devel,gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
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.
---
 stdlib/Makefile                                |  38 +++++++++
 stdlib/errno-printer.py                        | 105 +++++++++++++++++++++++++
 stdlib/make-errno-constants.awk                |  66 ++++++++++++++++
 stdlib/test-errno-constants.py                 |  58 ++++++++++++++
 stdlib/test-errno-printer.c                    |  43 ++++++++++
 stdlib/test-errno-printer.py                   |  71 +++++++++++++++++
 sysdeps/mach/hurd/Makefile                     |  10 +++
 sysdeps/mach/hurd/hurd-add-errno-constants.awk |  80 +++++++++++++++++++
 8 files changed, 471 insertions(+)
 create mode 100644 stdlib/errno-printer.py
 create mode 100644 stdlib/make-errno-constants.awk
 create mode 100644 stdlib/test-errno-constants.py
 create mode 100644 stdlib/test-errno-printer.c
 create mode 100644 stdlib/test-errno-printer.py
 create mode 100644 sysdeps/mach/hurd/hurd-add-errno-constants.awk

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;
+  }
-- 
2.11.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.