Subject: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

Adding a new format conversion for *printf() and friends.

If CONFIG_ERRNO_PRINTF_VERBOSE is enabled, prints human-readable
strerror()-like texts, otherwise just the number.
---
lib/Kconfig | 19 +++++++
lib/vsprintf.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 189 insertions(+), 2 deletions(-)

diff --git a/lib/Kconfig b/lib/Kconfig
index 0c8b78a9ae2e..b28ab2162435 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -7,6 +7,25 @@ config BINARY_PRINTF

menu "Library routines"

+config ERRNO_PRINTF
+ bool "printf conversion %M for errno codes"
+ default n
+ help
+ This option adds an %M modifier for *printf() for errno values.
+ (and callers like printk() etc)
+
+ In conjunction with ERRNO_PRINTF_VERBOSE, it prints human readable
+ strerror()-like textsm, otherwise just numeric values
+
+config ERRNO_PRINTF_VERBOSE
+ bool "Verbose errno strings"
+ default y
+ depends on ERRNO_PRINTF
+ help
+ Enable verbose error strings for ERRNO_PRINTF.
+
+ Small embedded systems might disable it for reducing kernel size.
+
config RAID6_PQ
tristate

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 2d41de3f98a1..9778e17fc178 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -382,7 +382,8 @@ enum format_type {
FORMAT_TYPE_UINT,
FORMAT_TYPE_INT,
FORMAT_TYPE_SIZE_T,
- FORMAT_TYPE_PTRDIFF
+ FORMAT_TYPE_PTRDIFF,
+ FORMAT_TYPE_ERRNO,
};

struct printf_spec {
@@ -600,6 +601,164 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec)
return widen_string(buf, len, end, spec);
}

