libbpf-tools: add funclatency
authorBarret Rhoden <brho@google.com>
Fri, 26 Feb 2021 20:58:58 +0000 (15:58 -0500)
committeryonghong-song <ys114321@gmail.com>
Fri, 5 Mar 2021 22:54:16 +0000 (14:54 -0800)
This is a port of BCC's funclatency.  Usage:

---------
Time functions and print latency as a histogram

Usage: funclatency [-h] [-m|-u] [-p PID] [-d DURATION] [ -i INTERVAL ]
                   [-T] FUNCTION
       Choices for FUNCTION: FUNCTION         (kprobe)
                             LIBRARY:FUNCTION (uprobe a library in -p PID)
                             :FUNCTION        (uprobe the binary of -p PID)

  -m, --milliseconds         Output in milliseconds
  -u, --microseconds         Output in microseconds
  -p, --pid=PID              Process ID to trace
  -d, --duration=DURATION    Duration to trace
  -i, --interval=INTERVAL    Summary interval in seconds
  -T, --timestamp            Print timestamp

  -?, --help                 Give this help list
      --usage                Give a short usage message
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Examples:
  ./funclatency do_sys_open         # time the do_sys_open() kernel function
  ./funclatency -m do_nanosleep     # time do_nanosleep(), in milliseconds
  ./funclatency -u vfs_read         # time vfs_read(), in microseconds
  ./funclatency -p 181 vfs_read     # time process 181 only
  ./funclatency -p 181 c:read       # time the read() C library function
  ./funclatency -p 181 :foo         # time foo() from pid 181's userspace
  ./funclatency -i 2 -d 10 vfs_read # output every 2 seconds, for 10s
  ./funclatency -mTi 5 vfs_read     # output every 5 seconds, with timestamps

---------

It supports kprobes and has limited support for uprobes.  Currently, you
cannot uprobe a library unless you provide a PID.  It does not support
wildcard patterns.

Some of the functions for uprobes are useful for other programs, so I
put those in uprobe_helpers.{c,h}.

Signed-off-by: Barret Rhoden <brho@google.com>
libbpf-tools/.gitignore
libbpf-tools/Makefile
libbpf-tools/funclatency.bpf.c [new file with mode: 0644]
libbpf-tools/funclatency.c [new file with mode: 0644]
libbpf-tools/funclatency.h [new file with mode: 0644]
libbpf-tools/uprobe_helpers.c [new file with mode: 0644]
libbpf-tools/uprobe_helpers.h [new file with mode: 0644]

index bfceccf328d75b1082e87abc3e8e022d8a547d1d..b67d7af45a088f7dcd1974a0abac2c4a46b86bc2 100644 (file)
@@ -9,6 +9,7 @@
 /drsnoop
 /execsnoop
 /filelife
+/funclatency
 /hardirqs
 /llcstat
 /numamove
index bd561a7be5dceb582071c7ffb37e352cea693db1..9e07c73a76af2b4e0bc66882f3989e7c348f4c34 100644 (file)
@@ -26,6 +26,7 @@ APPS = \
        drsnoop \
        execsnoop \
        filelife \
+       funclatency \
        hardirqs \
        llcstat \
        numamove \
@@ -47,6 +48,7 @@ COMMON_OBJ = \
        $(OUTPUT)/syscall_helpers.o \
        $(OUTPUT)/errno_helpers.o \
        $(OUTPUT)/map_helpers.o \
+       $(OUTPUT)/uprobe_helpers.o \
        #
 
 .PHONY: all
