Skip to content

Commit 669a062

Browse files
authoredApr 22, 2024··
Initial commit
0 parents  commit 669a062

30 files changed

+618161
-0
lines changed
 

‎.devcontainer/devcontainer.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"build": {
3+
"context": "..",
4+
"dockerfile": "../dev.dockerfile"
5+
}
6+
}

‎.envrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use flake

‎.github/workflows/docker.yml

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Build and public docker image
2+
3+
on:
4+
push:
5+
branches: "master"
6+
7+
jobs:
8+
build-and-push-image:
9+
runs-on: ubuntu-latest
10+
# run only when code is compiling and tests are passing
11+
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
12+
# steps to perform in job
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v3
16+
with:
17+
submodules: 'recursive'
18+
19+
- name: install deps
20+
run: |
21+
sudo apt-get install -y --no-install-recommends \
22+
libelf1 libelf-dev zlib1g-dev libclang-13-dev \
23+
make git clang llvm pkg-config build-essential
24+
25+
- name: build package
26+
run: make build
27+
28+
# setup Docker buld action
29+
- name: Set up Docker Buildx
30+
id: buildx
31+
uses: docker/setup-buildx-action@v2
32+
33+
- name: Login to Github Packages
34+
uses: docker/login-action@v2
35+
with:
36+
registry: ghcr.io
37+
username: ${{ github.repository_owner }}
38+
password: ${{ secrets.GITHUB_TOKEN }}
39+
40+
- name: Build image and push to GitHub Container Registry
41+
uses: docker/build-push-action@v2
42+
with:
43+
# relative path to the place where source code with Dockerfile is located
44+
context: ./
45+
file: dockerfile
46+
platforms: linux/amd64
47+
# Note: tags has to be all lower-case
48+
tags: |
49+
ghcr.io/${{ github.repository_owner }}/libbpf-template:latest
50+
push: true
51+
52+
- name: Image digest
53+
run: echo ${{ steps.docker_build.outputs.digest }}

‎.github/workflows/publish.yml

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Build and publish
2+
3+
on:
4+
push:
5+
branches: "master"
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
11+
strategy:
12+
matrix:
13+
target: [ x86_64-unknown-linux-gnu ]
14+
15+
steps:
16+
- uses: actions/checkout@v3
17+
with:
18+
submodules: 'recursive'
19+
20+
- name: Set latest release version
21+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
22+
id: set_version
23+
uses: actions/github-script@v6
24+
with:
25+
result-encoding: string
26+
script: |
27+
const { data: releases } = await github.rest.repos.listReleases({
28+
owner: context.repo.owner,
29+
repo: context.repo.repo,
30+
});
31+
32+
const { data: tags } = await github.rest.repos.listTags({
33+
owner: context.repo.owner,
34+
repo: context.repo.repo
35+
});
36+
37+
if (releases.length === 0) { return "v0.0.1"; }
38+
39+
function increase_v(version) {
40+
const parts = version.split(".");
41+
const last = parseInt(parts[2]) + 1;
42+
const next_version = `${parts[0]}.${parts[1]}.${last.toString()}`;
43+
return next_version;
44+
}
45+
46+
const latest_release_tag = releases[0].tag_name;
47+
48+
const tag = tags.find(tag => tag.commit.sha === context.sha);
49+
50+
return tag ? tag.name : increase_v(latest_release_tag)
51+
52+
- name: install deps
53+
run: |
54+
sudo apt-get install -y --no-install-recommends \
55+
libelf1 libelf-dev zlib1g-dev libclang-13-dev \
56+
make git clang llvm pkg-config build-essential
57+
58+
- name: build package
59+
run: make build
60+
61+
- name: Publish
62+
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
63+
uses: softprops/action-gh-release@v1
64+
with:
65+
files: |
66+
src/bootstrap
67+
prerelease: false
68+
tag_name: ${{ steps.set_version.outputs.result }}
69+
generate_release_notes: true
70+
env:
71+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.direnv
2+
/build

‎.gitmodules

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "libbpf"]
2+
path = libbpf
3+
url = https://github.com/libbpf/libbpf.git
4+
[submodule "bpftool"]
5+
path = bpftool
6+
url = https://github.com/libbpf/bpftool.git

‎LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 eunomia-bpf org.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎Makefile

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
build:
2+
make -C src
3+
4+
clean:
5+
make -C src clean
6+
7+
install:
8+
sudo apt update
9+
sudo apt-get install -y --no-install-recommends \
10+
libelf1 libelf-dev zlib1g-dev \
11+
make clang llvm

