Skip to content

Commit

Permalink
examples/c: Update uprobe example
Browse files Browse the repository at this point in the history
Update uprobe example to showcase the usage of libbpf uprobe
manual attach and auto attach.

Signed-off-by: Hengqi Chen <chenhengqi@outlook.com>
  • Loading branch information
chenhengqi authored and anakryiko committed Aug 4, 2022
1 parent ca83fd6 commit e6c815c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 50 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ TIME EVENT COMM PID PPID FILENAME/EXIT CODE

`uprobe` is an example of dealing with user-space entry and exit (return) probes,
`uprobe` and `uretprobe` in libbpf lingo. It attached `uprobe` and `uretprobe`
BPF programs to its own function (`uprobe_trigger()`) and logs input arguments
BPF programs to its own functions (`uprobed_add()` and `uprobed_sub()`) and logs input arguments
and return result, respectively, using `bpf_printk()` macro. The user-space
function is triggered once every second:

Expand All @@ -107,12 +107,14 @@ Successfully started!
You can see `uprobe` demo output in `/sys/kernel/debug/tracing/trace_pipe`:
```shell
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
<...>-461101 [018] d... 505432.345032: bpf_trace_printk: UPROBE ENTRY: a = 0, b = 1
<...>-461101 [018] d... 505432.345042: bpf_trace_printk: UPROBE EXIT: return = 1
<...>-461101 [018] d... 505433.345186: bpf_trace_printk: UPROBE ENTRY: a = 1, b = 2
<...>-461101 [018] d... 505433.345202: bpf_trace_printk: UPROBE EXIT: return = 3
<...>-461101 [018] d... 505434.345342: bpf_trace_printk: UPROBE ENTRY: a = 2, b = 3
<...>-461101 [018] d... 505434.345367: bpf_trace_printk: UPROBE EXIT: return = 5
uprobe-1809291 [007] .... 4017233.106596: 0: uprobed_add ENTRY: a = 0, b = 1
uprobe-1809291 [007] .... 4017233.106605: 0: uprobed_add EXIT: return = 1
uprobe-1809291 [007] .... 4017233.106606: 0: uprobed_sub ENTRY: a = 0, b = 0
uprobe-1809291 [007] .... 4017233.106607: 0: uprobed_sub EXIT: return = 0
uprobe-1809291 [007] .... 4017234.106694: 0: uprobed_add ENTRY: a = 1, b = 2
uprobe-1809291 [007] .... 4017234.106697: 0: uprobed_add EXIT: return = 3
uprobe-1809291 [007] .... 4017234.106700: 0: uprobed_sub ENTRY: a = 1, b = 1
uprobe-1809291 [007] .... 4017234.106701: 0: uprobed_sub EXIT: return = 0
```

## USDT
Expand Down
26 changes: 20 additions & 6 deletions examples/c/uprobe.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,30 @@

char LICENSE[] SEC("license") = "Dual BSD/GPL";

SEC("uprobe/func")
int BPF_KPROBE(uprobe, int a, int b)
SEC("uprobe")
int BPF_KPROBE(uprobe_add, int a, int b)
{
bpf_printk("UPROBE ENTRY: a = %d, b = %d\n", a, b);
bpf_printk("uprobed_add ENTRY: a = %d, b = %d", a, b);
return 0;
}

SEC("uretprobe/func")
int BPF_KRETPROBE(uretprobe, int ret)
SEC("uretprobe")
int BPF_KRETPROBE(uretprobe_add, int ret)
{
bpf_printk("UPROBE EXIT: return = %d\n", ret);
bpf_printk("uprobed_add EXIT: return = %d", ret);
return 0;
}

SEC("uprobe//proc/self/exe:uprobed_sub")
int BPF_KPROBE(uprobe_sub, int a, int b)
{
bpf_printk("uprobed_sub ENTRY: a = %d, b = %d", a, b);
return 0;
}

SEC("uretprobe//proc/self/exe:uprobed_sub")
int BPF_KRETPROBE(uretprobe_sub, int ret)
{
bpf_printk("uprobed_sub EXIT: return = %d", ret);
return 0;
}
84 changes: 47 additions & 37 deletions examples/c/uprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,52 @@ static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va
return vfprintf(stderr, format, args);
}

