tools: bpftool: add C-style "#define" output for probes

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>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Quentin Monnet 2019-01-17 15:27:56 +00:00 коммит произвёл Alexei Starovoitov
Родитель 2d3ea5e85d
Коммит d267cff467
2 изменённых файлов: 132 добавлений и 29 удалений

Просмотреть файл

@ -19,15 +19,24 @@ SYNOPSIS
MAP COMMANDS MAP COMMANDS
============= =============
| **bpftool** **feature probe** [**kernel**] | **bpftool** **feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
| **bpftool** **feature help** | **bpftool** **feature help**
DESCRIPTION DESCRIPTION
=========== ===========
**bpftool feature probe** [**kernel**] **bpftool feature probe** [**kernel**] [**macros** [**prefix** *PREFIX*]]
Probe the running kernel and dump a number of eBPF-related Probe the running kernel and dump a number of eBPF-related
parameters, such as availability of the **bpf()** system call. 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. Keyword **kernel** can be omitted.
Note that when probed, some eBPF helpers (e.g. Note that when probed, some eBPF helpers (e.g.

Просмотреть файл

@ -46,13 +46,25 @@ static bool check_procfs(void)
return true; 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 */ /* Printing utility functions */
static void 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) if (json_output)
jsonw_bool_field(json_wtr, feat_name, res); 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 else
printf("%s is %savailable\n", plain_name, res ? "" : "NOT "); 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; char *endptr;
int res; int res;
/* No support for C-style ouptut */
if (json_output) { if (json_output) {
if (!value) { if (!value) {
jsonw_null_field(json_wtr, name); jsonw_null_field(json_wtr, name);
@ -82,25 +96,31 @@ static void print_kernel_option(const char *name, const char *value)
} }
static void 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) { if (json_output) {
jsonw_name(json_wtr, json_title); jsonw_name(json_wtr, json_title);
jsonw_start_object(json_wtr); jsonw_start_object(json_wtr);
} else if (define_prefix) {
printf("%s\n", define_comment);
} else { } else {
printf("%s\n", plain_title); printf("%s\n", plain_title);
} }
} }
static void 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) if (json_output)
jsonw_end_object(json_wtr); jsonw_end_object(json_wtr);
else else
printf("\n"); printf("\n");
print_start_section(json_title, plain_title); print_start_section(json_title, plain_title, define_comment,
define_prefix);
} }
/* Probing functions */ /* Probing functions */
@ -134,6 +154,8 @@ static void probe_unprivileged_disabled(void)
{ {
int res; int res;
/* No support for C-style ouptut */
res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled"); res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
if (json_output) { if (json_output) {
jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res); jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
@ -158,6 +180,8 @@ static void probe_jit_enable(void)
{ {
int res; int res;
/* No support for C-style ouptut */
res = read_procfs("/proc/sys/net/core/bpf_jit_enable"); res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
if (json_output) { if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_enable", res); jsonw_int_field(json_wtr, "bpf_jit_enable", res);
@ -186,6 +210,8 @@ static void probe_jit_harden(void)
{ {
int res; int res;
/* No support for C-style ouptut */
res = read_procfs("/proc/sys/net/core/bpf_jit_harden"); res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
if (json_output) { if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_harden", res); jsonw_int_field(json_wtr, "bpf_jit_harden", res);
@ -214,6 +240,8 @@ static void probe_jit_kallsyms(void)
{ {
int res; int res;
/* No support for C-style ouptut */
res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms"); res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
if (json_output) { if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res); jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
@ -238,6 +266,8 @@ static void probe_jit_limit(void)
{ {
int res; int res;
/* No support for C-style ouptut */
res = read_procfs("/proc/sys/net/core/bpf_jit_limit"); res = read_procfs("/proc/sys/net/core/bpf_jit_limit");
if (json_output) { if (json_output) {
jsonw_int_field(json_wtr, "bpf_jit_limit", res); jsonw_int_field(json_wtr, "bpf_jit_limit", res);
@ -409,7 +439,7 @@ no_config:
print_kernel_option(options[i], NULL); print_kernel_option(options[i], NULL);
} }
static bool probe_bpf_syscall(void) static bool probe_bpf_syscall(const char *define_prefix)
{ {
bool res; bool res;
@ -418,15 +448,18 @@ static bool probe_bpf_syscall(void)
print_bool_feature("have_bpf_syscall", print_bool_feature("have_bpf_syscall",
"bpf() syscall", "bpf() syscall",
res); "BPF_SYSCALL",
res, define_prefix);
return res; 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 "; const char *plain_comment = "eBPF program_type ";
char feat_name[128], plain_desc[128];
size_t maxlen; size_t maxlen;
bool res; 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(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]); 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 "; const char *plain_comment = "eBPF map_type ";
char feat_name[128], plain_desc[128];
size_t maxlen; size_t maxlen;
bool res; 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(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]); 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 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]; const char *ptype_name = prog_type_name[prog_type];
char feat_name[128]; 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); sprintf(feat_name, "%s_available_helpers", ptype_name);
jsonw_name(json_wtr, feat_name); jsonw_name(json_wtr, feat_name);
jsonw_start_array(json_wtr); jsonw_start_array(json_wtr);
} else { } else if (!define_prefix) {
printf("eBPF helpers supported for program type %s:", printf("eBPF helpers supported for program type %s:",
ptype_name); ptype_name);
} }
@ -491,6 +532,10 @@ probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type)
if (json_output) { if (json_output) {
if (res) if (res)
jsonw_string(json_wtr, helper_name[id]); 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 { } else {
if (res) if (res)
printf("\n\t- %s", helper_name[id]); 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) if (json_output)
jsonw_end_array(json_wtr); jsonw_end_array(json_wtr);
else else if (!define_prefix)
printf("\n"); printf("\n");
} }
static int do_probe(int argc, char **argv) static int do_probe(int argc, char **argv)
{ {
enum probe_component target = COMPONENT_UNSPEC; enum probe_component target = COMPONENT_UNSPEC;
const char *define_prefix = NULL;
bool supported_types[128] = {}; bool supported_types[128] = {};
unsigned int i; unsigned int i;
@ -527,21 +573,45 @@ static int do_probe(int argc, char **argv)
} }
target = COMPONENT_KERNEL; target = COMPONENT_KERNEL;
NEXT_ARG(); 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 { } else {
p_err("expected no more arguments, 'kernel', got: '%s'?", p_err("expected no more arguments, 'kernel', 'macros' or 'prefix', got: '%s'?",
*argv); *argv);
return -1; return -1;
} }
} }
if (json_output) if (json_output) {
define_prefix = NULL;
jsonw_start_object(json_wtr); jsonw_start_object(json_wtr);
}
switch (target) { switch (target) {
case COMPONENT_KERNEL: case COMPONENT_KERNEL:
case COMPONENT_UNSPEC: case COMPONENT_UNSPEC:
if (define_prefix)
break;
print_start_section("system_config", 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()) { if (check_procfs()) {
probe_unprivileged_disabled(); probe_unprivileged_disabled();
probe_jit_enable(); probe_jit_enable();
@ -560,29 +630,53 @@ static int do_probe(int argc, char **argv)
} }
print_start_section("syscall_config", 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 */ /* bpf() syscall unavailable, don't probe other BPF features */
goto exit_close_json; goto exit_close_json;
print_end_then_start_section("program_types", 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++) 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", 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++) 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", 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++) 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: exit_close_json:
if (json_output) { if (json_output) {
@ -603,7 +697,7 @@ static int do_help(int argc, char **argv)
} }
fprintf(stderr, fprintf(stderr,
"Usage: %s %s probe [kernel]\n" "Usage: %s %s probe [kernel] [macros [prefix PREFIX]]\n"
" %s %s help\n" " %s %s help\n"
"", "",
bin_name, argv[-2], bin_name, argv[-2]); bin_name, argv[-2], bin_name, argv[-2]);