‎README.md

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# **libbpf-starter-template**
2+
3+
![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)
4+
[![Build and publish](https://github.com/eunomia-bpf/libbpf-starter-template/actions/workflows/publish.yml/badge.svg)](https://github.com/eunomia-bpf/libbpf-starter-template/actions/workflows/publish.yml)
5+
![GitHub stars](https://img.shields.io/github/stars/eunomia-bpf/libbpf-starter-template?style=social)
6+
7+
Welcome to the **`libbpf-starter-template`**! This project template is designed to help you quickly start
8+
developing eBPF projects using libbpf in C. The template provides a solid starting point with a Makefile,
9+
Dockerfile, and GitHub action, along with all necessary dependencies to simplify your development process.
10+
11+
借助于 GitHub 模板和 Github Codespace,可以轻松构建 eBPF 项目和开发环境,一键在线编译运行 eBPF 程序。关于中文的文档和详细的 eBPF 开发教程,可以参考:https://github.com/eunomia-bpf/bpf-developer-tutorial
12+
13+
There are other templates for other languages:
14+
15+
- <https://github.com/eunomia-bpf/libbpf-starter-template>: eBPF project template based on the C language and the libbpf framework.
16+
- <https://github.com/eunomia-bpf/cilium-ebpf-starter-template>: eBPF project template based on the Go language and the cilium/ebpf framework.
17+
- <https://github.com/eunomia-bpf/libbpf-rs-starter-template>: eBPF project template based on the Rust language and the libbpf-rs framework.
18+
- <https://github.com/eunomia-bpf/eunomia-template>: eBPF project template based on the C language and the eunomia-bpf framework.
19+
20+
## **Getting Started**
21+
22+
To get started, simply click the "Use this template" button on the GitHub repository page. This will create
23+
a new repository in your account with the same files and structure as this template.
24+
25+
### Use docker
26+
27+
Run the following code to run the eBPF code from the cloud to your local machine in one line:
28+
29+
```console
30+
$ sudo docker run --rm -it --privileged ghcr.io/eunomia-bpf/libbpf-template:latest
31+
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
32+
09:25:14 EXEC sh 28142 1788 /bin/sh
33+
09:25:14 EXEC playerctl 28142 1788 /nix/store/vf3rsb7j3p7zzyjpb0a3axl8yq4z1sq5-playerctl-2.4.1/bin/playerctl
34+
09:25:14 EXIT playerctl 28142 1788 [1] (6ms)
35+
09:25:15 EXEC sh 28145 1788 /bin/sh
36+
09:25:15 EXEC playerctl 28145 1788 /nix/store/vf3rsb7j3p7zzyjpb0a3axl8yq4z1sq5-playerctl-2.4.1/bin/playerctl
37+
09:25:15 EXIT playerctl 28145 1788 [1] (6ms)
38+
```
39+
40+
### Use Nix
41+
42+
Using [direnv](https://github.com/direnv/direnv) and nix, you can quickly access a dev shell with a complete development environment.
43+
44+
With direnv, you can automatically load the required dependencies when you enter the directory.
45+
This way you don't have to worry about installing dependencies to break your other project development environment.
46+
47+
See how to install direnv and Nix:
48+
- direnv: https://github.com/direnv/direnv/blob/master/docs/installation.md
49+
- Nix: run
50+
```
51+
sh <(curl -L https://nixos.org/nix/install) --daemon
52+
```
53+
54+
Then use the following command to enable direnv support in this directory.
55+
56+
```sh
57+
direnv allow
58+
```
59+
60+
If you want use nix flake without direnv, simply run:
61+
62+
```sh
63+
nix develop
64+
```
65+
66+
## **Features**
67+
68+
This starter template includes the following features:
69+
70+
- A **`Makefile`** that allows you to build the project in one command
71+
- A **`Dockerfile`** to create a containerized environment for your project
72+
- A **`flake.nix`** to enter a dev shell with needed dependencies
73+
- A GitHub action to automate your build and publish process
74+
and docker image
75+
- All necessary dependencies for C development with libbpf
76+
77+
## **How to use**
78+
79+
### **1. Create a new repository using this template**
80+
81+
Click the "Use this template" button on the GitHub repository page to create a new repository based on this template.
82+
83+
### **2. Clone your new repository**
84+
85+
Clone your newly created repository to your local machine:
86+
87+
```sh
88+
git clone https://github.com/your_username/your_new_repository.git --recursive
89+
```
90+
91+
Or after clone the repo, you can update the git submodule with following commands:
92+
93+
```sh
94+
git submodule update --init --recursive
95+
```
96+
97+
### **3. Install dependencies**
98+
99+
For dependencies, it varies from distribution to distribution. You can refer to shell.nix and dockerfile for installation.
100+
101+
On Ubuntu, you may run `make install` or
102+
103+
```sh
104+
sudo apt-get install -y --no-install-recommends \
105+
libelf1 libelf-dev zlib1g-dev \
106+
make clang llvm
107+
```
108+
109+
to install dependencies.
110+
111+
### **4. Build the project**
112+
113+
To build the project, run the following command:
114+
115+
```sh
116+
make build
117+
```
118+
119+
This will compile your code and create the necessary binaries. You can you the `Github Code space` or `Github Action` to build the project as well.
120+
121+
### ***Run the Project***
122+
123+
You can run the binary with:
124+
125+
```console
126+
sudo src/bootstrap
127+
```
128+
129+
Or with Github Packages locally:
130+
131+
```console
132+
docker run --rm -it --privileged -v $(pwd):/examples ghcr.io/eunomia-bpf/libbpf-template:latest
133+
```
134+
135+
### **7. GitHub Actions**
136+
137+
This template also includes a GitHub action that will automatically build and publish your project when you push to the repository.
138+
To customize this action, edit the **`.github/workflows/publish.yml`** file.
139+
140+
## **Contributing**
141+
142+
We welcome contributions to improve this template! If you have any ideas or suggestions,
143+
feel free to create an issue or submit a pull request.
144+
145+
## **License**
146+
147+
This project is licensed under the MIT License. See the **[LICENSE](LICENSE)** file for more information.

‎bpftool

Submodule bpftool added at 259f40f

‎dev.dockerfile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM ubuntu:22.04
2+
3+
WORKDIR /root/
4+
COPY . /root/
5+
6+
RUN apt-get update -y && \
7+
apt-get install -y --no-install-recommends \
8+
libelf1 libelf-dev zlib1g-dev libclang-13-dev \
9+
make git clang llvm pkg-config build-essential && \
10+
apt-get install -y --no-install-recommends ca-certificates && \
11+
update-ca-certificates && \
12+
apt-get clean && \
13+
rm -rf /var/lib/apt/lists/*
14+
15+
ENTRYPOINT ["/bin/bash"]

‎dockerfile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM ubuntu:22.04
2+
3+
WORKDIR /root/
4+
COPY . /root/
5+
6+
RUN apt-get update \
7+
&& apt-get install -y --no-install-recommends libelf1 \
8+
&& rm -rf /var/lib/apt/lists/*
9+
10+
ENTRYPOINT ["/root/src/bootstrap"]

‎flake.lock

+42
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎flake.nix

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
description = "libbpf starter template";
3+
4+
inputs = {
5+
flake-utils.url = "github:numtide/flake-utils";
6+
nixpkgs.url = "nixpkgs/nixos-unstable";
7+
};
8+
9+
outputs = { self, nixpkgs, flake-utils }:
10+
flake-utils.lib.eachDefaultSystem (system:
11+
let
12+
pkgs = nixpkgs.legacyPackages.${system};
13+
in
14+
{
15+
devShell = pkgs.mkShell {
16+
name = "libbpf-template";
17+
nativeBuildInputs = with pkgs; [
18+
clang
19+
llvm
20+
pkg-config
21+
];
22+
23+
packages = with pkgs; [
24+
elfutils
25+
zlib
26+
];
27+
};
28+
}
29+
);
30+
}
31+

‎libbpf

Submodule libbpf added at f9106f6

‎src/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/.output
2+
/bootstrap

‎src/Makefile

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
OUTPUT := .output
3+
CLANG ?= clang
4+
LLVM_STRIP ?= llvm-strip
5+
LIBBPF_SRC := $(abspath ../libbpf/src)
6+
BPFTOOL_SRC := $(abspath ../bpftool/src)
7+
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
8+
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
9+
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
10+
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
11+
| sed 's/aarch64/arm64/' \
12+
| sed 's/ppc64le/powerpc/' \
13+
| sed 's/mips.*/mips/' \
14+
| sed 's/arm.*/arm/' \
15+
| sed 's/riscv64/riscv/')
16+
VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h
17+
# Use our own libbpf API headers and Linux UAPI headers distributed with
18+
# libbpf to avoid dependency on system-wide headers, which could be missing or
19+
# outdated
20+
INCLUDES := -I$(OUTPUT) -I../libbpf/include/uapi -I$(dir $(VMLINUX))
21+
CFLAGS := -g -Wall
22+
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
23+
24+
APPS = bootstrap
25+
26+
# Get Clang's default includes on this system. We'll explicitly add these dirs
27+
# to the includes list when compiling with `-target bpf` because otherwise some
28+
# architecture-specific dirs will be "missing" on some architectures/distros -
29+
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
30+
# sys/cdefs.h etc. might be missing.
31+
#
32+
# Use '-idirafter': Don't interfere with include mechanics except where the
33+
# build would have failed anyways.
34+
CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - </dev/null 2>&1 \
35+
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
36+
37+
ifeq ($(V),1)
38+
Q =
39+
msg =
40+
else
41+
Q = @
42+
msg = @printf ' %-8s %s%s\n' \
43+
"$(1)" \
44+
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
45+
"$(if $(3), $(3))";
46+
MAKEFLAGS += --no-print-directory
47+
endif
48+
49+
define allow-override
50+
$(if $(or $(findstring environment,$(origin $(1))),\
51+
$(findstring command line,$(origin $(1)))),,\
52+
$(eval $(1) = $(2)))
53+
endef
54+
55+
$(call allow-override,CC,$(CROSS_COMPILE)cc)
56+
$(call allow-override,LD,$(CROSS_COMPILE)ld)
57+
58+
.PHONY: all
59+
all: $(APPS)
60+
61+
.PHONY: clean
62+
clean:
63+
$(call msg,CLEAN)
64+
$(Q)rm -rf $(OUTPUT) $(APPS)
65+
66+
$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
67+
$(call msg,MKDIR,$@)
68+
$(Q)mkdir -p $@
69+
70+
# Build libbpf
71+
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
72+
$(call msg,LIB,$@)
73+
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
74+
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
75+
INCLUDEDIR= LIBDIR= UAPIDIR= \
76+
install
77+
78+
# Build bpftool
79+
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
80+
$(call msg,BPFTOOL,$@)
81+
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap
82+
83+
# Build BPF code
84+
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT)
85+
$(call msg,BPF,$@)
86+
$(Q)$(CLANG) -g -fstack-protector -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@
87+
$(Q)$(LLVM_STRIP) -g $@ # strip useless DWARF info
88+
89+
# Generate BPF skeletons
90+
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
91+
$(call msg,GEN-SKEL,$@)
92+
$(Q)$(BPFTOOL) gen skeleton $< > $@
93+
94+
# Build user-space code
95+
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
96+
97+
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
98+
$(call msg,CC,$@)
99+
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
100+
101+
# Build application binary
102+
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
103+
$(call msg,BINARY,$@)
104+
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@
105+
106+
# delete failed targets
107+
.DELETE_ON_ERROR:
108+
109+
# keep intermediate (.skel.h, .bpf.o, etc) targets
110+
.SECONDARY:

‎src/bootstrap.bpf.c

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2+
/* Copyright (c) 2020 Facebook */
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 "bootstrap.h"
8+
9+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
10+
11+
struct {
12+
__uint(type, BPF_MAP_TYPE_HASH);
13+
__uint(max_entries, 8192);
14+
__type(key, pid_t);
15+
__type(value, u64);
16+
} exec_start SEC(".maps");
17+
18+
struct {
19+
__uint(type, BPF_MAP_TYPE_RINGBUF);
20+
__uint(max_entries, 256 * 1024);
21+
} rb SEC(".maps");
22+
23+
const volatile unsigned long long min_duration_ns = 0;
24+
25+
SEC("tp/sched/sched_process_exec")
26+
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
27+
{
28+
struct task_struct *task;
29+
unsigned fname_off;
30+
struct event *e;
31+
pid_t pid;
32+
u64 ts;
33+
34+
/* remember time exec() was executed for this PID */
35+
pid = bpf_get_current_pid_tgid() >> 32;
36+
ts = bpf_ktime_get_ns();
37+
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);
38+
39+
/* don't emit exec events when minimum duration is specified */
40+
if (min_duration_ns)
41+
return 0;
42+
43+
/* reserve sample from BPF ringbuf */
44+
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
45+
if (!e)
46+
return 0;
47+
48+
/* fill out the sample with data */
49+
task = (struct task_struct *)bpf_get_current_task();
50+
51+
e->exit_event = false;
52+
e->pid = pid;
53+
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
54+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
55+
56+
fname_off = ctx->__data_loc_filename & 0xFFFF;
57+
bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
58+
59+
/* successfully submit it to user-space for post-processing */
60+
bpf_ringbuf_submit(e, 0);
61+
return 0;
62+
}
63+
64+
SEC("tp/sched/sched_process_exit")
65+
int handle_exit(struct trace_event_raw_sched_process_template* ctx)
66+
{
67+
struct task_struct *task;
68+
struct event *e;
69+
pid_t pid, tid;
70+
u64 id, ts, *start_ts, duration_ns = 0;
71+
72+
/* get PID and TID of exiting thread/process */
73+
id = bpf_get_current_pid_tgid();
74+
pid = id >> 32;
75+
tid = (u32)id;
76+
77+
/* ignore thread exits */
78+
if (pid != tid)
79+
return 0;
80+
81+
/* if we recorded start of the process, calculate lifetime duration */
82+
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
83+
if (start_ts)
84+
duration_ns = bpf_ktime_get_ns() - *start_ts;
85+
else if (min_duration_ns)
86+
return 0;
87+
bpf_map_delete_elem(&exec_start, &pid);
88+
89+
/* if process didn't live long enough, return early */
90+
if (min_duration_ns && duration_ns < min_duration_ns)
91+
return 0;
92+
93+
/* reserve sample from BPF ringbuf */
94+
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
95+
if (!e)
96+
return 0;
97+
98+
/* fill out the sample with data */
99+
task = (struct task_struct *)bpf_get_current_task();
100+
101+
e->exit_event = true;
102+
e->duration_ns = duration_ns;
103+
e->pid = pid;
104+
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
105+
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
106+
bpf_get_current_comm(&e->comm, sizeof(e->comm));
107+
108+
/* send data to user-space for post-processing */
109+
bpf_ringbuf_submit(e, 0);
110+
return 0;
111+
}
112+

‎src/bootstrap.c

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
/* Copyright (c) 2020 Facebook */
3+
#include <argp.h>
4+
#include <signal.h>
5+
#include <stdio.h>
6+
#include <time.h>
7+
#include <sys/resource.h>
8+
#include <bpf/libbpf.h>
9+
#include "bootstrap.h"
10+
#include "bootstrap.skel.h"
11+
12+
static struct env {
13+
bool verbose;
14+
long min_duration_ms;
15+
} env;
16+
17+
const char *argp_program_version = "bootstrap 0.0";
18+
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
19+
const char argp_program_doc[] =
20+
"BPF bootstrap demo application.\n"
21+
"\n"
22+
"It traces process start and exits and shows associated \n"
23+
"information (filename, process duration, PID and PPID, etc).\n"
24+
"\n"
25+
"USAGE: ./bootstrap [-d <min-duration-ms>] [-v]\n";
26+
27+
static const struct argp_option opts[] = {
28+
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
29+
{ "duration", 'd', "DURATION-MS", 0, "Minimum process duration (ms) to report" },
30+
{},
31+
};
32+
33+
static error_t parse_arg(int key, char *arg, struct argp_state *state)
34+
{
35+
switch (key) {
36+
case 'v':
37+
env.verbose = true;
38+
break;
39+
case 'd':
40+
errno = 0;
41+
env.min_duration_ms = strtol(arg, NULL, 10);
42+
if (errno || env.min_duration_ms <= 0) {
43+
fprintf(stderr, "Invalid duration: %s\n", arg);
44+
argp_usage(state);
45+
}
46+
break;
47+
case ARGP_KEY_ARG:
48+
argp_usage(state);
49+
break;
50+
default:
51+
return ARGP_ERR_UNKNOWN;
52+
}
53+
return 0;
54+
}
55+
56+
static const struct argp argp = {
57+
.options = opts,
58+
.parser = parse_arg,
59+
.doc = argp_program_doc,
60+
};
61+
62+
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
63+
{
64+
if (level == LIBBPF_DEBUG && !env.verbose)
65+
return 0;
66+
return vfprintf(stderr, format, args);
67+
}
68+
69+
static volatile bool exiting = false;
70+
71+
static void sig_handler(int sig)
72+
{
73+
exiting = true;
74+
}
75+
76+
static int handle_event(void *ctx, void *data, size_t data_sz)
77+
{
78+
const struct event *e = data;
79+
struct tm *tm;
80+
char ts[32];
81+
time_t t;
82+
83+
time(&t);
84+
tm = localtime(&t);
85+
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
86+
87+
if (e->exit_event) {
88+
printf("%-8s %-5s %-16s %-7d %-7d [%u]",
89+
ts, "EXIT", e->comm, e->pid, e->ppid, e->exit_code);
90+
if (e->duration_ns)
91+
printf(" (%llums)", e->duration_ns / 1000000);
92+
printf("\n");
93+
} else {
94+
printf("%-8s %-5s %-16s %-7d %-7d %s\n",
95+
ts, "EXEC", e->comm, e->pid, e->ppid, e->filename);
96+
}
97+
98+
return 0;
99+
}
100+
101+
int main(int argc, char **argv)
102+
{
103+
struct ring_buffer *rb = NULL;
104+
struct bootstrap_bpf *skel;
105+
int err;
106+
107+
/* Parse command line arguments */
108+
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
109+
if (err)
110+
return err;
111+
112+
/* Set up libbpf errors and debug info callback */
113+
libbpf_set_print(libbpf_print_fn);
114+
115+
/* Cleaner handling of Ctrl-C */
116+
signal(SIGINT, sig_handler);
117+
signal(SIGTERM, sig_handler);
118+
119+
/* Load and verify BPF application */
120+
skel = bootstrap_bpf__open();
121+
if (!skel) {
122+
fprintf(stderr, "Failed to open and load BPF skeleton\n");
123+
return 1;
124+
}
125+
126+
/* Parameterize BPF code with minimum duration parameter */
127+
skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL;
128+
129+
/* Load & verify BPF programs */
130+
err = bootstrap_bpf__load(skel);
131+
if (err) {
132+
fprintf(stderr, "Failed to load and verify BPF skeleton\n");
133+
goto cleanup;
134+
}
135+
136+
/* Attach tracepoints */
137+
err = bootstrap_bpf__attach(skel);
138+
if (err) {
139+
fprintf(stderr, "Failed to attach BPF skeleton\n");
140+
goto cleanup;
141+
}
142+
143+
/* Set up ring buffer polling */
144+
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
145+
if (!rb) {
146+
err = -1;
147+
fprintf(stderr, "Failed to create ring buffer\n");
148+
goto cleanup;
149+
}
150+
151+
/* Process events */
152+
printf("%-8s %-5s %-16s %-7s %-7s %s\n",
153+
"TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE");
154+
while (!exiting) {
155+
err = ring_buffer__poll(rb, 100 /* timeout, ms */);
156+
/* Ctrl-C will cause -EINTR */
157+
if (err == -EINTR) {
158+
err = 0;
159+
break;
160+
}
161+
if (err < 0) {
162+
printf("Error polling perf buffer: %d\n", err);
163+
break;
164+
}
165+
}
166+
167+
cleanup:
168+
/* Clean up */
169+
ring_buffer__free(rb);
170+
bootstrap_bpf__destroy(skel);
171+
172+
return err < 0 ? -err : 0;
173+
}

‎src/bootstrap.h

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
/* Copyright (c) 2020 Facebook */
3+
#ifndef __BOOTSTRAP_H
4+
#define __BOOTSTRAP_H
5+
6+
#define TASK_COMM_LEN 16
7+
#define MAX_FILENAME_LEN 127
8+
9+
struct event {
10+
int pid;
11+
int ppid;
12+
unsigned exit_code;
13+
unsigned long long duration_ns;
14+
char comm[TASK_COMM_LEN];
15+
char filename[MAX_FILENAME_LEN];
16+
bool exit_event;
17+
};
18+
19+
#endif /* __BOOTSTRAP_H */

‎vmlinux/arm/vmlinux.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vmlinux_62.h

‎vmlinux/arm/vmlinux_62.h

+119,802
Large diffs are not rendered by default.

‎vmlinux/arm64/vmlinux.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vmlinux_601.h

‎vmlinux/arm64/vmlinux_516.h

+160,927
Large diffs are not rendered by default.

‎vmlinux/arm64/vmlinux_601.h

+91,425
Large diffs are not rendered by default.

‎vmlinux/riscv/vmlinux.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vmlinux_602.h

‎vmlinux/riscv/vmlinux_602.h

+146,849
Large diffs are not rendered by default.

‎vmlinux/vmlinux.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
x86/vmlinux.h

‎vmlinux/x86/vmlinux.h

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vmlinux_601.h

‎vmlinux/x86/vmlinux_601.h

+98,319
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.