mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-04 02:34:16 +08:00
add config for generate TOC
This commit is contained in:
1
src/0-introduce/.config
Normal file
1
src/0-introduce/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/1-helloworld/.config
Normal file
1
src/1-helloworld/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/10-hardirqs/.config
Normal file
1
src/10-hardirqs/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/11-bootstrap/.config
Normal file
1
src/11-bootstrap/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/12-profile/.config
Normal file
1
src/12-profile/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/13-tcpconnlat/.config
Normal file
1
src/13-tcpconnlat/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/14-tcpstates/.config
Normal file
1
src/14-tcpstates/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/15-javagc/.config
Normal file
1
src/15-javagc/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/16-memleak/.config
Normal file
1
src/16-memleak/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/17-biopattern/.config
Normal file
1
src/17-biopattern/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/18-further-reading/.config
Normal file
1
src/18-further-reading/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/19-lsm-connect/.config
Normal file
1
src/19-lsm-connect/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/2-kprobe-unlink/.config
Normal file
1
src/2-kprobe-unlink/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/20-tc/.config
Normal file
1
src/20-tc/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
1
src/21-xdp/.config
Normal file
1
src/21-xdp/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Advance
|
||||
2
src/22-android/.config
Normal file
2
src/22-android/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Android
|
||||
2
src/23-http/.config
Normal file
2
src/23-http/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Networking
|
||||
2
src/24-hide/.config
Normal file
2
src/24-hide/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Security
|
||||
2
src/25-signal/.config
Normal file
2
src/25-signal/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Security
|
||||
2
src/26-sudo/.config
Normal file
2
src/26-sudo/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Security
|
||||
2
src/27-replace/.config
Normal file
2
src/27-replace/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Security
|
||||
2
src/28-detach/.config
Normal file
2
src/28-detach/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Security
|
||||
2
src/29-sockops/.config
Normal file
2
src/29-sockops/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Networking
|
||||
1
src/3-fentry-unlink/.config
Normal file
1
src/3-fentry-unlink/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
2
src/30-sslsniff/.config
Normal file
2
src/30-sslsniff/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
2
src/31-goroutine/.config
Normal file
2
src/31-goroutine/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
2
src/32-http2/.config
Normal file
2
src/32-http2/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
@@ -1,4 +1,4 @@
|
||||
# func latency
|
||||
# trace http2 request in go
|
||||
|
||||
TODO: make it work
|
||||
|
||||
|
||||
2
src/33-funclatency/.config
Normal file
2
src/33-funclatency/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
2
src/34-syscall/.config
Normal file
2
src/34-syscall/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Security
|
||||
2
src/35-user-ringbuf/.config
Normal file
2
src/35-user-ringbuf/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Other
|
||||
2
src/36-userspace-ebpf/.config
Normal file
2
src/36-userspace-ebpf/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Other
|
||||
2
src/37-uprobe-rust/.config
Normal file
2
src/37-uprobe-rust/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
2
src/38-btf-uprobe/.config
Normal file
2
src/38-btf-uprobe/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Other
|
||||
2
src/39-nginx/.config
Normal file
2
src/39-nginx/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
1
src/4-opensnoop/.config
Normal file
1
src/4-opensnoop/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
2
src/40-mysql/.config
Normal file
2
src/40-mysql/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Tracing
|
||||
2
src/41-xdp-tcpdump/.config
Normal file
2
src/41-xdp-tcpdump/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Networking
|
||||
2
src/42-xdp-loadbalancer/.config
Normal file
2
src/42-xdp-loadbalancer/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Networking
|
||||
2
src/43-kfuncs/.config
Normal file
2
src/43-kfuncs/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Other
|
||||
2
src/44-scx-simple/.config
Normal file
2
src/44-scx-simple/.config
Normal file
@@ -0,0 +1,2 @@
|
||||
level=Depth
|
||||
type=Scheduler
|
||||
@@ -1,6 +1,6 @@
|
||||
# eBPF Tutorial by Example: Introduction to the BPF Scheduler
|
||||
# eBPF Tutorial: Introduction to the BPF Scheduler
|
||||
|
||||
Welcome to our deep dive into the world of eBPF with a focus on the BPF scheduler! If you're looking to extend your eBPF knowledge beyond the basics, you're in the right place. In this tutorial, we'll explore the **scx_simple scheduler**, a minimal example of the sched_ext scheduler class introduced in Linux kernel version 6.12. We'll walk you through its architecture, how it leverages BPF programs to define scheduling behavior, and guide you through compiling and running the example. By the end, you'll have a solid understanding of how to create and manage advanced scheduling policies using eBPF.
|
||||
Welcome to our deep dive into the world of eBPF with a focus on the BPF scheduler! If you're looking to extend your eBPF knowledge beyond the basics, you're in the right place. In this tutorial, we'll explore the **scx_simple scheduler**, a minimal example of the sched_ext scheduler class introduced in Linux kernel version `6.12`. We'll walk you through its architecture, how it leverages BPF programs to define scheduling behavior, and guide you through compiling and running the example. By the end, you'll have a solid understanding of how to create and manage advanced scheduling policies using eBPF.
|
||||
|
||||
## Understanding the Extensible BPF Scheduler
|
||||
|
||||
@@ -18,7 +18,7 @@ With these features, sched_ext provides a robust foundation for experimenting wi
|
||||
|
||||
## Introducing scx_simple: A Minimal sched_ext Scheduler
|
||||
|
||||
The **scx_simple** scheduler is a straightforward example of a sched_ext scheduler. It's designed to be easy to understand and serves as a foundation for more complex scheduling policies. scx_simple can operate in two modes:
|
||||
The **scx_simple** scheduler is a straightforward example of a sched_ext scheduler in the linux tools. It's designed to be easy to understand and serves as a foundation for more complex scheduling policies. scx_simple can operate in two modes:
|
||||
|
||||
1. **Global Weighted Virtual Time (vtime) Mode:** Prioritizes tasks based on their virtual time, allowing for fair scheduling across different workloads.
|
||||
2. **FIFO (First-In-First-Out) Mode:** Simple queue-based scheduling where tasks are executed in the order they arrive.
|
||||
@@ -60,121 +60,121 @@ UEI_DEFINE(uei);
|
||||
#define SHARED_DSQ 0
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(key_size, sizeof(u32));
|
||||
__uint(value_size, sizeof(u64));
|
||||
__uint(max_entries, 2); /* [local, global] */
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(key_size, sizeof(u32));
|
||||
__uint(value_size, sizeof(u64));
|
||||
__uint(max_entries, 2); /* [local, global] */
|
||||
} stats SEC(".maps");
|
||||
|
||||
static void stat_inc(u32 idx)
|
||||
{
|
||||
u64 *cnt_p = bpf_map_lookup_elem(&stats, &idx);
|
||||
if (cnt_p)
|
||||
(*cnt_p)++;
|
||||
u64 *cnt_p = bpf_map_lookup_elem(&stats, &idx);
|
||||
if (cnt_p)
|
||||
(*cnt_p)++;
|
||||
}
|
||||
|
||||
static inline bool vtime_before(u64 a, u64 b)
|
||||
{
|
||||
return (s64)(a - b) < 0;
|
||||
return (s64)(a - b) < 0;
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS(simple_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags)
|
||||
{
|
||||
bool is_idle = false;
|
||||
s32 cpu;
|
||||
bool is_idle = false;
|
||||
s32 cpu;
|
||||
|
||||
cpu = scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &is_idle);
|
||||
if (is_idle) {
|
||||
stat_inc(0); /* count local queueing */
|
||||
scx_bpf_dispatch(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, 0);
|
||||
}
|
||||
cpu = scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags, &is_idle);
|
||||
if (is_idle) {
|
||||
stat_inc(0); /* count local queueing */
|
||||
scx_bpf_dispatch(p, SCX_DSQ_LOCAL, SCX_SLICE_DFL, 0);
|
||||
}
|
||||
|
||||
return cpu;
|
||||
return cpu;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(simple_enqueue, struct task_struct *p, u64 enq_flags)
|
||||
{
|
||||
stat_inc(1); /* count global queueing */
|
||||
stat_inc(1); /* count global queueing */
|
||||
|
||||
if (fifo_sched) {
|
||||
scx_bpf_dispatch(p, SHARED_DSQ, SCX_SLICE_DFL, enq_flags);
|
||||
} else {
|
||||
u64 vtime = p->scx.dsq_vtime;
|
||||
if (fifo_sched) {
|
||||
scx_bpf_dispatch(p, SHARED_DSQ, SCX_SLICE_DFL, enq_flags);
|
||||
} else {
|
||||
u64 vtime = p->scx.dsq_vtime;
|
||||
|
||||
/*
|
||||
* Limit the amount of budget that an idling task can accumulate
|
||||
* to one slice.
|
||||
*/
|
||||
if (vtime_before(vtime, vtime_now - SCX_SLICE_DFL))
|
||||
vtime = vtime_now - SCX_SLICE_DFL;
|
||||
/*
|
||||
* Limit the amount of budget that an idling task can accumulate
|
||||
* to one slice.
|
||||
*/
|
||||
if (vtime_before(vtime, vtime_now - SCX_SLICE_DFL))
|
||||
vtime = vtime_now - SCX_SLICE_DFL;
|
||||
|
||||
scx_bpf_dispatch_vtime(p, SHARED_DSQ, SCX_SLICE_DFL, vtime,
|
||||
enq_flags);
|
||||
}
|
||||
scx_bpf_dispatch_vtime(p, SHARED_DSQ, SCX_SLICE_DFL, vtime,
|
||||
enq_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(simple_dispatch, s32 cpu, struct task_struct *prev)
|
||||
{
|
||||
scx_bpf_consume(SHARED_DSQ);
|
||||
scx_bpf_consume(SHARED_DSQ);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(simple_running, struct task_struct *p)
|
||||
{
|
||||
if (fifo_sched)
|
||||
return;
|
||||
if (fifo_sched)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Global vtime always progresses forward as tasks start executing. The
|
||||
* test and update can be performed concurrently from multiple CPUs and
|
||||
* thus racy. Any error should be contained and temporary. Let's just
|
||||
* live with it.
|
||||
*/
|
||||
if (vtime_before(vtime_now, p->scx.dsq_vtime))
|
||||
vtime_now = p->scx.dsq_vtime;
|
||||
/*
|
||||
* Global vtime always progresses forward as tasks start executing. The
|
||||
* test and update can be performed concurrently from multiple CPUs and
|
||||
* thus racy. Any error should be contained and temporary. Let's just
|
||||
* live with it.
|
||||
*/
|
||||
if (vtime_before(vtime_now, p->scx.dsq_vtime))
|
||||
vtime_now = p->scx.dsq_vtime;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(simple_stopping, struct task_struct *p, bool runnable)
|
||||
{
|
||||
if (fifo_sched)
|
||||
return;
|
||||
if (fifo_sched)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Scale the execution time by the inverse of the weight and charge.
|
||||
*
|
||||
* Note that the default yield implementation yields by setting
|
||||
* @p->scx.slice to zero and the following would treat the yielding task
|
||||
* as if it has consumed all its slice. If this penalizes yielding tasks
|
||||
* too much, determine the execution time by taking explicit timestamps
|
||||
* instead of depending on @p->scx.slice.
|
||||
*/
|
||||
p->scx.dsq_vtime += (SCX_SLICE_DFL - p->scx.slice) * 100 / p->scx.weight;
|
||||
/*
|
||||
* Scale the execution time by the inverse of the weight and charge.
|
||||
*
|
||||
* Note that the default yield implementation yields by setting
|
||||
* @p->scx.slice to zero and the following would treat the yielding task
|
||||
* as if it has consumed all its slice. If this penalizes yielding tasks
|
||||
* too much, determine the execution time by taking explicit timestamps
|
||||
* instead of depending on @p->scx.slice.
|
||||
*/
|
||||
p->scx.dsq_vtime += (SCX_SLICE_DFL - p->scx.slice) * 100 / p->scx.weight;
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(simple_enable, struct task_struct *p)
|
||||
{
|
||||
p->scx.dsq_vtime = vtime_now;
|
||||
p->scx.dsq_vtime = vtime_now;
|
||||
}
|
||||
|
||||
s32 BPF_STRUCT_OPS_SLEEPABLE(simple_init)
|
||||
{
|
||||
return scx_bpf_create_dsq(SHARED_DSQ, -1);
|
||||
return scx_bpf_create_dsq(SHARED_DSQ, -1);
|
||||
}
|
||||
|
||||
void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei)
|
||||
{
|
||||
UEI_RECORD(uei, ei);
|
||||
UEI_RECORD(uei, ei);
|
||||
}
|
||||
|
||||
SCX_OPS_DEFINE(simple_ops,
|
||||
.select_cpu = (void *)simple_select_cpu,
|
||||
.enqueue = (void *)simple_enqueue,
|
||||
.dispatch = (void *)simple_dispatch,
|
||||
.running = (void *)simple_running,
|
||||
.stopping = (void *)simple_stopping,
|
||||
.enable = (void *)simple_enable,
|
||||
.init = (void *)simple_init,
|
||||
.exit = (void *)simple_exit,
|
||||
.name = "simple");
|
||||
.select_cpu = (void *)simple_select_cpu,
|
||||
.enqueue = (void *)simple_enqueue,
|
||||
.dispatch = (void *)simple_dispatch,
|
||||
.running = (void *)simple_running,
|
||||
.stopping = (void *)simple_stopping,
|
||||
.enable = (void *)simple_enable,
|
||||
.init = (void *)simple_init,
|
||||
.exit = (void *)simple_exit,
|
||||
.name = "simple");
|
||||
```
|
||||
|
||||
#### Kernel-Side Breakdown
|
||||
@@ -213,70 +213,70 @@ This modular structure allows scx_simple to be both simple and effective, provid
|
||||
```c
|
||||
static void read_stats(struct scx_simple *skel, __u64 *stats)
|
||||
{
|
||||
int nr_cpus = libbpf_num_possible_cpus();
|
||||
__u64 cnts[2][nr_cpus];
|
||||
__u32 idx;
|
||||
int nr_cpus = libbpf_num_possible_cpus();
|
||||
__u64 cnts[2][nr_cpus];
|
||||
__u32 idx;
|
||||
|
||||
memset(stats, 0, sizeof(stats[0]) * 2);
|
||||
memset(stats, 0, sizeof(stats[0]) * 2);
|
||||
|
||||
for (idx = 0; idx < 2; idx++) {
|
||||
int ret, cpu;
|
||||
for (idx = 0; idx < 2; idx++) {
|
||||
int ret, cpu;
|
||||
|
||||
ret = bpf_map_lookup_elem(bpf_map__fd(skel->maps.stats),
|
||||
&idx, cnts[idx]);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++)
|
||||
stats[idx] += cnts[idx][cpu];
|
||||
}
|
||||
ret = bpf_map_lookup_elem(bpf_map__fd(skel->maps.stats),
|
||||
&idx, cnts[idx]);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++)
|
||||
stats[idx] += cnts[idx][cpu];
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct scx_simple *skel;
|
||||
struct bpf_link *link;
|
||||
__u32 opt;
|
||||
__u64 ecode;
|
||||
struct scx_simple *skel;
|
||||
struct bpf_link *link;
|
||||
__u32 opt;
|
||||
__u64 ecode;
|
||||
|
||||
libbpf_set_print(libbpf_print_fn);
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
libbpf_set_print(libbpf_print_fn);
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
restart:
|
||||
skel = SCX_OPS_OPEN(simple_ops, scx_simple);
|
||||
skel = SCX_OPS_OPEN(simple_ops, scx_simple);
|
||||
|
||||
while ((opt = getopt(argc, argv, "fvh")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
skel->rodata->fifo_sched = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, help_fmt, basename(argv[0]));
|
||||
return opt != 'h';
|
||||
}
|
||||
}
|
||||
while ((opt = getopt(argc, argv, "fvh")) != -1) {
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
skel->rodata->fifo_sched = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, help_fmt, basename(argv[0]));
|
||||
return opt != 'h';
|
||||
}
|
||||
}
|
||||
|
||||
SCX_OPS_LOAD(skel, simple_ops, scx_simple, uei);
|
||||
link = SCX_OPS_ATTACH(skel, simple_ops, scx_simple);
|
||||
SCX_OPS_LOAD(skel, simple_ops, scx_simple, uei);
|
||||
link = SCX_OPS_ATTACH(skel, simple_ops, scx_simple);
|
||||
|
||||
while (!exit_req && !UEI_EXITED(skel, uei)) {
|
||||
__u64 stats[2];
|
||||
while (!exit_req && !UEI_EXITED(skel, uei)) {
|
||||
__u64 stats[2];
|
||||
|
||||
read_stats(skel, stats);
|
||||
printf("local=%llu global=%llu\n", stats[0], stats[1]);
|
||||
fflush(stdout);
|
||||
sleep(1);
|
||||
}
|
||||
read_stats(skel, stats);
|
||||
printf("local=%llu global=%llu\n", stats[0], stats[1]);
|
||||
fflush(stdout);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
bpf_link__destroy(link);
|
||||
ecode = UEI_REPORT(skel, uei);
|
||||
scx_simple__destroy(skel);
|
||||
bpf_link__destroy(link);
|
||||
ecode = UEI_REPORT(skel, uei);
|
||||
scx_simple__destroy(skel);
|
||||
|
||||
if (UEI_ECODE_RESTART(ecode))
|
||||
goto restart;
|
||||
return 0;
|
||||
if (UEI_ECODE_RESTART(ecode))
|
||||
goto restart;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
1
src/5-uprobe-bashreadline/.config
Normal file
1
src/5-uprobe-bashreadline/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/6-sigsnoop/.config
Normal file
1
src/6-sigsnoop/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/7-execsnoop/.config
Normal file
1
src/7-execsnoop/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/8-exitsnoop/.config
Normal file
1
src/8-exitsnoop/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
1
src/9-runqlat/.config
Normal file
1
src/9-runqlat/.config
Normal file
@@ -0,0 +1 @@
|
||||
level=Basic
|
||||
@@ -12,32 +12,33 @@ For the complete source code of the tutorial, please refer to the repo [https://
|
||||
|
||||
This section contains simple eBPF program examples and introductions. It primarily utilizes the `eunomia-bpf` framework to simplify development and introduces the basic usage and development process of eBPF.
|
||||
|
||||
- [lesson 0-introduce](0-introduce/README.md) Introduces basic concepts of eBPF and common development tools
|
||||
- [lesson 1-helloworld](1-helloworld/README.md) Develops the simplest "Hello World" program using eBPF and introduces the basic framework and development process of eBPF
|
||||
- [lesson 2-kprobe-unlink](2-kprobe-unlink/README.md) Uses kprobe in eBPF to capture the unlink system call
|
||||
- [lesson 3-fentry-unlink](3-fentry-unlink/README.md) Uses fentry in eBPF to capture the unlink system call
|
||||
- [lesson 4-opensnoop](4-opensnoop/README.md) Uses eBPF to capture the system call collection of processes opening files, and filters process PIDs in eBPF using global variables
|
||||
- [lesson 5-uprobe-bashreadline](5-uprobe-bashreadline/README.md) Uses uprobe in eBPF to capture the readline function calls in bash
|
||||
- [lesson 6-sigsnoop](6-sigsnoop/README.md) Captures the system call collection of processes sending signals and uses a hash map to store states
|
||||
- [lesson 7-execsnoop](7-execsnoop/README.md) Captures process execution times and prints output to user space through perf event array
|
||||
- [lesson 8-exitsnoop](8-exitsnoop/README.md) Captures process exit events and prints output to user space using a ring buffer
|
||||
- [lesson 9-runqlat](9-runqlat/README.md) Captures process scheduling delays and records them in histogram format
|
||||
- [lesson 10-hardirqs](10-hardirqs/README.md) Captures interrupt events using hardirqs or softirqs
|
||||
- [lesson 0-introduce](0-introduce/README_en.md) Introduction to Core Concepts and Tools
|
||||
- [lesson 1-helloworld](1-helloworld/README_en.md) Hello World, Framework and Development
|
||||
- [lesson 2-kprobe-unlink](2-kprobe-unlink/README_en.md) Monitoring unlink System Calls with kprobe
|
||||
- [lesson 3-fentry-unlink](3-fentry-unlink/README_en.md) Monitoring unlink System Calls with fentry
|
||||
- [lesson 4-opensnoop](4-opensnoop/README_en.md) Capturing Opening Files and Filter with Global Variables
|
||||
- [lesson 5-uprobe-bashreadline](5-uprobe-bashreadline/README_en.md) Capturing readline Function Calls with Uprobe
|
||||
- [lesson 6-sigsnoop](6-sigsnoop/README_en.md) Capturing Signal Sending and Store State with Hash Maps
|
||||
- [lesson 7-execsnoop](7-execsnoop/README_en.md) Capturing Process Execution, Output with perf event array
|
||||
- [lesson 8-exitsnoop](8-exitsnoop/README_en.md) Monitoring Process Exit Events, Output with Ring Buffer
|
||||
- [lesson 9-runqlat](9-runqlat/README_en.md) Capturing Scheduling Latency and Recording as Histogram
|
||||
- [lesson 10-hardirqs](10-hardirqs/README_en.md) Capturing Interrupts with hardirqs or softirqs
|
||||
|
||||
# Advanced Documents and Examples
|
||||
|
||||
We start to build complete eBPF projects mainly based on `libbpf` and combine them with various application scenarios for practical use.
|
||||
|
||||
- [lesson 11-bootstrap](11-bootstrap/README.md) Writes native libbpf user space code for eBPF using libbpf-bootstrap and establishes a complete libbpf project.
|
||||
- [lesson 12-profile](12-profile/README.md) Performs performance analysis using eBPF
|
||||
- [lesson 13-tcpconnlat](13-tcpconnlat/README.md) Records TCP connection latency and processes data in user space using libbpf
|
||||
- [lesson 14-tcpstates](14-tcpstates/README.md) Records TCP connection state and TCP RTT.- [lesson 15-javagc](15-javagc/README.md) Capture user-level Java GC event duration using usdt
|
||||
- [lesson 16-memleak](16-memleak/README.md) Detect memory leaks
|
||||
- [lesson 17-biopattern](17-biopattern/README.md) Capture disk IO patterns
|
||||
- [lesson 18-further-reading](18-further-reading/README.md) Further reading: papers list, projects, blogs, etc.
|
||||
- [lesson 19-lsm-connect](19-lsm-connect/README.md) Use LSM for security detection and defense
|
||||
- [lesson 20-tc](20-tc/README.md) Use eBPF for tc traffic control
|
||||
- [lesson 21-xdp](21-xdp/README.md) Use eBPF for XDP packet processing
|
||||
- [lesson 11-bootstrap](11-bootstrap/README_en.md) Develop User-Space Programs with libbpf and Trace exec() and exit()
|
||||
- [lesson 12-profile](12-profile/README_en.md) Using eBPF Program Profile for Performance Analysis
|
||||
- [lesson 13-tcpconnlat](13-tcpconnlat/README_en.md) Statistics of TCP Connection Delay with libbpf
|
||||
- [lesson 14-tcpstates](14-tcpstates/README_en.md) Recording TCP Connection Status and TCP RTT
|
||||
- [lesson 15-javagc](15-javagc/README_en.md) Capturing User-Space Java GC Duration Using USDT
|
||||
- [lesson 16-memleak](16-memleak/README_en.md) Monitoring Memory Leaks
|
||||
- [lesson 17-biopattern](17-biopattern/README_en.md) Count Random/Sequential Disk I/O
|
||||
- [lesson 18-further-reading](18-further-reading/README_en.md) More Reference Materials: papers, projects
|
||||
- [lesson 19-lsm-connect](19-lsm-connect/README_en.md) Security Detection and Defense using LSM
|
||||
- [lesson 20-tc](20-tc/README_en.md) tc Traffic Control
|
||||
- [lesson 21-xdp](21-xdp/README_en.md) Programmable Packet Processing with XDP
|
||||
|
||||
# In-Depth Topics
|
||||
|
||||
@@ -45,41 +46,36 @@ This section covers advanced topics related to eBPF, including using eBPF progra
|
||||
|
||||
Android:
|
||||
|
||||
- [Using eBPF programs on Android](22-android/README.md)
|
||||
- [lesson 22-android](22-android/README_en.md) Using eBPF Programs on Android
|
||||
|
||||
Networking:
|
||||
|
||||
- [Accelerating network request forwarding using sockops](29-sockops/README.md)
|
||||
- [Capturing TCP Information with XDP](41-xdp-tcpdump/README.md)
|
||||
- [XDP Load Balancer](42-xdp-loadbalancer/README.md)
|
||||
|
||||
tracing:
|
||||
|
||||
- [Tracing HTTP requests or other layer-7 protocols using eBPF socket filter or syscall trace](23-http/README.md)
|
||||
- [Capturing Plain Text Data of Various Libraries' SSL/TLS Using uprobe](30-sslsniff/README.md)
|
||||
- [Using eBPF to Trace Go Routine States](31-goroutine/README.md)
|
||||
- [Measuring Function Latency with eBPF](33-funclatency/README.md)
|
||||
- [Use uprobe to trace Rust programs](37-uprobe-rust/README.md)
|
||||
- [Using eBPF to Trace Nginx Requests](39-nginx/README.md)
|
||||
- [Using eBPF to Trace MySQL Queries](src/40-mysql)
|
||||
- [lesson 23-http](23-http/README_en.md) L7 Tracing with eBPF: HTTP and Beyond via Socket Filters and Syscall Tracepoints
|
||||
- [lesson 29-sockops](29-sockops/README_en.md) Accelerating Network Request Forwarding with Sockops
|
||||
- [lesson 41-xdp-tcpdump](41-xdp-tcpdump/README_en.md) Capturing TCP Information with XDP
|
||||
- [lesson 42-xdp-loadbalancer](42-xdp-loadbalancer/README_en.md) XDP Load Balancer
|
||||
|
||||
Security:
|
||||
|
||||
- [Use eBPF to modify syscall parameters](34-syscall/README.md)
|
||||
- [The Secure Path Forward for eBPF: Challenges and Innovations](18-further-reading/ebpf-security.md)
|
||||
- [Hiding process or file information using eBPF](24-hide/README.md)
|
||||
- [Terminating processes by sending signals using bpf_send_signal](25-signal/README.md)
|
||||
- [Adding sudo users using eBPF](26-sudo/README.md)
|
||||
- [Replacing text read or written by any program using eBPF](27-replace/README.md)
|
||||
- [BPF lifecycle: Running eBPF programs continuously in Detached mode after user-mode applications exit](28-detach/README.md)
|
||||
- [Modifying System Call Parameters with eBPF](34-syscall/README.md)
|
||||
- [lesson 24-hide](24-hide/README_en.md) Hiding Process or File Information
|
||||
- [lesson 25-signal](25-signal/README_en.md) Using bpf_send_signal to Terminate Malicious Processes in eBPF
|
||||
- [lesson 26-sudo](26-sudo/README_en.md) Using eBPF to add sudo user
|
||||
- [lesson 27-replace](27-replace/README_en.md) Replace Text Read or Written by Any Program with eBPF
|
||||
- [lesson 28-detach](28-detach/README_en.md) Running eBPF After Application Exits: The Lifecycle of eBPF Programs
|
||||
- [lesson 34-syscall](34-syscall/README_en.md) Modifying System Call Arguments with eBPF
|
||||
|
||||
Scheduler:
|
||||
|
||||
- [lesson 44-scx-simple](44-scx-simple/README_en.md) Introduction to the BPF Scheduler
|
||||
|
||||
Other:
|
||||
|
||||
- [Using user ring buffer to send information to the kernel](35-user-ringbuf/README.md)
|
||||
- [Userspace eBPF Runtimes: Overview and Applications](36-userspace-ebpf/README.md)
|
||||
- [Compile Once, Run Everywhere for userspace with eBPF and BTF](38-btf-uprobe/README.md)
|
||||
- [Extending eBPF Beyond Its Limits: Custom kfuncs in Kernel Modules](43-kfuncs/README.md)
|
||||
- [lesson 35-user-ringbuf](35-user-ringbuf/README_en.md) Asynchronously Send to Kernel with User Ring Buffer
|
||||
- [lesson 36-userspace-ebpf](36-userspace-ebpf/README_en.md) Userspace eBPF Runtimes: Overview and Applications
|
||||
- [lesson 38-btf-uprobe](38-btf-uprobe/README_en.md) Expanding eBPF Compile Once, Run Everywhere(CO-RE) to Userspace Compatibility
|
||||
- [lesson 43-kfuncs](43-kfuncs/README_en.md) Extending eBPF Beyond Its Limits: Custom kfuncs in Kernel Modules
|
||||
|
||||
Continuously updating...
|
||||
|
||||
# bcc and bpftrace tutorial
|
||||
|
||||
@@ -90,4 +86,4 @@ For reference:
|
||||
- [bcc Reference Guide](bcc-documents/reference_guide.md)
|
||||
- [Special Filtering](bcc-documents/special_filtering.md)
|
||||
- [bcc Tutorial](bcc-documents/tutorial.md)".- [bcc Python Developer Tutorial](bcc-documents/tutorial_bcc_python_developer.md)
|
||||
- [bpftrace Tutorial](bpftrace-tutorial/README.md)
|
||||
- [bpftrace Tutorial](bpftrace-tutorial/README.md)
|
||||
|
||||
219
src/scripts/generate_toc.py
Normal file
219
src/scripts/generate_toc.py
Normal file
@@ -0,0 +1,219 @@
|
||||
import os
|
||||
import re
|
||||
|
||||
# Define a function to walk through the directory and generate the TOC structure
|
||||
def generate_toc(base_dir, project_root):
|
||||
toc = "## Table of Contents\n\n"
|
||||
section_headers = {
|
||||
"Basic": "### Getting Started Examples\n\nThis section contains simple eBPF program examples and introductions. It primarily utilizes the `eunomia-bpf` framework to simplify development and introduces the basic usage and development process of eBPF.\n\n",
|
||||
"Advance": "### Advanced Documents and Examples\n\nWe start to build complete eBPF projects mainly based on `libbpf` and combine them with various application scenarios for practical use.\n\n",
|
||||
"Depth": "### In-Depth Topics\n\nThis section covers advanced topics related to eBPF, including using eBPF programs on Android, possible attacks and defenses using eBPF programs, and complex tracing. Combining the user-mode and kernel-mode aspects of eBPF can bring great power (as well as security risks).\n\n"
|
||||
}
|
||||
|
||||
subsection_titles = {
|
||||
"Android": "\n\nAndroid:\n\n",
|
||||
"Networking": "\n\nNetworking:\n\n",
|
||||
"tracing": "\n\ntracing:\n\n",
|
||||
"Security": "\n\nSecurity:\n\n",
|
||||
"Scheduler": "\n\nScheduler:\n\n",
|
||||
"Other": "\n\nOther:\n\n"
|
||||
}
|
||||
|
||||
subsection_order = ['Android', 'Networking', 'tracing', 'Security', 'Scheduler', 'Other']
|
||||
|
||||
# To ensure numeric sorting of directories
|
||||
def sort_key(directory_name):
|
||||
return list(map(int, re.findall(r'\d+', directory_name)))
|
||||
|
||||
sections = {} # {section_level: {subsection_type: [lessons]}}
|
||||
|
||||
# Sort directories properly by numeric order
|
||||
all_dirs = sorted([d for d in os.listdir(base_dir) if os.path.isdir(os.path.join(base_dir, d))], key=sort_key)
|
||||
|
||||
# Loop over the sorted directories
|
||||
for directory in all_dirs:
|
||||
lesson_path = os.path.join(base_dir, directory)
|
||||
config_path = os.path.join(lesson_path, ".config")
|
||||
readme_path = os.path.join(lesson_path, "README_en.md")
|
||||
|
||||
if os.path.exists(config_path) and os.path.exists(readme_path):
|
||||
# Read the .config file for 'level', 'type', and 'desc'
|
||||
with open(config_path, 'r') as config_file:
|
||||
config_lines = config_file.readlines()
|
||||
level = None
|
||||
lesson_type = None
|
||||
desc = None
|
||||
for line in config_lines:
|
||||
if line.startswith("level="):
|
||||
level = line.split("=",1)[1].strip()
|
||||
elif line.startswith("type="):
|
||||
lesson_type = line.split("=",1)[1].strip()
|
||||
elif line.startswith("desc="):
|
||||
desc = line.split("=",1)[1].strip()
|
||||
|
||||
# Extract the first markdown title in README_en.md
|
||||
with open(readme_path, 'r') as readme_file:
|
||||
first_title = None
|
||||
for line in readme_file:
|
||||
if line.startswith("#"):
|
||||
first_title = line.strip().lstrip("#").strip()
|
||||
break
|
||||
|
||||
# If title starts with "eBPF", remove the part before the colon
|
||||
if first_title and first_title.startswith("eBPF"):
|
||||
if ":" in first_title:
|
||||
first_title = first_title.split(":", 1)[1].strip()
|
||||
|
||||
# Get the relative path for the lesson
|
||||
lesson_rel_path = os.path.relpath(readme_path, project_root)
|
||||
|
||||
# Prepare lesson data
|
||||
lesson_number = directory.split('-')[0]
|
||||
lesson_name = directory.split('-', 1)[1]
|
||||
link_text = f"lesson {lesson_number}-{lesson_name}"
|
||||
link = f"{lesson_rel_path}"
|
||||
# Use description if available, else use first title
|
||||
lesson_desc = desc if desc else first_title
|
||||
|
||||
lesson_entry = {
|
||||
'link_text': link_text,
|
||||
'link': link,
|
||||
'desc': lesson_desc
|
||||
}
|
||||
|
||||
# Organize lessons into sections and subsections
|
||||
sections.setdefault(level, {}).setdefault(lesson_type, []).append(lesson_entry)
|
||||
|
||||
# Now, output the TOC in the desired order
|
||||
section_order = ['Basic', 'Advance', 'Depth']
|
||||
|
||||
for level in section_order:
|
||||
if level in sections:
|
||||
toc += section_headers.get(level, "")
|
||||
# For Basic and Advance sections, no subsections
|
||||
if level != 'Depth':
|
||||
for lesson in sum(sections[level].values(), []): # Flatten the list
|
||||
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
|
||||
else:
|
||||
# For Depth section, output subsections in the desired order
|
||||
for subsection in subsection_order:
|
||||
if subsection in sections[level]:
|
||||
toc += subsection_titles.get(subsection, "")
|
||||
for lesson in sections[level][subsection]:
|
||||
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
|
||||
|
||||
toc += "\nContinuously updating..."
|
||||
return toc
|
||||
|
||||
|
||||
# Define a function to walk through the directory and generate the TOC structure in Chinese
|
||||
def generate_toc_cn(base_dir, project_root):
|
||||
toc = "## 目录\n\n"
|
||||
section_headers = {
|
||||
"Basic": "### 入门示例\n\n这一部分包含简单的 eBPF 程序示例和介绍。主要利用 `eunomia-bpf` 框架简化开发,介绍 eBPF 的基本用法和开发流程。\n\n",
|
||||
"Advance": "### 高级文档和示例\n\n我们开始构建完整的 eBPF 项目,主要基于 `libbpf`,并将其与各种应用场景结合起来,以便实际使用。\n\n",
|
||||
"Depth": "### 深入主题\n\n这一部分涵盖了与 eBPF 相关的高级主题,包括在 Android 上使用 eBPF 程序、利用 eBPF 程序进行的潜在攻击和防御以及复杂的追踪。结合用户模式和内核模式的 eBPF 可以带来强大的能力(也可能带来安全风险)。\n\n"
|
||||
}
|
||||
|
||||
subsection_titles = {
|
||||
"Android": "Android:\n\n",
|
||||
"Networking": "网络:\n\n",
|
||||
"tracing": "追踪:\n\n",
|
||||
"Security": "安全:\n\n",
|
||||
"Scheduler": "调度器:\n\n",
|
||||
"Other": "其他:\n\n"
|
||||
}
|
||||
|
||||
subsection_order = ['Android', 'Networking', 'tracing', 'Security', 'Scheduler', 'Other']
|
||||
|
||||
# To ensure numeric sorting of directories
|
||||
def sort_key(directory_name):
|
||||
return list(map(int, re.findall(r'\d+', directory_name)))
|
||||
|
||||
sections = {} # {section_level: {subsection_type: [lessons]}}
|
||||
|
||||
# Sort directories properly by numeric order
|
||||
all_dirs = sorted([d for d in os.listdir(base_dir) if os.path.isdir(os.path.join(base_dir, d))], key=sort_key)
|
||||
|
||||
# Loop over the sorted directories
|
||||
for directory in all_dirs:
|
||||
lesson_path = os.path.join(base_dir, directory)
|
||||
config_path = os.path.join(lesson_path, ".config")
|
||||
readme_path = os.path.join(lesson_path, "README.md")
|
||||
|
||||
if os.path.exists(config_path) and os.path.exists(readme_path):
|
||||
# Read the .config file for 'level', 'type', and 'desc'
|
||||
with open(config_path, 'r') as config_file:
|
||||
config_lines = config_file.readlines()
|
||||
level = None
|
||||
lesson_type = None
|
||||
desc = None
|
||||
for line in config_lines:
|
||||
if line.startswith("level="):
|
||||
level = line.split("=",1)[1].strip()
|
||||
elif line.startswith("type="):
|
||||
lesson_type = line.split("=",1)[1].strip()
|
||||
elif line.startswith("desc="):
|
||||
desc = line.split("=",1)[1].strip()
|
||||
|
||||
# Extract the first markdown title in README.md
|
||||
with open(readme_path, 'r') as readme_file:
|
||||
first_title = None
|
||||
for line in readme_file:
|
||||
if line.startswith("#"):
|
||||
first_title = line.strip().lstrip("#").strip()
|
||||
break
|
||||
|
||||
# If title starts with "eBPF", remove the part before the colon
|
||||
if first_title and first_title.startswith("eBPF"):
|
||||
if ":" in first_title:
|
||||
first_title = first_title.split(":", 1)[1].strip()
|
||||
|
||||
# Get the relative path for the lesson
|
||||
lesson_rel_path = os.path.relpath(readme_path, project_root)
|
||||
|
||||
# Prepare lesson data
|
||||
lesson_number = directory.split('-')[0]
|
||||
lesson_name = directory.split('-', 1)[1]
|
||||
link_text = f"lesson {lesson_number}-{lesson_name}"
|
||||
link = f"{lesson_rel_path}"
|
||||
# Use description if available, else use first title
|
||||
lesson_desc = desc if desc else first_title
|
||||
|
||||
lesson_entry = {
|
||||
'link_text': link_text,
|
||||
'link': link,
|
||||
'desc': lesson_desc
|
||||
}
|
||||
|
||||
# Organize lessons into sections and subsections
|
||||
sections.setdefault(level, {}).setdefault(lesson_type, []).append(lesson_entry)
|
||||
|
||||
# Now, output the TOC in the desired order
|
||||
section_order = ['Basic', 'Advance', 'Depth']
|
||||
|
||||
for level in section_order:
|
||||
if level in sections:
|
||||
toc += section_headers.get(level, "")
|
||||
# For Basic and Advance sections, no subsections
|
||||
if level != 'Depth':
|
||||
for lesson in sum(sections[level].values(), []): # Flatten the list
|
||||
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
|
||||
else:
|
||||
# For Depth section, output subsections in the desired order
|
||||
for subsection in subsection_order:
|
||||
if subsection in sections[level]:
|
||||
toc += subsection_titles.get(subsection, "")
|
||||
for lesson in sections[level][subsection]:
|
||||
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
|
||||
|
||||
toc += "\n持续更新中..."
|
||||
return toc
|
||||
|
||||
# Example usage
|
||||
base_directory = "/root/bpf-developer-tutorial/src/" # Replace with the actual base directory
|
||||
project_root = "/root/bpf-developer-tutorial/src/" # The root of the project
|
||||
toc_output = generate_toc(base_directory, project_root)
|
||||
|
||||
# Output the TOC
|
||||
print(toc_output)
|
||||
23
src/scripts/rename.py
Normal file
23
src/scripts/rename.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import os
|
||||
|
||||
def rename_readme_files(base_dir):
|
||||
# Walk through all directories and files starting from base_dir
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
|
||||
# Rename README.md to README.zh.md if README.md exists after the previous rename
|
||||
if file == "README.md":
|
||||
zh_file_path = os.path.join(root, "README.zh.md")
|
||||
os.rename(file_path, zh_file_path)
|
||||
print(f"Renamed {file_path} to {zh_file_path}")
|
||||
|
||||
# Rename README_en.md to README.md
|
||||
elif file == "README_en.md":
|
||||
new_file_path = os.path.join(root, "README.md")
|
||||
os.rename(file_path, new_file_path)
|
||||
print(f"Renamed {file_path} to {new_file_path}")
|
||||
|
||||
# Example usage
|
||||
base_directory = "/root/bpf-developer-tutorial/src" # Replace with the actual base directory
|
||||
rename_readme_files(base_directory)
|
||||
Reference in New Issue
Block a user