diff mbox series

[bpf-next,v5,7/9] tools: bpftool: add C-style "#define" output for probes

Message ID 20190117152758.14883-8-quentin.monnet@netronome.com
State Accepted
Delegated to: BPF Maintainers
Headers show
Series tools: bpftool: add probes for system and device | expand

Commit Message

Quentin Monnet Jan. 17, 2019, 3:27 p.m. UTC
Make bpftool able to dump a subset of the parameters collected by
probing the system as a listing of C-style #define macros, so that
external projects can reuse the result of this probing and build
BPF-based project in accordance with the features available on the
system.

The new "macros" keyword is used to select this output. An additional
"prefix" keyword is added so that users can select a custom prefix for
macro names, in order to avoid any namespace conflict.

Sample output:

    # bpftool feature probe kernel macros prefix FOO_
    /*** System call availability ***/
    #define FOO_HAVE_BPF_SYSCALL

    /*** eBPF program types ***/
    #define FOO_HAVE_SOCKET_FILTER_PROG_TYPE
    #define FOO_HAVE_KPROBE_PROG_TYPE
    #define FOO_HAVE_SCHED_CLS_PROG_TYPE
    ...

    /*** eBPF map types ***/
    #define FOO_HAVE_HASH_MAP_TYPE
    #define FOO_HAVE_ARRAY_MAP_TYPE
    #define FOO_HAVE_PROG_ARRAY_MAP_TYPE
    ...

    /*** eBPF helper functions ***/
    /*
     * Use FOO_HAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)
     * to determine if <helper_name> is available for <prog_type_name>,
     * e.g.
     *      #if FOO_HAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)
     *              // do stuff with this helper
     *      #elif
     *              // use a workaround
     *      #endif
     */
    #define FOO_HAVE_PROG_TYPE_HELPER(prog_type, helper)        \
            FOO_BPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper
    ...
    #define FOO_BPF__PROG_TYPE_socket_filter__HELPER_bpf_probe_read 0
    #define FOO_BPF__PROG_TYPE_socket_filter__HELPER_bpf_ktime_get_ns 1
    #define FOO_BPF__PROG_TYPE_socket_filter__HELPER_bpf_trace_printk 1
    ...

v3:
- Change output for helpers again: add a
  HAVE_PROG_TYPE_HELPER(type, helper) macro that can be used to tell
  if <helper> is available for program <type>.

v2:
- #define-based output added as a distinct patch.
- "HAVE_" prefix appended to macro names.
- Output limited to bpf() syscall availability, BPF prog and map types,
  helper functions. In this version kernel config options, procfs
  parameter or kernel version are intentionally left aside.
- Following the change on helper probes, format for helper probes in
  this output style has changed (now a list of compatible program
  types).

Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Stanislav Fomichev <sdf@google.com>
---
 .../bpftool/Documentation/bpftool-feature.rst |  13 +-
 tools/bpf/bpftool/feature.c                   | 150 ++++++++++++++----
 2 files changed, 133 insertions(+), 30 deletions(-)
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/Documentation/bpftool-feature.rst b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
index 255e3b3629a0..53092995f46b 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-feature.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-feature.rst
@@ -19,15 +19,24 @@  SYNOPSIS
 MAP COMMANDS
 =============
 
-|	**bpftool** **feature probe** [**kernel**]
+|	**bpftool** **feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
 |	**bpftool** **feature help**
 
 DESCRIPTION
 ===========
-	**bpftool feature probe** [**kernel**]
+	**bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
 		  Probe the running kernel and dump a number of eBPF-related
 		  parameters, such as availability of the **bpf()** system call.
 
+		  If the **macros** keyword (but not the **-j** option) is
+		  passed, a subset of the output is dumped as a list of
+		  **#define** macros that are ready to be included in a C
+		  header file, for example. If, additionally, **prefix** is
+		  used to define a *PREFIX*, the provided string will be used
+		  as a prefix to the names of the macros: this can be used to
+		  avoid conflicts on macro names when including the output of
+		  this command as a header file.
+
 		  Keyword **kernel** can be omitted.
 
 		  Note that when probed, some eBPF helpers (e.g.
diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 55c8d215ca44..a62e637953b7 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -46,13 +46,25 @@  static bool check_procfs(void)
 	return true;
 }
 
+static void uppercase(char *str, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len && str[i] != '\0'; i++)
+		str[i] = toupper(str[i]);
+}
+
 /* Printing utility functions */
 
 static void
-print_bool_feature(const char *feat_name, const char *plain_name, bool res)
+print_bool_feature(const char *feat_name, const char *plain_name,
+		   const char *define_name, bool res, const char *define_prefix)
 {
 	if (json_output)
 		jsonw_bool_field(json_wtr, feat_name, res);
+	else if (define_prefix)
+		printf("#define %s%sHAVE_%s\n", define_prefix,
+		       res ? "" : "NO_", define_name);
 	else
 		printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
 }
