// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Facebook */
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/zalloc.h>
#include <api/fs/fs.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <perf/bpf_perf.h>
#include "bpf_counter.h"
#include "bpf-utils.h"
#include "counts.h"
#include "debug.h"
#include "evsel.h"
#include "evlist.h"
#include "target.h"
#include "cgroup.h"
#include "cpumap.h"
#include "thread_map.h"
#include "bpf_skel/bpf_prog_profiler.skel.h"
#include "bpf_skel/bperf_u.h"
#include "bpf_skel/bperf_leader.skel.h"
#include "bpf_skel/bperf_follower.skel.h"
struct bpf_counter {
void *skel;
struct list_head list;
};
#define ATTR_MAP_SIZE 16
static void *u64_to_ptr(__u64 ptr)
{
return (void *)(unsigned long)ptr;
}
void set_max_rlimit(void)
{
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
setrlimit(RLIMIT_MEMLOCK, &rinf);
}
static __u32 bpf_link_get_id(int fd)
{
struct bpf_link_info link_info = { .id = 0, };
__u32 link_info_len = sizeof(link_info);
bpf_obj_get_info_by_fd(fd, &link_info, &link_info_len);
return link_info.id;
}
static __u32 bpf_link_get_prog_id(int fd)
{
struct bpf_link_info link_info = { .id = 0, };
__u32 link_info_len = sizeof(link_info);
bpf_obj_get_info_by_fd(fd, &link_info, &link_info_len);
return link_info.prog_id;
}
static __u32 bpf_map_get_id(int fd)
{
struct bpf_map_info map_info = { .id = 0, };
__u32 map_info_len = sizeof(map_info);
bpf_obj_get_info_by_fd(fd, &map_info, &map_info_len);
return map_info.id;
}
/* trigger the leader program on a cpu */
int bperf_trigger_reading(int prog_fd, int cpu)
{
DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
.ctx_in = NULL,
.ctx_size_in = 0,
.flags = BPF_F_TEST_RUN_ON_CPU,
.cpu = cpu,
.retval = 0,
);
return bpf_prog_test_run_opts(prog_fd, &opts);
}
static struct bpf_counter *bpf_counter_alloc(void)
{
struct bpf_counter *counter;
counter = zalloc(sizeof(*counter));
if (counter)
INIT_LIST_HEAD(&counter->list);
return counter;
}
static int bpf_program_profiler__destroy(struct evsel *evsel)
{
struct bpf_counter *counter, *tmp;
list_for_each_entry_safe(counter, tmp,
&evsel->bpf_counter_list, list) {
list_del_init(&counter->list);
bpf_prog_profiler_bpf__destroy(counter->skel);
free(counter);
}
assert(list_empty(&evsel->bpf_counter_list));
return 0;
}
static char *bpf_target_prog_name(int tgt_fd)
{
struct bpf_func_info *func_info;
struct perf_bpil *info_linear;
const struct btf_type *t;
struct btf *btf = NULL;
char *name = NULL;
info_linear = get_bpf_prog_info_linear(tgt_fd, 1UL << PERF_BPIL_FUNC_INFO);
if (IS_ERR_OR_NULL(info_linear)) {
pr_debug("failed to get info_linear for prog FD %d\n", tgt_fd);
return NULL;
}
if (info_linear->info.btf_id == 0) {
pr_debug("prog FD %d doesn't have valid btf\n", tgt_fd);
goto out;
}
btf = btf__load_from_kernel_by_id(info_linear->info.btf_id);
if (libbpf_get_error(btf)) {
pr_debug("failed to load btf for prog FD %d\n", tgt_fd);
goto out;
}
func_info = u64_to_ptr(info_linear->info.func_info);
t = btf__type_by_id(btf, func_info[0].type_id);
if (!t) {
pr_debug("btf %d doesn't have type %d\n",
info_linear->info.btf_id, func_info[0].type_id);
goto out;
}
name = strdup(btf__name_by_offset(btf, t->name_off));
out:
btf__free(btf);
free(info_linear);
return name;
}
static int bpf_program_profiler_load_one(struct evsel *evsel, u32 prog_id)
{
struct bpf_prog_profiler_bpf *skel;
struct bpf_counter *counter;
struct bpf_program *prog;
char *prog_name = NULL;
int prog_fd;
int err;
prog_fd = bpf_prog_get_fd_by_id(prog_id);
if (prog_fd < 0) {
pr_err("Failed to open fd for bpf prog %u