Skip to content

Commit 57f5677

Browse files
Villemoespmladek
authored andcommitted
printf: add support for printing symbolic error names
It has been suggested several times to extend vsnprintf() to be able to convert the numeric value of ENOSPC to print "ENOSPC". This implements that as a %p extension: With %pe, one can do if (IS_ERR(foo)) { pr_err("Sorry, can't do that: %pe\n", foo); return PTR_ERR(foo); } instead of what is seen in quite a few places in the kernel: if (IS_ERR(foo)) { pr_err("Sorry, can't do that: %ld\n", PTR_ERR(foo)); return PTR_ERR(foo); } If the value passed to %pe is an ERR_PTR, but the library function errname() added here doesn't know about the value, the value is simply printed in decimal. If the value passed to %pe is not an ERR_PTR, we treat it as an ordinary %p and thus print the hashed value (passing non-ERR_PTR values to %pe indicates a bug in the caller, but we can't do much about that). With my embedded hat on, and because it's not very invasive to do, I've made it possible to remove this. The errname() function and associated lookup tables take up about 3K. For most, that's probably quite acceptable and a price worth paying for more readable dmesg (once this starts getting used), while for those that disable printk() it's of very little use - I don't see a procfs/sysfs/seq_printf() file reasonably making use of this - and they clearly want to squeeze vmlinux as much as possible. Hence the default y if PRINTK. The symbols to include have been found by massaging the output of find arch include -iname 'errno*.h' | xargs grep -E 'define\s*E' In the cases where some common aliasing exists (e.g. EAGAIN=EWOULDBLOCK on all platforms, EDEADLOCK=EDEADLK on most), I've moved the more popular one (in terms of 'git grep -w Efoo | wc) to the bottom so that one takes precedence. Link: http://lkml.kernel.org/r/20191015190706.15989-1-linux@rasmusvillemoes.dk To: "Jonathan Corbet" <corbet@lwn.net> To: linux-kernel@vger.kernel.org Cc: "Andy Shevchenko" <andy.shevchenko@gmail.com> Cc: "Andrew Morton" <akpm@linux-foundation.org> Cc: "Joe Perches" <joe@perches.com> Cc: linux-doc@vger.kernel.org Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> Acked-by: Uwe Kleine-König <uwe@kleine-koenig.org> Reviewed-by: Petr Mladek <pmladek@suse.com> [andy.shevchenko@gmail.com: use abs()] Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Petr Mladek <pmladek@suse.com>
1 parent ae88de5 commit 57f5677

7 files changed

Lines changed: 309 additions & 0 deletions

File tree

Documentation/core-api/printk-formats.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ has the added benefit of providing a unique identifier. On 64-bit machines
7979
the first 32 bits are zeroed. The kernel will print ``(ptrval)`` until it
8080
gathers enough entropy. If you *really* want the address see %px below.
8181

82+
Error Pointers
83+
--------------
84+
85+
::
86+
87+
%pe -ENOSPC
88+
89+
For printing error pointers (i.e. a pointer for which IS_ERR() is true)
90+
as a symbolic error name. Error values for which no symbolic name is
91+
known are printed in decimal, while a non-ERR_PTR passed as the
92+
argument to %pe gets treated as ordinary %p.
93+
8294
Symbols/Function Pointers
8395
-------------------------
8496

include/linux/errname.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_ERRNAME_H
3+
#define _LINUX_ERRNAME_H
4+
5+
#include <linux/stddef.h>
6+
7+
#ifdef CONFIG_SYMBOLIC_ERRNAME
8+
const char *errname(int err);
9+
#else
10+
static inline const char *errname(int err)
11+
{
12+
return NULL;
13+
}
14+
#endif
15+
16+
#endif /* _LINUX_ERRNAME_H */

lib/Kconfig.debug

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,15 @@ config DYNAMIC_DEBUG
164164
See Documentation/admin-guide/dynamic-debug-howto.rst for additional
165165
information.
166166

167+
config SYMBOLIC_ERRNAME
168+
bool "Support symbolic error names in printf"
169+
default y if PRINTK
170+
help
171+
If you say Y here, the kernel's printf implementation will
172+
be able to print symbolic error names such as ENOSPC instead
173+
of the number 28. It makes the kernel image slightly larger
174+
(about 3KB), but can make the kernel logs easier to read.
175+
167176
endmenu # "printk and dmesg options"
168177

169178
menu "Compile-time checks and compiler options"

lib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o
183183
obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
184184

185185
obj-$(CONFIG_DYNAMIC_DEBUG) += dynamic_debug.o
186+
obj-$(CONFIG_SYMBOLIC_ERRNAME) += errname.o
186187

187188
obj-$(CONFIG_NLATTR) += nlattr.o
188189

lib/errname.c

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/build_bug.h>
3+
#include <linux/errno.h>
4+
#include <linux/errname.h>
5+
#include <linux/kernel.h>
6+
7+
/*
8+
* Ensure these tables do not accidentally become gigantic if some
9+
* huge errno makes it in. On most architectures, the first table will
10+
* only have about 140 entries, but mips and parisc have more sparsely
11+
* allocated errnos (with EHWPOISON = 257 on parisc, and EDQUOT = 1133
12+
* on mips), so this wastes a bit of space on those - though we
13+
* special case the EDQUOT case.
14+
*/
15+
#define E(err) [err + BUILD_BUG_ON_ZERO(err <= 0 || err > 300)] = "-" #err
16+
static const char *names_0[] = {
17+
E(E2BIG),
18+
E(EACCES),
19+
E(EADDRINUSE),
20+
E(EADDRNOTAVAIL),
21+
E(EADV),
22+
E(EAFNOSUPPORT),
23+
E(EALREADY),
24+
E(EBADE),
25+
E(EBADF),
26+
E(EBADFD),
27+
E(EBADMSG),
28+
E(EBADR),
29+
E(EBADRQC),
30+
E(EBADSLT),
31+
E(EBFONT),
32+
E(EBUSY),
33+
#ifdef ECANCELLED
34+
E(ECANCELLED),
35+
#endif
36+
E(ECHILD),
37+
E(ECHRNG),
38+
E(ECOMM),
39+
E(ECONNABORTED),
40+
E(ECONNRESET),
41+
E(EDEADLOCK),
42+
E(EDESTADDRREQ),
43+
E(EDOM),
44+
E(EDOTDOT),
45+
#ifndef CONFIG_MIPS
46+
E(EDQUOT),
47+
#endif
48+
E(EEXIST),
49+
E(EFAULT),
50+
E(EFBIG),
51+
E(EHOSTDOWN),
52+
E(EHOSTUNREACH),
53+
E(EHWPOISON),
54+
E(EIDRM),
55+
E(EILSEQ),
56+
#ifdef EINIT
57+
E(EINIT),
58+
#endif
59+
E(EINPROGRESS),
60+
E(EINTR),
61+
E(EINVAL),
62+
E(EIO),
63+
E(EISCONN),
64+
E(EISDIR),
65+
E(EISNAM),
66+
E(EKEYEXPIRED),
67+
E(EKEYREJECTED),
68+
E(EKEYREVOKED),
69+
E(EL2HLT),
70+
E(EL2NSYNC),
71+
E(EL3HLT),
72+
E(EL3RST),
73+
E(ELIBACC),
74+
E(ELIBBAD),
75+
E(ELIBEXEC),
76+
E(ELIBMAX),
77+
E(ELIBSCN),
78+
E(ELNRNG),
79+
E(ELOOP),
80+
E(EMEDIUMTYPE),
81+
E(EMFILE),
82+
E(EMLINK),
83+
E(EMSGSIZE),
84+
E(EMULTIHOP),
85+
E(ENAMETOOLONG),
86+
E(ENAVAIL),
87+
E(ENETDOWN),
88+
E(ENETRESET),
89+
E(ENETUNREACH),
90+
E(ENFILE),
91+
E(ENOANO),
92+
E(ENOBUFS),
93+
E(ENOCSI),
94+
E(ENODATA),
95+
E(ENODEV),
96+
E(ENOENT),
97+
E(ENOEXEC),
98+
E(ENOKEY),
99+
E(ENOLCK),
100+
E(ENOLINK),
101+
E(ENOMEDIUM),
102+
E(ENOMEM),
103+
E(ENOMSG),
104+
E(ENONET),
105+
E(ENOPKG),
106+
E(ENOPROTOOPT),
107+
E(ENOSPC),
108+
E(ENOSR),
109+
E(ENOSTR),
110+
#ifdef ENOSYM
111+
E(ENOSYM),
112+
#endif
113+
E(ENOSYS),
114+
E(ENOTBLK),
115+
E(ENOTCONN),
116+
E(ENOTDIR),
117+
E(ENOTEMPTY),
118+
E(ENOTNAM),
119+
E(ENOTRECOVERABLE),
120+
E(ENOTSOCK),
121+
E(ENOTTY),
122+
E(ENOTUNIQ),
123+
E(ENXIO),
124+
E(EOPNOTSUPP),
125+
E(EOVERFLOW),
126+
E(EOWNERDEAD),
127+
E(EPERM),
128+
E(EPFNOSUPPORT),
129+
E(EPIPE),
130+
#ifdef EPROCLIM
131+
E(EPROCLIM),
132+
#endif
133+
E(EPROTO),
134+
E(EPROTONOSUPPORT),
135+
E(EPROTOTYPE),
136+
E(ERANGE),
137+
E(EREMCHG),
138+
#ifdef EREMDEV
139+
E(EREMDEV),
140+
#endif
141+
E(EREMOTE),
142+
E(EREMOTEIO),
143+
#ifdef EREMOTERELEASE
144+
E(EREMOTERELEASE),
145+
#endif
146+
E(ERESTART),
147+
E(ERFKILL),
148+
E(EROFS),
149+
#ifdef ERREMOTE
150+
E(ERREMOTE),
151+
#endif
152+
E(ESHUTDOWN),
153+
E(ESOCKTNOSUPPORT),
154+
E(ESPIPE),
155+
E(ESRCH),
156+
E(ESRMNT),
157+
E(ESTALE),
158+
E(ESTRPIPE),
159+
E(ETIME),
160+
E(ETIMEDOUT),
161+
E(ETOOMANYREFS),
162+
E(ETXTBSY),
163+
E(EUCLEAN),
164+
E(EUNATCH),
165+
E(EUSERS),
166+
E(EXDEV),
167+
E(EXFULL),
168+
169+
E(ECANCELED), /* ECANCELLED */
170+
E(EAGAIN), /* EWOULDBLOCK */
171+
E(ECONNREFUSED), /* EREFUSED */
172+
E(EDEADLK), /* EDEADLOCK */
173+
};
174+
#undef E
175+
176+
#define E(err) [err - 512 + BUILD_BUG_ON_ZERO(err < 512 || err > 550)] = "-" #err
177+
static const char *names_512[] = {
178+
E(ERESTARTSYS),
179+
E(ERESTARTNOINTR),
180+
E(ERESTARTNOHAND),
181+
E(ENOIOCTLCMD),
182+
E(ERESTART_RESTARTBLOCK),
183+
E(EPROBE_DEFER),
184+
E(EOPENSTALE),
185+
E(ENOPARAM),
186+
187+
E(EBADHANDLE),
188+
E(ENOTSYNC),
189+
E(EBADCOOKIE),
190+
E(ENOTSUPP),
191+
E(ETOOSMALL),
192+
E(ESERVERFAULT),
193+
E(EBADTYPE),
194+
E(EJUKEBOX),
195+
E(EIOCBQUEUED),
196+
E(ERECALLCONFLICT),
197+
};
198+
#undef E
199+
200+
static const char *__errname(unsigned err)
201+
{
202+
if (err < ARRAY_SIZE(names_0))
203+
return names_0[err];
204+
if (err >= 512 && err - 512 < ARRAY_SIZE(names_512))
205+
return names_512[err - 512];
206+
/* But why? */
207+
if (IS_ENABLED(CONFIG_MIPS) && err == EDQUOT) /* 1133 */
208+
return "-EDQUOT";
209+
return NULL;
210+
}
211+
212+
/*
213+
* errname(EIO) -> "EIO"
214+
* errname(-EIO) -> "-EIO"
215+
*/
216+
const char *errname(int err)
217+
{
218+
const char *name = __errname(abs(err));
219+
if (!name)
220+
return NULL;
221+
222+
return err > 0 ? name + 1 : name;
223+
}

lib/test_printf.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,26 @@ flags(void)
593593
kfree(cmp_buffer);
594594
}
595595