+#if IS_ENABLED(CONFIG_ERRNO_PRINTF_VERBOSE)
+static noinline_for_stack
+const char *errno_to_str(int no)
+{
+ switch (no) {
+ case 0: return "Success";
+ case EPERM: return "Operation not permitted";
+ case ENOENT: return "No such file or directory";
+ case ESRCH: return "No such process";
+ case EINTR: return "Interrupted system call";
+ case EIO: return "I/O error";
+ case ENXIO: return "No such device or address";
+ case E2BIG: return "Argument list too long";
+ case ENOEXEC: return "Exec format error";
+ case EBADF: return "Bad file number";
+ case ECHILD: return "No child processes";
+ case EAGAIN: return "Try again";
+ case ENOMEM: return "Out of memory";
+ case EACCES: return "Permission denied";
+ case EFAULT: return "Bad address";
+ case ENOTBLK: return "Block device required";
+ case EBUSY: return "Device or resource busy";
+ case EEXIST: return "File exists";
+ case EXDEV: return "Cross-device link";
+ case ENODEV: return "No such device";
+ case ENOTDIR: return "Not a directory";
+ case EISDIR: return "Is a directory";
+ case EINVAL: return "Invalid argument";
+ case ENFILE: return "File table overflow";
+ case EMFILE: return "Too many open files";
+ case ENOTTY: return "Not a typewriter";
+ case ETXTBSY: return "Text file busy";
+ case EFBIG: return "File too large";
+ case ENOSPC: return "No space left on device";
+ case ESPIPE: return "Illegal seek";
+ case EROFS: return "Read-only file system";
+ case EMLINK: return "Too many links";
+ case EPIPE: return "Broken pipe";
+ case EDOM: return "Math argument out of domain of func";
+ case ERANGE: return "Math result not representable";
+ case EDEADLK: return "Resource deadlock would occur";
+ case ENAMETOOLONG: return "File name too long";
+ case ENOLCK: return "No record locks available";
+ case ENOSYS: return "Invalid system call number";
+ case ENOTEMPTY: return "Directory not empty";
+ case ELOOP: return "Too many symbolic links encountered";
+ case ENOMSG: return "No message of desired type";
+ case EIDRM: return "Identifier removed";
+ case ECHRNG: return "Channel number out of range";
+ case EL2NSYNC: return "Level 2 not synchronized";
+ case EL3HLT: return "Level 3 halted";
+ case EL3RST: return "Level 3 reset";
+ case ELNRNG: return "Link number out of range";
+ case EUNATCH: return "Protocol driver not attached";
+ case ENOCSI: return "No CSI structure available";
+ case EL2HLT: return "Level 2 halted";
+ case EBADE: return "Invalid exchange";
+ case EBADR: return "Invalid request descriptor";
+ case EXFULL: return "Exchange full";
+ case ENOANO: return "No anode";
+ case EBADRQC: return "Invalid request code";
+ case EBADSLT: return "Invalid slot";
+ case EBFONT: return "Bad font file format";
+ case ENOSTR: return "Device not a stream";
+ case ENODATA: return "No data available";
+ case ETIME: return "Timer expired";
+ case ENOSR: return "Out of streams resources";
+ case ENONET: return "Machine is not on the network";
+ case ENOPKG: return "Package not installed";
+ case EREMOTE: return "Object is remote";
+ case ENOLINK: return "Link has been severed";
+ case EADV: return "Advertise error";
+ case ESRMNT: return "Srmount error";
+ case ECOMM: return "Communication error on send";
+ case EPROTO: return "Protocol error";
+ case EMULTIHOP: return "Multihop attempted";
+ case EDOTDOT: return "RFS specific error";
+ case EBADMSG: return "Not a data message";
+ case EOVERFLOW: return "Value too large for defined data type";
+ case ENOTUNIQ: return "Name not unique on network";
+ case EBADFD: return "File descriptor in bad state";
+ case EREMCHG: return "Remote address changed";
+ case ELIBACC: return "Can not access a needed shared library";
+ case ELIBBAD: return "Accessing a corrupted shared library";
+ case ELIBSCN: return ".lib section in a.out corrupted";
+ case ELIBMAX: return "Attempting to link in too many shared libraries";
+ case ELIBEXEC: return "Cannot exec a shared library directly";
+ case EILSEQ: return "Illegal byte sequence";
+ case ERESTART: return "Interrupted system call should be restarted";
+ case ESTRPIPE: return "Streams pipe error";
+ case EUSERS: return "Too many users";
+ case EDESTADDRREQ: return "Destination address required";
+ case EMSGSIZE: return "Message too long";
+ case EPROTOTYPE: return "Protocol wrong type for socket";
+ case ENOPROTOOPT: return "Protocol not available";
+ case EPROTONOSUPPORT: return "Protocol not supported";
+ case ESOCKTNOSUPPORT: return "Socket type not supported";
+ case EOPNOTSUPP: return "Operation not supported on transport endpoint";
+ case EPFNOSUPPORT: return "Protocol family not supported";
+ case EAFNOSUPPORT: return "Address family not supported by protocol";
+ case EADDRINUSE: return "Address already in use";
+ case EADDRNOTAVAIL: return "Cannot assign requested address";
+ case ENETDOWN: return "Network is down";
+ case ENETUNREACH: return "Network is unreachable";
+ case ENETRESET: return "Network dropped connection because of reset";
+ case ECONNABORTED: return "Software caused connection abort";
+ case ECONNRESET: return "Connection reset by peer";
+ case ENOBUFS: return "No buffer space available";
+ case EISCONN: return "Transport endpoint is already connected";
+ case ENOTCONN: return "Transport endpoint is not connected";
+ case ESHUTDOWN: return "Cannot send after transport endpoint shutdown";
+ case ETOOMANYREFS: return "Too many references: cannot splice";
+ case ETIMEDOUT: return "Connection timed out";
+ case ECONNREFUSED: return "Connection refused";
+ case EHOSTDOWN: return "Host is down";
+ case EHOSTUNREACH: return "No route to host";
+ case EALREADY: return "Operation already in progress";
+ case EINPROGRESS: return "Operation now in progress";
+ case ESTALE: return "Stale file handle";
+ case EUCLEAN: return "Structure needs cleaning";
+ case ENOTNAM: return "Not a XENIX named type file";
+ case ENAVAIL: return "No XENIX semaphores available";
+ case EISNAM: return "Is a named type file";
+ case EREMOTEIO: return "Remote I/O error";
+ case EDQUOT: return "Quota exceeded";
+ case ENOMEDIUM: return "No medium found";
+ case EMEDIUMTYPE: return "Wrong medium type";
+ case ECANCELED: return "Operation Canceled";
+ case ENOKEY: return "Required key not available";
+ case EKEYEXPIRED: return "Key has expired";
+ case EKEYREVOKED: return "Key has been revoked";
+ case EKEYREJECTED: return "Key was rejected by service";
+ case EOWNERDEAD: return "Owner died";
+ case ENOTRECOVERABLE: return "State not recoverable";
+ case ERFKILL: return "Operation not possible due to RF-kill";
+ case EHWPOISON: return "Memory page has hardware error";
+ }
+ return NULL;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_ERRNO_PRINTF)
+static noinline_for_stack
+char *errno_string(char *buf, char *end, int errcode, struct printf_spec spec)
+{
+ char buffer[32];
+
+#if IS_ENABLED(CONFIG_ERRNO_PRINTF_VERBOSE)
+ const char *estr = errno_to_str(errcode);
+ if (estr != NULL)
+ return string(buf, end, estr, spec);
+#endif
+
+ snprintf(buffer, sizeof(buffer), "error %d", errcode);
+ return string(buf, end, buffer, spec);
+}
+#endif
+
static noinline_for_stack
char *dentry_name(char *buf, char *end, const struct dentry *d, struct printf_spec spec,
const char *fmt)
@@ -1744,6 +1903,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
* 'z' changed to 'Z' --davidm 1/25/99
* 'Z' changed to 'z' --adobriyan 2017-01-25
* 't' added for ptrdiff_t
+ * 'M' added for strerror()-like output --mtx 2017-06-25
*
* @fmt: the format string
* @type of the token returned
@@ -1866,6 +2026,10 @@ int format_decode(const char *fmt, struct printf_spec *spec)
spec->type = FORMAT_TYPE_STR;
return ++fmt - start;

