diff options
author | Zack Weinberg <zackw@panix.com> | 2017-06-22 18:28:45 -0400 |
---|---|---|
committer | Zack Weinberg <zackw@panix.com> | 2017-06-22 18:28:45 -0400 |
commit | 09e65f085e8491db0ada73c558ffe8dc296ade47 (patch) | |
tree | c31ff4f8f871bab4762248bffc9c3c508037ef61 | |
parent | bf7aa82eab0f1f9c78d39cf57a3f6b25eefc47c1 (diff) | |
download | glibc-zack/errno-prettyprint.zip glibc-zack/errno-prettyprint.tar.gz glibc-zack/errno-prettyprint.tar.bz2 |
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/Makefile | 38 | ||||
-rw-r--r-- | stdlib/errno-printer.py | 105 | ||||
-rw-r--r-- | stdlib/make-errno-constants.awk | 66 | ||||
-rw-r--r-- | stdlib/test-errno-constants.py | 58 | ||||
-rw-r--r-- | stdlib/test-errno-printer.c | 43 | ||||
-rw-r--r-- | stdlib/test-errno-printer.py | 71 | ||||
-rw-r--r-- | sysdeps/mach/hurd/Makefile | 10 | ||||
-rw-r--r-- | sysdeps/mach/hurd/hurd-add-errno-constants.awk | 80 |
8 files changed, 471 insertions, 0 deletions
diff --git a/stdlib/Makefile b/stdlib/Makefile index 0314d59..3102546 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 0000000..aa09c78 --- /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 0000000..32344dc --- /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 0000000..a79df97 --- /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 0000000..da2345f --- /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 0000000..b55ee08 --- /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 13bdf5c..5171425 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 0000000..0d51766 --- /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; + } |