From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:36
This series introduces a new helper, bpf_trace_vprintk, which functions
like bpf_trace_printk but supports > 3 arguments via a pseudo-vararg u64
array. The bpf_printk libbpf convenience macro is modified to use
bpf_trace_vprintk when > 3 varargs are passed, otherwise the previous
behavior - using bpf_trace_printk - is retained.
Helper functions and macros added during the implementation of
bpf_seq_printf and bpf_snprintf do most of the heavy lifting for
bpf_trace_vprintk. There's no novel format string wrangling here.
Usecase here is straightforward: Giving BPF program writers a more
powerful printk will ease development of BPF programs, particularly
during debugging and testing, where printk tends to be used.
This feature was proposed by Andrii in libbpf mirror's issue tracker
[1].
[1] https://github.com/libbpf/libbpf/issues/315
v1 -> v2:
* Naming conversation seems to have gone in favor of keeping
bpf_trace_vprintk, names are unchanged
* Patch 3 now modifies bpf_printk convenience macro to choose between
__bpf_printk and __bpf_vprintk 'implementation' macros based on arg
count. __bpf_vprintk is a renaming of bpf_vprintk convenience macro
from v1, __bpf_printk is the existing bpf_printk implementation.
This patch could use some scrutiny as I think current implementation
may regress developer experience in a specific case, turning a
compile-time error into a load-time error. Unclear to me how
common the case is, or whether the macro magic I chose is ideal.
* char ___fmt[] to static const char ___fmt[] change was not done,
wanted to leave __bpf_printk 'implementation' macro unchanged for v2
to ease discussion of above point
* Removed __always_inline from __set_printk_clr_event [Andrii]
* Simplified bpf_trace_printk docstring to refer to other functions
instead of copy/pasting and avoid specifying 12 vararg limit [Andrii]
* Migrated trace_printk selftest to use ASSERT_ instead of CHECK
* Adds new patch 5, previous patch 5 is now 6
* Migrated trace_vprintk selftest to use ASSERT_ instead of CHECK,
open_and_load instead of separate open, load [Andrii]
* Patch 2's commit message now correctly mentions trace_pipe instead of
dmesg [Andrii]
Dave Marchevsky (6):
bpf: merge printk and seq_printf VARARG max macros
bpf: add bpf_trace_vprintk helper
libbpf: Modify bpf_printk to choose helper based on arg count
bpftool: only probe trace_vprintk feature in 'full' mode
selftests/bpf: Migrate prog_tests/trace_printk CHECKs to ASSERTs
selftests/bpf: add trace_vprintk test prog
include/linux/bpf.h | 3 +
include/uapi/linux/bpf.h | 9 +++
kernel/bpf/core.c | 5 ++
kernel/bpf/helpers.c | 6 +-
kernel/trace/bpf_trace.c | 54 ++++++++++++++-
tools/bpf/bpftool/feature.c | 1 +
tools/include/uapi/linux/bpf.h | 9 +++
tools/lib/bpf/bpf_helpers.h | 45 ++++++++++---
tools/testing/selftests/bpf/Makefile | 3 +-
.../selftests/bpf/prog_tests/trace_printk.c | 24 +++----
.../selftests/bpf/prog_tests/trace_vprintk.c | 65 +++++++++++++++++++
.../selftests/bpf/progs/trace_vprintk.c | 25 +++++++
tools/testing/selftests/bpf/test_bpftool.py | 22 +++----
13 files changed, 228 insertions(+), 43 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/trace_vprintk.c
create mode 100644 tools/testing/selftests/bpf/progs/trace_vprintk.c
--
2.30.2
From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:40
MAX_SNPRINTF_VARARGS and MAX_SEQ_PRINTF_VARARGS are used by bpf helpers
bpf_snprintf and bpf_seq_printf to limit their varargs. Both call into
bpf_bprintf_prepare for print formatting logic and have convenience
macros in libbpf (BPF_SNPRINTF, BPF_SEQ_PRINTF) which use the same
helper macros to convert varargs to a byte array.
Changing shared functionality to support more varargs for either bpf
helper would affect the other as well, so let's combine the _VARARGS
macros to make this more obvious.
Signed-off-by: Dave Marchevsky <redacted>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
---
include/linux/bpf.h | 2 ++
kernel/bpf/helpers.c | 4 +---
kernel/trace/bpf_trace.c | 4 +---
3 files changed, 4 insertions(+), 6 deletions(-)
From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:46
This helper is meant to be "bpf_trace_printk, but with proper vararg
support". Follow bpf_snprintf's example and take a u64 pseudo-vararg
array. Write to /sys/kernel/debug/tracing/trace_pipe using the same
mechanism as bpf_trace_printk.
Signed-off-by: Dave Marchevsky <redacted>
---
include/linux/bpf.h | 1 +
include/uapi/linux/bpf.h | 9 ++++++
kernel/bpf/core.c | 5 ++++
kernel/bpf/helpers.c | 2 ++
kernel/trace/bpf_trace.c | 52 +++++++++++++++++++++++++++++++++-
tools/include/uapi/linux/bpf.h | 9 ++++++
6 files changed, 77 insertions(+), 1 deletion(-)
@@ -4871,6 +4871,14 @@ union bpf_attr {*Return*ValuespecifiedbyuseratBPFlinkcreation/attachmenttime*or0,ifitwasnotspecified.+*+*u64bpf_trace_vprintk(constchar*fmt,u32fmt_size,constvoid*data,u32data_len)+*Description+*Behaveslike**bpf_trace_printk**\()helper,buttakesanarrayofu64+*toformat.Argumentsaretobeusedasin**bpf_seq_printf**\()helper.+*Return+*Thenumberofbyteswrittentothebuffer,oranegativeerror+*incaseoffailure.*/#define __BPF_FUNC_MAPPER(FN) \FN(unspec),\
@@ -5048,6 +5056,7 @@ union bpf_attr {FN(timer_cancel),\FN(get_func_ip),\FN(get_attach_cookie),\+FN(trace_vprintk),\/* *//* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -4871,6 +4871,14 @@ union bpf_attr {*Return*ValuespecifiedbyuseratBPFlinkcreation/attachmenttime*or0,ifitwasnotspecified.+*+*u64bpf_trace_vprintk(constchar*fmt,u32fmt_size,constvoid*data,u32data_len)+*Description+*Behaveslike**bpf_trace_printk**\()helper,buttakesanarrayofu64+*toformat.Argumentsaretobeusedasin**bpf_seq_printf**\()helper.+*Return+*Thenumberofbyteswrittentothebuffer,oranegativeerror+*incaseoffailure.*/#define __BPF_FUNC_MAPPER(FN) \FN(unspec),\
@@ -5048,6 +5056,7 @@ union bpf_attr {FN(timer_cancel),\FN(get_func_ip),\FN(get_attach_cookie),\+FN(trace_vprintk),\/* *//* integer value in 'imm' field of BPF_CALL instruction selects which helper
From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:48
Instead of being a thin wrapper which calls into bpf_trace_printk,
libbpf's bpf_printk convenience macro now chooses between
bpf_trace_printk and bpf_trace_vprintk. If the arg count (excluding
format string) is >3, use bpf_trace_vprintk, otherwise use the older
helper.
The motivation behind this added complexity - instead of migrating
entirely to bpf_trace_vprintk - is to maintain good developer experience
for users compiling against new libbpf but running on older kernels.
Users who are passing <=3 args to bpf_printk will see no change in their
bytecode.
__bpf_vprintk functions similarly to BPF_SEQ_PRINTF and BPF_SNPRINTF
macros elsewhere in the file - it allows use of bpf_trace_vprintk
without manual conversion of varargs to u64 array. Previous
implementation of bpf_printk macro is moved to __bpf_printk for use by
the new implementation.
This does change behavior of bpf_printk calls with >3 args in the "new
libbpf, old kernels" scenario. On my system, using a clang built from
recent upstream sources (14.0.0 https://github.com/llvm/llvm-project.git
50b62731452cb83979bbf3c06e828d26a4698dca), attempting to use 4 args to
__bpf_printk (old impl) results in a compile-time error:
progs/trace_printk.c:21:21: error: too many args to 0x6cdf4b8: i64 = Constant<6>
trace_printk_ret = __bpf_printk("testing,testing %d %d %d %d\n",
I was able to replicate this behavior with an older clang as well. When
the format string has >3 format specifiers, there is no output to the
trace_pipe in either case.
After this patch, using bpf_printk with 4 args would result in a
trace_vprintk helper call being emitted and a load-time failure on older
kernels.
Signed-off-by: Dave Marchevsky <redacted>
---
tools/lib/bpf/bpf_helpers.h | 45 ++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:49
This commit adds a test prog for vprintk which confirms that:
* bpf_trace_vprintk is writing to dmesg
* __bpf_vprintk macro works as expected
* >3 args are printed
Approach and code are borrowed from trace_printk test.
Signed-off-by: Dave Marchevsky <redacted>
---
tools/testing/selftests/bpf/Makefile | 3 +-
.../selftests/bpf/prog_tests/trace_vprintk.c | 65 +++++++++++++++++++
.../selftests/bpf/progs/trace_vprintk.c | 25 +++++++
3 files changed, 92 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/trace_vprintk.c
create mode 100644 tools/testing/selftests/bpf/progs/trace_vprintk.c
@@ -0,0 +1,65 @@+// SPDX-License-Identifier: GPL-2.0+/* Copyright (c) 2021 Facebook */++#include<test_progs.h>++#include"trace_vprintk.lskel.h"++#define TRACEBUF "/sys/kernel/debug/tracing/trace_pipe"+#define SEARCHMSG "1,2,3,4,5,6,7,8,9,10"++voidtest_trace_vprintk(void)+{+interr=0,iter=0,found=0;+structtrace_vprintk__bss*bss;+structtrace_vprintk*skel;+char*buf=NULL;+FILE*fp=NULL;+size_tbuflen;++skel=trace_vprintk__open_and_load();+if(!ASSERT_OK_PTR(skel,"trace_vprintk__open_and_load"))+gotocleanup;++bss=skel->bss;++err=trace_vprintk__attach(skel);+if(!ASSERT_OK(err,"trace_vprintk__attach"))+gotocleanup;++fp=fopen(TRACEBUF,"r");+if(!ASSERT_OK_PTR(fp,"fopen(TRACEBUF)"))+gotocleanup;++/* We do not want to wait forever if this test fails... */+fcntl(fileno(fp),F_SETFL,O_NONBLOCK);++/* wait for tracepoint to trigger */+usleep(1);+trace_vprintk__detach(skel);++if(!ASSERT_GT(bss->trace_vprintk_ran,0,"bss->trace_vprintk_ran"))+gotocleanup;++if(!ASSERT_GT(bss->trace_vprintk_ret,0,"bss->trace_vprintk_ret"))+gotocleanup;++/* verify our search string is in the trace buffer */+while(getline(&buf,&buflen,fp)>=0||errno==EAGAIN){+if(strstr(buf,SEARCHMSG)!=NULL)+found++;+if(found==bss->trace_vprintk_ran)+break;+if(++iter>1000)+break;+}++if(!ASSERT_EQ(found,bss->trace_vprintk_ran,"found"))+gotocleanup;++cleanup:+trace_vprintk__destroy(skel);+free(buf);+if(fp)+fclose(fp);+}
From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:50
Guidance for new tests is to use ASSERT macros instead of CHECK. Since
trace_vprintk test will borrow heavily from trace_printk's, migrate its
CHECKs so it remains obvious that the two are closely related.
Signed-off-by: Dave Marchevsky <redacted>
---
.../selftests/bpf/prog_tests/trace_printk.c | 24 +++++++------------
1 file changed, 9 insertions(+), 15 deletions(-)
@@ -18,25 +18,24 @@ void test_trace_printk(void)size_tbuflen;skel=trace_printk__open();-if(CHECK(!skel,"skel_open","failed to open skeleton\n"))+if(!ASSERT_OK_PTR(skel,"trace_printk__open"))return;-ASSERT_EQ(skel->rodata->fmt[0],'T',"invalid printk fmt string");+ASSERT_EQ(skel->rodata->fmt[0],'T',"skel->rodata->fmt[0]");skel->rodata->fmt[0]='t';err=trace_printk__load(skel);-if(CHECK(err,"skel_load","failed to load skeleton: %d\n",err))+if(!ASSERT_OK(err,"trace_printk__load"))gotocleanup;bss=skel->bss;err=trace_printk__attach(skel);-if(CHECK(err,"skel_attach","skeleton attach failed: %d\n",err))+if(!ASSERT_OK(err,"trace_printk__attach"))gotocleanup;fp=fopen(TRACEBUF,"r");-if(CHECK(fp==NULL,"could not open trace buffer",-"error %d opening %s",errno,TRACEBUF))+if(!ASSERT_OK_PTR(fp,"fopen(TRACEBUF)"))gotocleanup;/* We do not want to wait forever if this test fails... */
@@ -46,14 +45,10 @@ void test_trace_printk(void)usleep(1);trace_printk__detach(skel);-if(CHECK(bss->trace_printk_ran==0,-"bpf_trace_printk never ran",-"ran == %d",bss->trace_printk_ran))+if(!ASSERT_GT(bss->trace_printk_ran,0,"bss->trace_printk_ran"))gotocleanup;-if(CHECK(bss->trace_printk_ret<=0,-"bpf_trace_printk returned <= 0 value",-"got %d",bss->trace_printk_ret))+if(!ASSERT_GT(bss->trace_printk_ret,0,"bss->trace_printk_ret"))gotocleanup;/* verify our search string is in the trace buffer */
@@ -66,8 +61,7 @@ void test_trace_printk(void)break;}-if(CHECK(!found,"message from bpf_trace_printk not found",-"no instance of %s in %s",SEARCHMSG,TRACEBUF))+if(!ASSERT_EQ(found,bss->trace_printk_ran,"found"))gotocleanup;cleanup:
From: Dave Marchevsky <hidden> Date: 2021-08-25 19:58:54
Since commit 368cb0e7cdb5e ("bpftool: Make probes which emit dmesg
warnings optional"), some helpers aren't probed by bpftool unless
`full` arg is added to `bpftool feature probe`.
bpf_trace_vprintk can emit dmesg warnings when probed, so include it.
Signed-off-by: Dave Marchevsky <redacted>
---
tools/bpf/bpftool/feature.c | 1 +
tools/testing/selftests/bpf/test_bpftool.py | 22 +++++++++------------
2 files changed, 10 insertions(+), 13 deletions(-)
@@ -67,10 +72,7 @@ class TestBpftool(unittest.TestCase):@default_ifacedeftest_feature_dev_json(self,iface):-unexpected_helpers=[-"bpf_probe_write_user",-"bpf_trace_printk",-]+unexpected_helpers=DMESG_EMITTING_HELPERSexpected_keys=["syscall_config","program_types",
@@ -94,10 +96,7 @@ class TestBpftool(unittest.TestCase):bpftool_json(["feature","probe"]),bpftool_json(["feature"]),]-unexpected_helpers=[-"bpf_probe_write_user",-"bpf_trace_printk",-]+unexpected_helpers=DMESG_EMITTING_HELPERSexpected_keys=["syscall_config","system_config",
@@ -121,10 +120,7 @@ class TestBpftool(unittest.TestCase):bpftool_json(["feature","probe","kernel","full"]),bpftool_json(["feature","probe","full"]),]-expected_helpers=[-"bpf_probe_write_user",-"bpf_trace_printk",-]+expected_helpers=DMESG_EMITTING_HELPERSfortcintest_cases:# Check if expected helpers are included at least once in any
@@ -157,7 +153,7 @@ class TestBpftool(unittest.TestCase):not_full_set.add(helper)self.assertCountEqual(full_set-not_full_set,-{"bpf_probe_write_user","bpf_trace_printk"})+set(DMESG_EMITTING_HELPERS))self.assertCountEqual(not_full_set-full_set,set())deftest_feature_macros(self):
On Wed, Aug 25, 2021 at 12:58 PM Dave Marchevsky [off-list ref] wrote:
Instead of being a thin wrapper which calls into bpf_trace_printk,
libbpf's bpf_printk convenience macro now chooses between
bpf_trace_printk and bpf_trace_vprintk. If the arg count (excluding
format string) is >3, use bpf_trace_vprintk, otherwise use the older
helper.
The motivation behind this added complexity - instead of migrating
entirely to bpf_trace_vprintk - is to maintain good developer experience
for users compiling against new libbpf but running on older kernels.
Users who are passing <=3 args to bpf_printk will see no change in their
bytecode.
__bpf_vprintk functions similarly to BPF_SEQ_PRINTF and BPF_SNPRINTF
macros elsewhere in the file - it allows use of bpf_trace_vprintk
without manual conversion of varargs to u64 array. Previous
implementation of bpf_printk macro is moved to __bpf_printk for use by
the new implementation.
This does change behavior of bpf_printk calls with >3 args in the "new
libbpf, old kernels" scenario. On my system, using a clang built from
recent upstream sources (14.0.0 https://github.com/llvm/llvm-project.git
50b62731452cb83979bbf3c06e828d26a4698dca), attempting to use 4 args to
__bpf_printk (old impl) results in a compile-time error:
progs/trace_printk.c:21:21: error: too many args to 0x6cdf4b8: i64 = Constant<6>
trace_printk_ret = __bpf_printk("testing,testing %d %d %d %d\n",
and with a new bpf_printk it will compile to use bpf_trace_vprintk
and gets rejected during load on old kernels, right?
That will be the case for any clang.
It's fine.
Would be good to clarify the commit log.
I was able to replicate this behavior with an older clang as well. When
the format string has >3 format specifiers, there is no output to the
trace_pipe in either case.
I don't understand this paragraph. What are the cases?
After this patch, using bpf_printk with 4 args would result in a
trace_vprintk helper call being emitted and a load-time failure on older
kernels.
From: Dave Marchevsky <hidden> Date: 2021-08-28 02:05:52
On 8/25/21 9:01 PM, Alexei Starovoitov wrote:
On Wed, Aug 25, 2021 at 12:58 PM Dave Marchevsky [off-list ref] wrote:
quoted
Instead of being a thin wrapper which calls into bpf_trace_printk,
libbpf's bpf_printk convenience macro now chooses between
bpf_trace_printk and bpf_trace_vprintk. If the arg count (excluding
format string) is >3, use bpf_trace_vprintk, otherwise use the older
helper.
The motivation behind this added complexity - instead of migrating
entirely to bpf_trace_vprintk - is to maintain good developer experience
for users compiling against new libbpf but running on older kernels.
Users who are passing <=3 args to bpf_printk will see no change in their
bytecode.
__bpf_vprintk functions similarly to BPF_SEQ_PRINTF and BPF_SNPRINTF
macros elsewhere in the file - it allows use of bpf_trace_vprintk
without manual conversion of varargs to u64 array. Previous
implementation of bpf_printk macro is moved to __bpf_printk for use by
the new implementation.
This does change behavior of bpf_printk calls with >3 args in the "new
libbpf, old kernels" scenario. On my system, using a clang built from
recent upstream sources (14.0.0 https://github.com/llvm/llvm-project.git
50b62731452cb83979bbf3c06e828d26a4698dca), attempting to use 4 args to
__bpf_printk (old impl) results in a compile-time error:
progs/trace_printk.c:21:21: error: too many args to 0x6cdf4b8: i64 = Constant<6>
trace_printk_ret = __bpf_printk("testing,testing %d %d %d %d\n",
and with a new bpf_printk it will compile to use bpf_trace_vprintk
and gets rejected during load on old kernels, right?
That will be the case for any clang.
It's fine.
Would be good to clarify the commit log.
Yep, I think we're on the same page here. Wanted to call out the
changed behavior in case it felt more like 'breaking user expectations'.
Will simplify the commit message for this patch in v3.
quoted
I was able to replicate this behavior with an older clang as well. When
the format string has >3 format specifiers, there is no output to the
trace_pipe in either case.
I don't understand this paragraph. What are the cases?
This was me trying to enumerate behavior before/after this patch in
order to answer the 'does this break user expectations' question. I was
curious whether clang version affected error messages users would see
when doing things old bpf_printk didn't support (>3 args, >3 format
specifiers). Format specifier >3 case is intentional runtime behavior,
so in retrospect there was no reason to focus on clang version there.
Will remove from commit msg.
quoted
After this patch, using bpf_printk with 4 args would result in a
trace_vprintk helper call being emitted and a load-time failure on older
kernels.
On Wed, Aug 25, 2021 at 12:58 PM Dave Marchevsky [off-list ref] wrote:
quoted hunk
Instead of being a thin wrapper which calls into bpf_trace_printk,
libbpf's bpf_printk convenience macro now chooses between
bpf_trace_printk and bpf_trace_vprintk. If the arg count (excluding
format string) is >3, use bpf_trace_vprintk, otherwise use the older
helper.
The motivation behind this added complexity - instead of migrating
entirely to bpf_trace_vprintk - is to maintain good developer experience
for users compiling against new libbpf but running on older kernels.
Users who are passing <=3 args to bpf_printk will see no change in their
bytecode.
__bpf_vprintk functions similarly to BPF_SEQ_PRINTF and BPF_SNPRINTF
macros elsewhere in the file - it allows use of bpf_trace_vprintk
without manual conversion of varargs to u64 array. Previous
implementation of bpf_printk macro is moved to __bpf_printk for use by
the new implementation.
This does change behavior of bpf_printk calls with >3 args in the "new
libbpf, old kernels" scenario. On my system, using a clang built from
recent upstream sources (14.0.0 https://github.com/llvm/llvm-project.git
50b62731452cb83979bbf3c06e828d26a4698dca), attempting to use 4 args to
__bpf_printk (old impl) results in a compile-time error:
progs/trace_printk.c:21:21: error: too many args to 0x6cdf4b8: i64 = Constant<6>
trace_printk_ret = __bpf_printk("testing,testing %d %d %d %d\n",
I was able to replicate this behavior with an older clang as well. When
the format string has >3 format specifiers, there is no output to the
trace_pipe in either case.
After this patch, using bpf_printk with 4 args would result in a
trace_vprintk helper call being emitted and a load-time failure on older
kernels.
Signed-off-by: Dave Marchevsky <redacted>
---
tools/lib/bpf/bpf_helpers.h | 45 ++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
There is no best solution with macros, but I think this one is
extremely error prone because __bpf_nth invocation is very long and
it's hard to even see where printk turns into vprintk.
How about doing it similarly to ___empty in bpf_core_read.h? It will
be something like this (untested and not even compiled, just a demo)
#define __bpf_printk_kind(...) ___bpf_nth(_, ##__VA_ARGS__, new, new,
new, new, new, <however many>, new, old /*3*/, old /*2*/, old /*1*/,
old /*0*/)
#define bpf_printk(fmt, args...) ___bpf_apply(___bpf_printk_,
___bpf_narg(args))(fmt, args)
And you'll have s/__bpf_printk/__bpf_printk_old/ (using
bpf_trace_printk) and s/__bpf_printk_new/__bpf_vprintk/ (using
bpf_trace_vprintk).
This new/old distinction makes it a bit clearer to me. I find
__bpf_nth so counterintuitive that I try not to use it directly
anywhere at all.
From: Dave Marchevsky <hidden> Date: 2021-09-01 23:29:54
On 8/30/21 7:55 PM, Andrii Nakryiko wrote:
On Wed, Aug 25, 2021 at 12:58 PM Dave Marchevsky [off-list ref] wrote:
quoted
Instead of being a thin wrapper which calls into bpf_trace_printk,
libbpf's bpf_printk convenience macro now chooses between
bpf_trace_printk and bpf_trace_vprintk. If the arg count (excluding
format string) is >3, use bpf_trace_vprintk, otherwise use the older
helper.
The motivation behind this added complexity - instead of migrating
entirely to bpf_trace_vprintk - is to maintain good developer experience
for users compiling against new libbpf but running on older kernels.
Users who are passing <=3 args to bpf_printk will see no change in their
bytecode.
__bpf_vprintk functions similarly to BPF_SEQ_PRINTF and BPF_SNPRINTF
macros elsewhere in the file - it allows use of bpf_trace_vprintk
without manual conversion of varargs to u64 array. Previous
implementation of bpf_printk macro is moved to __bpf_printk for use by
the new implementation.
This does change behavior of bpf_printk calls with >3 args in the "new
libbpf, old kernels" scenario. On my system, using a clang built from
recent upstream sources (14.0.0 https://github.com/llvm/llvm-project.git
50b62731452cb83979bbf3c06e828d26a4698dca), attempting to use 4 args to
__bpf_printk (old impl) results in a compile-time error:
progs/trace_printk.c:21:21: error: too many args to 0x6cdf4b8: i64 = Constant<6>
trace_printk_ret = __bpf_printk("testing,testing %d %d %d %d\n",
I was able to replicate this behavior with an older clang as well. When
the format string has >3 format specifiers, there is no output to the
trace_pipe in either case.
After this patch, using bpf_printk with 4 args would result in a
trace_vprintk helper call being emitted and a load-time failure on older
kernels.
Signed-off-by: Dave Marchevsky <redacted>
---
tools/lib/bpf/bpf_helpers.h | 45 ++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
There is no best solution with macros, but I think this one is
extremely error prone because __bpf_nth invocation is very long and
it's hard to even see where printk turns into vprintk.
How about doing it similarly to ___empty in bpf_core_read.h? It will
be something like this (untested and not even compiled, just a demo)
#define __bpf_printk_kind(...) ___bpf_nth(_, ##__VA_ARGS__, new, new,
new, new, new, <however many>, new, old /*3*/, old /*2*/, old /*1*/,
old /*0*/)
#define bpf_printk(fmt, args...) ___bpf_apply(___bpf_printk_,
___bpf_narg(args))(fmt, args)
And you'll have s/__bpf_printk/__bpf_printk_old/ (using
bpf_trace_printk) and s/__bpf_printk_new/__bpf_vprintk/ (using
bpf_trace_vprintk).
This new/old distinction makes it a bit clearer to me. I find
__bpf_nth so counterintuitive that I try not to use it directly
anywhere at all.
When you're saying 'error prone' here, do you mean something like
'hard to understand and modify'? Asking because IMO adding
___bpf_apply here makes it harder to understand. Having the full
helper macros in ___bpf_nth makes it obvious that they're being used
somehow.
But I feel more strongly that these should not be renamed to __bpf_printk_{old,new}.
Although this is admittedly an edge case, I'd like to leave an 'escape
hatch' for power users who might not want bpf_printk to change the
helper call underneath them - they could use the __bpf_{v}printk
macros directly. Of course they could do the same with _{old,new},
but the rename obscures the name of the underlying helper called,
which is the very thing the hypothetical power user cares about in
this scenario.
One concrete example of such a user: someone who keeps up with
latest bpf developments but needs to run their programs on a fleet
which has some % of older kernels. Using __bpf_printk directly to
force a compile error for >3 fmt args instead of being bitten at
load time would be desireable.
Also, 'new' name leaves open possibility that something newer comes
along in the future and turns 'new' into 'old', which feels churny.
Although if these are never used directly it doesn't matter.
I agree with 'it's hard to even see where printk turns into vprintk'
and like your comment idea. If you're fine with keeping names as-is,
will still add /*3*/ /*2*/... and perhaps a /*BOUNDARY*/ marking the
switch from vprintk to printk.
On Wed, Sep 1, 2021 at 4:29 PM Dave Marchevsky [off-list ref] wrote:
On 8/30/21 7:55 PM, Andrii Nakryiko wrote:
quoted
On Wed, Aug 25, 2021 at 12:58 PM Dave Marchevsky [off-list ref] wrote:
quoted
Instead of being a thin wrapper which calls into bpf_trace_printk,
libbpf's bpf_printk convenience macro now chooses between
bpf_trace_printk and bpf_trace_vprintk. If the arg count (excluding
format string) is >3, use bpf_trace_vprintk, otherwise use the older
helper.
The motivation behind this added complexity - instead of migrating
entirely to bpf_trace_vprintk - is to maintain good developer experience
for users compiling against new libbpf but running on older kernels.
Users who are passing <=3 args to bpf_printk will see no change in their
bytecode.
__bpf_vprintk functions similarly to BPF_SEQ_PRINTF and BPF_SNPRINTF
macros elsewhere in the file - it allows use of bpf_trace_vprintk
without manual conversion of varargs to u64 array. Previous
implementation of bpf_printk macro is moved to __bpf_printk for use by
the new implementation.
This does change behavior of bpf_printk calls with >3 args in the "new
libbpf, old kernels" scenario. On my system, using a clang built from
recent upstream sources (14.0.0 https://github.com/llvm/llvm-project.git
50b62731452cb83979bbf3c06e828d26a4698dca), attempting to use 4 args to
__bpf_printk (old impl) results in a compile-time error:
progs/trace_printk.c:21:21: error: too many args to 0x6cdf4b8: i64 = Constant<6>
trace_printk_ret = __bpf_printk("testing,testing %d %d %d %d\n",
I was able to replicate this behavior with an older clang as well. When
the format string has >3 format specifiers, there is no output to the
trace_pipe in either case.
After this patch, using bpf_printk with 4 args would result in a
trace_vprintk helper call being emitted and a load-time failure on older
kernels.
Signed-off-by: Dave Marchevsky <redacted>
---
tools/lib/bpf/bpf_helpers.h | 45 ++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 8 deletions(-)
There is no best solution with macros, but I think this one is
extremely error prone because __bpf_nth invocation is very long and
it's hard to even see where printk turns into vprintk.
How about doing it similarly to ___empty in bpf_core_read.h? It will
be something like this (untested and not even compiled, just a demo)
#define __bpf_printk_kind(...) ___bpf_nth(_, ##__VA_ARGS__, new, new,
new, new, new, <however many>, new, old /*3*/, old /*2*/, old /*1*/,
old /*0*/)
#define bpf_printk(fmt, args...) ___bpf_apply(___bpf_printk_,
___bpf_narg(args))(fmt, args)
And you'll have s/__bpf_printk/__bpf_printk_old/ (using
bpf_trace_printk) and s/__bpf_printk_new/__bpf_vprintk/ (using
bpf_trace_vprintk).
This new/old distinction makes it a bit clearer to me. I find
__bpf_nth so counterintuitive that I try not to use it directly
anywhere at all.
When you're saying 'error prone' here, do you mean something like
'hard to understand and modify'? Asking because IMO adding
___bpf_apply here makes it harder to understand. Having the full
helper macros in ___bpf_nth makes it obvious that they're being used
somehow.
Hm... I disagree on ___bpf_nth being easier, because of both *reverse*
and *positional* notation. But whichever you prefer, not a big deal.
In this particular case it takes lots of attention to even see at
which position __bpf_vprintk switches to __bpf_printk. They are too
similar and not both verbose and not distinctive enough, IMO.
bpf_apply feels more natural, but I'm the one who wrote a bunch of
bpf_core_read.h macro using that approach, so I'm totally biased.
(Though I wrote and used bpf__nth as well, yet I still hate it, but
it's just a necessary evil).
But I feel more strongly that these should not be renamed to __bpf_printk_{old,new}.
Although this is admittedly an edge case, I'd like to leave an 'escape
hatch' for power users who might not want bpf_printk to change the
helper call underneath them - they could use the __bpf_{v}printk
macros directly. Of course they could do the same with _{old,new},
but the rename obscures the name of the underlying helper called,
which is the very thing the hypothetical power user cares about in
this scenario.
Any of the __ prefixed macro should not be used by anyone and are not
considered part of the API. We can rename, remove, break them at any
time. So regardless of the above, one should not use __bpf_vprintk or
__bpf_vprintk directly in their BPF apps.
One concrete example of such a user: someone who keeps up with
latest bpf developments but needs to run their programs on a fleet
which has some % of older kernels. Using __bpf_printk directly to
force a compile error for >3 fmt args instead of being bitten at
load time would be desireable.
it's not hard for such users to just copy/paste (and actually have
cleaner name). __bpf_printk() is not a hard macro that needs to be
reused by end users.
Also, 'new' name leaves open possibility that something newer comes
along in the future and turns 'new' into 'old', which feels churny.
Although if these are never used directly it doesn't matter.
Right, internal implementation details, as far as end users are concerned.
I agree with 'it's hard to even see where printk turns into vprintk'
and like your comment idea. If you're fine with keeping names as-is,
will still add /*3*/ /*2*/... and perhaps a /*BOUNDARY*/ marking the
switch from vprintk to printk.
BOUNDARY is probably an overkill. Positional comments might be nice, try it.