+ case 'M':
+ spec->type = FORMAT_TYPE_ERRNO;
+ return ++fmt - start;
+
case 'p':
spec->type = FORMAT_TYPE_PTR;
return ++fmt - start;
@@ -2047,7 +2211,11 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
case FORMAT_TYPE_STR:
str = string(str, end, va_arg(args, char *), spec);
break;
-
+#if IS_ENABLED(CONFIG_ERRNO_PRINTF)
+ case FORMAT_TYPE_ERRNO:
+ str = errno_string(str, end, va_arg(args, int), spec);
+ break;
+#endif
case FORMAT_TYPE_PTR:
str = pointer(fmt, str, end, va_arg(args, void *),
spec);
--
2.11.0.rc0.7.gbe5a750


2017-06-25 17:24:16

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On 06/25/2017 10:12 AM, Enrico Weigelt, metux IT consult wrote:
> Adding a new format conversion for *printf() and friends.
>
> If CONFIG_ERRNO_PRINTF_VERBOSE is enabled, prints human-readable
> strerror()-like texts, otherwise just the number.
> ---
> lib/Kconfig | 19 +++++++
> lib/vsprintf.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 189 insertions(+), 2 deletions(-)
>
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 0c8b78a9ae2e..b28ab2162435 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -7,6 +7,25 @@ config BINARY_PRINTF
>
> menu "Library routines"
>
> +config ERRNO_PRINTF
> + bool "printf conversion %M for errno codes"
> + default n
> + help
> + This option adds an %M modifier for *printf() for errno values.
> + (and callers like printk() etc)
> +
> + In conjunction with ERRNO_PRINTF_VERBOSE, it prints human readable
> + strerror()-like textsm, otherwise just numeric values

text, values.

> +
> +config ERRNO_PRINTF_VERBOSE
> + bool "Verbose errno strings"
> + default y
> + depends on ERRNO_PRINTF
> + help
> + Enable verbose error strings for ERRNO_PRINTF.
> +
> + Small embedded systems might disable it for reducing kernel size.
> +
> config RAID6_PQ
> tristate
>


--
~Randy

2017-06-25 17:27:08

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On Sun, 2017-06-25 at 19:12 +0200, Enrico Weigelt, metux IT consult
wrote:
> Adding a new format conversion for *printf() and friends.

Every use of %M is going to cause gcc when using __printf to emit
a warning like:

unknown conversion type character ‘M’ in format [-Wformat=]

Beyond that, why is this useful?
There can't possibly be any fast-path use.
Why not just create a function that does errno/string conversions?

Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On 25.06.2017 19:27, Joe Perches wrote:

> Every use of %M is going to cause gcc when using __printf to emit
> a warning like:
>
> unknown conversion type character ‘M’ in format [-Wformat=]

Yeah, that's still an open problem. Actually, I still haven't found out,
how it's done w/ all the other kernel-internal conversions. I was under
the impression, there was some magic to tell the compiler which letters
correspond to which types - unfortunately, didn't find anything like
that. Is that really hardcoded in gcc ?

> Beyond that, why is this useful?

Use that instead of %d where errno values are printed/logged.

> There can't possibly be any fast-path use.

I'm using it eg. for driver development - always having to look up the
numbers is quite ugly and time consuming.

> Why not just create a function that does errno/string conversions?

Already was about to do so. Shall I call it strerror() ?


--mtx

--
Enrico, Sohn von Wilfried, a.d.F. Weigelt,
metux IT consulting
+49-151-27565287

2017-06-25 20:10:54

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On Sun, 2017-06-25 at 21:53 +0200, Enrico Weigelt, metux IT consult
wrote:
> On 25.06.2017 19:27, Joe Perches wrote:
>
> > Every use of %M is going to cause gcc when using __printf to emit
> > a warning like:
> >
> > unknown conversion type character ‘M’ in format [-Wformat=]
>
> Yeah, that's still an open problem. Actually, I still haven't found out,
> how it's done w/ all the other kernel-internal conversions.