/* Find process's base load address. We use /proc/self/maps for that,
* searching for the first executable (r-xp) memory mapping:
/*
* Taken from https://github.com/torvalds/linux/blob/9b59ec8d50a1f28747ceff9a4f39af5deba9540e/tools/testing/selftests/bpf/trace_helpers.c#L149-L205
*
* 5574fd254000-5574fd258000 r-xp 00002000 fd:01 668759 /usr/bin/cat
* ^^^^^^^^^^^^ ^^^^^^^^
*
* Subtracting that region's offset (4th column) from its absolute start
* memory address (1st column) gives us the process's base load address.
* See discussion in https://github.com/libbpf/libbpf-bootstrap/pull/90
*/
static long get_base_addr() {
size_t start, offset;
ssize_t get_uprobe_offset(const void *addr)
{
size_t start, end, base;
char buf[256];
bool found = false;
FILE *f;

f = fopen("/proc/self/maps", "r");
if (!f)
return -errno;

while (fscanf(f, "%zx-%*x %s %zx %*[^\n]\n", &start, buf, &offset) == 3) {
if (strcmp(buf, "r-xp") == 0) {
fclose(f);
return start - offset;
while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
found = true;
break;
}
}

fclose(f);
return -1;

if (!found)
return -ESRCH;

return (uintptr_t)addr - start + base;
}

/* It's a global function to make sure compiler doesn't inline it. */
int uprobed_function(int a, int b)
int uprobed_add(int a, int b)
{
return a + b;
}

int uprobed_sub(int a, int b)
{
return a - b;
}

int main(int argc, char **argv)
{
struct uprobe_bpf *skel;
long base_addr, uprobe_offset;
long uprobe_offset;
int err, i;

libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
Expand All @@ -64,29 +71,22 @@ int main(int argc, char **argv)
return 1;
}

base_addr = get_base_addr();
if (base_addr < 0) {
fprintf(stderr, "Failed to determine process's load address\n");
err = base_addr;
goto cleanup;
}

/* uprobe/uretprobe expects relative offset of the function to attach
* to. This offset is relateve to the process's base load address. So
* easy way to do this is to take an absolute address of the desired
* function and substract base load address from it. If we were to
* parse ELF to calculate this function, we'd need to add .text
* section offset and function's offset within .text ELF section.
*/
uprobe_offset = (long)&uprobed_function - base_addr;
uprobe_offset = get_uprobe_offset(&uprobed_add);

/* Attach tracepoint handler */
skel->links.uprobe = bpf_program__attach_uprobe(skel->progs.uprobe,
false /* not uretprobe */,
0 /* self pid */,
"/proc/self/exe",
uprobe_offset);
if (!skel->links.uprobe) {
skel->links.uprobe_add = bpf_program__attach_uprobe(skel->progs.uprobe_add,
false /* not uretprobe */,
0 /* self pid */,
"/proc/self/exe",
uprobe_offset);
if (!skel->links.uprobe_add) {
err = -errno;
fprintf(stderr, "Failed to attach uprobe: %d\n", err);
goto cleanup;
Expand All @@ -96,24 +96,34 @@ int main(int argc, char **argv)
* processes that use the same binary executable; to do that we need
* to specify -1 as PID, as we do here
*/
skel->links.uretprobe = bpf_program__attach_uprobe(skel->progs.uretprobe,
true /* uretprobe */,
-1 /* any pid */,
"/proc/self/exe",
uprobe_offset);
if (!skel->links.uretprobe) {
skel->links.uretprobe_add = bpf_program__attach_uprobe(skel->progs.uretprobe_add,
true /* uretprobe */,
-1 /* any pid */,
"/proc/self/exe",
uprobe_offset);
if (!skel->links.uretprobe_add) {
err = -errno;
fprintf(stderr, "Failed to attach uprobe: %d\n", err);
goto cleanup;
}

/* Let libbpf perform auto-attach for uprobe_sub/uretprobe_sub
* NOTICE: we provide path and symbol info in SEC for BPF programs
*/
err = uprobe_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err);
goto cleanup;
}

printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
"to see output of the BPF programs.\n");

for (i = 0; ; i++) {
/* trigger our BPF programs */
fprintf(stderr, ".");
uprobed_function(i, i + 1);
uprobed_add(i, i + 1);
uprobed_sub(i * i, i);
sleep(1);
}

Expand Down

0 comments on commit e6c815c

Please sign in to comment.