@@ -62,6 +74,8 @@  static void print_kernel_option(const char *name, const char *value)
 	char *endptr;
 	int res;
 
+	/* No support for C-style ouptut */
+
 	if (json_output) {
 		if (!value) {
 			jsonw_null_field(json_wtr, name);
@@ -82,25 +96,31 @@  static void print_kernel_option(const char *name, const char *value)
 }
 
 static void
-print_start_section(const char *json_title, const char *plain_title)
+print_start_section(const char *json_title, const char *plain_title,
+		    const char *define_comment, const char *define_prefix)
 {
 	if (json_output) {
 		jsonw_name(json_wtr, json_title);
 		jsonw_start_object(json_wtr);
+	} else if (define_prefix) {
+		printf("%s\n", define_comment);
 	} else {
 		printf("%s\n", plain_title);
 	}
 }
 
 static void
-print_end_then_start_section(const char *json_title, const char *plain_title)
+print_end_then_start_section(const char *json_title, const char *plain_title,
+			     const char *define_comment,
+			     const char *define_prefix)
 {
 	if (json_output)
 		jsonw_end_object(json_wtr);
 	else
 		printf("\n");
 
-	print_start_section(json_title, plain_title);
+	print_start_section(json_title, plain_title, define_comment,
+			    define_prefix);
 }
 
 /* Probing functions */
@@ -134,6 +154,8 @@  static void probe_unprivileged_disabled(void)
 {
 	int res;
 
+	/* No support for C-style ouptut */
+
 	res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
 	if (json_output) {
 		jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
@@ -158,6 +180,8 @@  static void probe_jit_enable(void)
 {
 	int res;
 
+	/* No support for C-style ouptut */
+
 	res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
 	if (json_output) {
 		jsonw_int_field(json_wtr, "bpf_jit_enable", res);
@@ -186,6 +210,8 @@  static void probe_jit_harden(void)
 {
 	int res;
 
+	/* No support for C-style ouptut */
+
 	res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
 	if (json_output) {
 		jsonw_int_field(json_wtr, "bpf_jit_harden", res);
@@ -214,6 +240,8 @@  static void probe_jit_kallsyms(void)
 {
 	int res;
 
+	/* No support for C-style ouptut */
+
 	res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
 	if (json_output) {
 		jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
@@ -238,6 +266,8 @@  static void probe_jit_limit(void)
 {
 	int res;
 
+	/* No support for C-style ouptut */
+
 	res = read_procfs("/proc/sys/net/core/bpf_jit_limit");
 	if (json_output) {
 		jsonw_int_field(json_wtr, "bpf_jit_limit", res);
@@ -409,7 +439,7 @@  static void probe_kernel_image_config(void)
 		print_kernel_option(options[i], NULL);
 }
 
-static bool probe_bpf_syscall(void)
+static bool probe_bpf_syscall(const char *define_prefix)
 {
 	bool res;
 
@@ -418,15 +448,18 @@  static bool probe_bpf_syscall(void)
 
 	print_bool_feature("have_bpf_syscall",
 			   "bpf() syscall",
-			   res);
+			   "BPF_SYSCALL",
+			   res, define_prefix);
 
 	return res;
 }
 
-static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types)
+static void
+probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
+		const char *define_prefix)
 {
+	char feat_name[128], plain_desc[128], define_name[128];
 	const char *plain_comment = "eBPF program_type ";
-	char feat_name[128], plain_desc[128];
 	size_t maxlen;
 	bool res;
 
@@ -441,14 +474,18 @@  static void probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types)
 	}
 
 	sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
+	sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]);
+	uppercase(define_name, sizeof(define_name));
 	sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
-	print_bool_feature(feat_name, plain_desc, res);
+	print_bool_feature(feat_name, plain_desc, define_name, res,
+			   define_prefix);
 }
 
-static void probe_map_type(enum bpf_map_type map_type)
+static void
+probe_map_type(enum bpf_map_type map_type, const char *define_prefix)
 {
+	char feat_name[128], plain_desc[128], define_name[128];
 	const char *plain_comment = "eBPF map_type ";
-	char feat_name[128], plain_desc[128];
 	size_t maxlen;
 	bool res;
 
@@ -461,12 +498,16 @@  static void probe_map_type(enum bpf_map_type map_type)
 	}
 
 	sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
+	sprintf(define_name, "%s_map_type", map_type_name[map_type]);
+	uppercase(define_name, sizeof(define_name));
 	sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
-	print_bool_feature(feat_name, plain_desc, res);
+	print_bool_feature(feat_name, plain_desc, define_name, res,
+			   define_prefix);
 }
 
 static void
-probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type)
+probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
+			   const char *define_prefix)
 {
 	const char *ptype_name = prog_type_name[prog_type];
 	char feat_name[128];
@@ -477,7 +518,7 @@  probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type)
 		sprintf(feat_name, "%s_available_helpers", ptype_name);
 		jsonw_name(json_wtr, feat_name);
 		jsonw_start_array(json_wtr);
