Skip to content

Commit

Permalink
runtime: implement userspace USDT (#139)
Browse files Browse the repository at this point in the history
* Place holder

* Update

* Add usdt example

* Fix fd allocation

* Update llvm-jit

* Fix CI

* Full cookie support

* Update CI

* Fix CI

* Fix CI

* update README
  • Loading branch information
Officeyutong authored Jan 18, 2024
1 parent aeeb6d6 commit 83064ae
Show file tree
Hide file tree
Showing 27 changed files with 452 additions and 75 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/test-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ jobs:
victim: ./victim
syscall_trace: false
expected_str: "See /sys/kernel/debug/tracing/trace_pipe for output (15)"
- path: usdt_minimal
executable: ./usdt_minimal
victim: ./victim
syscall_trace: false
expected_str: "bpf:"

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -141,7 +146,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y zlib1g-dev libelf-dev llvm
sudo apt-get install -y zlib1g-dev libelf-dev llvm systemtap-sdt-dev
sudo mkdir /mnt/ramdisk
- name: Build test assets
run: |
Expand Down
11 changes: 11 additions & 0 deletions example/usdt_minimal/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.vscode
package.json
*.o
*.skel.json
*.skel.yaml
package.yaml
ecli
.output
test
victim
usdt_minimal
141 changes: 141 additions & 0 deletions example/usdt_minimal/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../../third_party/libbpf/src)
BPFTOOL_SRC := $(abspath ../../third_party/bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../../third_party/vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../third_party/libbpf/include/uapi -I$(dir $(VMLINUX))
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)

APPS = usdt_minimal

CARGO ?= $(shell which cargo)
ifeq ($(strip $(CARGO)),)
BZS_APPS :=
else
BZS_APPS := # profile
APPS += $(BZS_APPS)
# Required by libblazesym
ALL_LDFLAGS += -lrt -ldl -lpthread -lm
endif

# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
# architecture-specific dirs will be "missing" on some architectures/distros -
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
# sys/cdefs.h etc. might be missing.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')

ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' \
"$(1)" \
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
"$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif

define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef

$(call allow-override,CC,$(CROSS_COMPILE)cc)
$(call allow-override,LD,$(CROSS_COMPILE)ld)

.PHONY: all
all: $(APPS) victim

victim: victim.cpp
g++ victim.cpp -o victim -Wall -g

.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS)

$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@

# Build libbpf
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
$(call msg,LIB,$@)
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
INCLUDEDIR= LIBDIR= UAPIDIR= \
install

# Build bpftool
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
$(call msg,BPFTOOL,$@)
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap


$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release

$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@

$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@

# Build BPF code
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)

# Generate BPF skeletons
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@

# Build user-space code
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h

$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@

$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER)

$(BZS_APPS): $(LIBBLAZESYM_OBJ)

# Build application binary
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@

# delete failed targets
.DELETE_ON_ERROR:

# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:
3 changes: 3 additions & 0 deletions example/usdt_minimal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# USDT example

This is an example demonstrating USDT. The ebpf program will be hooked onto the tracepoint named `probe1`, and print arguments through `bpf_printk`
14 changes: 14 additions & 0 deletions example/usdt_minimal/usdt_minimal.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/usdt.bpf.h>

