Skip to content

Commit

Permalink
libbpf-tools: use fentry in funclatency
Browse files Browse the repository at this point in the history
For tracing kernel functions, funclatency will attempt to use fentry and
fallback to kprobes if that fails.  I left the option to force the use
of kprobes if you'd like.

Under load, fentry is more efficient:

With kprobes:

cpu:/tmp/b# ./funclatency -u -k bpf_spin_lock
Tracing bpf_spin_lock.  Hit Ctrl-C to exit
^C
     usec                : count    distribution
         0 -> 1          : 565159   |****************************************|
         2 -> 3          : 216204   |***************                         |
         4 -> 7          : 306316   |*********************                   |
         8 -> 15         : 416990   |*****************************           |
        16 -> 31         : 415110   |*****************************           |
        32 -> 63         : 170298   |************                            |
        64 -> 127        : 29726    |**                                      |
       128 -> 255        : 427962   |******************************          |
       256 -> 511        : 233207   |****************                        |
       512 -> 1023       : 453      |                                        |
      1024 -> 2047       : 0        |                                        |
Exiting trace of bpf_spin_lock

With fentry:
cpu:/tmp/b# ./funclatency -u bpf_spin_lock
Tracing bpf_spin_lock.  Hit Ctrl-C to exit
^C
     usec                : count    distribution
         0 -> 1          : 6371351  |****************************************+|
         2 -> 3          : 1100093  |******                                  |
         4 -> 7          : 518197   |***                                     |
         8 -> 15         : 174881   |*                                       |
        16 -> 31         : 25022    |                                        |
        32 -> 63         : 1553     |                                        |
        64 -> 127        : 1312     |                                        |
       128 -> 255        : 2376     |                                        |
       256 -> 511        : 0        |                                        |
Exiting trace of bpf_spin_lock

Signed-off-by: Barret Rhoden <[email protected]>
  • Loading branch information
brho authored and yonghong-song committed May 15, 2023
1 parent 7d350d9 commit 3e2a251
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 26 deletions.
38 changes: 30 additions & 8 deletions libbpf-tools/funclatency.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,37 @@ 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;
u32 pid = id;
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();
Expand All @@ -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;

Expand All @@ -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;
}

Expand Down
85 changes: 67 additions & 18 deletions libbpf-tools/funclatency.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"},
{},
};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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");
Expand All @@ -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);

Expand Down

0 comments on commit 3e2a251

Please sign in to comment.