diff mbox series

[RFC,bpf-next,v3,3/9] tools: bpftool: add probes for kernel configuration options

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

Commit Message

Quentin Monnet Jan. 3, 2019, 2:02 p.m. UTC
Add probes to dump a number of options set (or not set) for compiling
the kernel image. These parameters provide information about what BPF
components should be available on the system. A number of them are not
directly related to eBPF, but are in fact used in the kernel as
conditions on which to compile, or not to compile, some of the eBPF
helper functions.

Sample output:

    # bpftool feature probe kernel
    Scanning system configuration...
    ...
    CONFIG_BPF is set to y
    CONFIG_BPF_SYSCALL is set to y
    CONFIG_HAVE_EBPF_JIT is set to y
    ...

    # bpftool --pretty --json feature probe kernel
    {
        "system_config": {
            ...
            "CONFIG_BPF": "y",
            "CONFIG_BPF_SYSCALL": "y",
            "CONFIG_HAVE_EBPF_JIT": "y",
            ...
        }
    }

v3:
- Add a comment about /proc/config.gz not being supported as a path for
  the config file at this time.
- Use p_info() instead of p_err() on failure to get options from config
  file, as bpftool keeps probing other parameters and that would
  possibly create duplicate "error" entries for JSON.

v2:
- Remove C-style macros output from this patch.
- NOT addressed: grouping of those config options into subsections
  (I don't see an easy way of grouping them at the moment, please see
  also the discussion on v1 thread).

Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 tools/bpf/bpftool/feature.c | 142 ++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)

Comments

