Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

example: add mountsnoop from libbpf-tools #110

Merged
merged 13 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions .github/script/run_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async def main():
)

await asyncio.wait_for(server_started_signal.wait(), SERVER_TIMEOUT)
await asyncio.sleep(2)
await asyncio.sleep(5)
print("Server started!")

# Start the agent
Expand All @@ -94,11 +94,15 @@ async def main():
print("Test successfully")
finally:
should_exit.set()
server.send_signal(signal.SIGINT)
agent.send_signal(signal.SIGINT)
await asyncio.gather(server_out, agent_out)
await asyncio.gather(server.communicate(), agent.communicate())

try:
server.send_signal(signal.SIGINT)
agent.send_signal(signal.SIGINT)
await asyncio.gather(server_out, agent_out)
for task in asyncio.all_tasks():
task.cancel()
await asyncio.gather(server.communicate(), agent.communicate())
except:
pass

if __name__ == "__main__":
asyncio.run(main())
9 changes: 8 additions & 1 deletion .github/workflows/test-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ jobs:
victim: ./victim
syscall_trace: false
expected_str: ": 1"
- path: libbpf-tools/mountsnoop
executable: ./mountsnoop
victim: ./victim
syscall_trace: true
expected_str: mount(


steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -132,6 +138,7 @@ jobs:
run: |
sudo apt-get update -y
sudo apt-get install -y zlib1g-dev libelf-dev llvm
sudo mkdir /mnt/ramdisk
- name: Build test assets
run: |
make -C example/${{matrix.examples.path}} -j
Expand All @@ -150,4 +157,4 @@ jobs:
shell: "sudo /bin/bash -e {0}"
run: |
cd example/${{matrix.examples.path}}
sudo -E python3 /home/runner/work/bpftime/bpftime/.github/script/run_example.py "${{matrix.examples.executable}}" "${{matrix.examples.victim}}" "${{matrix.examples.expected_str}}" "/home/runner/.bpftime/bpftime -i /home/runner/.bpftime" 0
sudo -E python3 /home/runner/work/bpftime/bpftime/.github/script/run_example.py "${{matrix.examples.executable}}" "${{matrix.examples.victim}}" '${{matrix.examples.expected_str}}' "/home/runner/.bpftime/bpftime -i /home/runner/.bpftime" 0
11 changes: 11 additions & 0 deletions example/libbpf-tools/mountsnoop/.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
bootstrap
.output
mountsnoop
victim
141 changes: 141 additions & 0 deletions example/libbpf-tools/mountsnoop/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 = mountsnoop # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall

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

.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) CFLAGS="-O0 -g" 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:

victim: victim.c
gcc victim.c -g -Wall -o victim
134 changes: 134 additions & 0 deletions example/libbpf-tools/mountsnoop/mountsnoop.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2021 Hengqi Chen */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>

#include "mountsnoop.h"

#define MAX_ENTRIES 10240

const volatile pid_t target_pid = 0;

#define RINGBUF_SIZE (1024 * 256)


struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, RINGBUF_SIZE);
} events SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, __u32);
__type(value, struct arg);
} args SEC(".maps");

static __always_inline int probe_entry(const char *src, const char *dest, const char *fs,
__u64 flags, const char *data, enum op op)
{
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
__u32 tid = (__u32)pid_tgid;
struct arg arg = {};

if (target_pid && target_pid != pid)
return 0;

arg.ts = bpf_ktime_get_ns();
arg.flags = flags;
arg.src = src;
arg.dest = dest;
arg.fs = fs;
arg.data= data;
arg.op = op;
bpf_map_update_elem(&args, &tid, &arg, BPF_ANY);
return 0;
};

static __always_inline int probe_exit(void *ctx, int ret)
{
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
__u32 tid = (__u32)pid_tgid;
// struct task_struct *task;
struct event *eventp;
struct arg *argp;

argp = bpf_map_lookup_elem(&args, &tid);
if (!argp)
return 0;

eventp = bpf_ringbuf_reserve(&events, sizeof(*eventp), 0);
if (!eventp)
goto cleanup;

// task = (struct task_struct *)bpf_get_current_task();
eventp->delta = bpf_ktime_get_ns() - argp->ts;
eventp->flags = argp->flags;
eventp->pid = pid;
eventp->tid = tid;
eventp->mnt_ns = 0;
eventp->ret = ret;
eventp->op = argp->op;
bpf_get_current_comm(&eventp->comm, sizeof(eventp->comm));
if (argp->src)
bpf_probe_read_user_str(eventp->src, sizeof(eventp->src), argp->src);
else
eventp->src[0] = '\0';
if (argp->dest)
bpf_probe_read_user_str(eventp->dest, sizeof(eventp->dest), argp->dest);
else
eventp->dest[0] = '\0';
if (argp->fs)
bpf_probe_read_user_str(eventp->fs, sizeof(eventp->fs), argp->fs);
else
eventp->fs[0] = '\0';
if (argp->data)
bpf_probe_read_user_str(eventp->data, sizeof(eventp->data), argp->data);
else
eventp->data[0] = '\0';

bpf_ringbuf_submit(eventp, 0);

cleanup:
bpf_map_delete_elem(&args, &tid);
return 0;
}

SEC("tracepoint/syscalls/sys_enter_mount")
int mount_entry(struct trace_event_raw_sys_enter *ctx)
{
const char *src = (const char *)ctx->args[0];
const char *dest = (const char *)ctx->args[1];
const char *fs = (const char *)ctx->args[2];
__u64 flags = (__u64)ctx->args[3];
const char *data = (const char *)ctx->args[4];

return probe_entry(src, dest, fs, flags, data, MOUNT);
}

SEC("tracepoint/syscalls/sys_exit_mount")
int mount_exit(struct trace_event_raw_sys_exit *ctx)
{
return probe_exit(ctx, (int)ctx->ret);
}

SEC("tracepoint/syscalls/sys_enter_umount")
int umount_entry(struct trace_event_raw_sys_enter *ctx)
{
const char *dest = (const char *)ctx->args[0];
__u64 flags = (__u64)ctx->args[1];

return probe_entry(NULL, dest, NULL, flags, NULL, UMOUNT);
}

SEC("tracepoint/syscalls/sys_exit_umount")
int umount_exit(struct trace_event_raw_sys_exit *ctx)
{
return probe_exit(ctx, (int)ctx->ret);
}

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