diff options
-rw-r--r-- | Documentation/core-api/printk-formats.rst | 22 | ||||
-rw-r--r-- | lib/test_printf.c | 20 | ||||
-rw-r--r-- | lib/vsprintf.c | 81 |
3 files changed, 121 insertions, 2 deletions
diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index 5e89497ba314..8fc0b62af158 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -625,6 +625,28 @@ Examples:: %p4cc Y10 little-endian (0x20303159) %p4cc NV12 big-endian (0xb231564e) +Calling a pretty printer function +--------------------------------- + +:: + + %pf(%p) pretty printer function taking one argument + %pf(%p,%p) pretty printer function taking two arguments + +For calling generic pretty printers. A pretty printer is a function that takes +as its first argument a pointer to a printbuf, and then zero or more additional +pointer arguments. For example: + + void foo_to_text(struct printbuf *out, struct foo *foo) + { + pr_buf(out, "bar=%u baz=%u", foo->bar, foo->baz); + } + + printf("%pf(%p)", foo_to_text, foo); + +Note that a pretty-printer may not sleep, if called from printk(). If called +from pr_buf() or sprintf() there are no such restrictions. + Thanks ====== diff --git a/lib/test_printf.c b/lib/test_printf.c index 07309c45f327..52df049d4ab8 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -783,6 +783,25 @@ test_pointer(void) fourcc_pointer(); } +static void printf_test_fn(struct printbuf *out, void *p) +{ + int *i = p; + + pr_buf(out, "%i", *i); +} + +static void __init +test_fn(void) +{ + int i = 1; + + test("1", "%pf(%p)", printf_test_fn, &i); + /* + * Not tested, so we don't fail the build with -Werror: + */ + //test("1", "%(%p)", printf_test_fn, &i); +} + static void __init selftest(void) { alloced_buffer = kmalloc(BUF_SIZE + 2*PAD_SIZE, GFP_KERNEL); @@ -794,6 +813,7 @@ static void __init selftest(void) test_number(); test_string(); test_pointer(); + test_fn(); kfree(alloced_buffer); } diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 09b259e03041..7fbeaf50d11c 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -431,7 +431,8 @@ enum format_type { FORMAT_TYPE_UINT, FORMAT_TYPE_INT, FORMAT_TYPE_SIZE_T, - FORMAT_TYPE_PTRDIFF + FORMAT_TYPE_PTRDIFF, + FORMAT_TYPE_FN, }; struct printf_spec { @@ -2512,7 +2513,16 @@ qualifier: return ++fmt - start; case 'p': - spec->type = FORMAT_TYPE_PTR; + fmt++; + if (fmt[0] == 'f' && + fmt[1] == '(') { + fmt += 2; + spec->type = FORMAT_TYPE_FN; + } else + spec->type = FORMAT_TYPE_PTR; + return fmt - start; + case '(': + spec->type = FORMAT_TYPE_FN; return ++fmt - start; case '%': @@ -2594,6 +2604,49 @@ set_precision(struct printf_spec *spec, int prec) } } +static void call_pr_fn(struct printbuf *out, void *fn, void **fn_args, unsigned nr_args) +{ + typedef void (*printf_fn_0)(struct printbuf *); + typedef void (*printf_fn_1)(struct printbuf *, void *); + typedef void (*printf_fn_2)(struct printbuf *, void *, void *); + typedef void (*printf_fn_3)(struct printbuf *, void *, void *, void *); + typedef void (*printf_fn_4)(struct printbuf *, void *, void *, void *, void *); + typedef void (*printf_fn_5)(struct printbuf *, void *, void *, void *, void *, void *); + typedef void (*printf_fn_6)(struct printbuf *, void *, void *, void *, void *, void *, void *); + typedef void (*printf_fn_7)(struct printbuf *, void *, void *, void *, void *, void *, void *, void *); + typedef void (*printf_fn_8)(struct printbuf *, void *, void *, void *, void *, void *, void *, void *, void *); + + switch (nr_args) { + case 0: + ((printf_fn_0)fn)(out); + break; + case 1: + ((printf_fn_1)fn)(out, fn_args[0]); + break; + case 2: + ((printf_fn_2)fn)(out, fn_args[0], fn_args[1]); + break; + case 3: + ((printf_fn_3)fn)(out, fn_args[0], fn_args[1], fn_args[2]); + break; + case 4: + ((printf_fn_4)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3]); + break; + case 5: + ((printf_fn_5)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4]); + break; + case 6: + ((printf_fn_6)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4], fn_args[5]); + break; + case 7: + ((printf_fn_7)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4], fn_args[5], fn_args[6]); + break; + case 8: + ((printf_fn_8)fn)(out, fn_args[0], fn_args[1], fn_args[2], fn_args[3], fn_args[4], fn_args[5], fn_args[6], fn_args[7]); + break; + } +} + /** * vpr_buf - Format a string, outputting to a printbuf * @out: The printbuf to output to @@ -2657,6 +2710,30 @@ void vpr_buf(struct printbuf *out, const char *fmt, va_list args) fmt++; break; + case FORMAT_TYPE_FN: { + unsigned nr_args = 0; + void *fn_args[8]; + void *fn = va_arg(args, void *); + + while (1) { + if (WARN_ON_ONCE(nr_args == ARRAY_SIZE(fn_args))) + goto out; + if (*fmt++ != '%') + goto out; + if (*fmt++ != 'p') + goto out; + fn_args[nr_args++] = va_arg(args, void *); + if (*fmt == ')') + break; + if (*fmt++ != ',') + goto out; + } + + call_pr_fn(out, fn, fn_args, nr_args); + fmt++; /* past trailing ) */ + break; + } + case FORMAT_TYPE_PERCENT_CHAR: __pr_char(out, '%'); break; |