aboutsummaryrefslogtreecommitdiff
path: root/stdlib/errno-printer.py
blob: aa09c782350a01dd8a036f96190a9c41eaaf1b53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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())