Everything else uses "%p<foo>", <object reference>

> I was under
> the impression, there was some magic to tell the compiler which letters
> correspond to which types - unfortunately, didn't find anything like
> that. Is that really hardcoded in gcc ?
>
> > Beyond that, why is this useful?
>
> Use that instead of %d where errno values are printed/logged.
> > There can't possibly be any fast-path use.
>
> I'm using it eg. for driver development - always having to look up the
> numbers is quite ugly and time consuming.
>
> > Why not just create a function that does errno/string conversions?
>
> Already was about to do so. Shall I call it strerror() ?

I presume kstrerror

So use something like
"%d: (%s)", errno, kstrerror(errno)

Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On 25.06.2017 22:10, Joe Perches wrote:

>> Yeah, that's still an open problem. Actually, I still haven't found out,
>> how it's done w/ all the other kernel-internal conversions.
>
> Everything else uses "%p<foo>", <object reference>

hmm, but errno's aren't pointers. Isn't %p checked for pointer values ?
>> Already was about to do so. Shall I call it strerror() ?
>
> I presume kstrerror
>
> So use something like
> "%d: (%s)", errno, kstrerror(errno)

Okay, sounds good.

--mtx

2017-06-25 22:47:50

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On 06/25/2017 02:18 PM, Enrico Weigelt, metux IT consult wrote:
> On 25.06.2017 22:10, Joe Perches wrote:
>
>>> Yeah, that's still an open problem. Actually, I still haven't found out,
>>> how it's done w/ all the other kernel-internal conversions.
>>
>> Everything else uses "%p<foo>", <object reference>
>
> hmm, but errno's aren't pointers. Isn't %p checked for pointer values ?
>>> Already was about to do so. Shall I call it strerror() ?
>>
>> I presume kstrerror
>>
>> So use something like
>> "%d: (%s)", errno, kstrerror(errno)
>
> Okay, sounds good.

but why not just do that in userspace. Sure, you can keep that as your
internal kernel patch, but there's not really much need for it to be in the
mainline kernel. So when your driver prints "blah: foo bar error 49",
just run a little program that converts 49 to <whatever>.

--
~Randy

Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

On 26.06.2017 00:47, Randy Dunlap wrote:

> but why not just do that in userspace.

Patch up syslogd (which one, actually?) to decode all the dozens of
different cases that print out errno values ?

Applying your argument more consequently - why do we have human-readable
messages at all, instead of just writing out message IDs as hexdumps or
binary stream (would make the kernel a lot smaller) ?

> but there's not really much need for it to be in the mainline kernel.

The idea was having a generic and convenient solution that can be used
by other parts of the kernel, too. (just like w/ all the other logging
helpers). That's also the reason why I added into vsprintf().

> So when your driver prints "blah: foo bar error 49",
> just run a little program that converts 49 to <whatever>.

How could that help when userland isn't even up yet ?


--mtx

--
Enrico, Sohn von Wilfried, a.d.F. Weigelt,
metux IT consulting
+49-151-27565287

2017-06-27 11:03:59

by Bernd Petrovitsch

[permalink] [raw]
Subject: Re: [PATCH] lib: vsprintf: add printf format conversion %M for errno strings

Hi all!

On Sun, 2017-06-25 at 15:47 -0700, Randy Dunlap wrote:
> On 06/25/2017 02:18 PM, Enrico Weigelt, metux IT consult wrote:
> > On 25.06.2017 22:10, Joe Perches wrote:
> >
> > > > Yeah, that's still an open problem. Actually, I still haven't found out,
> > > > how it's done w/ all the other kernel-internal conversions.

That is hard-coded in gcc ATM TTBOMK (AFAIK BSDs ship patched gcc's for
their in-kernel extensions of format strings).

> > > Everything else uses "%p<foo>", <object reference>
> >
> > hmm, but errno's aren't pointers. Isn't %p checked for pointer
> > values ?

Yup, and gcc will also generate a format string warning/error.

> > > > Already was about to do so. Shall I call it strerror() ?
> > >
> > > I presume kstrerror
> > >
> > > So use something like
> > > "%d: (%s)", errno, kstrerror(errno)
> >
> > Okay, sounds good.
>
> but why not just do that in userspace. Sure, you can keep that as your
> internal kernel patch, but there's not really much need for it to be in the
> mainline kernel.  So when your driver prints "blah: foo bar error 49",
> just run a little program that converts 49 to <whatever>.

Userspace can just guess if a given "49" is an errno or not ...

MfG,
Bernd
--
Bernd Petrovitsch Email : [email protected]
LUGA : http://www.luga.at