mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kent Overstreet <kent.overstreet@gmail.com>
To: linux-kernel@vger.kernel.org, linux-mm@vger.kernel.org,
	pmladek@suse.com, rostedt@goodmis.org, senozhatsky@chromium.org
Cc: Kent Overstreet <kent.overstreet@gmail.com>,
	andriy.shevchenko@linux.intel.com, willy@infradead.org
Subject: [PATCH v2 03/28] vsprintf: %pf(%p)
Date: Thu, 19 May 2022 13:23:56 -0400	[thread overview]
Message-ID: <20220519172421.162394-4-kent.overstreet@gmail.com> (raw)
In-Reply-To: <20220519172421.162394-1-kent.overstreet@gmail.com>

This implements two new format strings: both do the same thing, one more
compatible with current gcc format string checking, the other that we'd
like to standardize:

 %p(%p) - more compatible
 %(%p)  - more prettier

Both can take variable numbers of arguments, i.e. %(%p,%p,%p).

They're used to indicate that snprintf or pr_buf should interpret the
next argument as a pretty-printer function to call, and subsequent
arguments within the parentheses should be passed to the pretty-printer.

A pretty printer takes as its first argument a printbuf, and then zero
or more pointer arguments - integer arguments are not (currently) supported.

Example usage:

static void foo_to_text(struct printbuf *out, struct foo *foo)
{
	pr_buf(out, "bar=%u baz=%u", foo->bar, foo->baz);
}

printf("%(%p)", foo_to_text, foo);

The goal is to replace most of our %p format extensions with this
interface, and to move pretty-printers out of the core vsprintf.c code -
this will get us better organization and better discoverability (you'll
be able to cscope to pretty printer calls!), as well as eliminate a lot
of dispatch code in vsprintf.c.

Currently, we can only call pretty printers with pointer arguments. This
could be changed to also allow at least integer arguments in the future
by using libffi.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
---
 Documentation/core-api/printk-formats.rst | 19 ++++++
 lib/test_printf.c                         | 17 +++++
 lib/vsprintf.c                            | 81 ++++++++++++++++++++++-
 3 files changed, 115 insertions(+), 2 deletions(-)

diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index 5e89497ba3..e4ed40960e 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -625,6 +625,25 @@ Examples::
 	%p4cc	Y10  little-endian (0x20303159)
 	%p4cc	NV12 big-endian (0xb231564e)
 
+Calling a pretty printer function
+---------------------------------
+
+::
+
+        %p(%p)     pretty printer function taking one argument
+        %p(%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("%p(%p)", foo_to_text, foo);
+
 Thanks
 ======
 
diff --git a/lib/test_printf.c b/lib/test_printf.c
index 07309c45f3..1e1604ef1a 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -783,6 +783,22 @@ 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);
+	test("1", "%(%p)", printf_test_fn, &i);
+}
+
 static void __init selftest(void)
 {
 	alloced_buffer = kmalloc(BUF_SIZE + 2*PAD_SIZE, GFP_KERNEL);
@@ -794,6 +810,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 09b259e030..7fbeaf50d1 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 @@ int format_decode(const char *fmt, struct printf_spec *spec)
 		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;
-- 
2.36.0


  parent reply	other threads:[~2022-05-19 17:25 UTC|newest]

Thread overview: 62+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-19 17:23 [PATCH v2 00/28] Printbufs (now with more printbufs!) Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 01/28] lib/printbuf: New data structure for printing strings Kent Overstreet
2022-05-19 18:21   ` Matthew Wilcox
2022-05-20  4:44     ` Kent Overstreet
2022-05-26 15:06   ` Petr Mladek
2022-05-26 15:21     ` Kent Overstreet
2022-05-26 15:36       ` Joe Perches
2022-05-27 10:29       ` Petr Mladek
2022-05-29 18:15         ` Kent Overstreet
2022-05-31 22:03         ` Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 02/28] vsprintf: Convert to printbuf Kent Overstreet
2022-05-19 17:23 ` Kent Overstreet [this message]
2022-05-19 18:33   ` [PATCH v2 03/28] vsprintf: %pf(%p) Matthew Wilcox
2022-05-20  4:43     ` Kent Overstreet
2022-05-19 21:06   ` David Laight
2022-05-19 21:15     ` Matthew Wilcox
2022-05-20  8:03       ` David Laight
2022-05-20 13:08         ` Matthew Wilcox
2022-05-20  4:49     ` Kent Overstreet
2022-05-20 14:17       ` andriy.shevchenko
2022-05-19 21:20   ` Matthew Wilcox
2022-05-20  7:40   ` Michal Hocko
2022-05-20 15:01     ` Kent Overstreet
2022-05-20 17:56     ` Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 04/28] lib/string_helpers: string_get_size() now returns characters wrote Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 05/28] lib/printbuf: Heap allocation Kent Overstreet
2022-05-19 17:23 ` [PATCH v2 06/28] lib/printbuf: Tabstops, indenting Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 07/28] lib/printbuf: Unit specifiers Kent Overstreet
2022-05-19 20:21   ` Andy Shevchenko
2022-05-19 20:26     ` Kent Overstreet
2022-05-19 21:11       ` Matthew Wilcox
2022-05-20  4:40         ` Kent Overstreet
2022-05-19 21:16       ` Andy Shevchenko
2022-05-19 21:22         ` Matthew Wilcox
2022-05-20  4:36         ` Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 08/28] lib/pretty-printers: pr_string_option(), pr_bitflags() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 09/28] vsprintf: Improve number() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 10/28] vsprintf: pr_u64_minwidth(), pr_u64() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 11/28] vsprintf: Lift pr_hex_bytes() out from hex_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 12/28] test_printf: Drop requirement that sprintf not write past nul Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 13/28] vsprintf: Start consolidating printf_spec handling Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 14/28] vsprintf: Refactor resource_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 15/28] vsprintf: Refactor fourcc_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 16/28] vsprintf: Refactor ip_addr_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 17/28] vsprintf: Refactor mac_address_string() Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 18/28] vsprintf: time_and_date() no longer takes printf_spec Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 19/28] vsprintf: flags_string() " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 20/28] vsprintf: Refactor device_node_string, fwnode_string Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 21/28] vsprintf: Refactor hex_string, bitmap_string_list, bitmap_string Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 22/28] Input/joystick/analog: Convert from seq_buf -> printbuf Kent Overstreet
2022-05-19 20:04   ` Andy Shevchenko
2022-05-19 20:19     ` Kent Overstreet
2022-05-19 21:09       ` Andy Shevchenko
2022-05-19 17:24 ` [PATCH v2 23/28] mm/memcontrol.c: Convert to printbuf Kent Overstreet
2022-05-20  7:59   ` Michal Hocko
2022-05-19 17:24 ` [PATCH v2 24/28] clk: tegra: bpmp: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 25/28] tools/testing/nvdimm: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 26/28] powerpc: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 27/28] x86/resctrl: " Kent Overstreet
2022-05-19 17:24 ` [PATCH v2 28/28] PCI/P2PDMA: " Kent Overstreet
2022-05-26 14:44 ` [PATCH v2 00/28] Printbufs (now with more printbufs!) Petr Mladek
2022-05-26 15:11   ` Kent Overstreet

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220519172421.162394-4-kent.overstreet@gmail.com \
    --to=kent.overstreet@gmail.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@vger.kernel.org \
    --cc=pmladek@suse.com \
    --cc=rostedt@goodmis.org \
    --cc=senozhatsky@chromium.org \
    --cc=willy@infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®