forked from Minki/linux
2bf3e2ef42
There are two files in the tree called libbpf.h which is becoming problematic. Most samples don't actually need the local libbpf.h they simply include it to get to bpf/bpf.h. Include bpf/bpf.h directly instead. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Acked-by: Jesper Dangaard Brouer <brouer@redhat.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
190 lines
4.6 KiB
C
190 lines
4.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#define _GNU_SOURCE
|
|
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <linux/perf_event.h>
|
|
#include <linux/bpf.h>
|
|
#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include "bpf_load.h"
|
|
#include <bpf/bpf.h>
|
|
#include "perf-sys.h"
|
|
|
|
#define SAMPLE_PERIOD 0x7fffffffffffffffULL
|
|
|
|
static void check_on_cpu(int cpu, struct perf_event_attr *attr)
|
|
{
|
|
struct bpf_perf_event_value value2;
|
|
int pmu_fd, error = 0;
|
|
cpu_set_t set;
|
|
__u64 value;
|
|
|
|
/* Move to target CPU */
|
|
CPU_ZERO(&set);
|
|
CPU_SET(cpu, &set);
|
|
assert(sched_setaffinity(0, sizeof(set), &set) == 0);
|
|
/* Open perf event and attach to the perf_event_array */
|
|
pmu_fd = sys_perf_event_open(attr, -1/*pid*/, cpu/*cpu*/, -1/*group_fd*/, 0);
|
|
if (pmu_fd < 0) {
|
|
fprintf(stderr, "sys_perf_event_open failed on CPU %d\n", cpu);
|
|
error = 1;
|
|
goto on_exit;
|
|
}
|
|
assert(bpf_map_update_elem(map_fd[0], &cpu, &pmu_fd, BPF_ANY) == 0);
|
|
assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0) == 0);
|
|
/* Trigger the kprobe */
|
|
bpf_map_get_next_key(map_fd[1], &cpu, NULL);
|
|
/* Check the value */
|
|
if (bpf_map_lookup_elem(map_fd[1], &cpu, &value)) {
|
|
fprintf(stderr, "Value missing for CPU %d\n", cpu);
|
|
error = 1;
|
|
goto on_exit;
|
|
} else {
|
|
fprintf(stderr, "CPU %d: %llu\n", cpu, value);
|
|
}
|
|
/* The above bpf_map_lookup_elem should trigger the second kprobe */
|
|
if (bpf_map_lookup_elem(map_fd[2], &cpu, &value2)) {
|
|
fprintf(stderr, "Value2 missing for CPU %d\n", cpu);
|
|
error = 1;
|
|
goto on_exit;
|
|
} else {
|
|
fprintf(stderr, "CPU %d: counter: %llu, enabled: %llu, running: %llu\n", cpu,
|
|
value2.counter, value2.enabled, value2.running);
|
|
}
|
|
|
|
on_exit:
|
|
assert(bpf_map_delete_elem(map_fd[0], &cpu) == 0 || error);
|
|
assert(ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE, 0) == 0 || error);
|
|
assert(close(pmu_fd) == 0 || error);
|
|
assert(bpf_map_delete_elem(map_fd[1], &cpu) == 0 || error);
|
|
exit(error);
|
|
}
|
|
|
|
static void test_perf_event_array(struct perf_event_attr *attr,
|
|
const char *name)
|
|
{
|
|
int i, status, nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
|
pid_t pid[nr_cpus];
|
|
int err = 0;
|
|
|
|
printf("Test reading %s counters\n", name);
|
|
|
|
for (i = 0; i < nr_cpus; i++) {
|
|
pid[i] = fork();
|
|
assert(pid[i] >= 0);
|
|
if (pid[i] == 0) {
|
|
check_on_cpu(i, attr);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nr_cpus; i++) {
|
|
assert(waitpid(pid[i], &status, 0) == pid[i]);
|
|
err |= status;
|
|
}
|
|
|
|
if (err)
|
|
printf("Test: %s FAILED\n", name);
|
|
}
|
|
|
|
static void test_bpf_perf_event(void)
|
|
{
|
|
struct perf_event_attr attr_cycles = {
|
|
.freq = 0,
|
|
.sample_period = SAMPLE_PERIOD,
|
|
.inherit = 0,
|
|
.type = PERF_TYPE_HARDWARE,
|
|
.read_format = 0,
|
|
.sample_type = 0,
|
|
.config = PERF_COUNT_HW_CPU_CYCLES,
|
|
};
|
|
struct perf_event_attr attr_clock = {
|
|
.freq = 0,
|
|
.sample_period = SAMPLE_PERIOD,
|
|
.inherit = 0,
|
|
.type = PERF_TYPE_SOFTWARE,
|
|
.read_format = 0,
|
|
.sample_type = 0,
|
|
.config = PERF_COUNT_SW_CPU_CLOCK,
|
|
};
|
|
struct perf_event_attr attr_raw = {
|
|
.freq = 0,
|
|
.sample_period = SAMPLE_PERIOD,
|
|
.inherit = 0,
|
|
.type = PERF_TYPE_RAW,
|
|
.read_format = 0,
|
|
.sample_type = 0,
|
|
/* Intel Instruction Retired */
|
|
.config = 0xc0,
|
|
};
|
|
struct perf_event_attr attr_l1d_load = {
|
|
.freq = 0,
|
|
.sample_period = SAMPLE_PERIOD,
|
|
.inherit = 0,
|
|
.type = PERF_TYPE_HW_CACHE,
|
|
.read_format = 0,
|
|
.sample_type = 0,
|
|
.config =
|
|
PERF_COUNT_HW_CACHE_L1D |
|
|
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
|
(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
|
|
};
|
|
struct perf_event_attr attr_llc_miss = {
|
|
.freq = 0,
|
|
.sample_period = SAMPLE_PERIOD,
|
|
.inherit = 0,
|
|
.type = PERF_TYPE_HW_CACHE,
|
|
.read_format = 0,
|
|
.sample_type = 0,
|
|
.config =
|
|
PERF_COUNT_HW_CACHE_LL |
|
|
(PERF_COUNT_HW_CACHE_OP_READ << 8) |
|
|
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
|
|
};
|
|
struct perf_event_attr attr_msr_tsc = {
|
|
.freq = 0,
|
|
.sample_period = 0,
|
|
.inherit = 0,
|
|
/* From /sys/bus/event_source/devices/msr/ */
|
|
.type = 7,
|
|
.read_format = 0,
|
|
.sample_type = 0,
|
|
.config = 0,
|
|
};
|
|
|
|
test_perf_event_array(&attr_cycles, "HARDWARE-cycles");
|
|
test_perf_event_array(&attr_clock, "SOFTWARE-clock");
|
|
test_perf_event_array(&attr_raw, "RAW-instruction-retired");
|
|
test_perf_event_array(&attr_l1d_load, "HW_CACHE-L1D-load");
|
|
|
|
/* below tests may fail in qemu */
|
|
test_perf_event_array(&attr_llc_miss, "HW_CACHE-LLC-miss");
|
|
test_perf_event_array(&attr_msr_tsc, "Dynamic-msr-tsc");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
|
|
char filename[256];
|
|
|
|
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
|
|
|
|
setrlimit(RLIMIT_MEMLOCK, &r);
|
|
if (load_bpf_file(filename)) {
|
|
printf("%s", bpf_log_buf);
|
|
return 1;
|
|
}
|
|
|
|
test_bpf_perf_event();
|
|
return 0;
|
|
}
|