596+
static void __init
597+
errptr(void)
598+
{
599+
test("-1234", "%pe", ERR_PTR(-1234));
600+
601+
/* Check that %pe with a non-ERR_PTR gets treated as ordinary %p. */
602+
BUILD_BUG_ON(IS_ERR(PTR));
603+
test_hashed("%pe", PTR);
604+
605+
#ifdef CONFIG_SYMBOLIC_ERRNAME
606+
test("(-ENOTSOCK)", "(%pe)", ERR_PTR(-ENOTSOCK));
607+
test("(-EAGAIN)", "(%pe)", ERR_PTR(-EAGAIN));
608+
BUILD_BUG_ON(EAGAIN != EWOULDBLOCK);
609+
test("(-EAGAIN)", "(%pe)", ERR_PTR(-EWOULDBLOCK));
610+
test("[-EIO ]", "[%-8pe]", ERR_PTR(-EIO));
611+
test("[ -EIO]", "[%8pe]", ERR_PTR(-EIO));
612+
test("-EPROBE_DEFER", "%pe", ERR_PTR(-EPROBE_DEFER));
613+
#endif
614+
}
615+
596616
static void __init
597617
test_pointer(void)
598618
{
@@ -615,6 +635,7 @@ test_pointer(void)
615635
bitmap();
616636
netdev_features();
617637
flags();
638+
errptr();
618639
}
619640

