├── .devcontainer └── devcontainer.json ├── .envrc ├── .github └── workflows │ ├── docker.yml │ └── publish.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── dev.dockerfile ├── dockerfile ├── flake.lock ├── flake.nix └── src ├── template.bpf.c └── template.h /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "context": "..", 4 | "dockerfile": "../dev.dockerfile" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build and public docker image 2 | 3 | on: 4 | push: 5 | branches: "main" 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 | # setup Docker buld action 20 | - name: Set up Docker Buildx 21 | id: buildx 22 | uses: docker/setup-buildx-action@v2 23 | 24 | - name: Login to Github Packages 25 | uses: docker/login-action@v2 26 | with: 27 | registry: ghcr.io 28 | username: ${{ github.repository_owner }} 29 | password: ${{ secrets.GITHUB_TOKEN }} 30 | 31 | - name: Build image and push to GitHub Container Registry 32 | uses: docker/build-push-action@v2 33 | with: 34 | # relative path to the place where source code with Dockerfile is located 35 | context: ./ 36 | file: dockerfile 37 | platforms: linux/amd64 38 | # Note: tags has to be all lower-case 39 | tags: | 40 | ghcr.io/${{ github.repository_owner }}/eunomia-template:latest 41 | push: true 42 | 43 | - name: Image digest 44 | run: echo ${{ steps.docker_build.outputs.digest }} 45 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" 10 | strategy: 11 | matrix: 12 | target: [ x86_64-unknown-linux-gnu ] 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Set latest release version 18 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 19 | id: set_version 20 | uses: actions/github-script@v6 21 | with: 22 | result-encoding: string 23 | script: | 24 | const { data: releases } = await github.rest.repos.listReleases({ 25 | owner: context.repo.owner, 26 | repo: context.repo.repo, 27 | }); 28 | 29 | const { data: tags } = await github.rest.repos.listTags({ 30 | owner: context.repo.owner, 31 | repo: context.repo.repo 32 | }); 33 | 34 | if (releases.length === 0) { return "v0.0.1"; } 35 | 36 | function increase_v(version) { 37 | const parts = version.split("."); 38 | const last = parseInt(parts[2]) + 1; 39 | const next_version = `${parts[0]}.${parts[1]}.${last.toString()}`; 40 | return next_version; 41 | } 42 | 43 | const latest_release_tag = releases[0].tag_name; 44 | 45 | const tag = tags.find(tag => tag.commit.sha === context.sha); 46 | 47 | return tag ? tag.name : increase_v(latest_release_tag) 48 | 49 | - name: install deps 50 | run: | 51 | sudo apt update 52 | sudo apt-get install -y --no-install-recommends \ 53 | libcurl4-openssl-dev libelf-dev clang llvm cmake zlib1g-dev 54 | wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc && chmod +x ./ecc 55 | 56 | - name: build package 57 | run: | 58 | cd src 59 | ../ecc template.bpf.c template.h 60 | 61 | - name: Publish 62 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 63 | uses: softprops/action-gh-release@v1 64 | with: 65 | files: src/package.json 66 | prerelease: false 67 | tag_name: ${{ steps.set_version.outputs.result }} 68 | generate_release_notes: true 69 | env: 70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | .vscode 3 | package.json 4 | *.o 5 | *.skel.json 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | cd src && ecc template.bpf.c template.h 3 | 4 | clean: 5 | cd src && rm *.json *.o 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **eunomia-template** 2 | 3 | ![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) 4 | [![Build and publish](https://github.com/eunomia-bpf/eunomia-template/actions/workflows/publish.yml/badge.svg)](https://github.com/eunomia-bpf/eunomia-template/actions/workflows/publish.yml) 5 | ![GitHub stars](https://img.shields.io/github/stars/eunomia-bpf/eunomia-template?style=social) 6 | 7 | Welcome to the **`eunomia-template`**! This project template is designed to help you quickly start 8 | developing eBPF projects using eunomia-bpf 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 | - : eBPF project template based on the C language and the libbpf framework. 16 | - : eBPF project template based on the Go language and the cilium/ebpf framework. 17 | - : eBPF project template based on the Rust language and the libbpf-rs framework. 18 | 19 | ## **Getting Started** 20 | 21 | To get started, simply click the "Use this template" button on the GitHub repository page. This will create 22 | a new repository in your account with the same files and structure as this template. 23 | 24 | ### Use docker 25 | 26 | Run the following code to run the eBPF code from the cloud to your local machine in one line: 27 | 28 | ```console 29 | $ sudo docker run --rm -it --privileged ghcr.io/eunomia-bpf/eunomia-template:latest 30 | TIME EVENT COMM PID PPID FILENAME/EXIT CODE 31 | 09:25:14 EXEC sh 28142 1788 /bin/sh 32 | 09:25:14 EXEC playerctl 28142 1788 /nix/store/vf3rsb7j3p7zzyjpb0a3axl8yq4z1sq5-playerctl-2.4.1/bin/playerctl 33 | 09:25:14 EXIT playerctl 28142 1788 [1] (6ms) 34 | 09:25:15 EXEC sh 28145 1788 /bin/sh 35 | 09:25:15 EXEC playerctl 28145 1788 /nix/store/vf3rsb7j3p7zzyjpb0a3axl8yq4z1sq5-playerctl-2.4.1/bin/playerctl 36 | 09:25:15 EXIT playerctl 28145 1788 [1] (6ms) 37 | ``` 38 | 39 | ### Use Nix 40 | 41 | Using [direnv](https://github.com/direnv/direnv) and nix, you can quickly access a dev shell with a complete development environment. 42 | 43 | With direnv, you can automatically load the required dependencies when you enter the directory. 44 | This way you don't have to worry about installing dependencies to break your other project development environment. 45 | 46 | See how to install direnv and Nix: 47 | - direnv: https://github.com/direnv/direnv/blob/master/docs/installation.md 48 | - Nix: run 49 | ``` 50 | sh <(curl -L https://nixos.org/nix/install) --daemon 51 | ``` 52 | 53 | Then use the following command to enable direnv support in this directory. 54 | 55 | ```sh 56 | direnv allow 57 | ``` 58 | 59 | If you want use nix flake without direnv, simply run: 60 | 61 | ```sh 62 | nix develop 63 | ``` 64 | 65 | ## **Features** 66 | 67 | This starter template includes the following features: 68 | 69 | - A **`Makefile`** that allows you to build the project in one command 70 | - A **`Dockerfile`** to create a containerized environment for your project 71 | - A **`flake.nix`** to enter a dev shell with needed dependencies 72 | - A GitHub action to automate your build and publish process 73 | and docker image 74 | - All necessary dependencies for C development with eunomia-bpf 75 | 76 | ## **How to use** 77 | 78 | ### **1. Create a new repository using this template** 79 | 80 | Click the "Use this template" button on the GitHub repository page to create a new repository based on this template. 81 | 82 | ### **2. Clone your new repository** 83 | 84 | Clone your newly created repository to your local machine: 85 | 86 | ```sh 87 | git clone https://github.com/your_username/your_new_repository.git 88 | ``` 89 | 90 | ### **3. Install dependencies** 91 | 92 | For dependencies, it varies from distribution to distribution. You can refer to shell.nix and dockerfile for installation. 93 | 94 | On Ubuntu, you may run `make install` or 95 | 96 | ```sh 97 | sudo apt-get install -y --no-install-recommends \ 98 | libelf1 libelf-dev zlib1g-dev \ 99 | make clang llvm 100 | ``` 101 | 102 | to install dependencies. 103 | 104 | ### **4. Build the project** 105 | 106 | To build the project, run the following command: 107 | 108 | ```sh 109 | make build 110 | ``` 111 | 112 | 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. 113 | 114 | ### ***Run the Project*** 115 | 116 | You can run the binary with: 117 | 118 | ```console 119 | ecli run src/package.json 120 | ``` 121 | 122 | Or with Github Packages locally: 123 | 124 | ```console 125 | docker run --rm -it --privileged -v $(pwd):/examples ghcr.io/eunomia-bpf/eunomia-template:latest 126 | ``` 127 | 128 | ### **7. GitHub Actions** 129 | 130 | This template also includes a GitHub action that will automatically build and publish your project when you push to the repository. 131 | To customize this action, edit the **`.github/workflows/publish.yml`** file. 132 | 133 | ## **Contributing** 134 | 135 | We welcome contributions to improve this template! If you have any ideas or suggestions, 136 | feel free to create an issue or submit a pull request. 137 | 138 | ## **License** 139 | 140 | This project is licensed under the MIT License. See the **[LICENSE](LICENSE)** file for more information. 141 | ## detail documents 142 | 143 | For more documents, please refer to https://eunomia-bpf.github.io/ 144 | -------------------------------------------------------------------------------- /dev.dockerfile: -------------------------------------------------------------------------------- 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"] 16 | -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/eunomia-bpf/ecc-x86_64:latest 2 | 3 | COPY . /root/template 4 | WORKDIR /root/template 5 | 6 | RUN wget -q -O /root/.eunomia/bin/ecli https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecli 7 | 8 | RUN chmod +x /root/.eunomia/bin/ecli 9 | 10 | ENV PATH="/root/.eunomia/bin:${PATH}" 11 | 12 | RUN make build 13 | 14 | ENTRYPOINT ["ecli"] 15 | 16 | CMD ["run", "src/package.json"] 17 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1678901627, 6 | "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1679437018, 21 | "narHash": "sha256-vOuiDPLHSEo/7NkiWtxpHpHgoXoNmrm+wkXZ6a072Fc=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "19cf008bb18e47b6e3b4e16e32a9a4bdd4b45f7e", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "id": "nixpkgs", 29 | "ref": "nixos-unstable", 30 | "type": "indirect" 31 | } 32 | }, 33 | "root": { 34 | "inputs": { 35 | "flake-utils": "flake-utils", 36 | "nixpkgs": "nixpkgs" 37 | } 38 | } 39 | }, 40 | "root": "root", 41 | "version": 7 42 | } 43 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "eunomia-bpf 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 = "eunomia-template"; 17 | nativeBuildInputs = with pkgs; [ 18 | clang 19 | llvm 20 | pkg-config 21 | ]; 22 | 23 | packages = with pkgs; [ 24 | elfutils 25 | zlib 26 | openssl 27 | ]; 28 | }; 29 | } 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/template.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "template.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/template.h: -------------------------------------------------------------------------------- 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 */ 20 | --------------------------------------------------------------------------------