xref: /openbmc/linux/tools/bpf/bpftool/feature.c (revision be709d48)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (c) 2019 Netronome Systems, Inc. */
3 
4 #include <ctype.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <net/if.h>
9 #include <sys/utsname.h>
10 #include <sys/vfs.h>
11 
12 #include <linux/filter.h>
13 #include <linux/limits.h>
14 
15 #include <bpf.h>
16 #include <libbpf.h>
17 
18 #include "main.h"
19 
20 #ifndef PROC_SUPER_MAGIC
21 # define PROC_SUPER_MAGIC	0x9fa0
22 #endif
23 
24 enum probe_component {
25 	COMPONENT_UNSPEC,
26 	COMPONENT_KERNEL,
27 	COMPONENT_DEVICE,
28 };
29 
30 #define BPF_HELPER_MAKE_ENTRY(name)	[BPF_FUNC_ ## name] = "bpf_" # name
31 static const char * const helper_name[] = {
32 	__BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY)
33 };
34 
35 #undef BPF_HELPER_MAKE_ENTRY
36 
37 /* Miscellaneous utility functions */
38 
39 static bool check_procfs(void)
40 {
41 	struct statfs st_fs;
42 
43 	if (statfs("/proc", &st_fs) < 0)
44 		return false;
45 	if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC)
46 		return false;
47 
48 	return true;
49 }
50 
51 static void uppercase(char *str, size_t len)
52 {
53 	size_t i;
54 
55 	for (i = 0; i < len && str[i] != '\0'; i++)
56 		str[i] = toupper(str[i]);
57 }
58 
59 /* Printing utility functions */
60 
61 static void
62 print_bool_feature(const char *feat_name, const char *plain_name,
63 		   const char *define_name, bool res, const char *define_prefix)
64 {
65 	if (json_output)
66 		jsonw_bool_field(json_wtr, feat_name, res);
67 	else if (define_prefix)
68 		printf("#define %s%sHAVE_%s\n", define_prefix,
69 		       res ? "" : "NO_", define_name);
70 	else
71 		printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
72 }
73 
74 static void print_kernel_option(const char *name, const char *value)
75 {
76 	char *endptr;
77 	int res;
78 
79 	/* No support for C-style ouptut */
80 
81 	if (json_output) {
82 		if (!value) {
83 			jsonw_null_field(json_wtr, name);
84 			return;
85 		}
86 		errno = 0;
87 		res = strtol(value, &endptr, 0);
88 		if (!errno && *endptr == '\n')
89 			jsonw_int_field(json_wtr, name, res);
90 		else
91 			jsonw_string_field(json_wtr, name, value);
92 	} else {
93 		if (value)
94 			printf("%s is set to %s\n", name, value);
95 		else
96 			printf("%s is not set\n", name);
97 	}
98 }
99 
100 static void
101 print_start_section(const char *json_title, const char *plain_title,
102 		    const char *define_comment, const char *define_prefix)
103 {
104 	if (json_output) {
105 		jsonw_name(json_wtr, json_title);
106 		jsonw_start_object(json_wtr);
107 	} else if (define_prefix) {
108 		printf("%s\n", define_comment);
109 	} else {
110 		printf("%s\n", plain_title);
111 	}
112 }
113 
114 static void
115 print_end_then_start_section(const char *json_title, const char *plain_title,
116 			     const char *define_comment,
117 			     const char *define_prefix)
118 {
119 	if (json_output)
120 		jsonw_end_object(json_wtr);
121 	else
122 		printf("\n");
123 
124 	print_start_section(json_title, plain_title, define_comment,
125 			    define_prefix);
126 }
127 
128 /* Probing functions */
129 
130 static int read_procfs(const char *path)
131 {
132 	char *endptr, *line = NULL;
133 	size_t len = 0;
134 	FILE *fd;
135 	int res;
136 
137 	fd = fopen(path, "r");
138 	if (!fd)
139 		return -1;
140 
141 	res = getline(&line, &len, fd);
142 	fclose(fd);
143 	if (res < 0)
144 		return -1;
145 
146 	errno = 0;
147 	res = strtol(line, &endptr, 10);
148 	if (errno || *line == '\0' || *endptr != '\n')
149 		res = -1;
150 	free(line);
151 
152 	return res;
153 }
154 
155 static void probe_unprivileged_disabled(void)
156 {
157 	int res;
158 
159 	/* No support for C-style ouptut */
160 
161 	res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
162 	if (json_output) {
163 		jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
164 	} else {
165 		switch (res) {
166 		case 0:
167 			printf("bpf() syscall for unprivileged users is enabled\n");
168 			break;
169 		case 1:
170 			printf("bpf() syscall restricted to privileged users\n");
171 			break;
172 		case -1:
173 			printf("Unable to retrieve required privileges for bpf() syscall\n");
174 			break;
175 		default:
176 			printf("bpf() syscall restriction has unknown value %d\n", res);
177 		}
178 	}
179 }
180 
181 static void probe_jit_enable(void)
182 {
183 	int res;
184 
185 	/* No support for C-style ouptut */
186 
187 	res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
188 	if (json_output) {
189 		jsonw_int_field(json_wtr, "bpf_jit_enable", res);
190 	} else {
191 		switch (res) {
192 		case 0:
193 			printf("JIT compiler is disabled\n");
194 			break;
195 		case 1:
196 			printf("JIT compiler is enabled\n");
197 			break;
198 		case 2:
199 			printf("JIT compiler is enabled with debugging traces in kernel logs\n");
200 			break;
201 		case -1:
202 			printf("Unable to retrieve JIT-compiler status\n");
203 			break;
204 		default:
205 			printf("JIT-compiler status has unknown value %d\n",
206 			       res);
207 		}
208 	}
209 }
210 
211 static void probe_jit_harden(void)
212 {
213 	int res;
214 
215 	/* No support for C-style ouptut */
216 
217 	res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
218 	if (json_output) {
219 		jsonw_int_field(json_wtr, "bpf_jit_harden", res);
220 	} else {
221 		switch (res) {
222 		case 0:
223 			printf("JIT compiler hardening is disabled\n");
224 			break;
225 		case 1:
226 			printf("JIT compiler hardening is enabled for unprivileged users\n");
227 			break;
228 		case 2:
229 			printf("JIT compiler hardening is enabled for all users\n");
230 			break;
231 		case -1:
232 			printf("Unable to retrieve JIT hardening status\n");
233 			break;
234 		default:
235 			printf("JIT hardening status has unknown value %d\n",
236 			       res);
237 		}
238 	}
239 }
240 
241 static void probe_jit_kallsyms(void)
242 {
243 	int res;
244 
245 	/* No support for C-style ouptut */
246 
247 	res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
248 	if (json_output) {
249 		jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
250 	} else {
251 		switch (res) {
252 		case 0:
253 			printf("JIT compiler kallsyms exports are disabled\n");
254 			break;
255 		case 1:
256 			printf("JIT compiler kallsyms exports are enabled for root\n");
257 			break;
258 		case -1:
259 			printf("Unable to retrieve JIT kallsyms export status\n");
260 			break;
261 		default:
262 			printf("JIT kallsyms exports status has unknown value %d\n", res);
263 		}
264 	}
265 }
266 
267 static void probe_jit_limit(void)
268 {
269 	int res;
270 
271 	/* No support for C-style ouptut */
272 
273 	res = read_procfs("/proc/sys/net/core/bpf_jit_limit");
274 	if (json_output) {
275 		jsonw_int_field(json_wtr, "bpf_jit_limit", res);
276 	} else {
277 		switch (res) {
278 		case -1:
279 			printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n");
280 			break;
281 		default:
282 			printf("Global memory limit for JIT compiler for unprivileged users is %d bytes\n", res);
283 		}
284 	}
285 }
286 
287 static char *get_kernel_config_option(FILE *fd, const char *option)
288 {
289 	size_t line_n = 0, optlen = strlen(option);
290 	char *res, *strval, *line = NULL;
291 	ssize_t n;
292 
293 	rewind(fd);
294 	while ((n = getline(&line, &line_n, fd)) > 0) {
295 		if (strncmp(line, option, optlen))
296 			continue;
297 		/* Check we have at least '=', value, and '\n' */
298 		if (strlen(line) < optlen + 3)
299 			continue;
300 		if (*(line + optlen) != '=')
301 			continue;
302 
303 		/* Trim ending '\n' */
304 		line[strlen(line) - 1] = '\0';
305 
306 		/* Copy and return config option value */
307 		strval = line + optlen + 1;
308 		res = strdup(strval);
309 		free(line);
310 		return res;
311 	}
312 	free(line);
313 
314 	return NULL;
315 }
316 
317 static void probe_kernel_image_config(void)
318 {
319 	static const char * const options[] = {
320 		/* Enable BPF */
321 		"CONFIG_BPF",
322 		/* Enable bpf() syscall */
323 		"CONFIG_BPF_SYSCALL",
324 		/* Does selected architecture support eBPF JIT compiler */
325 		"CONFIG_HAVE_EBPF_JIT",
326 		/* Compile eBPF JIT compiler */
327 		"CONFIG_BPF_JIT",
328 		/* Avoid compiling eBPF interpreter (use JIT only) */
329 		"CONFIG_BPF_JIT_ALWAYS_ON",
330 
331 		/* cgroups */
332 		"CONFIG_CGROUPS",
333 		/* BPF programs attached to cgroups */
334 		"CONFIG_CGROUP_BPF",
335 		/* bpf_get_cgroup_classid() helper */
336 		"CONFIG_CGROUP_NET_CLASSID",
337 		/* bpf_skb_{,ancestor_}cgroup_id() helpers */
338 		"CONFIG_SOCK_CGROUP_DATA",
339 
340 		/* Tracing: attach BPF to kprobes, tracepoints, etc. */
341 		"CONFIG_BPF_EVENTS",
342 		/* Kprobes */
343 		"CONFIG_KPROBE_EVENTS",
344 		/* Uprobes */
345 		"CONFIG_UPROBE_EVENTS",
346 		/* Tracepoints */
347 		"CONFIG_TRACING",
348 		/* Syscall tracepoints */
349 		"CONFIG_FTRACE_SYSCALLS",
350 		/* bpf_override_return() helper support for selected arch */
351 		"CONFIG_FUNCTION_ERROR_INJECTION",
352 		/* bpf_override_return() helper */
353 		"CONFIG_BPF_KPROBE_OVERRIDE",
354 
355 		/* Network */
356 		"CONFIG_NET",
357 		/* AF_XDP sockets */
358 		"CONFIG_XDP_SOCKETS",
359 		/* BPF_PROG_TYPE_LWT_* and related helpers */
360 		"CONFIG_LWTUNNEL_BPF",
361 		/* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */
362 		"CONFIG_NET_ACT_BPF",
363 		/* BPF_PROG_TYPE_SCHED_CLS, TC filters */
364 		"CONFIG_NET_CLS_BPF",
365 		/* TC clsact qdisc */
366 		"CONFIG_NET_CLS_ACT",
367 		/* Ingress filtering with TC */
368 		"CONFIG_NET_SCH_INGRESS",
369 		/* bpf_skb_get_xfrm_state() helper */
370 		"CONFIG_XFRM",
371 		/* bpf_get_route_realm() helper */
372 		"CONFIG_IP_ROUTE_CLASSID",
373 		/* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */
374 		"CONFIG_IPV6_SEG6_BPF",
375 		/* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */
376 		"CONFIG_BPF_LIRC_MODE2",
377 		/* BPF stream parser and BPF socket maps */
378 		"CONFIG_BPF_STREAM_PARSER",
379 		/* xt_bpf module for passing BPF programs to netfilter  */
380 		"CONFIG_NETFILTER_XT_MATCH_BPF",
381 		/* bpfilter back-end for iptables */
382 		"CONFIG_BPFILTER",
383 		/* bpftilter module with "user mode helper" */
384 		"CONFIG_BPFILTER_UMH",
385 
386 		/* test_bpf module for BPF tests */
387 		"CONFIG_TEST_BPF",
388 	};
389 	char *value, *buf = NULL;
390 	struct utsname utsn;
391 	char path[PATH_MAX];
392 	size_t i, n;
393 	ssize_t ret;
394 	FILE *fd;
395 
396 	if (uname(&utsn))
397 		goto no_config;
398 
399 	snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
400 
401 	fd = fopen(path, "r");
402 	if (!fd && errno == ENOENT) {
403 		/* Some distributions put the config file at /proc/config, give
404 		 * it a try.
405 		 * Sometimes it is also at /proc/config.gz but we do not try
406 		 * this one for now, it would require linking against libz.
407 		 */
408 		fd = fopen("/proc/config", "r");
409 	}
410 	if (!fd) {
411 		p_info("skipping kernel config, can't open file: %s",
412 		       strerror(errno));
413 		goto no_config;
414 	}
415 	/* Sanity checks */
416 	ret = getline(&buf, &n, fd);
417 	ret = getline(&buf, &n, fd);
418 	if (!buf || !ret) {
419 		p_info("skipping kernel config, can't read from file: %s",
420 		       strerror(errno));
421 		free(buf);
422 		goto no_config;
423 	}
424 	if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
425 		p_info("skipping kernel config, can't find correct file");
426 		free(buf);
427 		goto no_config;
428 	}
429 	free(buf);
430 
431 	for (i = 0; i < ARRAY_SIZE(options); i++) {
432 		value = get_kernel_config_option(fd, options[i]);
433 		print_kernel_option(options[i], value);
434 		free(value);
435 	}
436 	fclose(fd);
437 	return;
438 
439 no_config:
440 	for (i = 0; i < ARRAY_SIZE(options); i++)
441 		print_kernel_option(options[i], NULL);
442 }
443 
444 static bool probe_bpf_syscall(const char *define_prefix)
445 {
446 	bool res;
447 
448 	bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0);
449 	res = (errno != ENOSYS);
450 
451 	print_bool_feature("have_bpf_syscall",
452 			   "bpf() syscall",
453 			   "BPF_SYSCALL",
454 			   res, define_prefix);
455 
456 	return res;
457 }
458 
459 static void
460 probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
461 		const char *define_prefix, __u32 ifindex)
462 {
463 	char feat_name[128], plain_desc[128], define_name[128];
464 	const char *plain_comment = "eBPF program_type ";
465 	size_t maxlen;
466 	bool res;
467 
468 	if (ifindex)
469 		/* Only test offload-able program types */
470 		switch (prog_type) {
471 		case BPF_PROG_TYPE_SCHED_CLS:
472 		case BPF_PROG_TYPE_XDP:
473 			break;
474 		default:
475 			return;
476 		}
477 
478 	res = bpf_probe_prog_type(prog_type, ifindex);
479 
480 	supported_types[prog_type] |= res;
481 
482 	maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
483 	if (strlen(prog_type_name[prog_type]) > maxlen) {
484 		p_info("program type name too long");
485 		return;
486 	}
487 
488 	sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
489 	sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]);
490 	uppercase(define_name, sizeof(define_name));
491 	sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
492 	print_bool_feature(feat_name, plain_desc, define_name, res,
493 			   define_prefix);
494 }
495 
496 static void
497 probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
498 	       __u32 ifindex)
499 {
500 	char feat_name[128], plain_desc[128], define_name[128];
501 	const char *plain_comment = "eBPF map_type ";
502 	size_t maxlen;
503 	bool res;
504 
505 	res = bpf_probe_map_type(map_type, ifindex);
506 
507 	maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
508 	if (strlen(map_type_name[map_type]) > maxlen) {
509 		p_info("map type name too long");
510 		return;
511 	}
512 
513 	sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
514 	sprintf(define_name, "%s_map_type", map_type_name[map_type]);
515 	uppercase(define_name, sizeof(define_name));
516 	sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
517 	print_bool_feature(feat_name, plain_desc, define_name, res,
518 			   define_prefix);
519 }
520 
521 static void
522 probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
523 			   const char *define_prefix, __u32 ifindex)
524 {
525 	const char *ptype_name = prog_type_name[prog_type];
526 	char feat_name[128];
527 	unsigned int id;
528 	bool res;
529 
530 	if (ifindex)
531 		/* Only test helpers for offload-able program types */
532 		switch (prog_type) {
533 		case BPF_PROG_TYPE_SCHED_CLS:
534 		case BPF_PROG_TYPE_XDP:
535 			break;
536 		default:
537 			return;
538 		}
539 
540 	if (json_output) {
541 		sprintf(feat_name, "%s_available_helpers", ptype_name);
542 		jsonw_name(json_wtr, feat_name);
543 		jsonw_start_array(json_wtr);
544 	} else if (!define_prefix) {
545 		printf("eBPF helpers supported for program type %s:",
546 		       ptype_name);
547 	}
548 
549 	for (id = 1; id < ARRAY_SIZE(helper_name); id++) {
550 		if (!supported_type)
551 			res = false;
552 		else
553 			res = bpf_probe_helper(id, prog_type, ifindex);
554 
555 		if (json_output) {
556 			if (res)
557 				jsonw_string(json_wtr, helper_name[id]);
558 		} else if (define_prefix) {
559 			printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n",
560 			       define_prefix, ptype_name, helper_name[id],
561 			       res ? "1" : "0");
562 		} else {
563 			if (res)
564 				printf("\n\t- %s", helper_name[id]);
565 		}
566 	}
567 
568 	if (json_output)
569 		jsonw_end_array(json_wtr);
570 	else if (!define_prefix)
571 		printf("\n");
572 }
573 
574 static int do_probe(int argc, char **argv)
575 {
576 	enum probe_component target = COMPONENT_UNSPEC;
577 	const char *define_prefix = NULL;
578 	bool supported_types[128] = {};
579 	__u32 ifindex = 0;
580 	unsigned int i;
581 	char *ifname;
582 
583 	/* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
584 	 * Let's approximate, and restrict usage to root user only.
585 	 */
586 	if (geteuid()) {
587 		p_err("please run this command as root user");
588 		return -1;
589 	}
590 
591 	set_max_rlimit();
592 
593 	while (argc) {
594 		if (is_prefix(*argv, "kernel")) {
595 			if (target != COMPONENT_UNSPEC) {
596 				p_err("component to probe already specified");
597 				return -1;
598 			}
599 			target = COMPONENT_KERNEL;
600 			NEXT_ARG();
601 		} else if (is_prefix(*argv, "dev")) {
602 			NEXT_ARG();
603 
604 			if (target != COMPONENT_UNSPEC || ifindex) {
605 				p_err("component to probe already specified");
606 				return -1;
607 			}
608 			if (!REQ_ARGS(1))
609 				return -1;
610 
611 			target = COMPONENT_DEVICE;
612 			ifname = GET_ARG();
613 			ifindex = if_nametoindex(ifname);
614 			if (!ifindex) {
615 				p_err("unrecognized netdevice '%s': %s", ifname,
616 				      strerror(errno));
617 				return -1;
618 			}
619 		} else if (is_prefix(*argv, "macros") && !define_prefix) {
620 			define_prefix = "";
621 			NEXT_ARG();
622 		} else if (is_prefix(*argv, "prefix")) {
623 			if (!define_prefix) {
624 				p_err("'prefix' argument can only be use after 'macros'");
625 				return -1;
626 			}
627 			if (strcmp(define_prefix, "")) {
628 				p_err("'prefix' already defined");
629 				return -1;
630 			}
631 			NEXT_ARG();
632 
633 			if (!REQ_ARGS(1))
634 				return -1;
635 			define_prefix = GET_ARG();
636 		} else {
637 			p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",
638 			      *argv);
639 			return -1;
640 		}
641 	}
642 
643 	if (json_output) {
644 		define_prefix = NULL;
645 		jsonw_start_object(json_wtr);
646 	}
647 
648 	switch (target) {
649 	case COMPONENT_KERNEL:
650 	case COMPONENT_UNSPEC:
651 		if (define_prefix)
652 			break;
653 
654 		print_start_section("system_config",
655 				    "Scanning system configuration...",
656 				    NULL, /* define_comment never used here */
657 				    NULL); /* define_prefix always NULL here */
658 		if (check_procfs()) {
659 			probe_unprivileged_disabled();
660 			probe_jit_enable();
661 			probe_jit_harden();
662 			probe_jit_kallsyms();
663 			probe_jit_limit();
664 		} else {
665 			p_info("/* procfs not mounted, skipping related probes */");
666 		}
667 		probe_kernel_image_config();
668 		if (json_output)
669 			jsonw_end_object(json_wtr);
670 		else
671 			printf("\n");
672 		break;
673 	default:
674 		break;
675 	}
676 
677 	print_start_section("syscall_config",
678 			    "Scanning system call availability...",
679 			    "/*** System call availability ***/",
680 			    define_prefix);
681 
682 	if (!probe_bpf_syscall(define_prefix))
683 		/* bpf() syscall unavailable, don't probe other BPF features */
684 		goto exit_close_json;
685 
686 	print_end_then_start_section("program_types",
687 				     "Scanning eBPF program types...",
688 				     "/*** eBPF program types ***/",
689 				     define_prefix);
690 
691 	for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
692 		probe_prog_type(i, supported_types, define_prefix, ifindex);
693 
694 	print_end_then_start_section("map_types",
695 				     "Scanning eBPF map types...",
696 				     "/*** eBPF map types ***/",
697 				     define_prefix);
698 
699 	for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
700 		probe_map_type(i, define_prefix, ifindex);
701 
702 	print_end_then_start_section("helpers",
703 				     "Scanning eBPF helper functions...",
704 				     "/*** eBPF helper functions ***/",
705 				     define_prefix);
706 
707 	if (define_prefix)
708 		printf("/*\n"
709 		       " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n"
710 		       " * to determine if <helper_name> is available for <prog_type_name>,\n"
711 		       " * e.g.\n"
712 		       " *	#if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n"
713 		       " *		// do stuff with this helper\n"
714 		       " *	#elif\n"
715 		       " *		// use a workaround\n"
716 		       " *	#endif\n"
717 		       " */\n"
718 		       "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper)	\\\n"
719 		       "	%sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n",
720 		       define_prefix, define_prefix, define_prefix,
721 		       define_prefix);
722 	for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
723 		probe_helpers_for_progtype(i, supported_types[i],
724 					   define_prefix, ifindex);
725 
726 exit_close_json:
727 	if (json_output) {
728 		/* End current "section" of probes */
729 		jsonw_end_object(json_wtr);
730 		/* End root object */
731 		jsonw_end_object(json_wtr);
732 	}
733 
734 	return 0;
735 }
736 
737 static int do_help(int argc, char **argv)
738 {
739 	if (json_output) {
740 		jsonw_null(json_wtr);
741 		return 0;
742 	}
743 
744 	fprintf(stderr,
745 		"Usage: %s %s probe [COMPONENT] [macros [prefix PREFIX]]\n"
746 		"       %s %s help\n"
747 		"\n"
748 		"       COMPONENT := { kernel | dev NAME }\n"
749 		"",
750 		bin_name, argv[-2], bin_name, argv[-2]);
751 
752 	return 0;
753 }
754 
755 static const struct cmd cmds[] = {
756 	{ "probe",	do_probe },
757 	{ "help",	do_help },
758 	{ 0 }
759 };
760 
761 int do_feature(int argc, char **argv)
762 {
763 	return cmd_select(cmds, argc, argv, do_help);
764 }
765