Y Song Jan. 4, 2019, 6:35 a.m. UTC | #1
On Thu, Jan 3, 2019 at 9:27 AM Quentin Monnet
<quentin.monnet@netronome.com> wrote:
>
> Add probes to dump a number of options set (or not set) for compiling
> the kernel image. These parameters provide information about what BPF
> components should be available on the system. A number of them are not
> directly related to eBPF, but are in fact used in the kernel as
> conditions on which to compile, or not to compile, some of the eBPF
> helper functions.
>
> Sample output:
>
>     # bpftool feature probe kernel
>     Scanning system configuration...
>     ...
>     CONFIG_BPF is set to y
>     CONFIG_BPF_SYSCALL is set to y
>     CONFIG_HAVE_EBPF_JIT is set to y
>     ...
>
>     # bpftool --pretty --json feature probe kernel
>     {
>         "system_config": {
>             ...
>             "CONFIG_BPF": "y",
>             "CONFIG_BPF_SYSCALL": "y",
>             "CONFIG_HAVE_EBPF_JIT": "y",
>             ...
>         }
>     }
>
> v3:
> - Add a comment about /proc/config.gz not being supported as a path for
>   the config file at this time.
> - Use p_info() instead of p_err() on failure to get options from config
>   file, as bpftool keeps probing other parameters and that would
>   possibly create duplicate "error" entries for JSON.
>
> v2:
> - Remove C-style macros output from this patch.
> - NOT addressed: grouping of those config options into subsections
>   (I don't see an easy way of grouping them at the moment, please see
>   also the discussion on v1 thread).
>
> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> ---
>  tools/bpf/bpftool/feature.c | 142 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 142 insertions(+)
>
> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> index 37fe79f59015..05c16fe67005 100644
> --- a/tools/bpf/bpftool/feature.c
> +++ b/tools/bpf/bpftool/feature.c
> @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res)
>                 printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
>  }
>
> +static void print_kernel_option(const char *name, const char *value)
> +{
> +       char *endptr;
> +       int res;
> +
> +       if (json_output) {
> +               if (!value) {
> +                       jsonw_null_field(json_wtr, name);
> +                       return;
> +               }
> +               errno = 0;
> +               res = strtol(value, &endptr, 0);
> +               if (!errno && *endptr == '\n')
> +                       jsonw_int_field(json_wtr, name, res);
> +               else
> +                       jsonw_string_field(json_wtr, name, value);
> +       } else {
> +               if (value)
> +                       printf("%s is set to %s\n", name, value);
> +               else
> +                       printf("%s is not set\n", name);
> +       }
> +}
> +
>  static void
>  print_start_section(const char *json_title, const char *plain_title)
>  {
> @@ -190,6 +214,123 @@ static void probe_jit_kallsyms(void)
>         }
>  }
>
> +static char *get_kernel_config_option(FILE *fd, const char *option)
> +{
> +       size_t line_n = 0, optlen = strlen(option);
> +       char *res, *strval, *line = NULL;
> +       ssize_t n;
> +
> +       rewind(fd);
> +       while ((n = getline(&line, &line_n, fd)) > 0) {
> +               if (strncmp(line, option, optlen))
> +                       continue;
> +               /* Check we have at least '=', value, and '\n' */
> +               if (strlen(line) < optlen + 3)
> +                       continue;
> +               if (*(line + optlen) != '=')
> +                       continue;
> +
> +               /* Trim ending '\n' */
> +               line[strlen(line) - 1] = '\0';
> +
> +               /* Copy and return config option value */
> +               strval = line + optlen + 1;
> +               res = strdup(strval);
> +               free(line);
> +               return res;
> +       }
> +       free(line);
> +
> +       return NULL;
> +}
> +
> +static void probe_kernel_image_config(void)
> +{
> +       const char * const options[] = {
> +               "CONFIG_BPF",
> +               "CONFIG_BPF_SYSCALL",
> +               "CONFIG_HAVE_EBPF_JIT",
> +               "CONFIG_BPF_JIT",
> +               "CONFIG_BPF_JIT_ALWAYS_ON",
> +               "CONFIG_NET",
> +               "CONFIG_XDP_SOCKETS",
> +               "CONFIG_CGROUPS",
> +               "CONFIG_CGROUP_BPF",
> +               "CONFIG_CGROUP_NET_CLASSID",
> +               "CONFIG_BPF_EVENTS",
> +               "CONFIG_LWTUNNEL_BPF",
> +               "CONFIG_NET_ACT_BPF",
> +               "CONFIG_NET_CLS_ACT",
> +               "CONFIG_NET_CLS_BPF",
> +               "CONFIG_NET_SCH_INGRESS",
> +               "CONFIG_XFRM",
> +               "CONFIG_SOCK_CGROUP_DATA",
> +               "CONFIG_IP_ROUTE_CLASSID",
> +               "CONFIG_IPV6_SEG6_BPF",
> +               "CONFIG_FUNCTION_ERROR_INJECTION",
> +               "CONFIG_BPF_KPROBE_OVERRIDE",
> +               "CONFIG_BPF_LIRC_MODE2",
> +               "CONFIG_NETFILTER_XT_MATCH_BPF",
> +               "CONFIG_TEST_BPF",
> +               "CONFIG_BPFILTER",
> +               "CONFIG_BPFILTER_UMH",
> +               "CONFIG_BPF_STREAM_PARSER",

The list does not have any tracing specific configs like
CONFIG_KPROBES, CONFIG_UPROBES, etc.
Should we check those as well?

> +       };
> +       char *value, *buf = NULL;
> +       struct utsname utsn;
> +       char path[PATH_MAX];
> +       size_t i, n;
> +       ssize_t ret;
> +       FILE *fd;
> +
> +       if (uname(&utsn))
> +               goto no_config;
> +
> +       snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
> +
> +       fd = fopen(path, "r");
> +       if (!fd && errno == ENOENT) {
> +               /* Some distributions put the config file at /proc/config, give
> +                * it a try.
> +                * Sometimes it is also at /proc/config.gz but we do not try
> +                * this one for now, it would require linking against libz.
> +                */
> +               fd = fopen("/proc/config", "r");
> +       }
> +       if (!fd) {
> +               p_info("skipping kernel config, can't open file: %s",
> +                      strerror(errno));
> +               goto no_config;
> +       }
> +       /* Sanity checks */
> +       ret = getline(&buf, &n, fd);
> +       ret = getline(&buf, &n, fd);
> +       if (!buf || !ret) {
> +               p_info("skipping kernel config, can't read from file: %s",
> +                      strerror(errno));
> +               free(buf);
> +               goto no_config;
> +       }
> +       if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
> +               p_info("skipping kernel config, can't find correct file");
> +               free(buf);
> +               goto no_config;
> +       }
> +       free(buf);
> +
> +       for (i = 0; i < ARRAY_SIZE(options); i++) {
> +               value = get_kernel_config_option(fd, options[i]);
> +               print_kernel_option(options[i], value);
> +               free(value);
> +       }
> +       fclose(fd);
> +       return;
> +
> +no_config:
> +       for (i = 0; i < ARRAY_SIZE(options); i++)
> +               print_kernel_option(options[i], NULL);
> +}
> +
>  static bool probe_bpf_syscall(void)
>  {
>         bool res;
> @@ -249,6 +390,7 @@ static int do_probe(int argc, char **argv)
>                 } else {
>                         p_info("/* procfs not mounted, skipping related probes */");
>                 }
> +               probe_kernel_image_config();
>                 if (json_output)
>                         jsonw_end_object(json_wtr);
>                 else
> --
> 2.17.1
>
Quentin Monnet Jan. 4, 2019, 2:27 p.m. UTC | #2
2019-01-03 22:35 UTC-0800 ~ Y Song <ys114321@gmail.com>
> On Thu, Jan 3, 2019 at 9:27 AM Quentin Monnet
> <quentin.monnet@netronome.com> wrote:
>>
>> Add probes to dump a number of options set (or not set) for compiling
>> the kernel image. These parameters provide information about what BPF
>> components should be available on the system. A number of them are not
>> directly related to eBPF, but are in fact used in the kernel as
>> conditions on which to compile, or not to compile, some of the eBPF
>> helper functions.
>>
>> Sample output:
>>
>>     # bpftool feature probe kernel
>>     Scanning system configuration...
>>     ...
>>     CONFIG_BPF is set to y
>>     CONFIG_BPF_SYSCALL is set to y
>>     CONFIG_HAVE_EBPF_JIT is set to y
>>     ...
>>
>>     # bpftool --pretty --json feature probe kernel
>>     {
>>         "system_config": {
>>             ...
>>             "CONFIG_BPF": "y",
>>             "CONFIG_BPF_SYSCALL": "y",
>>             "CONFIG_HAVE_EBPF_JIT": "y",
>>             ...
>>         }
>>     }
>>
>> v3:
>> - Add a comment about /proc/config.gz not being supported as a path for
>>   the config file at this time.
>> - Use p_info() instead of p_err() on failure to get options from config
>>   file, as bpftool keeps probing other parameters and that would
>>   possibly create duplicate "error" entries for JSON.
>>
>> v2:
>> - Remove C-style macros output from this patch.
>> - NOT addressed: grouping of those config options into subsections
>>   (I don't see an easy way of grouping them at the moment, please see
>>   also the discussion on v1 thread).
>>
>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
>> ---
>>  tools/bpf/bpftool/feature.c | 142 ++++++++++++++++++++++++++++++++++++
>>  1 file changed, 142 insertions(+)
>>
>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>> index 37fe79f59015..05c16fe67005 100644
>> --- a/tools/bpf/bpftool/feature.c
>> +++ b/tools/bpf/bpftool/feature.c
>> @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res)
>>                 printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
>>  }
>>
>> +static void print_kernel_option(const char *name, const char *value)
>> +{
>> +       char *endptr;
>> +       int res;
>> +
>> +       if (json_output) {
>> +               if (!value) {
>> +                       jsonw_null_field(json_wtr, name);
>> +                       return;
>> +               }
>> +               errno = 0;
>> +               res = strtol(value, &endptr, 0);
>> +               if (!errno && *endptr == '\n')
>> +                       jsonw_int_field(json_wtr, name, res);
>> +               else
>> +                       jsonw_string_field(json_wtr, name, value);
>> +       } else {
>> +               if (value)
>> +                       printf("%s is set to %s\n", name, value);
>> +               else
>> +                       printf("%s is not set\n", name);
>> +       }
>> +}
>> +
>>  static void
>>  print_start_section(const char *json_title, const char *plain_title)
>>  {
>> @@ -190,6 +214,123 @@ static void probe_jit_kallsyms(void)
>>         }
>>  }
>>
>> +static char *get_kernel_config_option(FILE *fd, const char *option)
>> +{
>> +       size_t line_n = 0, optlen = strlen(option);
>> +       char *res, *strval, *line = NULL;
>> +       ssize_t n;
>> +
>> +       rewind(fd);
>> +       while ((n = getline(&line, &line_n, fd)) > 0) {
>> +               if (strncmp(line, option, optlen))
>> +                       continue;
>> +               /* Check we have at least '=', value, and '\n' */
>> +               if (strlen(line) < optlen + 3)
>> +                       continue;
>> +               if (*(line + optlen) != '=')
>> +                       continue;
>> +
>> +               /* Trim ending '\n' */
>> +               line[strlen(line) - 1] = '\0';
>> +
>> +               /* Copy and return config option value */
>> +               strval = line + optlen + 1;
>> +               res = strdup(strval);
>> +               free(line);
>> +               return res;
>> +       }
>> +       free(line);
>> +
>> +       return NULL;
>> +}
>> +
>> +static void probe_kernel_image_config(void)
>> +{
>> +       const char * const options[] = {
>> +               "CONFIG_BPF",
>> +               "CONFIG_BPF_SYSCALL",
>> +               "CONFIG_HAVE_EBPF_JIT",
>> +               "CONFIG_BPF_JIT",
>> +               "CONFIG_BPF_JIT_ALWAYS_ON",
>> +               "CONFIG_NET",
>> +               "CONFIG_XDP_SOCKETS",
>> +               "CONFIG_CGROUPS",
>> +               "CONFIG_CGROUP_BPF",
>> +               "CONFIG_CGROUP_NET_CLASSID",
>> +               "CONFIG_BPF_EVENTS",
>> +               "CONFIG_LWTUNNEL_BPF",
>> +               "CONFIG_NET_ACT_BPF",
>> +               "CONFIG_NET_CLS_ACT",
>> +               "CONFIG_NET_CLS_BPF",
>> +               "CONFIG_NET_SCH_INGRESS",
>> +               "CONFIG_XFRM",
>> +               "CONFIG_SOCK_CGROUP_DATA",
>> +               "CONFIG_IP_ROUTE_CLASSID",
>> +               "CONFIG_IPV6_SEG6_BPF",
>> +               "CONFIG_FUNCTION_ERROR_INJECTION",
>> +               "CONFIG_BPF_KPROBE_OVERRIDE",
>> +               "CONFIG_BPF_LIRC_MODE2",
>> +               "CONFIG_NETFILTER_XT_MATCH_BPF",
>> +               "CONFIG_TEST_BPF",
>> +               "CONFIG_BPFILTER",
>> +               "CONFIG_BPFILTER_UMH",
>> +               "CONFIG_BPF_STREAM_PARSER",
> 
> The list does not have any tracing specific configs like
> CONFIG_KPROBES, CONFIG_UPROBES, etc.
> Should we check those as well?

I didn't find any BPF items for which compiling would depend on those
options, that's why they are not in the list at the moment. But yeah,
they are definitely useful to tell if kprobe-attached programs have any
chance to work, so it would make sense I guess... What options do you
have in mind exactly? CONFIG_KPROBES and CONFIG_UPROBES, do you believe
CONFING_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS would also be relevant?

Thanks,
Quentin
Y Song Jan. 4, 2019, 5:36 p.m. UTC | #3
On Fri, Jan 4, 2019 at 6:27 AM Quentin Monnet
<quentin.monnet@netronome.com> wrote:
>
> 2019-01-03 22:35 UTC-0800 ~ Y Song <ys114321@gmail.com>
> > On Thu, Jan 3, 2019 at 9:27 AM Quentin Monnet
> > <quentin.monnet@netronome.com> wrote:
> >>
> >> Add probes to dump a number of options set (or not set) for compiling
> >> the kernel image. These parameters provide information about what BPF
> >> components should be available on the system. A number of them are not
> >> directly related to eBPF, but are in fact used in the kernel as
> >> conditions on which to compile, or not to compile, some of the eBPF
> >> helper functions.
> >>
> >> Sample output:
> >>
> >>     # bpftool feature probe kernel
> >>     Scanning system configuration...
> >>     ...
> >>     CONFIG_BPF is set to y
> >>     CONFIG_BPF_SYSCALL is set to y
> >>     CONFIG_HAVE_EBPF_JIT is set to y
> >>     ...
> >>
> >>     # bpftool --pretty --json feature probe kernel
> >>     {
> >>         "system_config": {
> >>             ...
> >>             "CONFIG_BPF": "y",
> >>             "CONFIG_BPF_SYSCALL": "y",
> >>             "CONFIG_HAVE_EBPF_JIT": "y",
> >>             ...
> >>         }
> >>     }
> >>
> >> v3:
> >> - Add a comment about /proc/config.gz not being supported as a path for
> >>   the config file at this time.
> >> - Use p_info() instead of p_err() on failure to get options from config
> >>   file, as bpftool keeps probing other parameters and that would
> >>   possibly create duplicate "error" entries for JSON.
> >>
> >> v2:
> >> - Remove C-style macros output from this patch.
> >> - NOT addressed: grouping of those config options into subsections
> >>   (I don't see an easy way of grouping them at the moment, please see
> >>   also the discussion on v1 thread).
> >>
> >> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
> >> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> >> ---
> >>  tools/bpf/bpftool/feature.c | 142 ++++++++++++++++++++++++++++++++++++
> >>  1 file changed, 142 insertions(+)
> >>
> >> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
> >> index 37fe79f59015..05c16fe67005 100644
> >> --- a/tools/bpf/bpftool/feature.c
> >> +++ b/tools/bpf/bpftool/feature.c
> >> @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res)
> >>                 printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
> >>  }
> >>
> >> +static void print_kernel_option(const char *name, const char *value)
> >> +{
> >> +       char *endptr;
> >> +       int res;
> >> +
> >> +       if (json_output) {
> >> +               if (!value) {
> >> +                       jsonw_null_field(json_wtr, name);
> >> +                       return;
> >> +               }
> >> +               errno = 0;
> >> +               res = strtol(value, &endptr, 0);
> >> +               if (!errno && *endptr == '\n')
> >> +                       jsonw_int_field(json_wtr, name, res);
> >> +               else
> >> +                       jsonw_string_field(json_wtr, name, value);
> >> +       } else {
> >> +               if (value)
> >> +                       printf("%s is set to %s\n", name, value);
> >> +               else
> >> +                       printf("%s is not set\n", name);
> >> +       }
> >> +}
> >> +
> >>  static void
> >>  print_start_section(const char *json_title, const char *plain_title)
> >>  {
> >> @@ -190,6 +214,123 @@ static void probe_jit_kallsyms(void)
> >>         }
> >>  }
> >>
> >> +static char *get_kernel_config_option(FILE *fd, const char *option)
> >> +{
> >> +       size_t line_n = 0, optlen = strlen(option);
> >> +       char *res, *strval, *line = NULL;
> >> +       ssize_t n;
> >> +
> >> +       rewind(fd);
> >> +       while ((n = getline(&line, &line_n, fd)) > 0) {
> >> +               if (strncmp(line, option, optlen))
> >> +                       continue;
> >> +               /* Check we have at least '=', value, and '\n' */
> >> +               if (strlen(line) < optlen + 3)
> >> +                       continue;
> >> +               if (*(line + optlen) != '=')
> >> +                       continue;
> >> +
> >> +               /* Trim ending '\n' */
> >> +               line[strlen(line) - 1] = '\0';
> >> +
> >> +               /* Copy and return config option value */
> >> +               strval = line + optlen + 1;
> >> +               res = strdup(strval);
> >> +               free(line);
> >> +               return res;
> >> +       }
> >> +       free(line);
> >> +
> >> +       return NULL;
> >> +}
> >> +
> >> +static void probe_kernel_image_config(void)
> >> +{
> >> +       const char * const options[] = {
> >> +               "CONFIG_BPF",
> >> +               "CONFIG_BPF_SYSCALL",
> >> +               "CONFIG_HAVE_EBPF_JIT",
> >> +               "CONFIG_BPF_JIT",
> >> +               "CONFIG_BPF_JIT_ALWAYS_ON",
> >> +               "CONFIG_NET",
> >> +               "CONFIG_XDP_SOCKETS",
> >> +               "CONFIG_CGROUPS",
> >> +               "CONFIG_CGROUP_BPF",
> >> +               "CONFIG_CGROUP_NET_CLASSID",
> >> +               "CONFIG_BPF_EVENTS",
> >> +               "CONFIG_LWTUNNEL_BPF",
> >> +               "CONFIG_NET_ACT_BPF",
> >> +               "CONFIG_NET_CLS_ACT",
> >> +               "CONFIG_NET_CLS_BPF",
> >> +               "CONFIG_NET_SCH_INGRESS",
> >> +               "CONFIG_XFRM",
> >> +               "CONFIG_SOCK_CGROUP_DATA",
> >> +               "CONFIG_IP_ROUTE_CLASSID",
> >> +               "CONFIG_IPV6_SEG6_BPF",
> >> +               "CONFIG_FUNCTION_ERROR_INJECTION",
> >> +               "CONFIG_BPF_KPROBE_OVERRIDE",
> >> +               "CONFIG_BPF_LIRC_MODE2",
> >> +               "CONFIG_NETFILTER_XT_MATCH_BPF",
> >> +               "CONFIG_TEST_BPF",
> >> +               "CONFIG_BPFILTER",
> >> +               "CONFIG_BPFILTER_UMH",
> >> +               "CONFIG_BPF_STREAM_PARSER",
> >
> > The list does not have any tracing specific configs like
> > CONFIG_KPROBES, CONFIG_UPROBES, etc.
> > Should we check those as well?
>
> I didn't find any BPF items for which compiling would depend on those
> options, that's why they are not in the list at the moment. But yeah,

That is true. They are kind of independent for compilations.

> they are definitely useful to tell if kprobe-attached programs have any
> chance to work, so it would make sense I guess... What options do you
> have in mind exactly? CONFIG_KPROBES and CONFIG_UPROBES, do you believe
> CONFING_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS would also be relevant?

The following list should be a good start:
CONFIG_TRACING
CONFIG_KPROBE_EVENTS
CONFIG_UPROBE_EVENTS
CONFIG_BPF_EVENTS
CONFIG_FTRACE_SYSCALLS

No need for CONFIG_KPROBES and CONFIG_UPROBES which
are implied by CONFIG_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS respectively,
and BPF mostly hooked to the events.

TRACING will enable tracepoint.
BPF_EVENTS depends on KPROBE_EVENTS or UPROBE_EVENTS, and PERF_EVENTS.
Having both CONFIG_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS will tell
whether one of them or both are supported.
CONFIG_FTRACE_SYSCALLS needed to tell whether syscall tracepoints will
be supported or not
so bpf programs attached to these syscall tracepoints will work.

>
> Thanks,
> Quentin
Quentin Monnet Jan. 5, 2019, 1:16 p.m. UTC | #4
2019-01-04 09:36 UTC-0800 ~ Y Song <ys114321@gmail.com>
> On Fri, Jan 4, 2019 at 6:27 AM Quentin Monnet
> <quentin.monnet@netronome.com> wrote:
>>
>> 2019-01-03 22:35 UTC-0800 ~ Y Song <ys114321@gmail.com>
>>> On Thu, Jan 3, 2019 at 9:27 AM Quentin Monnet
>>> <quentin.monnet@netronome.com> wrote:
>>>>
>>>> Add probes to dump a number of options set (or not set) for compiling
>>>> the kernel image. These parameters provide information about what BPF
>>>> components should be available on the system. A number of them are not
>>>> directly related to eBPF, but are in fact used in the kernel as
>>>> conditions on which to compile, or not to compile, some of the eBPF
>>>> helper functions.
>>>>
>>>> Sample output:
>>>>
>>>>      # bpftool feature probe kernel
>>>>      Scanning system configuration...
>>>>      ...
>>>>      CONFIG_BPF is set to y
>>>>      CONFIG_BPF_SYSCALL is set to y
>>>>      CONFIG_HAVE_EBPF_JIT is set to y
>>>>      ...
>>>>
>>>>      # bpftool --pretty --json feature probe kernel
>>>>      {
>>>>          "system_config": {
>>>>              ...
>>>>              "CONFIG_BPF": "y",
>>>>              "CONFIG_BPF_SYSCALL": "y",
>>>>              "CONFIG_HAVE_EBPF_JIT": "y",
>>>>              ...
>>>>          }
>>>>      }
>>>>
>>>> v3:
>>>> - Add a comment about /proc/config.gz not being supported as a path for
>>>>    the config file at this time.
>>>> - Use p_info() instead of p_err() on failure to get options from config
>>>>    file, as bpftool keeps probing other parameters and that would
>>>>    possibly create duplicate "error" entries for JSON.
>>>>
>>>> v2:
>>>> - Remove C-style macros output from this patch.
>>>> - NOT addressed: grouping of those config options into subsections
>>>>    (I don't see an easy way of grouping them at the moment, please see
>>>>    also the discussion on v1 thread).
>>>>
>>>> Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
>>>> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
>>>> ---
>>>>   tools/bpf/bpftool/feature.c | 142 ++++++++++++++++++++++++++++++++++++
>>>>   1 file changed, 142 insertions(+)
>>>>
>>>> diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
>>>> index 37fe79f59015..05c16fe67005 100644
>>>> --- a/tools/bpf/bpftool/feature.c
>>>> +++ b/tools/bpf/bpftool/feature.c
>>>> @@ -48,6 +48,30 @@ print_bool_feature(const char *feat_name, const char *plain_name, bool res)
>>>>                  printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
>>>>   }
>>>>
>>>> +static void print_kernel_option(const char *name, const char *value)
>>>> +{
>>>> +       char *endptr;
>>>> +       int res;
>>>> +
>>>> +       if (json_output) {
>>>> +               if (!value) {
>>>> +                       jsonw_null_field(json_wtr, name);
>>>> +                       return;
>>>> +               }
>>>> +               errno = 0;
>>>> +               res = strtol(value, &endptr, 0);
>>>> +               if (!errno && *endptr == '\n')
>>>> +                       jsonw_int_field(json_wtr, name, res);
>>>> +               else
>>>> +                       jsonw_string_field(json_wtr, name, value);
>>>> +       } else {
>>>> +               if (value)
>>>> +                       printf("%s is set to %s\n", name, value);
>>>> +               else
>>>> +                       printf("%s is not set\n", name);
>>>> +       }
>>>> +}
>>>> +
>>>>   static void
>>>>   print_start_section(const char *json_title, const char *plain_title)
>>>>   {
>>>> @@ -190,6 +214,123 @@ static void probe_jit_kallsyms(void)
>>>>          }
>>>>   }
>>>>
>>>> +static char *get_kernel_config_option(FILE *fd, const char *option)
>>>> +{
>>>> +       size_t line_n = 0, optlen = strlen(option);
>>>> +       char *res, *strval, *line = NULL;
>>>> +       ssize_t n;
>>>> +
>>>> +       rewind(fd);
>>>> +       while ((n = getline(&line, &line_n, fd)) > 0) {
>>>> +               if (strncmp(line, option, optlen))
>>>> +                       continue;
>>>> +               /* Check we have at least '=', value, and '\n' */
>>>> +               if (strlen(line) < optlen + 3)
>>>> +                       continue;
>>>> +               if (*(line + optlen) != '=')
>>>> +                       continue;
>>>> +
>>>> +               /* Trim ending '\n' */
>>>> +               line[strlen(line) - 1] = '\0';
>>>> +
>>>> +               /* Copy and return config option value */
>>>> +               strval = line + optlen + 1;
>>>> +               res = strdup(strval);
>>>> +               free(line);
>>>> +               return res;
>>>> +       }
>>>> +       free(line);
>>>> +
>>>> +       return NULL;
>>>> +}
>>>> +
>>>> +static void probe_kernel_image_config(void)
>>>> +{
>>>> +       const char * const options[] = {
>>>> +               "CONFIG_BPF",
>>>> +               "CONFIG_BPF_SYSCALL",
>>>> +               "CONFIG_HAVE_EBPF_JIT",
>>>> +               "CONFIG_BPF_JIT",
>>>> +               "CONFIG_BPF_JIT_ALWAYS_ON",
>>>> +               "CONFIG_NET",
>>>> +               "CONFIG_XDP_SOCKETS",
>>>> +               "CONFIG_CGROUPS",
>>>> +               "CONFIG_CGROUP_BPF",
>>>> +               "CONFIG_CGROUP_NET_CLASSID",
>>>> +               "CONFIG_BPF_EVENTS",
>>>> +               "CONFIG_LWTUNNEL_BPF",
>>>> +               "CONFIG_NET_ACT_BPF",
>>>> +               "CONFIG_NET_CLS_ACT",
>>>> +               "CONFIG_NET_CLS_BPF",
>>>> +               "CONFIG_NET_SCH_INGRESS",
>>>> +               "CONFIG_XFRM",
>>>> +               "CONFIG_SOCK_CGROUP_DATA",
>>>> +               "CONFIG_IP_ROUTE_CLASSID",
>>>> +               "CONFIG_IPV6_SEG6_BPF",
>>>> +               "CONFIG_FUNCTION_ERROR_INJECTION",
>>>> +               "CONFIG_BPF_KPROBE_OVERRIDE",
>>>> +               "CONFIG_BPF_LIRC_MODE2",
>>>> +               "CONFIG_NETFILTER_XT_MATCH_BPF",
>>>> +               "CONFIG_TEST_BPF",
>>>> +               "CONFIG_BPFILTER",
>>>> +               "CONFIG_BPFILTER_UMH",
>>>> +               "CONFIG_BPF_STREAM_PARSER",
>>>
>>> The list does not have any tracing specific configs like
>>> CONFIG_KPROBES, CONFIG_UPROBES, etc.
>>> Should we check those as well?
>>
>> I didn't find any BPF items for which compiling would depend on those
>> options, that's why they are not in the list at the moment. But yeah,
> 
> That is true. They are kind of independent for compilations.
> 
>> they are definitely useful to tell if kprobe-attached programs have any
>> chance to work, so it would make sense I guess... What options do you
>> have in mind exactly? CONFIG_KPROBES and CONFIG_UPROBES, do you believe
>> CONFING_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS would also be relevant?
> 
> The following list should be a good start:
> CONFIG_TRACING
> CONFIG_KPROBE_EVENTS
> CONFIG_UPROBE_EVENTS
> CONFIG_BPF_EVENTS
> CONFIG_FTRACE_SYSCALLS
> 
> No need for CONFIG_KPROBES and CONFIG_UPROBES which
> are implied by CONFIG_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS respectively,
> and BPF mostly hooked to the events.
> 
> TRACING will enable tracepoint.
> BPF_EVENTS depends on KPROBE_EVENTS or UPROBE_EVENTS, and PERF_EVENTS.
> Having both CONFIG_KPROBE_EVENTS and CONFIG_UPROBE_EVENTS will tell
> whether one of them or both are supported.
> CONFIG_FTRACE_SYSCALLS needed to tell whether syscall tracepoints will
> be supported or not
> so bpf programs attached to these syscall tracepoints will work.

Sounds good. CONFIG_BPF_EVENTS is in the list already, I'll add the 
other ones for v4 once bpf-next reopens. Thanks a lot for the details!

Quentin
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/feature.c b/tools/bpf/bpftool/feature.c
index 37fe79f59015..05c16fe67005 100644
--- a/tools/bpf/bpftool/feature.c
+++ b/tools/bpf/bpftool/feature.c
@@ -48,6 +48,30 @@  print_bool_feature(const char *feat_name, const char *plain_name, bool res)
 		printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
 }
 
+static void print_kernel_option(const char *name, const char *value)
+{
+	char *endptr;
+	int res;
+
+	if (json_output) {
+		if (!value) {
+			jsonw_null_field(json_wtr, name);
+			return;
+		}
+		errno = 0;
+		res = strtol(value, &endptr, 0);
+		if (!errno && *endptr == '\n')
+			jsonw_int_field(json_wtr, name, res);
+		else
+			jsonw_string_field(json_wtr, name, value);
+	} else {
+		if (value)
+			printf("%s is set to %s\n", name, value);
+		else
+			printf("%s is not set\n", name);
+	}
+}
+
 static void
 print_start_section(const char *json_title, const char *plain_title)
 {
@@ -190,6 +214,123 @@  static void probe_jit_kallsyms(void)
 	}
 }
 
