forked from mirrors/linux
		
	 6da4fea89d
			
		
	
	
		6da4fea89d
		
	
	
	
	
		
			
			Currently, bpftool perf subcommand has typo with the help message.
    $ tools/bpf/bpftool/bpftool perf help
    Usage: bpftool perf { show | list }
           bpftool perf help }
Since this bpftool perf subcommand help message has the extra bracket,
this commit fix the typo by removing the extra bracket.
Signed-off-by: Daniel T. Lee <danieltimlee@gmail.com>
Reviewed-by: Quentin Monnet <quentin@isovalent.com>
Link: https://lore.kernel.org/r/20230811121603.17429-1-danieltimlee@gmail.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
		
	
			
		
			
				
	
	
		
			258 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			258 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
 | |
| // Copyright (C) 2018 Facebook
 | |
| // Author: Yonghong Song <yhs@fb.com>
 | |
| 
 | |
| #ifndef _GNU_SOURCE
 | |
| #define _GNU_SOURCE
 | |
| #endif
 | |
| #include <ctype.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/types.h>
 | |
| #include <unistd.h>
 | |
| #include <dirent.h>
 | |
| 
 | |
| #include <bpf/bpf.h>
 | |
| 
 | |
| #include "main.h"
 | |
| 
 | |
| /* 0: undecided, 1: supported, 2: not supported */
 | |
| static int perf_query_supported;
 | |
| static bool has_perf_query_support(void)
 | |
| {
 | |
| 	__u64 probe_offset, probe_addr;
 | |
| 	__u32 len, prog_id, fd_type;
 | |
| 	char buf[256];
 | |
| 	int fd;
 | |
| 
 | |
| 	if (perf_query_supported)
 | |
| 		goto out;
 | |
| 
 | |
| 	fd = open("/", O_RDONLY);
 | |
| 	if (fd < 0) {
 | |
| 		p_err("perf_query_support: cannot open directory \"/\" (%s)",
 | |
| 		      strerror(errno));
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/* the following query will fail as no bpf attachment,
 | |
| 	 * the expected errno is ENOTSUPP
 | |
| 	 */
 | |
| 	errno = 0;
 | |
| 	len = sizeof(buf);
 | |
| 	bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id,
 | |
| 			  &fd_type, &probe_offset, &probe_addr);
 | |
| 
 | |
| 	if (errno == 524 /* ENOTSUPP */) {
 | |
| 		perf_query_supported = 1;
 | |
| 		goto close_fd;
 | |
| 	}
 | |
| 
 | |
| 	perf_query_supported = 2;
 | |
| 	p_err("perf_query_support: %s", strerror(errno));
 | |
| 	fprintf(stderr,
 | |
| 		"HINT: non root or kernel doesn't support TASK_FD_QUERY\n");
 | |
| 
 | |
| close_fd:
 | |
| 	close(fd);
 | |
| out:
 | |
| 	return perf_query_supported == 1;
 | |
| }
 | |
| 
 | |
| static void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type,
 | |
| 			    char *buf, __u64 probe_offset, __u64 probe_addr)
 | |
| {
 | |
| 	jsonw_start_object(json_wtr);
 | |
| 	jsonw_int_field(json_wtr, "pid", pid);
 | |
| 	jsonw_int_field(json_wtr, "fd", fd);
 | |
| 	jsonw_uint_field(json_wtr, "prog_id", prog_id);
 | |
| 	switch (fd_type) {
 | |
| 	case BPF_FD_TYPE_RAW_TRACEPOINT:
 | |
| 		jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint");
 | |
| 		jsonw_string_field(json_wtr, "tracepoint", buf);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_TRACEPOINT:
 | |
| 		jsonw_string_field(json_wtr, "fd_type", "tracepoint");
 | |
| 		jsonw_string_field(json_wtr, "tracepoint", buf);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_KPROBE:
 | |
| 		jsonw_string_field(json_wtr, "fd_type", "kprobe");
 | |
| 		if (buf[0] != '\0') {
 | |
| 			jsonw_string_field(json_wtr, "func", buf);
 | |
| 			jsonw_lluint_field(json_wtr, "offset", probe_offset);
 | |
| 		} else {
 | |
| 			jsonw_lluint_field(json_wtr, "addr", probe_addr);
 | |
| 		}
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_KRETPROBE:
 | |
| 		jsonw_string_field(json_wtr, "fd_type", "kretprobe");
 | |
| 		if (buf[0] != '\0') {
 | |
| 			jsonw_string_field(json_wtr, "func", buf);
 | |
| 			jsonw_lluint_field(json_wtr, "offset", probe_offset);
 | |
| 		} else {
 | |
| 			jsonw_lluint_field(json_wtr, "addr", probe_addr);
 | |
| 		}
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_UPROBE:
 | |
| 		jsonw_string_field(json_wtr, "fd_type", "uprobe");
 | |
| 		jsonw_string_field(json_wtr, "filename", buf);
 | |
| 		jsonw_lluint_field(json_wtr, "offset", probe_offset);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_URETPROBE:
 | |
| 		jsonw_string_field(json_wtr, "fd_type", "uretprobe");
 | |
| 		jsonw_string_field(json_wtr, "filename", buf);
 | |
| 		jsonw_lluint_field(json_wtr, "offset", probe_offset);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	jsonw_end_object(json_wtr);
 | |
| }
 | |
| 
 | |
| static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
 | |
| 			     char *buf, __u64 probe_offset, __u64 probe_addr)
 | |
