Skip to content

Commit d78c929

Browse files
jordalgoanakryiko
authored andcommitted
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 <jordalgo@meta.com>
1 parent 8ca2914 commit d78c929

File tree

6 files changed

+211
-2
lines changed

6 files changed

+211
-2
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,21 @@ interface:lo protocol: UDP 127.0.0.1:51845(src) -> 127.0.0.1:53(dst)
303303
interface:lo protocol: UDP 127.0.0.1:41552(src) -> 127.0.0.1:53(dst)
304304
```
305305

306+
## task_iter
307+
308+
`task_iter` is an example of using [BPF Iterators](https://docs.kernel.org/bpf/bpf_iterators.html).
309+
This example iterates over all tasks on the host and gets their pid, process name,
310+
kernel stack, and their state. Note: you can use BlazeSym to symbolize the kernel stacktraces
311+
(like in `profile`) but that code is omitted for simplicity.
312+
313+
```shell
314+
$ sudo ./task_iter
315+
Task Info. Pid: 3647645. Process Name: TTLSFWorker59. Kernel Stack Len: 3. State: INTERRUPTIBLE
316+
Task Info. Pid: 1600495. Process Name: tmux: client. Kernel Stack Len: 6. State: INTERRUPTIBLE
317+
Task Info. Pid: 1600497. Process Name: tmux: server. Kernel Stack Len: 0. State: RUNNING
318+
Task Info. Pid: 1600498. Process Name: bash. Kernel Stack Len: 5. State: INTERRUPTIBLE
319+
```
320+
306321
# Building
307322

308323
libbpf-bootstrap supports multiple build systems that do the same thing.

examples/c/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
/sockfilter
1111
/tc
1212
/ksyscall
13+
/task_iter
1314
/cmake-build-debug/
14-
/cmake-build-release/
15+
/cmake-build-release/

examples/c/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBB
2424
CFLAGS := -g -Wall
2525
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
2626

27-
APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc ksyscall
27+
APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc ksyscall task_iter
2828

2929
CARGO ?= $(shell which cargo)
3030
ifeq ($(strip $(CARGO)),)

examples/c/task_iter.bpf.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2+
/* Copyright (c) 2023 Meta */
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include <bpf/bpf_core_read.h>
7+
#include "task_iter.h"
8+
9+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
10+
11+
struct {
12+
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
13+
__uint(max_entries, 1);
14+
__type(key, __u32);
15+
__type(value, struct task_info);
16+
} task_info_buf SEC(".maps");
17+
18+
struct task_struct___post514 {
19+
unsigned int __state;
20+
} __attribute__((preserve_access_index));
21+
22+
struct task_struct___pre514 {
23+
long state;
24+
} __attribute__((preserve_access_index));
25+
26+
static __u32 get_task_state(void *arg)
27+
{
28+
if (bpf_core_field_exists(struct task_struct___pre514, state)) {
29+
struct task_struct___pre514 *task = arg;
30+
31+
return task->state;
32+
} else {
33+
struct task_struct___post514 *task = arg;
34+
35+
return task->__state;
36+
}
37+
}
38+
39+
static __u32 zero = 0;
40+
41+
SEC("iter/task")
42+
int get_tasks(struct bpf_iter__task *ctx)
43+
{
44+
struct seq_file *seq = ctx->meta->seq;
45+
struct task_struct *task = ctx->task;
46+
struct task_info *t;
47+
long res;
48+
49+
if (!task)
50+
return 0;
51+
52+
t = bpf_map_lookup_elem(&task_info_buf, &zero);
53+
if (!t)
54+
return 0;
55+
56+
t->pid = task->tgid;
57+
t->tid = task->pid;
58+
t->state = get_task_state(task);
59+
60+
bpf_probe_read_kernel_str(t->comm, TASK_COMM_LEN, task->comm);
61+
62+
res = bpf_get_task_stack(task, t->kstack, sizeof(__u64) * MAX_STACK_LEN, 0);
63+
t->kstack_len = res <= 0 ? res : res / sizeof(t->kstack[0]);
64+
65+
bpf_seq_write(seq, t, sizeof(struct task_info));
66+
return 0;
67+
}

examples/c/task_iter.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
/* Copyright (c) 2023 Meta */
3+
#include <argp.h>
4+
#include <signal.h>
5+
#include <stdio.h>
6+
#include <sys/resource.h>
7+
#include <bpf/bpf.h>
8+
#include <bpf/libbpf.h>
9+
#include <unistd.h>
10+
#include "task_iter.h"
11+
#include "task_iter.skel.h"
12+
13+
static struct env {
14+
bool verbose;
15+
} env;
16+
17+
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
18+
{
19+
if (level == LIBBPF_DEBUG && !env.verbose)
20+
return 0;
21+
return vfprintf(stderr, format, args);
22+
}
23+
24+
static volatile bool exiting = false;
25+
26+
static void sig_handler(int sig)
27+
{
28+
exiting = true;
29+
}
30+
31+
static const char *get_task_state(__u32 state)
32+
{
33+
/* Taken from:
34+
* https://elixir.bootlin.com/linux/latest/source/include/linux/sched.h#L85
35+
* There are a lot more states not covered here but these are common ones.
36+
*/
37+
switch (state) {
38+
case 0x0000: return "RUNNING";
39+
case 0x0001: return "INTERRUPTIBLE";
40+
case 0x0002: return "UNINTERRUPTIBLE";
41+
case 0x0200: return "WAKING";
42+
case 0x0400: return "NOLOAD";
43+
case 0x0402: return "IDLE";
44+
case 0x0800: return "NEW";
45+
default: return "<unknown>";
46+
}
47+
}
48+
49+
int main(int argc, char **argv)
50+
{
51+
struct task_iter_bpf *skel;
52+
struct task_info buf;
53+
int iter_fd;
54+
ssize_t ret;
55+
int err;
56+
57+
/* Set up libbpf errors and debug info callback */
58+
libbpf_set_print(libbpf_print_fn);
59+
60+
/* Cleaner handling of Ctrl-C */
61+
signal(SIGINT, sig_handler);
62+
signal(SIGTERM, sig_handler);
63+
64+
/* Open, load, and verify BPF application */
65+
skel = task_iter_bpf__open_and_load();
66+
if (!skel) {
67+
fprintf(stderr, "Failed to open and load BPF skeleton\n");
68+
goto cleanup;
69+
}
70+
71+
/* Attach tracepoints */
72+
err = task_iter_bpf__attach(skel);
73+
if (err) {
74+
fprintf(stderr, "Failed to attach BPF skeleton\n");
75+
goto cleanup;
76+
}
77+
78+
iter_fd = bpf_iter_create(bpf_link__fd(skel->links.get_tasks));
79+
if (iter_fd < 0) {
80+
err = -1;
81+
fprintf(stderr, "Failed to create iter\n");
82+
goto cleanup;
83+
}
84+
85+
while (true) {
86+
ret = read(iter_fd, &buf, sizeof(struct task_info));
87+
if (ret < 0) {
88+
if (errno == EAGAIN)
89+
continue;
90+
err = -errno;
91+
break;
92+
}
93+
if (ret == 0)
94+
break;
95+
if (buf.kstack_len <= 0) {
96+
printf("Error getting kernel stack for task. Task Info. Pid: %d. Process Name: %s. Kernel Stack Error: %d. State: %s\n",
97+
buf.pid, buf.comm, buf.kstack_len, get_task_state(buf.state));
98+
} else {
99+
printf("Task Info. Pid: %d. Process Name: %s. Kernel Stack Len: %d. State: %s\n",
100+
buf.pid, buf.comm, buf.kstack_len, get_task_state(buf.state));
101+
}
102+
}
103+
104+
cleanup:
105+
/* Clean up */
106+
close(iter_fd);
107+
task_iter_bpf__destroy(skel);
108+
109+
return err < 0 ? -err : 0;
110+
}

examples/c/task_iter.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
/* Copyright (c) 2023 Meta */
3+
4+
#define TASK_COMM_LEN 16
5+
#define MAX_STACK_LEN 127
6+
7+
struct task_info {
8+
pid_t pid;
9+
pid_t tid;
10+
__u32 state;
11+
char comm[TASK_COMM_LEN];
12+
13+
int kstack_len;
14+
15+
__u64 kstack[MAX_STACK_LEN];
16+
};

0 commit comments

Comments
 (0)