| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <bpf/bpf.h> | |
| #include <linux/err.h> | |
| #include "util/ftrace.h" | |
| #include "util/cpumap.h" | |
| #include "util/thread_map.h" | |
| #include "util/debug.h" | |
| #include "util/evlist.h" | |
| #include "util/bpf_counter.h" | |
| #include "util/stat.h" | |
| #include "util/bpf_skel/func_latency.skel.h" | |
| static struct func_latency_bpf *skel; | |
| int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) | |
| { | |
| int fd, err; | |
| int i, ncpus = 1, ntasks = 1; | |
| struct filter_entry *func = NULL; | |
| if (!list_empty(&ftrace->filters)) { | |
| if (!list_is_singular(&ftrace->filters)) { | |
| pr_err("ERROR: Too many target functions.\n"); | |
| return -1; | |
| } | |
| func = list_first_entry(&ftrace->filters, struct filter_entry, list); | |
| } else { | |
| int count = 0; | |
| struct list_head *pos; | |
| list_for_each(pos, &ftrace->event_pair) | |
| count++; | |
| if (count != 2) { | |
| pr_err("ERROR: Needs two target events.\n"); | |
| return -1; | |
| } | |
| } | |
| skel = func_latency_bpf__open(); | |
| if (!skel) { | |
| pr_err("Failed to open func latency skeleton\n"); | |
| return -1; | |
| } | |
| skel->rodata->bucket_range = ftrace->bucket_range; | |
| skel->rodata->min_latency = ftrace->min_latency; | |
| skel->rodata->bucket_num = ftrace->bucket_num; | |
| if (ftrace->bucket_range && ftrace->bucket_num) { | |
| bpf_map__set_max_entries(skel->maps.latency, ftrace->bucket_num); | |
| } | |
| /* don't need to set cpu filter for system-wide mode */ | |
| if (ftrace->target.cpu_list) { | |
| ncpus = perf_cpu_map__nr(evlist__core(ftrace->evlist)->user_requested_cpus); | |
| bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); | |
| skel->rodata->has_cpu = 1; | |
| } | |
| if (target__has_task(&ftrace->target) || target__none(&ftrace->target)) { | |
| ntasks = perf_thread_map__nr(evlist__core(ftrace->evlist)->threads); | |
| bpf_map__set_max_entries(skel->maps.task_filter, ntasks); | |
| skel->rodata->has_task = 1; | |
| } | |
| skel->rodata->use_nsec = ftrace->use_nsec; | |
| set_max_rlimit(); | |
| err = func_latency_bpf__load(skel); | |
| if (err) { | |
| pr_err("Failed to load func latency skeleton\n"); | |
| goto out; | |
| } | |
| if (ftrace->target.cpu_list) { | |
| u32 cpu; | |
| u8 val = 1; | |
| fd = bpf_map__fd(skel->maps.cpu_filter); | |
| for (i = 0; i < ncpus; i++) { | |
| cpu = perf_cpu_map__cpu( | |
| evlist__core(ftrace->evlist)->user_requested_cpus, i).cpu; | |
| bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); | |
| } | |
| } | |
| if (target__has_task(&ftrace->target) || target__none(&ftrace->target)) { | |
| u32 pid; | |
| u8 val = 1; | |
| fd = bpf_map__fd(skel->maps.task_filter); | |
| for (i = 0; i < ntasks; i++) { | |
| pid = perf_thread_map__pid(evlist__core(ftrace->evlist)->threads, i); | |
| bpf_map_update_elem(fd, &pid, &val, BPF_ANY); | |
| } | |
| } | |
| skel->bss->min = INT64_MAX; | |
| if (func) { | |
| skel->links.func_begin = bpf_program__attach_kprobe(skel->progs.func_begin, | |
| false, func->name); | |
| if (IS_ERR(skel->links.func_begin)) { | |
| pr_err("Failed to attach fentry program\n"); | |
| err = PTR_ERR(skel->links.func_begin); | |
| goto out; | |
| } | |
| skel->links.func_end = bpf_program__attach_kprobe(skel->progs.func_end, | |
| true, func->name); | |
| if (IS_ERR(skel->links.func_end)) { | |
| pr_err("Failed to attach fexit program\n"); | |
| err = PTR_ERR(skel->links.func_end); | |
| goto out; | |
| } | |
| } else { | |
| struct filter_entry *event; | |
| event = list_first_entry(&ftrace->event_pair, struct filter_entry, list); | |
| skel->links.event_begin = bpf_program__attach_raw_tracepoint(skel->progs.event_begin, | |
| event->name); | |
| if (IS_ERR(skel->links.event_begin)) { | |
| pr_err("Failed to attach first tracepoint program\n"); | |
| err = PTR_ERR(skel->links.event_begin); | |
| goto out; | |
| } | |
| event = list_next_entry(event, list); | |
| skel->links.event_end = bpf_program__attach_raw_tracepoint(skel->progs.event_end, | |
| event->name); | |
| if (IS_ERR(skel->links.event_end)) { | |
| pr_err("Failed to attach second tracepoint program\n"); | |
| err = PTR_ERR(skel->links.event_end); | |
| goto out; | |
| } | |
| } | |
| /* XXX: we don't actually use this fd - just for poll() */ | |
| return open("/dev/null", O_RDONLY); | |
| out: | |
| return err; | |
| } | |
| int perf_ftrace__latency_start_bpf(struct perf_ftrace *ftrace __maybe_unused) | |
| { | |
| skel->bss->enabled = 1; | |
| return 0; | |
| } | |
| int perf_ftrace__latency_stop_bpf(struct perf_ftrace *ftrace __maybe_unused) | |
| { | |
| skel->bss->enabled = 0; | |
| return 0; | |
| } | |
| int perf_ftrace__latency_read_bpf(struct perf_ftrace *ftrace, | |
| int buckets[], struct stats *stats) | |
| { | |
| int i, fd, err; | |
| u32 idx; | |
| u64 *hist; | |
| int ncpus = cpu__max_cpu().cpu; | |
| fd = bpf_map__fd(skel->maps.latency); | |
| hist = calloc(ncpus, sizeof(*hist)); | |
| if (hist == NULL) | |
| return -ENOMEM; | |
| for (idx = 0; idx < skel->rodata->bucket_num; idx++) { | |
| err = bpf_map_lookup_elem(fd, &idx, hist); | |
| if (err) { | |
| buckets[idx] = 0; | |
| continue; | |
| } | |
| for (i = 0; i < ncpus; i++) | |
| buckets[idx] += hist[i]; | |
| } | |
| if (skel->bss->count) { | |
| stats->mean = skel->bss->total / skel->bss->count; | |
| stats->n = skel->bss->count; | |
| stats->max = skel->bss->max; | |
| stats->min = skel->bss->min; | |
| if (!ftrace->use_nsec) { | |
| stats->mean /= 1000; | |
| stats->max /= 1000; | |
| stats->min /= 1000; | |
| } | |
| } | |
| free(hist); | |
| return 0; | |
| } | |
| int perf_ftrace__latency_cleanup_bpf(struct perf_ftrace *ftrace __maybe_unused) | |
| { | |
| func_latency_bpf__destroy(skel); | |
| return 0; | |
| } |