-	} else {
+	} else if (!define_prefix) {
 		printf("eBPF helpers supported for program type %s:",
 		       ptype_name);
 	}
@@ -491,6 +532,10 @@  probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type)
 		if (json_output) {
 			if (res)
 				jsonw_string(json_wtr, helper_name[id]);
+		} else if (define_prefix) {
+			printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n",
+			       define_prefix, ptype_name, helper_name[id],
+			       res ? "1" : "0");
 		} else {
 			if (res)
 				printf("\n\t- %s", helper_name[id]);
@@ -499,13 +544,14 @@  probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type)
 
 	if (json_output)
 		jsonw_end_array(json_wtr);
-	else
+	else if (!define_prefix)
 		printf("\n");
 }
 
 static int do_probe(int argc, char **argv)
 {
 	enum probe_component target = COMPONENT_UNSPEC;
+	const char *define_prefix = NULL;
 	bool supported_types[128] = {};
 	unsigned int i;
 
@@ -527,21 +573,45 @@  static int do_probe(int argc, char **argv)
 			}
 			target = COMPONENT_KERNEL;
 			NEXT_ARG();
+		} else if (is_prefix(*argv, "macros") && !define_prefix) {
+			define_prefix = "";
+			NEXT_ARG();
+		} else if (is_prefix(*argv, "prefix")) {
+			if (!define_prefix) {
+				p_err("'prefix' argument can only be use after 'macros'");
+				return -1;
+			}
+			if (strcmp(define_prefix, "")) {
+				p_err("'prefix' already defined");
+				return -1;
+			}
+			NEXT_ARG();
+
+			if (!REQ_ARGS(1))
+				return -1;
+			define_prefix = GET_ARG();
 		} else {
-			p_err("expected no more arguments, 'kernel', got: '%s'?",
+			p_err("expected no more arguments, 'kernel', 'macros' or 'prefix', got: '%s'?",
 			      *argv);
 			return -1;
 		}
 	}
 
-	if (json_output)
+	if (json_output) {
+		define_prefix = NULL;
 		jsonw_start_object(json_wtr);
+	}
 
 	switch (target) {
 	case COMPONENT_KERNEL:
 	case COMPONENT_UNSPEC:
+		if (define_prefix)
+			break;
+
 		print_start_section("system_config",
-				    "Scanning system configuration...");
+				    "Scanning system configuration...",
+				    NULL, /* define_comment never used here */
+				    NULL); /* define_prefix always NULL here */
 		if (check_procfs()) {
 			probe_unprivileged_disabled();
 			probe_jit_enable();
@@ -560,29 +630,53 @@  static int do_probe(int argc, char **argv)
 	}
 
 	print_start_section("syscall_config",
-			    "Scanning system call availability...");
+			    "Scanning system call availability...",
+			    "/*** System call availability ***/",
+			    define_prefix);
 
-	if (!probe_bpf_syscall())
+	if (!probe_bpf_syscall(define_prefix))
 		/* bpf() syscall unavailable, don't probe other BPF features */
 		goto exit_close_json;
 
 	print_end_then_start_section("program_types",
-				     "Scanning eBPF program types...");
+				     "Scanning eBPF program types...",
+				     "/*** eBPF program types ***/",
+				     define_prefix);
 
 	for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
-		probe_prog_type(i, supported_types);
+		probe_prog_type(i, supported_types, define_prefix);
 
 	print_end_then_start_section("map_types",
-				     "Scanning eBPF map types...");
+				     "Scanning eBPF map types...",
+				     "/*** eBPF map types ***/",
+				     define_prefix);
 
 	for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
-		probe_map_type(i);
+		probe_map_type(i, define_prefix);
 
 	print_end_then_start_section("helpers",
-				     "Scanning eBPF helper functions...");
-
+				     "Scanning eBPF helper functions...",
+				     "/*** eBPF helper functions ***/",
+				     define_prefix);
+
+	if (define_prefix)
+		printf("/*\n"
+		       " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n"
+		       " * to determine if <helper_name> is available for <prog_type_name>,\n"
+		       " * e.g.\n"
+		       " *	#if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n"
+		       " *		// do stuff with this helper\n"
+		       " *	#elif\n"
+		       " *		// use a workaround\n"
+		       " *	#endif\n"
+		       " */\n"
+		       "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper)	\\\n"
+		       "	%sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n",
+		       define_prefix, define_prefix, define_prefix,
+		       define_prefix);
 	for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
-		probe_helpers_for_progtype(i, supported_types[i]);
+		probe_helpers_for_progtype(i, supported_types[i],
+					   define_prefix);
 
 exit_close_json:
 	if (json_output) {
@@ -603,7 +697,7 @@  static int do_help(int argc, char **argv)
 	}
 
 	fprintf(stderr,
-		"Usage: %s %s probe [kernel]\n"
+		"Usage: %s %s probe [kernel] [macros [prefix PREFIX]]\n"
 		"       %s %s help\n"
 		"",
 		bin_name, argv[-2], bin_name, argv[-2]);