diff --git a/libbpf-tools/funclatency.bpf.c b/libbpf-tools/funclatency.bpf.c
new file mode 100644 (file)
index 0000000..c2f9fdd
--- /dev/null
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Google LLC. */
+#include "vmlinux.h"
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "funclatency.h"
+#include "bits.bpf.h"
+
+const volatile pid_t targ_tgid;
+const volatile int units;
+
+/* key: pid.  value: start time */
+struct {
+       __uint(type, BPF_MAP_TYPE_HASH);
+       __uint(max_entries, MAX_PIDS);
+       __type(key, u32);
+       __type(value, u64);
+} starts SEC(".maps");
+
+__u32 hist[MAX_SLOTS];
+
+SEC("kprobe/dummy_kprobe")
+int BPF_KPROBE(dummy_kprobe)
+{
+       u64 id = bpf_get_current_pid_tgid();
+       u32 tgid = id >> 32;
+       u32 pid = id;
+       u64 nsec;
+
+       if (targ_tgid && targ_tgid != tgid)
+               return 0;
+       nsec = bpf_ktime_get_ns();
+       bpf_map_update_elem(&starts, &pid, &nsec, BPF_ANY);
+
+       return 0;
+}
+
+SEC("kretprobe/dummy_kretprobe")
+int BPF_KRETPROBE(dummy_kretprobe)
+{
+       u64 *start;
+       u64 nsec = bpf_ktime_get_ns();
+       u64 id = bpf_get_current_pid_tgid();
+       u32 pid = id;
+       u64 slot, delta;
+
+       start = bpf_map_lookup_elem(&starts, &pid);
+       if (!start)
+               return 0;
+
+       delta = nsec - *start;
+
+       switch (units) {
+       case USEC:
+               delta /= 1000;
+               break;
+       case MSEC:
+               delta /= 1000000;
+               break;
+       }
+
+       slot = log2l(delta);
+       if (slot >= MAX_SLOTS)
+               slot = MAX_SLOTS - 1;
+       __sync_fetch_and_add(&hist[slot], 1);
+
+       return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/libbpf-tools/funclatency.c b/libbpf-tools/funclatency.c
new file mode 100644 (file)
index 0000000..b225e7a
--- /dev/null
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2021 Google LLC.
+ *
+ * Based on funclatency from BCC by Brendan Gregg and others
+ * 2021-02-26   Barret Rhoden   Created this.
+ *
+ * TODO:
+ * - support uprobes on libraries without -p PID. (parse ld.so.cache)
+ * - support regexp pattern matching and per-function histograms
+ */
+#include <argp.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+#include "funclatency.h"
+#include "funclatency.skel.h"
+#include "trace_helpers.h"
+#include "map_helpers.h"
+#include "uprobe_helpers.h"
+
+#define warn(...) fprintf(stderr, __VA_ARGS__)
+
+static struct prog_env {
+       int units;
+       pid_t pid;
+       unsigned int duration;
+       unsigned int interval;
+       unsigned int iterations;
+       bool timestamp;
+       char *funcname;
+} env = {
+       .interval = 99999999,
+       .iterations = 99999999,
+};
+
+const char *argp_program_version = "funclatency 0.1";
+const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
+static const char args_doc[] = "FUNCTION";
+static const char program_doc[] =
+"Time functions and print latency as a histogram\n"
+"\n"
+"Usage: funclatency [-h] [-m|-u] [-p PID] [-d DURATION] [ -i INTERVAL ]\n"
+"                   [-T] FUNCTION\n"
+"       Choices for FUNCTION: FUNCTION         (kprobe)\n"
+"                             LIBRARY:FUNCTION (uprobe a library in -p PID)\n"
+"                             :FUNCTION        (uprobe the binary of -p PID)\n"
+"                             PROGRAM:FUNCTION (uprobe the binary PROGRAM)\n"
+"\v"
+"Examples:\n"
+"  ./funclatency do_sys_open         # time the do_sys_open() kernel function\n"
+"  ./funclatency -m do_nanosleep     # time do_nanosleep(), in milliseconds\n"
+"  ./funclatency -u vfs_read         # time vfs_read(), in microseconds\n"
+"  ./funclatency -p 181 vfs_read     # time process 181 only\n"
+"  ./funclatency -p 181 c:read       # time the read() C library function\n"
+"  ./funclatency -p 181 :foo         # time foo() from pid 181's userspace\n"
+"  ./funclatency -i 2 -d 10 vfs_read # output every 2 seconds, for 10s\n"
+"  ./funclatency -mTi 5 vfs_read     # output every 5 seconds, with timestamps\n"
+;
+
+static const struct argp_option opts[] = {
+       { "milliseconds", 'm', NULL, 0, "Output in milliseconds"},
+       { "microseconds", 'u', NULL, 0, "Output in microseconds"},
+       {0, 0, 0, 0, ""},
+       { "pid", 'p', "PID", 0, "Process ID to trace"},
+       {0, 0, 0, 0, ""},
+       { "interval", 'i', "INTERVAL", 0, "Summary interval in seconds"},
+       { "duration", 'd', "DURATION", 0, "Duration to trace"},
+       { "timestamp", 'T', NULL, 0, "Print timestamp"},
+       { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"},
+       {},
+};
+
+static error_t parse_arg(int key, char *arg, struct argp_state *state)
+{
+       struct prog_env *env = state->input;
+       long duration, interval, pid;
+
+       switch (key) {
+       case 'p':
+               errno = 0;
+               pid = strtol(arg, NULL, 10);
+               if (errno || pid <= 0) {
+                       warn("Invalid PID: %s\n", arg);
+                       argp_usage(state);
+               }
+               env->pid = pid;
+               break;
+       case 'm':
+               if (env->units != NSEC) {
+                       warn("only set one of -m or -u\n");
+                       argp_usage(state);
+               }
+               env->units = MSEC;
+               break;
+       case 'u':
+               if (env->units != NSEC) {
+                       warn("only set one of -m or -u\n");
+                       argp_usage(state);
+               }
+               env->units = USEC;
+               break;
+       case 'd':
+               errno = 0;
+               duration = strtol(arg, NULL, 10);
+               if (errno || duration <= 0) {
+                       warn("Invalid duration: %s\n", arg);
+                       argp_usage(state);
+               }
+               env->duration = duration;
+               break;
+       case 'i':
+               errno = 0;
+               interval = strtol(arg, NULL, 10);
+               if (errno || interval <= 0) {
+                       warn("Invalid interval: %s\n", arg);
+                       argp_usage(state);
+               }
+               env->interval = interval;
+               break;
+       case 'T':
+               env->timestamp = true;
+               break;
+       case 'h':
+               argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
+               break;
+       case ARGP_KEY_ARG:
+               if (env->funcname) {
+                       warn("Too many function names: %s\n", arg);
+                       argp_usage(state);
+               }
+               env->funcname = arg;
+               break;
+       case ARGP_KEY_END:
+               if (!env->funcname) {
+                       warn("Need a function to trace\n");
+                       argp_usage(state);
+               }
+               if (env->duration) {
+                       if (env->interval > env->duration)
+                               env->interval = env->duration;
+                       env->iterations = env->duration / env->interval;
+               }
+               break;
+       default:
+               return ARGP_ERR_UNKNOWN;
+       }
+       return 0;
+}
+
+static const char *unit_str(void)
+{
+       switch (env.units) {
+       case NSEC:
+               return "nsec";
+       case USEC:
+               return "usec";
+       case MSEC:
+               return "msec";
+       };
+
+       return "bad units";
+}
+
+static int attach_kprobes(struct funclatency_bpf *obj)
+{
+       long err;
+
+       obj->links.dummy_kprobe =
+               bpf_program__attach_kprobe(obj->progs.dummy_kprobe, false,
+                                          env.funcname);
+       err = libbpf_get_error(obj->links.dummy_kprobe);
+       if (err) {
+               warn("failed to attach kprobe: %ld\n", err);
+               return -1;
+       }
+
+       obj->links.dummy_kretprobe =
+               bpf_program__attach_kprobe(obj->progs.dummy_kretprobe, true,
+                                          env.funcname);
+       err = libbpf_get_error(obj->links.dummy_kretprobe);
+       if (err) {
+               warn("failed to attach kretprobe: %ld\n", err);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int attach_uprobes(struct funclatency_bpf *obj)
+{
+       char *binary, *function;
+       char bin_path[PATH_MAX];
+       off_t func_off;
+       int ret = -1;
+       long err;
+
+       binary = strdup(env.funcname);
+       if (!binary) {
+               warn("strdup failed");
+               return -1;
+       }
+       function = strchr(binary, ':');
+       if (!function) {
+               warn("Binary should have contained ':' (internal bug!)\n");
+               return -1;
+       }
+       *function = '\0';
+       function++;
+
+       if (resolve_binary_path(binary, env.pid, bin_path, sizeof(bin_path)))
+               goto out_binary;
+
+       func_off = get_elf_func_offset(bin_path, function);
+       if (func_off < 0) {
+               warn("Could not find %s in %s\n", function, bin_path);
+               goto out_binary;
+       }
+
+       obj->links.dummy_kprobe =
+               bpf_program__attach_uprobe(obj->progs.dummy_kprobe, false,
+                                          env.pid ?: -1, bin_path, func_off);
+       err = libbpf_get_error(obj->links.dummy_kprobe);
+       if (err) {
+               warn("Failed to attach uprobe: %ld\n", err);
+               goto out_binary;
+       }
+
+       obj->links.dummy_kretprobe =
+               bpf_program__attach_uprobe(obj->progs.dummy_kretprobe, true,
+                                          env.pid ?: -1, bin_path, func_off);
+       err = libbpf_get_error(obj->links.dummy_kretprobe);
+       if (err) {
+               warn("Failed to attach uretprobe: %ld\n", err);
+               goto out_binary;
+       }
+
+       ret = 0;
+
+out_binary:
+       free(binary);
+
+       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)
+{
+       exiting = true;
+}
+
+static struct sigaction sigact = {.sa_handler = sig_hand};
+
+int main(int argc, char **argv)
+{
+       static const struct argp argp = {
+               .options = opts,
+               .parser = parse_arg,
+               .args_doc = args_doc,
+               .doc = program_doc,
+       };
+       struct funclatency_bpf *obj;
+       int err;
+       struct tm *tm;
+       char ts[32];
+       time_t t;
+
+       err = argp_parse(&argp, argc, argv, 0, NULL, &env);
+       if (err)
+               return err;
+
+       sigaction(SIGINT, &sigact, 0);
+
+       err = bump_memlock_rlimit();
+       if (err) {
+               warn("failed to increase rlimit: %d\n", err);
+               return 1;
+       }
+
+       obj = funclatency_bpf__open();
+       if (!obj) {
+               warn("failed to open BPF object\n");
+               return 1;
+       }
+
+       obj->rodata->units = env.units;
+       obj->rodata->targ_tgid = env.pid;
+
+       err = funclatency_bpf__load(obj);
+       if (err) {
+               warn("failed to load BPF object\n");
+               return 1;
+       }
+
+       err = attach_probes(obj);
+       if (err)
+               goto cleanup;
+
+       printf("Tracing %s.  Hit Ctrl-C to exit\n", env.funcname);
+
+       for (int i = 0; i < env.iterations && !exiting; i++) {
+               sleep(env.interval);
+
+               printf("\n");
+               if (env.timestamp) {
+                       time(&t);
+                       tm = localtime(&t);
+                       strftime(ts, sizeof(ts), "%H:%M:%S", tm);
+                       printf("%-8s\n", ts);
+               }
+
+               print_log2_hist(obj->bss->hist, MAX_SLOTS, unit_str());
+       }
+
+       printf("Exiting trace of %s\n", env.funcname);
+
+cleanup:
+       funclatency_bpf__destroy(obj);
+
+       return err != 0;
+}
diff --git a/libbpf-tools/funclatency.h b/libbpf-tools/funclatency.h
new file mode 100644 (file)
index 0000000..a28fc2c
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#pragma once
+
+#define MAX_PIDS 102400
+#define MAX_SLOTS 25
+
+enum units {
+       NSEC,
+       USEC,
+       MSEC,
+};
diff --git a/libbpf-tools/uprobe_helpers.c b/libbpf-tools/uprobe_helpers.c
new file mode 100644 (file)
index 0000000..ff97946
--- /dev/null
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2021 Google LLC. */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <gelf.h>
+
+#define warn(...) fprintf(stderr, __VA_ARGS__)
+
+/*
+ * Returns 0 on success; -1 on failure.  On sucess, returns via `path` the full
+ * path to the program for pid.
+ */
+int get_pid_binary_path(pid_t pid, char *path, size_t path_sz)
+{
+       ssize_t ret;
+       char proc_pid_exe[32];
+
+       if (snprintf(proc_pid_exe, sizeof(proc_pid_exe), "/proc/%d/exe", pid)
+           >= sizeof(proc_pid_exe)) {
+               warn("snprintf /proc/PID/exe failed");
+               return -1;
+       }
+       ret = readlink(proc_pid_exe, path, path_sz);
+       if (ret < 0) {
+               warn("No such pid %d\n", pid);
+               return -1;
+       }
+       if (ret >= path_sz) {
+               warn("readlink truncation");
+               return -1;
+       }
+       path[ret] = '\0';
+
+       return 0;
+}
+
+/*
+ * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
+ * path to a library matching the name `lib` that is loaded into pid's address
+ * space.
+ */
+int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz)
+{
+       FILE *maps;
+       char *p;
+       char proc_pid_maps[32];
+       char line_buf[1024];
+
+       if (snprintf(proc_pid_maps, sizeof(proc_pid_maps), "/proc/%d/maps", pid)
+           >= sizeof(proc_pid_maps)) {
+               warn("snprintf /proc/PID/maps failed");
+               return -1;
+       }
+       maps = fopen(proc_pid_maps, "r");
+       if (!maps) {
+               warn("No such pid %d\n", pid);
+               return -1;
+       }
+       while (fgets(line_buf, sizeof(line_buf), maps)) {
+               if (sscanf(line_buf, "%*x-%*x %*s %*x %*s %*u %s", path) != 1)
+                       continue;
+               /* e.g. /usr/lib/x86_64-linux-gnu/libc-2.31.so */
+               p = strrchr(path, '/');
+               if (!p)
+                       continue;
+               if (strncmp(p, "/lib", 4))
+                       continue;
+               p += 4;
+               if (strncmp(lib, p, strlen(lib)))
+                       continue;
+               p += strlen(lib);
+               /* libraries can have - or . after the name */
+               if (*p != '.' && *p != '-')
+                       continue;
+
+               fclose(maps);
+               return 0;
+       }
+
+       warn("Cannot find library %s\n", lib);
+       fclose(maps);
+       return -1;
+}
+
+/*
+ * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
+ * path to the program.
+ */
+static int which_program(const char *prog, char *path, size_t path_sz)
+{
+       FILE *which;
+       char cmd[100];
+
+       if (snprintf(cmd, sizeof(cmd), "which %s", prog) >= sizeof(cmd)) {
+               warn("snprintf which prog failed");
+               return -1;
+       }
+       which = popen(cmd, "r");
+       if (!which) {
+               warn("which failed");
+               return -1;
+       }
+       if (!fgets(path, path_sz, which)) {
+               warn("fgets which failed");
+               pclose(which);
+               return -1;
+       }
+       /* which has a \n at the end of the string */
+       path[strlen(path) - 1] = '\0';
+       pclose(which);
+       return 0;
+}
+
+/*
+ * Returns 0 on success; -1 on failure.  On success, returns via `path` the full
+ * path to the binary for the given pid.
+ * 1) pid == x, binary == ""    : returns the path to x's program
+ * 2) pid == x, binary == "foo" : returns the path to libfoo linked in x
+ * 3) pid == 0, binary == ""    : failure: need a pid or a binary
+ * 4) pid == 0, binary == "bar" : returns the path to `which bar`
+ *
+ * For case 4), ideally we'd like to search for libbar too, but we don't support
+ * that yet.
+ */
+int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz)
+{
+       if (!strcmp(binary, "")) {
+               if (!pid) {
+                       warn("Uprobes need a pid or a binary\n");
+                       return -1;
+               }
+               return get_pid_binary_path(pid, path, path_sz);
+       }
+       if (pid)
+               return get_pid_lib_path(pid, binary, path, path_sz);
+
+       if (which_program(binary, path, path_sz)) {
+               /*
+                * If the user is tracing a program by name, we can find it.
+                * But we can't find a library by name yet.  We'd need to parse
+                * ld.so.cache or something similar.
+                */
+               warn("Can't find %s (Need a PID if this is a library)\n", binary);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Opens an elf at `path` of kind ELF_K_ELF.  Returns NULL on failure.  On
+ * success, close with close_elf(e, fd_close).
+ */
+static Elf *open_elf(const char *path, int *fd_close)
+{
+       int fd;
+       Elf *e;
+
+       if (elf_version(EV_CURRENT) == EV_NONE) {
+               warn("elf init failed\n");
+               return NULL;
+       }
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               warn("Could not open %s\n", path);
+               return NULL;
+       }
+       e = elf_begin(fd, ELF_C_READ, NULL);
+       if (!e) {
+               warn("elf_begin failed: %s\n", elf_errmsg(-1));
+               close(fd);
+               return NULL;
+       }
+       if (elf_kind(e) != ELF_K_ELF) {
+               warn("elf kind %d is not ELF_K_ELF\n", elf_kind(e));
+               elf_end(e);
+               close(fd);
+               return NULL;
+       }
+       *fd_close = fd;
+       return e;
+}
+
+static void close_elf(Elf *e, int fd_close)
+{
+       elf_end(e);
+       close(fd_close);
+}
+
+/* Returns the offset of a function in the elf file `path`, or -1 on failure. */
+off_t get_elf_func_offset(const char *path, const char *func)
+{
+       off_t ret = -1;
+       int fd = -1;
+       Elf *e;
+       Elf_Scn *scn;
+       Elf_Data *data;
+       GElf_Shdr shdr[1];
+       GElf_Sym sym[1];
+       size_t shstrndx;
+       char *n;
+
+       e = open_elf(path, &fd);
+
+       if (elf_getshdrstrndx(e, &shstrndx) != 0)
+               goto out;
+
+       scn = NULL;
+       while ((scn = elf_nextscn(e, scn))) {
+               if (!gelf_getshdr(scn, shdr))
+                       continue;
+               if (!(shdr->sh_type == SHT_SYMTAB || shdr->sh_type == SHT_DYNSYM))
+                       continue;
+               data = NULL;
+               while ((data = elf_getdata(scn, data))) {
+                       for (int i = 0; gelf_getsym(data, i, sym); i++) {
+                               n = elf_strptr(e, shdr->sh_link, sym->st_name);
+                               if (!n)
+                                       continue;
+                               if (GELF_ST_TYPE(sym->st_info) != STT_FUNC)
+                                       continue;
+                               if (!strcmp(n, func)) {
+                                       ret = sym->st_value;
+                                       goto out;
+                               }
+                       }
+               }
+       }
+
+out:
+       close_elf(e, fd);
+       return ret;
+}
diff --git a/libbpf-tools/uprobe_helpers.h b/libbpf-tools/uprobe_helpers.h
new file mode 100644 (file)
index 0000000..c8a7580
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2021 Google LLC. */
+#ifndef __UPROBE_HELPERS_H
+#define __UPROBE_HELPERS_H
+
+#include <sys/types.h>
+#include <unistd.h>
+
+int get_pid_binary_path(pid_t pid, char *path, size_t path_sz);
+int get_pid_lib_path(pid_t pid, const char *lib, char *path, size_t path_sz);
+int resolve_binary_path(const char *binary, pid_t pid, char *path, size_t path_sz);
+off_t get_elf_func_offset(const char *path, const char *func);
+
+#endif /* __UPROBE_HELPERS_H */