SEC("usdt")
int BPF_USDT(simple_probe, int x, int y, int z)
{
bpf_printk("bpf: %d + %d = %d\n", x, y, z);
return 0;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";
71 changes: 71 additions & 0 deletions example/usdt_minimal/usdt_minimal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2022 Hengqi Chen */
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
#include <linux/limits.h>
#include ".output/usdt_minimal.skel.h"

static volatile sig_atomic_t exiting;

static void sig_int(int signo)
{
exiting = 1;
}

static int libbpf_print_fn(enum libbpf_print_level level, const char *format,
va_list args)
{
return vfprintf(stderr, format, args);
}

int main(int argc, char **argv)
{
struct usdt_minimal_bpf *skel;
int err;

libbpf_set_print(libbpf_print_fn);

skel = usdt_minimal_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}

err = usdt_minimal_bpf__load(skel);
if (!skel) {
fprintf(stderr, "Failed to load BPF skeleton\n");
return 1;
}

skel->links.simple_probe =
bpf_program__attach_usdt(skel->progs.simple_probe, -1,
"./victim", "victim", "probe1", NULL);
if (!skel->links.simple_probe) {
err = errno;
fprintf(stderr,
"Failed to attach BPF program `usdt_manual_attach`: %d\n",
err);
goto cleanup;
}

if (signal(SIGINT, sig_int) == SIG_ERR) {
err = errno;
fprintf(stderr, "can't set signal handler: %s\n",
strerror(errno));
goto cleanup;
}

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

while (!exiting) {
/* trigger our BPF programs */
fprintf(stderr, ".");
sleep(1);
}

cleanup:
usdt_minimal_bpf__destroy(skel);
return -err;
}
24 changes: 24 additions & 0 deletions example/usdt_minimal/victim.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <iostream>
#include <ostream>
#include <sys/sdt.h>
#include <random>
#include <thread>
using namespace std::chrono_literals;
int main()
{
std::mt19937 gen;
gen.seed(std::random_device()());
std::uniform_int_distribution<int> rand(1, 1e6);
while (true) {
int x = rand(gen);
int y = rand(gen);
int z = x + y;
DTRACE_PROBE3(victim, probe1, x, y, z);
std::cout << x << " + " << y << " = " << z << std::endl;
int x1 = y;
int y1 = x;
DTRACE_PROBE3(victim, probe1, x1, y1, z);
std::this_thread::sleep_for(1s);
}
return 0;
}
6 changes: 4 additions & 2 deletions runtime/include/bpftime_prog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
#define _BPFTIME_PROG_HPP

#include <ebpf-vm.h>
#include <cinttypes>
#include <optional>
#include <vector>
#include <string>
namespace bpftime
{

extern thread_local std::optional<uint64_t> current_thread_bpf_cookie;

// executable program for bpf function
class bpftime_prog {
public:
Expand All @@ -31,6 +33,7 @@ class bpftime_prog {
// exec in user space
int bpftime_prog_exec(void *memory, size_t memory_size,
uint64_t *return_val) const;

int bpftime_prog_register_raw_helper(struct bpftime_helper_info info);
const std::vector<ebpf_inst> &get_insns() const
{
Expand All @@ -55,7 +58,6 @@ class bpftime_prog {
struct bpftime_ffi_ctx *ffi_ctx;

// kernel runtime

};

} // namespace bpftime
Expand Down
5 changes: 5 additions & 0 deletions runtime/include/bpftime_shm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/smart_ptr/unique_ptr.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <cstdint>
#include <ebpf-vm.h>
#include <sys/epoll.h>

Expand Down Expand Up @@ -242,6 +243,8 @@ int bpftime_perf_event_disable(int fd);
int bpftime_find_minimal_unused_fd();

int bpftime_attach_perf_to_bpf(int perf_fd, int bpf_fd);
int bpftime_attach_perf_to_bpf_with_cookie(int perf_fd, int bpf_fd,
uint64_t cookie);
int bpftime_add_ringbuf_fd_to_epoll(int ringbuf_fd, int epoll_fd,
epoll_data_t extra_data);
int bpftime_add_software_perf_event_fd_to_epoll(int swpe_fd, int epoll_fd,
Expand Down Expand Up @@ -276,6 +279,8 @@ int bpftime_perf_event_output(int fd, const void *buf, size_t sz);
int bpftime_shared_perf_event_output(int map_fd, const void *buf, size_t sz);
int bpftime_add_ureplace_or_override(int fd, int pid, const char *name,
uint64_t offset, bool is_replace);

int bpftime_get_current_thread_cookie(uint64_t *out);
}

#endif // BPFTIME_SHM_CPP_H
Loading

0 comments on commit 83064ae

Please sign in to comment.