diff --git a/libbpf-tools/funclatency.bpf.c b/libbpf-tools/funclatency.bpf.c index f33eaabfc47..97804147ca9 100644 --- a/libbpf-tools/funclatency.bpf.c +++ b/libbpf-tools/funclatency.bpf.c @@ -28,8 +28,7 @@ struct { __u32 hist[MAX_SLOTS] = {}; -SEC("kprobe/dummy_kprobe") -int BPF_KPROBE(dummy_kprobe) +static void entry(void) { u64 id = bpf_get_current_pid_tgid(); u32 tgid = id >> 32; @@ -37,18 +36,29 @@ int BPF_KPROBE(dummy_kprobe) u64 nsec; if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0)) - return 0; + return; if (targ_tgid && targ_tgid != tgid) - return 0; + return; nsec = bpf_ktime_get_ns(); bpf_map_update_elem(&starts, &pid, &nsec, BPF_ANY); +} +SEC("fentry/dummy_fentry") +int BPF_PROG(dummy_fentry) +{ + entry(); return 0; } -SEC("kretprobe/dummy_kretprobe") -int BPF_KRETPROBE(dummy_kretprobe) +SEC("kprobe/dummy_kprobe") +int BPF_KPROBE(dummy_kprobe) +{ + entry(); + return 0; +} + +static void exit(void) { u64 *start; u64 nsec = bpf_ktime_get_ns(); @@ -57,11 +67,11 @@ int BPF_KRETPROBE(dummy_kretprobe) u64 slot, delta; if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0)) - return 0; + return; start = bpf_map_lookup_elem(&starts, &pid); if (!start) - return 0; + return; delta = nsec - *start; @@ -78,7 +88,19 @@ int BPF_KRETPROBE(dummy_kretprobe) if (slot >= MAX_SLOTS) slot = MAX_SLOTS - 1; __sync_fetch_and_add(&hist[slot], 1); +} + +SEC("fexit/dummy_fexit") +int BPF_PROG(dummy_fexit) +{ + exit(); + return 0; +} +SEC("kretprobe/dummy_kretprobe") +int BPF_KRETPROBE(dummy_kretprobe) +{ + exit(); return 0; } diff --git a/libbpf-tools/funclatency.c b/libbpf-tools/funclatency.c index 9284004f4a4..8012b09de58 100644 --- a/libbpf-tools/funclatency.c +++ b/libbpf-tools/funclatency.c @@ -38,8 +38,10 @@ static struct prog_env { bool timestamp; char *funcname; bool verbose; + bool kprobes; char *cgroupspath; bool cg; + bool is_kernel_func; } env = { .interval = 99999999, .iterations = 99999999, @@ -82,6 +84,7 @@ static const struct argp_option opts[] = { { "duration", 'd', "DURATION", 0, "Duration to trace"}, { "timestamp", 'T', NULL, 0, "Print timestamp"}, { "verbose", 'v', NULL, 0, "Verbose debug output" }, + { "kprobes", 'k', NULL, 0, "Use kprobes instead of fentry" }, { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"}, {}, }; @@ -140,6 +143,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) case 'T': env->timestamp = true; break; + case 'k': + env->kprobes = true; + break; case 'v': env->verbose = true; break; @@ -191,23 +197,56 @@ static const char *unit_str(void) return "bad units"; } -static int attach_kprobes(struct funclatency_bpf *obj) +static bool try_fentry(struct funclatency_bpf *obj) { long err; - obj->links.dummy_kprobe = bpf_program__attach_kprobe(obj->progs.dummy_kprobe, false, - env.funcname); + if (env.kprobes || !env.is_kernel_func || + !fentry_can_attach(env.funcname, NULL)) { + goto out_no_fentry; + } + + err = bpf_program__set_attach_target(obj->progs.dummy_fentry, 0, + env.funcname); + if (err) { + warn("failed to set attach fentry: %s\n", strerror(-err)); + goto out_no_fentry; + } + + err = bpf_program__set_attach_target(obj->progs.dummy_fexit, 0, + env.funcname); + if (err) { + warn("failed to set attach fexit: %s\n", strerror(-err)); + goto out_no_fentry; + } + + bpf_program__set_autoload(obj->progs.dummy_kprobe, false); + bpf_program__set_autoload(obj->progs.dummy_kretprobe, false); + + return true; + +out_no_fentry: + bpf_program__set_autoload(obj->progs.dummy_fentry, false); + bpf_program__set_autoload(obj->progs.dummy_fexit, false); + + return false; +} + +static int attach_kprobes(struct funclatency_bpf *obj) +{ + obj->links.dummy_kprobe = + bpf_program__attach_kprobe(obj->progs.dummy_kprobe, false, + env.funcname); if (!obj->links.dummy_kprobe) { - err = -errno; - warn("failed to attach kprobe: %ld\n", err); + warn("failed to attach kprobe: %d\n", -errno); return -1; } - obj->links.dummy_kretprobe = bpf_program__attach_kprobe(obj->progs.dummy_kretprobe, true, - env.funcname); + obj->links.dummy_kretprobe = + bpf_program__attach_kprobe(obj->progs.dummy_kretprobe, true, + env.funcname); if (!obj->links.dummy_kretprobe) { - err = -errno; - warn("failed to attach kretprobe: %ld\n", err); + warn("failed to attach kretprobe: %d\n", -errno); return -1; } @@ -270,13 +309,6 @@ static int attach_uprobes(struct funclatency_bpf *obj) return ret; } -static int attach_probes(struct funclatency_bpf *obj) -{ - if (strchr(env.funcname, ':')) - return attach_uprobes(obj); - return attach_kprobes(obj); -} - static volatile bool exiting; static void sig_hand(int signr) @@ -302,11 +334,14 @@ int main(int argc, char **argv) time_t t; int idx, cg_map_fd; int cgfd = -1; + bool used_fentry = false; err = argp_parse(&argp, argc, argv, 0, NULL, &env); if (err) return err; + env.is_kernel_func = !strchr(env.funcname, ':'); + sigaction(SIGINT, &sigact, 0); libbpf_set_print(libbpf_print_fn); @@ -327,6 +362,8 @@ int main(int argc, char **argv) obj->rodata->targ_tgid = env.pid; obj->rodata->filter_cg = env.cg; + used_fentry = try_fentry(obj); + err = funclatency_bpf__load(obj); if (err) { warn("failed to load BPF object\n"); @@ -353,9 +390,21 @@ int main(int argc, char **argv) goto cleanup; } - err = attach_probes(obj); - if (err) + if (!used_fentry) { + if (env.is_kernel_func) + err = attach_kprobes(obj); + else + err = attach_uprobes(obj); + if (err) + goto cleanup; + } + + err = funclatency_bpf__attach(obj); + if (err) { + fprintf(stderr, "failed to attach BPF programs: %s\n", + strerror(-err)); goto cleanup; + } printf("Tracing %s. Hit Ctrl-C to exit\n", env.funcname);