// 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);
}
Messung V0.5 C=93 H=90 G=91
¤ Dauer der Verarbeitung: 0.3 Sekunden
¤
*© Formatika GbR, Deutschland