From a7f5bdb4658078f145c7c676ca22d9385fd78160 Mon Sep 17 00:00:00 2001 From: Jordan Rome Date: Fri, 3 Nov 2023 15:34:56 -0700 Subject: [PATCH] examples/c: Add task_iter This is a simple example of using BPF iterators, which iterates over all tasks on the host. Signed-off-by: Jordan Rome --- README.md | 15 +++++ examples/c/.gitignore | 3 +- examples/c/Makefile | 2 +- examples/c/task_iter.bpf.c | 68 +++++++++++++++++++ examples/c/task_iter.c | 130 +++++++++++++++++++++++++++++++++++++ examples/c/task_iter.h | 16 +++++ 6 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 examples/c/task_iter.bpf.c create mode 100644 examples/c/task_iter.c create mode 100644 examples/c/task_iter.h diff --git a/README.md b/README.md index edc1c352..1ae46441 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,21 @@ interface:lo protocol: UDP 127.0.0.1:51845(src) -> 127.0.0.1:53(dst) interface:lo protocol: UDP 127.0.0.1:41552(src) -> 127.0.0.1:53(dst) ``` +## task_iter + +`task_iter` is an example of using [BPF Iterators](https://docs.kernel.org/bpf/bpf_iterators.html). +This example iterates over all tasks on the host and gets their pid, process name, +kernel stack, and their state. Note: you can use BlazeSym to symbolize the kernel stacktraces +(like in `profile`) but that code is omitted for simplicity. + +```shell +$ sudo ./task_iter +Task Info. Pid: 3647645. Process Name: TTLSFWorker59. Kernel Stack Len: 3. State: INTERRUPTIBLE +Task Info. Pid: 1600495. Process Name: tmux: client. Kernel Stack Len: 6. State: INTERRUPTIBLE +Task Info. Pid: 1600497. Process Name: tmux: server. Kernel Stack Len: 0. State: RUNNING +Task Info. Pid: 1600498. Process Name: bash. Kernel Stack Len: 5. State: INTERRUPTIBLE +``` + # Building libbpf-bootstrap supports multiple build systems that do the same thing. diff --git a/examples/c/.gitignore b/examples/c/.gitignore index 386d7e2f..4948fd35 100644 --- a/examples/c/.gitignore +++ b/examples/c/.gitignore @@ -10,5 +10,6 @@ /sockfilter /tc /ksyscall +/task_iter /cmake-build-debug/ -/cmake-build-release/ \ No newline at end of file +/cmake-build-release/ diff --git a/examples/c/Makefile b/examples/c/Makefile index 736750e9..bb3d704e 100644 --- a/examples/c/Makefile +++ b/examples/c/Makefile @@ -24,7 +24,7 @@ INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBB CFLAGS := -g -Wall ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) -APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc ksyscall +APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc ksyscall task_iter CARGO ?= $(shell which cargo) ifeq ($(strip $(CARGO)),) diff --git a/examples/c/task_iter.bpf.c b/examples/c/task_iter.bpf.c new file mode 100644 index 00000000..b4c6c6b3 --- /dev/null +++ b/examples/c/task_iter.bpf.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright (c) 2020 Facebook */ +#include "vmlinux.h" +#include +#include +#include +#include "task_iter.h" + +char LICENSE[] SEC("license") = "Dual BSD/GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, 1); + __type(key, __u32); + __type(value, struct task_iter); +} task_iter_buf SEC(".maps"); + +struct task_struct___post514 { + unsigned int __state; +} __attribute__((preserve_access_index)); + +struct task_struct___pre514 { + long state; +} __attribute__((preserve_access_index)); + +__u32 get_task_state(void *arg) +{ + if (bpf_core_field_exists(((struct task_struct___pre514 *)0)->state)) { + struct task_struct___pre514 *task = arg; + return task->state; + } else { + struct task_struct___post514 *task = arg; + return task->__state; + } +} + +SEC("iter/task") +int get_tasks(struct bpf_iter__task *ctx) +{ + struct seq_file *seq = ctx->meta->seq; + struct task_struct *task = ctx->task; + + if (task == (void *)0) { // end + return 0; + } + + __u32 zero = 0; + struct task_iter *t = bpf_map_lookup_elem(&task_iter_buf, &zero); + if (!t) { + return 0; + } + + t->pid = task->tgid; + t->tid = task->pid; + t->state = get_task_state(task); + + bpf_probe_read_kernel_str(t->comm, TASK_COMM_LEN, task->comm); + + int64_t res = bpf_get_task_stack(task, t->kstack, sizeof(__u64) * MAX_STACK_LEN, 0); + if (res <= 0) { + t->kstack_len = 0; + } else { + t->kstack_len = res / sizeof(__u64); + } + + bpf_seq_write(seq, t, sizeof(struct task_iter)); + return 0; +} diff --git a/examples/c/task_iter.c b/examples/c/task_iter.c new file mode 100644 index 00000000..d7c546c5 --- /dev/null +++ b/examples/c/task_iter.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +/* Copyright (c) 2020 Facebook */ +#include +#include +#include +#include +#include +#include +#include +#include "task_iter.h" +#include "task_iter.skel.h" + +static struct env { + bool verbose; +} env; + +static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) +{ + if (level == LIBBPF_DEBUG && !env.verbose) + return 0; + return vfprintf(stderr, format, args); +} + +static volatile bool exiting = false; + +static void sig_handler(int sig) +{ + exiting = true; +} + +void print_task_state(__u32 state) +{ + // taken from + // https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h#L85 + // there are a lot more states not covered here but these are common ones + switch (state) { + case 0x0000: + printf("State: RUNNING\n"); + break; + case 0x0001: + printf("State: INTERRUPTIBLE\n"); + break; + case 0x0002: + printf("State: UNINTERRUPTIBLE\n"); + break; + case 0x0200: + printf("State: WAKING\n"); + break; + case 0x0400: + printf("State: NOLOAD\n"); + break; + case 0x0402: + printf("State: IDLE\n"); + break; + case 0x0800: + printf("State: NEW\n"); + break; + default: + printf("Unknown Task state\n"); + } +} + +int main(int argc, char **argv) +{ + struct task_iter_bpf *skel; + int err; + int get_tasks_fd; + + /* Set up libbpf errors and debug info callback */ + libbpf_set_print(libbpf_print_fn); + + /* Cleaner handling of Ctrl-C */ + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + /* Load and verify BPF application */ + skel = task_iter_bpf__open(); + if (!skel) { + fprintf(stderr, "Failed to open and load BPF skeleton\n"); + return 1; + } + + /* Load & verify BPF programs */ + err = task_iter_bpf__load(skel); + if (err) { + fprintf(stderr, "Failed to load and verify BPF skeleton\n"); + goto cleanup; + } + + /* Attach tracepoints */ + err = task_iter_bpf__attach(skel); + if (err) { + fprintf(stderr, "Failed to attach BPF skeleton\n"); + goto cleanup; + } + + get_tasks_fd = bpf_iter_create(bpf_link__fd(skel->links.get_tasks)); + if (!get_tasks_fd) { + err = -1; + fprintf(stderr, "Failed to create iter\n"); + goto cleanup; + } + + struct task_iter buf; + ssize_t ret; + + while (true) { + ret = read(get_tasks_fd, &buf, sizeof(struct task_iter)); + if (ret < 0) { + if (errno == EAGAIN) { + continue; + } + err = -errno; + break; + } + if (ret == 0) { + break; + } + printf("Task Info. Pid: %d. Process Name: %s. Kernel Stack Len: %d. ", buf.pid, + buf.comm, buf.kstack_len); + print_task_state(buf.state); + } + +cleanup: + /* Clean up */ + close(get_tasks_fd); + task_iter_bpf__destroy(skel); + + return err < 0 ? -err : 0; +} diff --git a/examples/c/task_iter.h b/examples/c/task_iter.h new file mode 100644 index 00000000..64615496 --- /dev/null +++ b/examples/c/task_iter.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +/* Copyright (c) 2020 Facebook */ + +#define TASK_COMM_LEN 16 +#define MAX_STACK_LEN 127 + +struct task_iter { + pid_t pid; + pid_t tid; + __u32 state; + char comm[TASK_COMM_LEN]; + + int kstack_len; + + __u64 kstack[MAX_STACK_LEN]; +};