Skip to content

Commit

Permalink
examples/c: Add task_iter
Browse files Browse the repository at this point in the history
This is a simple example of using BPF iterators, which iterates
over all tasks on the host.

Signed-off-by: Jordan Rome <[email protected]>
  • Loading branch information
jordalgo committed Nov 5, 2023
1 parent 8ca2914 commit a7f5bdb
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 2 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion examples/c/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
/sockfilter
/tc
/ksyscall
/task_iter
/cmake-build-debug/
/cmake-build-release/
/cmake-build-release/
2 changes: 1 addition & 1 deletion examples/c/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)),)
Expand Down
68 changes: 68 additions & 0 deletions examples/c/task_iter.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#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;
}
130 changes: 130 additions & 0 deletions examples/c/task_iter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2020 Facebook */
#include <argp.h>
#include <signal.h>
#include <stdio.h>
#include <sys/resource.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <unistd.h>
#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;
}
16 changes: 16 additions & 0 deletions examples/c/task_iter.h
Original file line number Diff line number Diff line change
@@ -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];
};

0 comments on commit a7f5bdb

Please sign in to comment.