| {
 | |
| 	printf("pid %d  fd %d: prog_id %u  ", pid, fd, prog_id);
 | |
| 	switch (fd_type) {
 | |
| 	case BPF_FD_TYPE_RAW_TRACEPOINT:
 | |
| 		printf("raw_tracepoint  %s\n", buf);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_TRACEPOINT:
 | |
| 		printf("tracepoint  %s\n", buf);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_KPROBE:
 | |
| 		if (buf[0] != '\0')
 | |
| 			printf("kprobe  func %s  offset %llu\n", buf,
 | |
| 			       probe_offset);
 | |
| 		else
 | |
| 			printf("kprobe  addr %llu\n", probe_addr);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_KRETPROBE:
 | |
| 		if (buf[0] != '\0')
 | |
| 			printf("kretprobe  func %s  offset %llu\n", buf,
 | |
| 			       probe_offset);
 | |
| 		else
 | |
| 			printf("kretprobe  addr %llu\n", probe_addr);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_UPROBE:
 | |
| 		printf("uprobe  filename %s  offset %llu\n", buf, probe_offset);
 | |
| 		break;
 | |
| 	case BPF_FD_TYPE_URETPROBE:
 | |
| 		printf("uretprobe  filename %s  offset %llu\n", buf,
 | |
| 		       probe_offset);
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int show_proc(void)
 | |
| {
 | |
| 	struct dirent *proc_de, *pid_fd_de;
 | |
| 	__u64 probe_offset, probe_addr;
 | |
| 	__u32 len, prog_id, fd_type;
 | |
| 	DIR *proc, *pid_fd;
 | |
| 	int err, pid, fd;
 | |
| 	const char *pch;
 | |
| 	char buf[4096];
 | |
| 
 | |
| 	proc = opendir("/proc");
 | |
| 	if (!proc)
 | |
| 		return -1;
 | |
| 
 | |
| 	while ((proc_de = readdir(proc))) {
 | |
| 		pid = 0;
 | |
| 		pch = proc_de->d_name;
 | |
| 
 | |
| 		/* pid should be all numbers */
 | |
| 		while (isdigit(*pch)) {
 | |
| 			pid = pid * 10 + *pch - '0';
 | |
| 			pch++;
 | |
| 		}
 | |
| 		if (*pch != '\0')
 | |
| 			continue;
 | |
| 
 | |
| 		err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name);
 | |
| 		if (err < 0 || err >= (int)sizeof(buf))
 | |
| 			continue;
 | |
| 
 | |
| 		pid_fd = opendir(buf);
 | |
| 		if (!pid_fd)
 | |
| 			continue;
 | |
| 
 | |
| 		while ((pid_fd_de = readdir(pid_fd))) {
 | |
| 			fd = 0;
 | |
| 			pch = pid_fd_de->d_name;
 | |
| 
 | |
| 			/* fd should be all numbers */
 | |
| 			while (isdigit(*pch)) {
 | |
| 				fd = fd * 10 + *pch - '0';
 | |
| 				pch++;
 | |
| 			}
 | |
| 			if (*pch != '\0')
 | |
| 				continue;
 | |
| 
 | |
| 			/* query (pid, fd) for potential perf events */
 | |
| 			len = sizeof(buf);
 | |
| 			err = bpf_task_fd_query(pid, fd, 0, buf, &len,
 | |
| 						&prog_id, &fd_type,
 | |
| 						&probe_offset, &probe_addr);
 | |
| 			if (err < 0)
 | |
| 				continue;
 | |
| 
 | |
| 			if (json_output)
 | |
| 				print_perf_json(pid, fd, prog_id, fd_type, buf,
 | |
| 						probe_offset, probe_addr);
 | |
| 			else
 | |
| 				print_perf_plain(pid, fd, prog_id, fd_type, buf,
 | |
| 						 probe_offset, probe_addr);
 | |
| 		}
 | |
| 		closedir(pid_fd);
 | |
| 	}
 | |
| 	closedir(proc);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int do_show(int argc, char **argv)
 | |
| {
 | |
| 	int err;
 | |
| 
 | |
| 	if (!has_perf_query_support())
 | |
| 		return -1;
 | |
| 
 | |
| 	if (json_output)
 | |
| 		jsonw_start_array(json_wtr);
 | |
| 	err = show_proc();
 | |
| 	if (json_output)
 | |
| 		jsonw_end_array(json_wtr);
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int do_help(int argc, char **argv)
 | |
| {
 | |
| 	fprintf(stderr,
 | |
| 		"Usage: %1$s %2$s { show | list }\n"
 | |
| 		"       %1$s %2$s help\n"
 | |
| 		"\n"
 | |
| 		"       " HELP_SPEC_OPTIONS " }\n"
 | |
| 		"",
 | |
| 		bin_name, argv[-2]);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct cmd cmds[] = {
 | |
| 	{ "show",	do_show },
 | |
| 	{ "list",	do_show },
 | |
| 	{ "help",	do_help },
 | |
| 	{ 0 }
 | |
| };
 | |
| 
 | |
| int do_perf(int argc, char **argv)
 | |
| {
 | |
| 	return cmd_select(cmds, argc, argv, do_help);
 | |
| }
 |