162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 262306a36Sopenharmony_ci// Copyright (C) 2018 Facebook 362306a36Sopenharmony_ci// Author: Yonghong Song <yhs@fb.com> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#ifndef _GNU_SOURCE 662306a36Sopenharmony_ci#define _GNU_SOURCE 762306a36Sopenharmony_ci#endif 862306a36Sopenharmony_ci#include <ctype.h> 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <fcntl.h> 1162306a36Sopenharmony_ci#include <stdlib.h> 1262306a36Sopenharmony_ci#include <string.h> 1362306a36Sopenharmony_ci#include <sys/stat.h> 1462306a36Sopenharmony_ci#include <sys/types.h> 1562306a36Sopenharmony_ci#include <unistd.h> 1662306a36Sopenharmony_ci#include <dirent.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <bpf/bpf.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "main.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 0: undecided, 1: supported, 2: not supported */ 2362306a36Sopenharmony_cistatic int perf_query_supported; 2462306a36Sopenharmony_cistatic bool has_perf_query_support(void) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci __u64 probe_offset, probe_addr; 2762306a36Sopenharmony_ci __u32 len, prog_id, fd_type; 2862306a36Sopenharmony_ci char buf[256]; 2962306a36Sopenharmony_ci int fd; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (perf_query_supported) 3262306a36Sopenharmony_ci goto out; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci fd = open("/", O_RDONLY); 3562306a36Sopenharmony_ci if (fd < 0) { 3662306a36Sopenharmony_ci p_err("perf_query_support: cannot open directory \"/\" (%s)", 3762306a36Sopenharmony_ci strerror(errno)); 3862306a36Sopenharmony_ci goto out; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* the following query will fail as no bpf attachment, 4262306a36Sopenharmony_ci * the expected errno is ENOTSUPP 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci errno = 0; 4562306a36Sopenharmony_ci len = sizeof(buf); 4662306a36Sopenharmony_ci bpf_task_fd_query(getpid(), fd, 0, buf, &len, &prog_id, 4762306a36Sopenharmony_ci &fd_type, &probe_offset, &probe_addr); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (errno == 524 /* ENOTSUPP */) { 5062306a36Sopenharmony_ci perf_query_supported = 1; 5162306a36Sopenharmony_ci goto close_fd; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci perf_query_supported = 2; 5562306a36Sopenharmony_ci p_err("perf_query_support: %s", strerror(errno)); 5662306a36Sopenharmony_ci fprintf(stderr, 5762306a36Sopenharmony_ci "HINT: non root or kernel doesn't support TASK_FD_QUERY\n"); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciclose_fd: 6062306a36Sopenharmony_ci close(fd); 6162306a36Sopenharmony_ciout: 6262306a36Sopenharmony_ci return perf_query_supported == 1; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void print_perf_json(int pid, int fd, __u32 prog_id, __u32 fd_type, 6662306a36Sopenharmony_ci char *buf, __u64 probe_offset, __u64 probe_addr) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci jsonw_start_object(json_wtr); 6962306a36Sopenharmony_ci jsonw_int_field(json_wtr, "pid", pid); 7062306a36Sopenharmony_ci jsonw_int_field(json_wtr, "fd", fd); 7162306a36Sopenharmony_ci jsonw_uint_field(json_wtr, "prog_id", prog_id); 7262306a36Sopenharmony_ci switch (fd_type) { 7362306a36Sopenharmony_ci case BPF_FD_TYPE_RAW_TRACEPOINT: 7462306a36Sopenharmony_ci jsonw_string_field(json_wtr, "fd_type", "raw_tracepoint"); 7562306a36Sopenharmony_ci jsonw_string_field(json_wtr, "tracepoint", buf); 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case BPF_FD_TYPE_TRACEPOINT: 7862306a36Sopenharmony_ci jsonw_string_field(json_wtr, "fd_type", "tracepoint"); 7962306a36Sopenharmony_ci jsonw_string_field(json_wtr, "tracepoint", buf); 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case BPF_FD_TYPE_KPROBE: 8262306a36Sopenharmony_ci jsonw_string_field(json_wtr, "fd_type", "kprobe"); 8362306a36Sopenharmony_ci if (buf[0] != '\0') { 8462306a36Sopenharmony_ci jsonw_string_field(json_wtr, "func", buf); 8562306a36Sopenharmony_ci jsonw_lluint_field(json_wtr, "offset", probe_offset); 8662306a36Sopenharmony_ci } else { 8762306a36Sopenharmony_ci jsonw_lluint_field(json_wtr, "addr", probe_addr); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case BPF_FD_TYPE_KRETPROBE: 9162306a36Sopenharmony_ci jsonw_string_field(json_wtr, "fd_type", "kretprobe"); 9262306a36Sopenharmony_ci if (buf[0] != '\0') { 9362306a36Sopenharmony_ci jsonw_string_field(json_wtr, "func", buf); 9462306a36Sopenharmony_ci jsonw_lluint_field(json_wtr, "offset", probe_offset); 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci jsonw_lluint_field(json_wtr, "addr", probe_addr); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case BPF_FD_TYPE_UPROBE: 10062306a36Sopenharmony_ci jsonw_string_field(json_wtr, "fd_type", "uprobe"); 10162306a36Sopenharmony_ci jsonw_string_field(json_wtr, "filename", buf); 10262306a36Sopenharmony_ci jsonw_lluint_field(json_wtr, "offset", probe_offset); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case BPF_FD_TYPE_URETPROBE: 10562306a36Sopenharmony_ci jsonw_string_field(json_wtr, "fd_type", "uretprobe"); 10662306a36Sopenharmony_ci jsonw_string_field(json_wtr, "filename", buf); 10762306a36Sopenharmony_ci jsonw_lluint_field(json_wtr, "offset", probe_offset); 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci default: 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci jsonw_end_object(json_wtr); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type, 11662306a36Sopenharmony_ci char *buf, __u64 probe_offset, __u64 probe_addr) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci printf("pid %d fd %d: prog_id %u ", pid, fd, prog_id); 11962306a36Sopenharmony_ci switch (fd_type) { 12062306a36Sopenharmony_ci case BPF_FD_TYPE_RAW_TRACEPOINT: 12162306a36Sopenharmony_ci printf("raw_tracepoint %s\n", buf); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case BPF_FD_TYPE_TRACEPOINT: 12462306a36Sopenharmony_ci printf("tracepoint %s\n", buf); 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case BPF_FD_TYPE_KPROBE: 12762306a36Sopenharmony_ci if (buf[0] != '\0') 12862306a36Sopenharmony_ci printf("kprobe func %s offset %llu\n", buf, 12962306a36Sopenharmony_ci probe_offset); 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci printf("kprobe addr %llu\n", probe_addr); 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case BPF_FD_TYPE_KRETPROBE: 13462306a36Sopenharmony_ci if (buf[0] != '\0') 13562306a36Sopenharmony_ci printf("kretprobe func %s offset %llu\n", buf, 13662306a36Sopenharmony_ci probe_offset); 13762306a36Sopenharmony_ci else 13862306a36Sopenharmony_ci printf("kretprobe addr %llu\n", probe_addr); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case BPF_FD_TYPE_UPROBE: 14162306a36Sopenharmony_ci printf("uprobe filename %s offset %llu\n", buf, probe_offset); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case BPF_FD_TYPE_URETPROBE: 14462306a36Sopenharmony_ci printf("uretprobe filename %s offset %llu\n", buf, 14562306a36Sopenharmony_ci probe_offset); 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci default: 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int show_proc(void) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct dirent *proc_de, *pid_fd_de; 15562306a36Sopenharmony_ci __u64 probe_offset, probe_addr; 15662306a36Sopenharmony_ci __u32 len, prog_id, fd_type; 15762306a36Sopenharmony_ci DIR *proc, *pid_fd; 15862306a36Sopenharmony_ci int err, pid, fd; 15962306a36Sopenharmony_ci const char *pch; 16062306a36Sopenharmony_ci char buf[4096]; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci proc = opendir("/proc"); 16362306a36Sopenharmony_ci if (!proc) 16462306a36Sopenharmony_ci return -1; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci while ((proc_de = readdir(proc))) { 16762306a36Sopenharmony_ci pid = 0; 16862306a36Sopenharmony_ci pch = proc_de->d_name; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* pid should be all numbers */ 17162306a36Sopenharmony_ci while (isdigit(*pch)) { 17262306a36Sopenharmony_ci pid = pid * 10 + *pch - '0'; 17362306a36Sopenharmony_ci pch++; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci if (*pch != '\0') 17662306a36Sopenharmony_ci continue; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name); 17962306a36Sopenharmony_ci if (err < 0 || err >= (int)sizeof(buf)) 18062306a36Sopenharmony_ci continue; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci pid_fd = opendir(buf); 18362306a36Sopenharmony_ci if (!pid_fd) 18462306a36Sopenharmony_ci continue; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci while ((pid_fd_de = readdir(pid_fd))) { 18762306a36Sopenharmony_ci fd = 0; 18862306a36Sopenharmony_ci pch = pid_fd_de->d_name; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* fd should be all numbers */ 19162306a36Sopenharmony_ci while (isdigit(*pch)) { 19262306a36Sopenharmony_ci fd = fd * 10 + *pch - '0'; 19362306a36Sopenharmony_ci pch++; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci if (*pch != '\0') 19662306a36Sopenharmony_ci continue; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* query (pid, fd) for potential perf events */ 19962306a36Sopenharmony_ci len = sizeof(buf); 20062306a36Sopenharmony_ci err = bpf_task_fd_query(pid, fd, 0, buf, &len, 20162306a36Sopenharmony_ci &prog_id, &fd_type, 20262306a36Sopenharmony_ci &probe_offset, &probe_addr); 20362306a36Sopenharmony_ci if (err < 0) 20462306a36Sopenharmony_ci continue; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (json_output) 20762306a36Sopenharmony_ci print_perf_json(pid, fd, prog_id, fd_type, buf, 20862306a36Sopenharmony_ci probe_offset, probe_addr); 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci print_perf_plain(pid, fd, prog_id, fd_type, buf, 21162306a36Sopenharmony_ci probe_offset, probe_addr); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci closedir(pid_fd); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci closedir(proc); 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int do_show(int argc, char **argv) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!has_perf_query_support()) 22462306a36Sopenharmony_ci return -1; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (json_output) 22762306a36Sopenharmony_ci jsonw_start_array(json_wtr); 22862306a36Sopenharmony_ci err = show_proc(); 22962306a36Sopenharmony_ci if (json_output) 23062306a36Sopenharmony_ci jsonw_end_array(json_wtr); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return err; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int do_help(int argc, char **argv) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci fprintf(stderr, 23862306a36Sopenharmony_ci "Usage: %1$s %2$s { show | list }\n" 23962306a36Sopenharmony_ci " %1$s %2$s help\n" 24062306a36Sopenharmony_ci "\n" 24162306a36Sopenharmony_ci " " HELP_SPEC_OPTIONS " }\n" 24262306a36Sopenharmony_ci "", 24362306a36Sopenharmony_ci bin_name, argv[-2]); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic const struct cmd cmds[] = { 24962306a36Sopenharmony_ci { "show", do_show }, 25062306a36Sopenharmony_ci { "list", do_show }, 25162306a36Sopenharmony_ci { "help", do_help }, 25262306a36Sopenharmony_ci { 0 } 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciint do_perf(int argc, char **argv) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci return cmd_select(cmds, argc, argv, do_help); 25862306a36Sopenharmony_ci} 259