620641
static void __init selftest(void)

lib/vsprintf.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/build_bug.h>
2222
#include <linux/clk.h>
2323
#include <linux/clk-provider.h>
24+
#include <linux/errname.h>
2425
#include <linux/module.h> /* for KSYM_SYMBOL_LEN */
2526
#include <linux/types.h>
2627
#include <linux/string.h>
@@ -613,6 +614,25 @@ static char *string_nocheck(char *buf, char *end, const char *s,
613614
return widen_string(buf, len, end, spec);
614615
}
615616

617+
static char *err_ptr(char *buf, char *end, void *ptr,
618+
struct printf_spec spec)
619+
{
620+
int err = PTR_ERR(ptr);
621+
const char *sym = errname(err);
622+
623+
if (sym)
624+
return string_nocheck(buf, end, sym, spec);
625+
626+
/*
627+
* Somebody passed ERR_PTR(-1234) or some other non-existing
628+
* Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to
629+
* printing it as its decimal representation.
630+
*/
631+
spec.flags |= SIGN;
632+
spec.base = 10;
633+
return number(buf, end, err, spec);
634+
}
635+
616636
/* Be careful: error messages must fit into the given buffer. */
617637
static char *error_string(char *buf, char *end, const char *s,
618638
struct printf_spec spec)
@@ -2187,6 +2207,11 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
21872207
return kobject_string(buf, end, ptr, spec, fmt);
21882208
case 'x':
21892209
return pointer_string(buf, end, ptr, spec);
2210+
case 'e':
2211+
/* %pe with a non-ERR_PTR gets treated as plain %p */
2212+
if (!IS_ERR(ptr))
2213+
break;
2214+
return err_ptr(buf, end, ptr, spec);
21902215
}
21912216

21922217
/* default is to _not_ leak addresses, hash before printing */
@@ -2823,6 +2848,7 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
28232848
case 'f':
28242849
case 'x':
28252850
case 'K':
2851+
case 'e':
28262852
save_arg(void *);
28272853
break;
28282854
default:
@@ -2999,6 +3025,7 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
29993025
case 'f':
30003026
case 'x':
30013027
case 'K':
3028+
case 'e':
30023029
process = true;
30033030
break;
30043031
default:

0 commit comments

Comments
 (0)