+static char *get_kernel_config_option(FILE *fd, const char *option)
+{
+	size_t line_n = 0, optlen = strlen(option);
+	char *res, *strval, *line = NULL;
+	ssize_t n;
+
+	rewind(fd);
+	while ((n = getline(&line, &line_n, fd)) > 0) {
+		if (strncmp(line, option, optlen))
+			continue;
+		/* Check we have at least '=', value, and '\n' */
+		if (strlen(line) < optlen + 3)
+			continue;
+		if (*(line + optlen) != '=')
+			continue;
+
+		/* Trim ending '\n' */
+		line[strlen(line) - 1] = '\0';
+
+		/* Copy and return config option value */
+		strval = line + optlen + 1;
+		res = strdup(strval);
+		free(line);
+		return res;
+	}
+	free(line);
+
+	return NULL;
+}
+
+static void probe_kernel_image_config(void)
+{
+	const char * const options[] = {
+		"CONFIG_BPF",
+		"CONFIG_BPF_SYSCALL",
+		"CONFIG_HAVE_EBPF_JIT",
+		"CONFIG_BPF_JIT",
+		"CONFIG_BPF_JIT_ALWAYS_ON",
+		"CONFIG_NET",
+		"CONFIG_XDP_SOCKETS",
+		"CONFIG_CGROUPS",
+		"CONFIG_CGROUP_BPF",
+		"CONFIG_CGROUP_NET_CLASSID",
+		"CONFIG_BPF_EVENTS",
+		"CONFIG_LWTUNNEL_BPF",
+		"CONFIG_NET_ACT_BPF",
+		"CONFIG_NET_CLS_ACT",
+		"CONFIG_NET_CLS_BPF",
+		"CONFIG_NET_SCH_INGRESS",
+		"CONFIG_XFRM",
+		"CONFIG_SOCK_CGROUP_DATA",
+		"CONFIG_IP_ROUTE_CLASSID",
+		"CONFIG_IPV6_SEG6_BPF",
+		"CONFIG_FUNCTION_ERROR_INJECTION",
+		"CONFIG_BPF_KPROBE_OVERRIDE",
+		"CONFIG_BPF_LIRC_MODE2",
+		"CONFIG_NETFILTER_XT_MATCH_BPF",
+		"CONFIG_TEST_BPF",
+		"CONFIG_BPFILTER",
+		"CONFIG_BPFILTER_UMH",
+		"CONFIG_BPF_STREAM_PARSER",
+	};
+	char *value, *buf = NULL;
+	struct utsname utsn;
+	char path[PATH_MAX];
+	size_t i, n;
+	ssize_t ret;
+	FILE *fd;
+
+	if (uname(&utsn))
+		goto no_config;
+
+	snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
+
+	fd = fopen(path, "r");
+	if (!fd && errno == ENOENT) {
+		/* Some distributions put the config file at /proc/config, give
+		 * it a try.
+		 * Sometimes it is also at /proc/config.gz but we do not try
+		 * this one for now, it would require linking against libz.
+		 */
+		fd = fopen("/proc/config", "r");
+	}
+	if (!fd) {
+		p_info("skipping kernel config, can't open file: %s",
+		       strerror(errno));
+		goto no_config;
+	}
+	/* Sanity checks */
+	ret = getline(&buf, &n, fd);
+	ret = getline(&buf, &n, fd);
+	if (!buf || !ret) {
+		p_info("skipping kernel config, can't read from file: %s",
+		       strerror(errno));
+		free(buf);
+		goto no_config;
+	}
+	if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
+		p_info("skipping kernel config, can't find correct file");
+		free(buf);
+		goto no_config;
+	}
+	free(buf);
+
+	for (i = 0; i < ARRAY_SIZE(options); i++) {
+		value = get_kernel_config_option(fd, options[i]);
+		print_kernel_option(options[i], value);
+		free(value);
+	}
+	fclose(fd);
+	return;
+
+no_config:
+	for (i = 0; i < ARRAY_SIZE(options); i++)
+		print_kernel_option(options[i], NULL);
+}
+
 static bool probe_bpf_syscall(void)
 {
 	bool res;
@@ -249,6 +390,7 @@  static int do_probe(int argc, char **argv)
 		} else {
 			p_info("/* procfs not mounted, skipping related probes */");
 		}
+		probe_kernel_image_config();
 		if (json_output)
 			jsonw_end_object(json_wtr);
 		else