├── .clang-format ├── .github ├── dependabot.yml ├── docker │ └── Dockerfile.ubuntu └── workflows │ ├── build-android.yml │ └── build.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── examples ├── c │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── bootstrap.bpf.c │ ├── bootstrap.c │ ├── bootstrap.h │ ├── fentry.bpf.c │ ├── fentry.c │ ├── kprobe.bpf.c │ ├── kprobe.c │ ├── ksyscall.bpf.c │ ├── ksyscall.c │ ├── lsm.bpf.c │ ├── lsm.c │ ├── minimal.bpf.c │ ├── minimal.c │ ├── minimal_legacy.bpf.c │ ├── minimal_legacy.c │ ├── minimal_ns.bpf.c │ ├── minimal_ns.c │ ├── profile.bpf.c │ ├── profile.c │ ├── profile.h │ ├── sockfilter.bpf.c │ ├── sockfilter.c │ ├── sockfilter.h │ ├── task_iter.bpf.c │ ├── task_iter.c │ ├── task_iter.h │ ├── tc.bpf.c │ ├── tc.c │ ├── uprobe.bpf.c │ ├── uprobe.c │ ├── usdt.bpf.c │ ├── usdt.c │ └── xmake.lua └── rust │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── profile │ ├── .gitignore │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── bpf │ │ ├── profile.bpf.c │ │ └── profile.h │ │ ├── main.rs │ │ └── syscall.rs │ ├── tracecon │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── Vagrantfile │ ├── build.rs │ └── src │ │ ├── bpf │ │ └── tracecon.bpf.c │ │ └── main.rs │ └── xdp │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── bpf │ └── xdppass.bpf.c │ └── main.rs └── tools ├── cmake ├── FindBpfObject.cmake └── FindLibBpf.cmake └── gen_vmlinux_h.sh /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 11. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | AlignEscapedNewlines: Left 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | SplitEmptyFunction: true 45 | SplitEmptyRecord: true 46 | SplitEmptyNamespace: true 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | BreakBeforeInheritanceComma: false 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | BreakConstructorInitializers: BeforeComma 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 100 56 | CommentPragmas: '^ IWYU pragma:' 57 | CompactNamespaces: false 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | FixNamespaceComments: false 66 | 67 | ForEachMacros: 68 | - 'bpf__perf_for_each_map' 69 | - 'bpf__perf_for_each_map_named' 70 | - 'bpf_for_each_spilled_reg' 71 | - 'bpf_object__for_each_map' 72 | - 'bpf_object__for_each_program' 73 | - 'bpf_object__for_each_safe' 74 | - 'bpf_perf_object__for_each' 75 | - 'for_each_btf_ext_rec' 76 | - 'for_each_btf_ext_sec' 77 | - 'hashmap__for_each_entry' 78 | - 'hashmap__for_each_entry_safe' 79 | - 'hashmap__for_each_key_entry' 80 | - 'hashmap__for_each_key_entry_safe' 81 | - 'list_for_each_entry' 82 | - 'libbpf_nla_for_each_attr' 83 | - 'list_for_each_entry' 84 | - 'list_for_each_entry_continue' 85 | - 'list_for_each_entry_continue_rcu' 86 | - 'list_for_each_entry_continue_reverse' 87 | - 'list_for_each_entry_from' 88 | - 'list_for_each_entry_from_rcu' 89 | - 'list_for_each_entry_from_reverse' 90 | - 'list_for_each_entry_lockless' 91 | - 'list_for_each_entry_rcu' 92 | - 'list_for_each_entry_reverse' 93 | - 'list_for_each_entry_safe' 94 | - 'list_for_each_entry_safe_continue' 95 | - 'list_for_each_entry_safe_from' 96 | - 'list_for_each_entry_safe_reverse' 97 | - 'list_for_each_entry_srcu' 98 | 99 | 100 | IncludeBlocks: Preserve 101 | IncludeCategories: 102 | - Regex: '.*' 103 | Priority: 1 104 | IncludeIsMainRegex: '(Test)?$' 105 | IndentCaseLabels: false 106 | IndentGotoLabels: false 107 | IndentPPDirectives: None 108 | IndentWidth: 8 109 | IndentWrappedFunctionNames: false 110 | JavaScriptQuotes: Leave 111 | JavaScriptWrapImports: true 112 | KeepEmptyLinesAtTheStartOfBlocks: false 113 | MacroBlockBegin: '' 114 | MacroBlockEnd: '' 115 | MaxEmptyLinesToKeep: 1 116 | NamespaceIndentation: None 117 | ObjCBinPackProtocolList: Auto 118 | ObjCBlockIndentWidth: 8 119 | ObjCSpaceAfterProperty: true 120 | ObjCSpaceBeforeProtocolList: true 121 | 122 | # Taken from git's rules 123 | PenaltyBreakAssignment: 10 124 | PenaltyBreakBeforeFirstCallParameter: 30 125 | PenaltyBreakComment: 10 126 | PenaltyBreakFirstLessLess: 0 127 | PenaltyBreakString: 10 128 | PenaltyExcessCharacter: 100 129 | PenaltyReturnTypeOnItsOwnLine: 60 130 | 131 | PointerAlignment: Right 132 | ReflowComments: false 133 | SortIncludes: false 134 | SortUsingDeclarations: false 135 | SpaceAfterCStyleCast: false 136 | SpaceAfterTemplateKeyword: true 137 | SpaceBeforeAssignmentOperators: true 138 | SpaceBeforeCtorInitializerColon: true 139 | SpaceBeforeInheritanceColon: true 140 | SpaceBeforeParens: ControlStatementsExceptForEachMacros 141 | SpaceBeforeRangeBasedForLoopColon: true 142 | SpaceInEmptyParentheses: false 143 | SpacesBeforeTrailingComments: 1 144 | SpacesInAngles: false 145 | SpacesInContainerLiterals: false 146 | SpacesInCStyleCastParentheses: false 147 | SpacesInParentheses: false 148 | SpacesInSquareBrackets: false 149 | Standard: Cpp03 150 | TabWidth: 8 151 | UseTab: Always 152 | 153 | AlignConsecutiveMacros: AcrossEmptyLines -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: cargo 7 | versioning-strategy: auto 8 | directory: examples/rust/ 9 | allow: 10 | - dependency-name: libbpf-rs 11 | schedule: 12 | interval: daily 13 | -------------------------------------------------------------------------------- /.github/docker/Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | ARG VERSION="22.04" 2 | FROM ubuntu:${VERSION} 3 | 4 | ARG LLVM_VERSION="14" 5 | ENV LLVM_VERSION=$LLVM_VERSION 6 | 7 | ARG SHORTNAME="jammy" 8 | ENV SHORTNAME=$SHORTNAME 9 | 10 | RUN apt-get update && apt-get install -y curl gnupg 11 | RUN if [ "${LLVM_VERSION}" = "21" ]; \ 12 | then \ 13 | echo "\n\ 14 | deb http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME} main\n\ 15 | deb-src http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME} main\n\ 16 | " >> /etc/apt/sources.list;\ 17 | else \ 18 | echo "\n\ 19 | deb http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME}-${LLVM_VERSION} main\n\ 20 | deb-src http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME}-${LLVM_VERSION} main\n\ 21 | " >> /etc/apt/sources.list; \ 22 | fi 23 | RUN curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 24 | 25 | ARG DEBIAN_FRONTEND="noninteractive" 26 | ENV TZ="Etc/UTC" 27 | 28 | RUN apt-get update && apt-get install -y \ 29 | libelf-dev \ 30 | zlib1g-dev \ 31 | libbfd-dev \ 32 | clang-${LLVM_VERSION} \ 33 | libclang-${LLVM_VERSION}-dev \ 34 | libclang-common-${LLVM_VERSION}-dev \ 35 | libclang1-${LLVM_VERSION} \ 36 | llvm-${LLVM_VERSION} \ 37 | llvm-${LLVM_VERSION}-dev \ 38 | llvm-${LLVM_VERSION}-runtime \ 39 | libllvm${LLVM_VERSION} \ 40 | make cmake pkg-config \ 41 | rustc cargo rustfmt \ 42 | sudo \ 43 | && apt-get -y clean 44 | 45 | RUN ln -s /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang && ln -s /usr/bin/llvm-strip-${LLVM_VERSION} /usr/bin/llvm-strip 46 | -------------------------------------------------------------------------------- /.github/workflows/build-android.yml: -------------------------------------------------------------------------------- 1 | name: libbpf-bootstrap android build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | schedule: 9 | - cron: '7 17 * * *' 10 | 11 | jobs: 12 | build_libbpf_bootstrap_android: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | arch: [armeabi-v7a, arm64-v8a, x86_64] 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | submodules: true 22 | - uses: xmake-io/github-action-setup-xmake@v1 23 | with: 24 | xmake-version: branch@master 25 | - name: Install Dependencies 26 | run: | 27 | sudo apt-get update -y && sudo apt-get install -yqq \ 28 | build-essential clang llvm zlib1g-dev libc++-dev libc++abi-dev \ 29 | sudo \ 30 | && sudo apt-get -y clean 31 | - name: Build xmake android examples/c 32 | run: | 33 | cd examples/c && xmake f -p android -a ${{ matrix.arch }} --require-bpftool=y -y && xmake -y 34 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: libbpf-bootstrap build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | schedule: 9 | - cron: '7 17 * * *' 10 | 11 | jobs: 12 | build_libbpf_bootstrap: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | llvm: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | submodules: recursive 22 | - name: Build container (20.04) 23 | if: ${{ matrix.llvm < 14 }} 24 | uses: docker/build-push-action@v3 25 | with: 26 | push: false 27 | build-args: | 28 | LLVM_VERSION=${{ matrix.llvm }} 29 | VERSION=20.04 30 | SHORTNAME=focal 31 | file: ./.github/docker/Dockerfile.ubuntu 32 | tags: build_container 33 | - name: Build container (22.04) 34 | if: ${{ matrix.llvm >= 14 }} 35 | uses: docker/build-push-action@v3 36 | with: 37 | push: false 38 | build-args: | 39 | LLVM_VERSION=${{ matrix.llvm }} 40 | VERSION=22.04 41 | SHORTNAME=jammy 42 | file: ./.github/docker/Dockerfile.ubuntu 43 | tags: build_container 44 | - name: Build examples/c -- GNU Make 45 | run: | 46 | docker run \ 47 | -v $(pwd):/libbpf-bootstrap \ 48 | build_container \ 49 | /bin/bash -c \ 50 | 'cd /libbpf-bootstrap/examples/c && make -j`nproc`' 51 | - name: Build examples/c -- CMake 52 | run: | 53 | docker run \ 54 | -v $(pwd):/libbpf-bootstrap \ 55 | build_container \ 56 | /bin/bash -c \ 57 | 'cd /libbpf-bootstrap/examples/c && cmake ./ && make' 58 | - name: Build examples/rust 59 | run: | 60 | docker run \ 61 | -v $(pwd):/libbpf-bootstrap \ 62 | build_container \ 63 | /bin/bash -c \ 64 | 'cd /libbpf-bootstrap/examples/rust && cargo build' 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /examples/c/build 3 | /examples/c/.xmake 4 | /.idea 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 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 7 | [submodule "blazesym"] 8 | path = blazesym 9 | url = https://github.com/libbpf/blazesym.git 10 | [submodule "vmlinux.h"] 11 | path = vmlinux.h 12 | url = https://github.com/libbpf/vmlinux.h.git 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libbpf-bootstrap: demo BPF applications 2 | 3 | [![Github Actions](https://github.com/libbpf/libbpf-bootstrap/actions/workflows/build.yml/badge.svg)](https://github.com/libbpf/libbpf-bootstrap/actions/workflows/build.yml) 4 | [![Github Actions](https://github.com/libbpf/libbpf-bootstrap/actions/workflows/build-android.yml/badge.svg)](https://github.com/libbpf/libbpf-bootstrap/actions/workflows/build-android.yml) 5 | 6 | ## minimal 7 | 8 | `minimal` is just that – a minimal practical BPF application example. It 9 | doesn't use or require BPF CO-RE, so should run on quite old kernels. It 10 | installs a tracepoint handler which is triggered once every second. It uses 11 | `bpf_printk()` BPF helper to communicate with the world. To see it's output, 12 | read `/sys/kernel/debug/tracing/trace_pipe` file as a root: 13 | 14 | ```shell 15 | $ cd examples/c 16 | $ make minimal 17 | $ sudo ./minimal 18 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 19 | <...>-3840345 [010] d... 3220701.101143: bpf_trace_printk: BPF triggered from PID 3840345. 20 | <...>-3840345 [010] d... 3220702.101265: bpf_trace_printk: BPF triggered from PID 3840345. 21 | ``` 22 | 23 | `minimal` is great as a bare-bones experimental playground to quickly try out 24 | new ideas or BPF features. 25 | 26 | ## minimal_ns 27 | 28 | `minimal_ns` is as same as `minimal` but for namespaced environments. 29 | `minimal` would not work in environments that have namespace, like containers, 30 | or WSL2, because the perceived pid of the process in the namespace is not the 31 | actual pid of the process. For executing `minimal` in namespaced environments 32 | you need to use `minimal_ns` instead. 33 | 34 | ```shell 35 | $ cd examples/c 36 | $ make minimal_ns 37 | $ sudo ./minimal_ns 38 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 39 | <...>-3840345 [022] d...1 8804.331204: bpf_trace_printk: BPF triggered from PID 9087. 40 | <...>-3840345 [022] d...1 8804.331215: bpf_trace_printk: BPF triggered from PID 9087. 41 | ``` 42 | 43 | ## minimal_Legacy 44 | 45 | This version of `minimal` is modified to allow running on even older kernels 46 | that do not allow global variables. bpf_printk uses global variables unless 47 | BPF_NO_GLOBAL_DATA is defined before including bpf_helpers.h. Additionally, 48 | the global variable my_pid has been replaced with an array of one element to 49 | hold the process pid. 50 | 51 | ``` 52 | $ cd examples/c 53 | $ make minimal_legacy 54 | $ sudo ./minimal_legacy 55 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 56 | minimal_legacy-52030 [001] .... 491227.784078: 0x00000001: BPF triggered from PID 52030. 57 | minimal_legacy-52030 [001] .... 491228.840571: 0x00000001: BPF triggered from PID 52030. 58 | minimal_legacy-52030 [001] .... 491229.841643: 0x00000001: BPF triggered from PID 52030. 59 | minimal_legacy-52030 [001] .... 491230.842432: 0x00000001: BPF triggered from PID 52030. 60 | ``` 61 | 62 | ## bootstrap 63 | 64 | `bootstrap` is an example of a simple (but realistic) BPF application. It 65 | tracks process starts (`exec()` family of syscalls, to be precise) and exits 66 | and emits data about filename, PID and parent PID, as well as exit status and 67 | duration of the process life. With `-d ` you can specify 68 | minimum duration of the process to log. In such mode process start 69 | (technically, `exec()`) events are not output (see example output below). 70 | 71 | `bootstrap` was created in the similar spirit as 72 | [libbpf-tools](https://github.com/iovisor/bcc/tree/master/libbpf-tools) from 73 | BCC package, but is designed to be more stand-alone and with simpler Makefile 74 | to simplify adoption to user's particular needs. It demonstrates the use of 75 | typical BPF features: 76 | - cooperating BPF programs (tracepoint handlers for process `exec` and `exit` 77 | events, in this particular case); 78 | - BPF map for maintaining the state; 79 | - BPF ring buffer for sending data to user-space; 80 | - global variables for application behavior parameterization. 81 | - it utilizes BPF CO-RE and vmlinux.h to read extra process information from 82 | kernel's `struct task_struct`. 83 | 84 | `bootstrap` is intended to be the starting point for your own BPF application, 85 | with things like BPF CO-RE and vmlinux.h, consuming BPF ring buffer data, 86 | command line arguments parsing, graceful Ctrl-C handling, etc. all taken care 87 | of for you, which are crucial but mundane tasks that are no fun, but necessary 88 | to be able to do anything useful. Just copy/paste and do simple renaming to get 89 | yourself started. 90 | 91 | Here's an example output in minimum process duration mode: 92 | 93 | ```shell 94 | $ sudo ./bootstrap -d 50 95 | TIME EVENT COMM PID PPID FILENAME/EXIT CODE 96 | 19:18:32 EXIT timeout 3817109 402466 [0] (126ms) 97 | 19:18:32 EXIT sudo 3817117 3817111 [0] (259ms) 98 | 19:18:32 EXIT timeout 3817110 402466 [0] (264ms) 99 | 19:18:33 EXIT python3.7 3817083 1 [0] (1026ms) 100 | 19:18:38 EXIT python3 3817429 3817424 [1] (60ms) 101 | 19:18:38 EXIT sh 3817424 3817420 [0] (79ms) 102 | 19:18:38 EXIT timeout 3817420 402466 [0] (80ms) 103 | 19:18:43 EXIT timeout 3817610 402466 [0] (70ms) 104 | 19:18:43 EXIT grep 3817619 3817617 [1] (271ms) 105 | 19:18:43 EXIT timeout 3817609 402466 [0] (321ms) 106 | 19:18:44 EXIT iostat 3817585 3817531 [0] (3006ms) 107 | 19:18:44 EXIT tee 3817587 3817531 [0] (3005ms) 108 | ... 109 | ``` 110 | 111 | ## uprobe 112 | 113 | `uprobe` is an example of dealing with user-space entry and exit (return) probes, 114 | `uprobe` and `uretprobe`, in libbpf lingo. It attaches `uprobe` and `uretprobe` 115 | BPF programs to its own functions (`uprobed_add()` and `uprobed_sub()`) and logs input arguments 116 | and return result, respectively, using `bpf_printk()` macro. The user-space 117 | function is triggered once every second: 118 | 119 | ```shell 120 | $ sudo ./uprobe 121 | libbpf: loading object 'uprobe_bpf' from buffer 122 | ... 123 | Successfully started! 124 | ........... 125 | ``` 126 | 127 | You can see `uprobe` demo output in `/sys/kernel/debug/tracing/trace_pipe`: 128 | ```shell 129 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 130 | uprobe-1809291 [007] .... 4017233.106596: 0: uprobed_add ENTRY: a = 0, b = 1 131 | uprobe-1809291 [007] .... 4017233.106605: 0: uprobed_add EXIT: return = 1 132 | uprobe-1809291 [007] .... 4017233.106606: 0: uprobed_sub ENTRY: a = 0, b = 0 133 | uprobe-1809291 [007] .... 4017233.106607: 0: uprobed_sub EXIT: return = 0 134 | uprobe-1809291 [007] .... 4017234.106694: 0: uprobed_add ENTRY: a = 1, b = 2 135 | uprobe-1809291 [007] .... 4017234.106697: 0: uprobed_add EXIT: return = 3 136 | uprobe-1809291 [007] .... 4017234.106700: 0: uprobed_sub ENTRY: a = 1, b = 1 137 | uprobe-1809291 [007] .... 4017234.106701: 0: uprobed_sub EXIT: return = 0 138 | ``` 139 | 140 | ## usdt 141 | 142 | `usdt` is an example of dealing with USDT probe. It attaches USDT BPF programs to 143 | the [libc:setjmp](https://www.gnu.org/software/libc/manual/html_node/Non_002dlocal-Goto-Probes.html) probe, which is triggered by calling `setjmp` in user-space program once per second and logs USDT arguments using `bpf_printk()` macro: 144 | 145 | ```shell 146 | $ sudo ./usdt 147 | libbpf: loading object 'usdt_bpf' from buffer 148 | ... 149 | Successfully started! 150 | ........... 151 | ``` 152 | 153 | You can see `usdt` demo output in `/sys/kernel/debug/tracing/trace_pipe`: 154 | ```shell 155 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 156 | usdt-1919077 [005] d..21 537310.886092: bpf_trace_printk: USDT auto attach to libc:setjmp: arg1 = 55d03d6a42a0, arg2 = 0, arg3 = 55d03d65e54e 157 | usdt-1919077 [005] d..21 537310.886105: bpf_trace_printk: USDT manual attach to libc:setjmp: arg1 = 55d03d6a42a0, arg2 = 0, arg3 = 55d03d65e54e 158 | usdt-1919077 [005] d..21 537311.886214: bpf_trace_printk: USDT auto attach to libc:setjmp: arg1 = 55d03d6a42a0, arg2 = 0, arg3 = 55d03d65e54e 159 | usdt-1919077 [005] d..21 537311.886227: bpf_trace_printk: USDT manual attach to libc:setjmp: arg1 = 55d03d6a42a0, arg2 = 0, arg3 = 55d03d65e54e 160 | ``` 161 | 162 | ## fentry 163 | 164 | `fentry` is an example that uses fentry and fexit BPF programs for tracing. It 165 | attaches `fentry` and `fexit` traces to `do_unlinkat()` which is called when a 166 | file is deleted and logs the return value, PID, and filename to the 167 | trace pipe. 168 | 169 | Important differences, compared to kprobes, are improved performance and 170 | usability. In this example, better usability is shown with the ability to 171 | directly dereference pointer arguments, like in normal C, instead of using 172 | various read helpers. The big distinction between **fexit** and **kretprobe** 173 | programs is that fexit one has access to both input arguments and returned 174 | result, while kretprobe can only access the result. 175 | 176 | fentry and fexit programs are available starting from 5.5 kernels. 177 | 178 | ```shell 179 | $ sudo ./fentry 180 | libbpf: loading object 'fentry_bpf' from buffer 181 | ... 182 | Successfully started! 183 | .......... 184 | ``` 185 | 186 | The `fentry` output in `/sys/kernel/debug/tracing/trace_pipe` should look 187 | something like this: 188 | 189 | ```shell 190 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 191 | rm-9290 [004] d..2 4637.798698: bpf_trace_printk: fentry: pid = 9290, filename = test_file 192 | rm-9290 [004] d..2 4637.798843: bpf_trace_printk: fexit: pid = 9290, filename = test_file, ret = 0 193 | rm-9290 [004] d..2 4637.798698: bpf_trace_printk: fentry: pid = 9290, filename = test_file2 194 | rm-9290 [004] d..2 4637.798843: bpf_trace_printk: fexit: pid = 9290, filename = test_file2, ret = 0 195 | ``` 196 | 197 | ## kprobe 198 | 199 | `kprobe` is an example of dealing with kernel-space entry and exit (return) 200 | probes, `kprobe` and `kretprobe`, in libbpf lingo. It attaches `kprobe` and 201 | `kretprobe` BPF programs to the `do_unlinkat()` function and logs the PID, 202 | filename, and return result, respectively, using `bpf_printk()` macro. 203 | 204 | ```shell 205 | $ sudo ./kprobe 206 | libbpf: loading object 'kprobe_bpf' from buffer 207 | ... 208 | Successfully started! 209 | ........... 210 | ``` 211 | 212 | The `kprobe` demo output in `/sys/kernel/debug/tracing/trace_pipe` should look 213 | something like this: 214 | 215 | ```shell 216 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 217 | rm-9346 [005] d..3 4710.951696: bpf_trace_printk: KPROBE ENTRY pid = 9346, filename = test1 218 | rm-9346 [005] d..4 4710.951819: bpf_trace_printk: KPROBE EXIT: ret = 0 219 | rm-9346 [005] d..3 4710.951852: bpf_trace_printk: KPROBE ENTRY pid = 9346, filename = test2 220 | rm-9346 [005] d..4 4710.951895: bpf_trace_printk: KPROBE EXIT: ret = 0 221 | ``` 222 | 223 | ## xdp 224 | 225 | `xdp` is an example written in Rust (using libbpf-rs). It attaches to 226 | the ingress path of networking device and logs the size of each packet, 227 | returning `XDP_PASS` to allow the packet to be passed up to the kernel’s 228 | networking stack. 229 | 230 | ```shell 231 | $ sudo ./target/release/xdp 1 232 | .......... 233 | ``` 234 | 235 | The `xdp` output in `/sys/kernel/debug/tracing/trace_pipe` should look 236 | something like this: 237 | 238 | ```shell 239 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 240 | <...>-823887 [000] d.s1 602386.079100: bpf_trace_printk: packet size: 75 241 | <...>-823887 [000] d.s1 602386.079141: bpf_trace_printk: packet size: 66 242 | <...>-2813507 [000] d.s1 602386.696702: bpf_trace_printk: packet size: 77 243 | <...>-2813507 [000] d.s1 602386.696735: bpf_trace_printk: packet size: 66 244 | ``` 245 | 246 | ## tc 247 | 248 | `tc` (short for Traffic Control) is an example of handling ingress network traffics. 249 | It creates a qdisc on the `lo` interface and attaches the `tc_ingress` BPF program to it. 250 | It reports the metadata of the IP packets that coming into the `lo` interface. 251 | 252 | ```shell 253 | $ sudo ./tc 254 | ... 255 | Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF program. 256 | ...... 257 | ``` 258 | 259 | The `tc` output in `/sys/kernel/debug/tracing/trace_pipe` should look 260 | something like this: 261 | 262 | ``` 263 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 264 | node-1254811 [007] ..s1 8737831.671074: 0: Got IP packet: tot_len: 79, ttl: 64 265 | sshd-1254728 [006] ..s1 8737831.674334: 0: Got IP packet: tot_len: 79, ttl: 64 266 | sshd-1254728 [006] ..s1 8737831.674349: 0: Got IP packet: tot_len: 72, ttl: 64 267 | node-1254811 [007] ..s1 8737831.674550: 0: Got IP packet: tot_len: 71, ttl: 64 268 | ``` 269 | 270 | ## profile 271 | 272 | `profile` is an example written in Rust and C using the 273 | [`blazesym`](https://github.com/libbpf/blazesym) symbolization library. It 274 | attaches to perf events, sampling on every processor periodically. It 275 | shows addresses, symbols, file names, and line numbers of stacktraces (if 276 | available). 277 | 278 | ```shell 279 | $ sudo ./target/release/profile 280 | COMM: swapper/2 (pid=0) @ CPU 2 281 | Kernel: 282 | 0xffffffffb59141f8: mwait_idle_with_hints.constprop.0 @ 0xffffffffb59141b0+0x48 283 | 0xffffffffb5f731ce: intel_idle @ 0xffffffffb5f731b0+0x1e 284 | 0xffffffffb5c7bf09: cpuidle_enter_state @ 0xffffffffb5c7be80+0x89 285 | 0xffffffffb5c7c309: cpuidle_enter @ 0xffffffffb5c7c2e0+0x29 286 | 0xffffffffb516f57c: do_idle @ 0xffffffffb516f370+0x20c 287 | 0xffffffffb516f829: cpu_startup_entry @ 0xffffffffb516f810+0x19 288 | 0xffffffffb5075bfa: start_secondary @ 0xffffffffb5075ae0+0x11a 289 | 0xffffffffb500015a: secondary_startup_64_no_verify @ 0xffffffffb5000075+0xe5 290 | No Userspace Stack 291 | ``` 292 | 293 | C version and Rust version show the same content. Both of them use `blazesym` 294 | to symbolize stacktraces. 295 | 296 | ## sockfilter 297 | 298 | `sockfilter` is an example of monitoring packet and dealing with `__sk_buff` 299 | structure. It attaches `socket` BPF program to `sock_queue_rcv_skb()` function 300 | and retrieve information from `BPF_MAP_TYPE_RINGBUF`, then print 301 | protocol, src IP, src port, dst IP, dst port in standard output. 302 | Currently, most of the IPv4 protocols defined in `uapi/linux/in.h` are included, 303 | please check `ipproto_mapping` of `examples/c/sockfilter.c` for the supported protocols. 304 | 305 | ```shell 306 | $ sudo ./sockfilter -i 307 | interface:lo protocol: UDP 127.0.0.1:51845(src) -> 127.0.0.1:53(dst) 308 | interface:lo protocol: UDP 127.0.0.1:41552(src) -> 127.0.0.1:53(dst) 309 | ``` 310 | 311 | ## task_iter 312 | 313 | `task_iter` is an example of using [BPF Iterators](https://docs.kernel.org/bpf/bpf_iterators.html). 314 | This example iterates over all tasks on the host and gets their pid, process name, 315 | kernel stack, and their state. Note: you can use BlazeSym to symbolize the kernel stacktraces 316 | (like in `profile`) but that code is omitted for simplicity. 317 | 318 | ```shell 319 | $ sudo ./task_iter 320 | Task Info. Pid: 3647645. Process Name: TTLSFWorker59. Kernel Stack Len: 3. State: INTERRUPTIBLE 321 | Task Info. Pid: 1600495. Process Name: tmux: client. Kernel Stack Len: 6. State: INTERRUPTIBLE 322 | Task Info. Pid: 1600497. Process Name: tmux: server. Kernel Stack Len: 0. State: RUNNING 323 | Task Info. Pid: 1600498. Process Name: bash. Kernel Stack Len: 5. State: INTERRUPTIBLE 324 | ``` 325 | 326 | ## lsm 327 | `lsm` serves as an illustrative example of utilizing [LSM BPF](https://docs.kernel.org/bpf/prog_lsm.html). In this example, the `bpf()` system call is effectively blocked. Once the `lsm` program is operational, its successful execution can be confirmed by using the `bpftool prog list` command. 328 | 329 | ```shell 330 | $ sudo ./lsm 331 | libbpf: loading object 'lsm_bpf' from buffer 332 | ... 333 | Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF programs. 334 | .......... 335 | ``` 336 | 337 | The output from `lsm` in `/sys/kernel/debug/tracing/trace_pipe` is expected to resemble the following: 338 | 339 | ````shell 340 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 341 | bpftool-70646 [002] ...11 279318.416393: bpf_trace_printk: LSM: block bpf() worked 342 | bpftool-70646 [002] ...11 279318.416532: bpf_trace_printk: LSM: block bpf() worked 343 | bpftool-70646 [002] ...11 279318.416533: bpf_trace_printk: LSM: block bpf() worked 344 | ```` 345 | 346 | When the `bpf()` system call gets blocked, the `bpftool prog list` command yields the following output: 347 | 348 | ```shell 349 | $ sudo bpftool prog list 350 | Error: can't get next program: Operation not permitted 351 | ``` 352 | 353 | # Building 354 | 355 | libbpf-bootstrap supports multiple build systems that do the same thing. 356 | This serves as a cross reference for folks coming from different backgrounds. 357 | 358 | ## Install Dependencies 359 | 360 | You will need `clang` (at least v11 or later), `libelf` and `zlib` to build 361 | the examples, package names may vary across distros. 362 | 363 | On Ubuntu/Debian, you need: 364 | ```shell 365 | $ apt install clang libelf1 libelf-dev zlib1g-dev 366 | ``` 367 | 368 | On CentOS/Fedora, you need: 369 | ```shell 370 | $ dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel 371 | ``` 372 | ## Getting the source code 373 | 374 | Download the git repository and check out submodules: 375 | ```shell 376 | $ git clone --recurse-submodules https://github.com/libbpf/libbpf-bootstrap 377 | ``` 378 | 379 | ## C Examples 380 | 381 | Makefile build: 382 | 383 | ```shell 384 | $ git submodule update --init --recursive # check out libbpf 385 | $ cd examples/c 386 | $ make 387 | $ sudo ./bootstrap 388 | TIME EVENT COMM PID PPID FILENAME/EXIT CODE 389 | 00:21:22 EXIT python3.8 4032353 4032352 [0] (123ms) 390 | 00:21:22 EXEC mkdir 4032379 4032337 /usr/bin/mkdir 391 | 00:21:22 EXIT mkdir 4032379 4032337 [0] (1ms) 392 | 00:21:22 EXEC basename 4032382 4032381 /usr/bin/basename 393 | 00:21:22 EXIT basename 4032382 4032381 [0] (0ms) 394 | 00:21:22 EXEC sh 4032381 4032380 /bin/sh 395 | 00:21:22 EXEC dirname 4032384 4032381 /usr/bin/dirname 396 | 00:21:22 EXIT dirname 4032384 4032381 [0] (1ms) 397 | 00:21:22 EXEC readlink 4032387 4032386 /usr/bin/readlink 398 | ^C 399 | ``` 400 | 401 | CMake build: 402 | 403 | ```shell 404 | $ git submodule update --init --recursive # check out libbpf 405 | $ mkdir build && cd build 406 | $ cmake ../examples/c 407 | $ make 408 | $ sudo ./bootstrap 409 | <...> 410 | ``` 411 | 412 | XMake build (Linux): 413 | 414 | ```shell 415 | $ git submodule update --init --recursive # check out libbpf 416 | $ cd examples/c 417 | $ xmake 418 | $ xmake run bootstrap 419 | ``` 420 | 421 | XMake build (Android): 422 | 423 | ```shell 424 | $ git submodule update --init --recursive # check out libbpf 425 | $ cd examples/c 426 | $ xmake f -p android 427 | $ xmake 428 | ``` 429 | 430 | Install [Xmake](https://github.com/xmake-io/xmake) 431 | 432 | ```shell 433 | $ bash <(wget https://xmake.io/shget.text -O -) 434 | $ source ~/.xmake/profile 435 | ``` 436 | 437 | ## Rust Examples 438 | 439 | Install `libbpf-cargo`: 440 | ```shell 441 | $ cargo install libbpf-cargo 442 | ``` 443 | 444 | Build using `cargo`: 445 | ```shell 446 | $ cd examples/rust 447 | $ cargo build --release 448 | $ sudo ./target/release/xdp 1 449 | <...> 450 | ``` 451 | 452 | # Troubleshooting 453 | 454 | Libbpf debug logs are quire helpful to pinpoint the exact source of problems, 455 | so it's usually a good idea to look at them before starting to debug or 456 | posting question online. 457 | 458 | `./minimal` is always running with libbpf debug logs turned on. 459 | 460 | For `./bootstrap`, run it in verbose mode (`-v`) to see libbpf debug logs: 461 | 462 | ```shell 463 | $ sudo ./bootstrap -v 464 | libbpf: loading object 'bootstrap_bpf' from buffer 465 | libbpf: elf: section(2) tp/sched/sched_process_exec, size 384, link 0, flags 6, type=1 466 | libbpf: sec 'tp/sched/sched_process_exec': found program 'handle_exec' at insn offset 0 (0 bytes), code size 48 insns (384 bytes) 467 | libbpf: elf: section(3) tp/sched/sched_process_exit, size 432, link 0, flags 6, type=1 468 | libbpf: sec 'tp/sched/sched_process_exit': found program 'handle_exit' at insn offset 0 (0 bytes), code size 54 insns (432 bytes) 469 | libbpf: elf: section(4) license, size 13, link 0, flags 3, type=1 470 | libbpf: license of bootstrap_bpf is Dual BSD/GPL 471 | ... 472 | ``` 473 | -------------------------------------------------------------------------------- /examples/c/.gitignore: -------------------------------------------------------------------------------- 1 | /.output 2 | /bootstrap 3 | /minimal 4 | /minimal_legacy 5 | /minimal_ns 6 | /uprobe 7 | /kprobe 8 | /fentry 9 | /profile 10 | /usdt 11 | /sockfilter 12 | /tc 13 | /ksyscall 14 | /task_iter 15 | /lsm 16 | /cmake-build-debug/ 17 | /cmake-build-release/ 18 | -------------------------------------------------------------------------------- /examples/c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | cmake_minimum_required(VERSION 3.16) 4 | project(examples C) 5 | 6 | # Tell cmake where to find BpfObject module 7 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../tools/cmake) 8 | 9 | # Build vendored libbpf 10 | include(ExternalProject) 11 | ExternalProject_Add(libbpf 12 | PREFIX libbpf 13 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libbpf/src 14 | CONFIGURE_COMMAND "" 15 | BUILD_COMMAND make 16 | BUILD_STATIC_ONLY=1 17 | OBJDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf 18 | DESTDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf 19 | INCLUDEDIR= 20 | LIBDIR= 21 | UAPIDIR= 22 | install install_uapi_headers 23 | BUILD_IN_SOURCE TRUE 24 | INSTALL_COMMAND "" 25 | STEP_TARGETS build 26 | ) 27 | 28 | ExternalProject_Add(bpftool 29 | PREFIX bpftool 30 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../bpftool/src 31 | CONFIGURE_COMMAND "" 32 | BUILD_COMMAND make bootstrap 33 | OUTPUT=${CMAKE_CURRENT_BINARY_DIR}/bpftool/ 34 | BUILD_IN_SOURCE TRUE 35 | INSTALL_COMMAND "" 36 | STEP_TARGETS build 37 | ) 38 | 39 | find_program(CARGO_EXISTS cargo) 40 | if(CARGO_EXISTS) 41 | ExternalProject_Add(blazesym 42 | PREFIX blazesym 43 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../blazesym 44 | CONFIGURE_COMMAND "" 45 | BUILD_COMMAND cargo build --package=blazesym-c --release 46 | BUILD_IN_SOURCE TRUE 47 | INSTALL_COMMAND "" 48 | STEP_TARGETS build 49 | ) 50 | endif() 51 | 52 | # Set BpfObject input parameters -- note this is usually not necessary unless 53 | # you're in a highly vendored environment (like libbpf-bootstrap) 54 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") 55 | set(ARCH "x86") 56 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") 57 | set(ARCH "arm") 58 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") 59 | set(ARCH "arm64") 60 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ppc64le") 61 | set(ARCH "powerpc") 62 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "mips") 63 | set(ARCH "mips") 64 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "riscv64") 65 | set(ARCH "riscv") 66 | elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "loongarch64") 67 | set(ARCH "loongarch") 68 | endif() 69 | 70 | set(BPFOBJECT_BPFTOOL_EXE ${CMAKE_CURRENT_BINARY_DIR}/bpftool/bootstrap/bpftool) 71 | set(BPFOBJECT_VMLINUX_H ${CMAKE_CURRENT_SOURCE_DIR}/../../vmlinux.h/include/${ARCH}/vmlinux.h) 72 | set(LIBBPF_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/libbpf) 73 | set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf.a) 74 | find_package(BpfObject REQUIRED) 75 | 76 | # Create an executable for each application 77 | file(GLOB apps *.bpf.c) 78 | if(NOT CARGO_EXISTS) 79 | list(REMOVE_ITEM apps ${CMAKE_CURRENT_SOURCE_DIR}/profile.bpf.c) 80 | endif() 81 | foreach(app ${apps}) 82 | get_filename_component(app_stem ${app} NAME_WE) 83 | 84 | # Build object skeleton and depend skeleton on libbpf build 85 | bpf_object(${app_stem} ${app_stem}.bpf.c) 86 | add_dependencies(${app_stem}_skel libbpf-build bpftool-build) 87 | 88 | add_executable(${app_stem} ${app_stem}.c) 89 | target_link_libraries(${app_stem} ${app_stem}_skel) 90 | if(${app_stem} STREQUAL profile) 91 | target_include_directories(${app_stem} PRIVATE 92 | ${CMAKE_CURRENT_SOURCE_DIR}/../../blazesym/capi/include) 93 | target_link_libraries(${app_stem} 94 | ${CMAKE_CURRENT_SOURCE_DIR}/../../blazesym/target/release/libblazesym_c.a -lpthread -lrt -ldl) 95 | endif() 96 | endforeach() 97 | -------------------------------------------------------------------------------- /examples/c/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | OUTPUT := .output 3 | CLANG ?= clang 4 | LIBBPF_SRC := $(abspath ../../libbpf/src) 5 | BPFTOOL_SRC := $(abspath ../../bpftool/src) 6 | LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) 7 | BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) 8 | BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool 9 | LIBBLAZESYM_SRC := $(abspath ../../blazesym/) 10 | LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/capi/include) 11 | LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym_c.a) 12 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ 13 | | sed 's/arm.*/arm/' \ 14 | | sed 's/aarch64/arm64/' \ 15 | | sed 's/ppc64le/powerpc/' \ 16 | | sed 's/mips.*/mips/' \ 17 | | sed 's/riscv64/riscv/' \ 18 | | sed 's/loongarch64/loongarch/') 19 | VMLINUX := ../../vmlinux.h/include/$(ARCH)/vmlinux.h 20 | # Use our own libbpf API headers and Linux UAPI headers distributed with 21 | # libbpf to avoid dependency on system-wide headers, which could be missing or 22 | # outdated 23 | INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) 24 | CFLAGS := -g -Wall 25 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 26 | 27 | APPS = minimal minimal_legacy minimal_ns bootstrap uprobe kprobe fentry \ 28 | usdt sockfilter tc ksyscall task_iter lsm 29 | 30 | CARGO ?= $(shell which cargo) 31 | ifeq ($(strip $(CARGO)),) 32 | BZS_APPS := 33 | else 34 | BZS_APPS := profile 35 | APPS += $(BZS_APPS) 36 | # Required by libblazesym 37 | ALL_LDFLAGS += -lrt -ldl -lpthread -lm 38 | endif 39 | 40 | # Get Clang's default includes on this system. We'll explicitly add these dirs 41 | # to the includes list when compiling with `-target bpf` because otherwise some 42 | # architecture-specific dirs will be "missing" on some architectures/distros - 43 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 44 | # sys/cdefs.h etc. might be missing. 45 | # 46 | # Use '-idirafter': Don't interfere with include mechanics except where the 47 | # build would have failed anyways. 48 | CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - &1 \ 49 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 50 | 51 | ifeq ($(V),1) 52 | Q = 53 | msg = 54 | else 55 | Q = @ 56 | msg = @printf ' %-8s %s%s\n' \ 57 | "$(1)" \ 58 | "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ 59 | "$(if $(3), $(3))"; 60 | MAKEFLAGS += --no-print-directory 61 | endif 62 | 63 | define allow-override 64 | $(if $(or $(findstring environment,$(origin $(1))),\ 65 | $(findstring command line,$(origin $(1)))),,\ 66 | $(eval $(1) = $(2))) 67 | endef 68 | 69 | $(call allow-override,CC,$(CROSS_COMPILE)cc) 70 | $(call allow-override,LD,$(CROSS_COMPILE)ld) 71 | 72 | .PHONY: all 73 | all: $(APPS) 74 | 75 | .PHONY: clean 76 | clean: 77 | $(call msg,CLEAN) 78 | $(Q)rm -rf $(OUTPUT) $(APPS) 79 | 80 | $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): 81 | $(call msg,MKDIR,$@) 82 | $(Q)mkdir -p $@ 83 | 84 | # Build libbpf 85 | $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf 86 | $(call msg,LIB,$@) 87 | $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ 88 | OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ 89 | INCLUDEDIR= LIBDIR= UAPIDIR= \ 90 | install 91 | 92 | # Build bpftool 93 | $(BPFTOOL): | $(BPFTOOL_OUTPUT) 94 | $(call msg,BPFTOOL,$@) 95 | $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap 96 | 97 | 98 | $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a:: 99 | $(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --package=blazesym-c --release 100 | 101 | $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a | $(OUTPUT) 102 | $(call msg,LIB, $@) 103 | $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym_c.a $@ 104 | 105 | # Build BPF code 106 | $(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) 107 | $(call msg,BPF,$@) 108 | $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ 109 | $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ 110 | -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) 111 | $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) 112 | 113 | # Generate BPF skeletons 114 | $(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) 115 | $(call msg,GEN-SKEL,$@) 116 | $(Q)$(BPFTOOL) gen skeleton $< > $@ 117 | 118 | # Build user-space code 119 | $(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h 120 | 121 | $(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) 122 | $(call msg,CC,$@) 123 | $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ 124 | 125 | $(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_OBJ) 126 | 127 | $(BZS_APPS): $(LIBBLAZESYM_OBJ) 128 | 129 | # Build application binary 130 | $(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) 131 | $(call msg,BINARY,$@) 132 | $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ 133 | 134 | # delete failed targets 135 | .DELETE_ON_ERROR: 136 | 137 | # keep intermediate (.skel.h, .bpf.o, etc) targets 138 | .SECONDARY: 139 | -------------------------------------------------------------------------------- /examples/c/bootstrap.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 "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 | -------------------------------------------------------------------------------- /examples/c/bootstrap.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 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 = ""; 19 | const char argp_program_doc[] = "BPF bootstrap demo application.\n" 20 | "\n" 21 | "It traces process start and exits and shows associated \n" 22 | "information (filename, process duration, PID and PPID, etc).\n" 23 | "\n" 24 | "USAGE: ./bootstrap [-d ] [-v]\n"; 25 | 26 | static const struct argp_option opts[] = { 27 | { "verbose", 'v', NULL, 0, "Verbose debug output" }, 28 | { "duration", 'd', "DURATION-MS", 0, "Minimum process duration (ms) to report" }, 29 | {}, 30 | }; 31 | 32 | static error_t parse_arg(int key, char *arg, struct argp_state *state) 33 | { 34 | switch (key) { 35 | case 'v': 36 | env.verbose = true; 37 | break; 38 | case 'd': 39 | errno = 0; 40 | env.min_duration_ms = strtol(arg, NULL, 10); 41 | if (errno || env.min_duration_ms <= 0) { 42 | fprintf(stderr, "Invalid duration: %s\n", arg); 43 | argp_usage(state); 44 | } 45 | break; 46 | case ARGP_KEY_ARG: 47 | argp_usage(state); 48 | break; 49 | default: 50 | return ARGP_ERR_UNKNOWN; 51 | } 52 | return 0; 53 | } 54 | 55 | static const struct argp argp = { 56 | .options = opts, 57 | .parser = parse_arg, 58 | .doc = argp_program_doc, 59 | }; 60 | 61 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 62 | { 63 | if (level == LIBBPF_DEBUG && !env.verbose) 64 | return 0; 65 | return vfprintf(stderr, format, args); 66 | } 67 | 68 | static volatile bool exiting = false; 69 | 70 | static void sig_handler(int sig) 71 | { 72 | exiting = true; 73 | } 74 | 75 | static int handle_event(void *ctx, void *data, size_t data_sz) 76 | { 77 | const struct event *e = data; 78 | struct tm *tm; 79 | char ts[32]; 80 | time_t t; 81 | 82 | time(&t); 83 | tm = localtime(&t); 84 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 85 | 86 | if (e->exit_event) { 87 | printf("%-8s %-5s %-16s %-7d %-7d [%u]", ts, "EXIT", e->comm, e->pid, e->ppid, 88 | e->exit_code); 89 | if (e->duration_ns) 90 | printf(" (%llums)", e->duration_ns / 1000000); 91 | printf("\n"); 92 | } else { 93 | printf("%-8s %-5s %-16s %-7d %-7d %s\n", ts, "EXEC", e->comm, e->pid, e->ppid, 94 | e->filename); 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | int main(int argc, char **argv) 101 | { 102 | struct ring_buffer *rb = NULL; 103 | struct bootstrap_bpf *skel; 104 | int err; 105 | 106 | /* Parse command line arguments */ 107 | err = argp_parse(&argp, argc, argv, 0, NULL, NULL); 108 | if (err) 109 | return err; 110 | 111 | /* Set up libbpf errors and debug info callback */ 112 | libbpf_set_print(libbpf_print_fn); 113 | 114 | /* Cleaner handling of Ctrl-C */ 115 | signal(SIGINT, sig_handler); 116 | signal(SIGTERM, sig_handler); 117 | 118 | /* Load and verify BPF application */ 119 | skel = bootstrap_bpf__open(); 120 | if (!skel) { 121 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 122 | return 1; 123 | } 124 | 125 | /* Parameterize BPF code with minimum duration parameter */ 126 | skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL; 127 | 128 | /* Load & verify BPF programs */ 129 | err = bootstrap_bpf__load(skel); 130 | if (err) { 131 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 132 | goto cleanup; 133 | } 134 | 135 | /* Attach tracepoints */ 136 | err = bootstrap_bpf__attach(skel); 137 | if (err) { 138 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 139 | goto cleanup; 140 | } 141 | 142 | /* Set up ring buffer polling */ 143 | rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); 144 | if (!rb) { 145 | err = -1; 146 | fprintf(stderr, "Failed to create ring buffer\n"); 147 | goto cleanup; 148 | } 149 | 150 | /* Process events */ 151 | printf("%-8s %-5s %-16s %-7s %-7s %s\n", "TIME", "EVENT", "COMM", "PID", "PPID", 152 | "FILENAME/EXIT CODE"); 153 | while (!exiting) { 154 | err = ring_buffer__poll(rb, 100 /* timeout, ms */); 155 | /* Ctrl-C will cause -EINTR */ 156 | if (err == -EINTR) { 157 | err = 0; 158 | break; 159 | } 160 | if (err < 0) { 161 | printf("Error polling perf buffer: %d\n", err); 162 | break; 163 | } 164 | } 165 | 166 | cleanup: 167 | /* Clean up */ 168 | ring_buffer__free(rb); 169 | bootstrap_bpf__destroy(skel); 170 | 171 | return err < 0 ? -err : 0; 172 | } 173 | -------------------------------------------------------------------------------- /examples/c/bootstrap.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 | -------------------------------------------------------------------------------- /examples/c/fentry.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | 7 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 8 | 9 | SEC("fentry/do_unlinkat") 10 | int BPF_PROG(do_unlinkat, int dfd, struct filename *name) 11 | { 12 | pid_t pid; 13 | 14 | pid = bpf_get_current_pid_tgid() >> 32; 15 | bpf_printk("fentry: pid = %d, filename = %s\n", pid, name->name); 16 | return 0; 17 | } 18 | 19 | SEC("fexit/do_unlinkat") 20 | int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret) 21 | { 22 | pid_t pid; 23 | 24 | pid = bpf_get_current_pid_tgid() >> 32; 25 | bpf_printk("fexit: pid = %d, filename = %s, ret = %ld\n", pid, name->name, ret); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /examples/c/fentry.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "fentry.skel.h" 13 | 14 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 15 | { 16 | return vfprintf(stderr, format, args); 17 | } 18 | 19 | static volatile sig_atomic_t stop; 20 | 21 | void sig_int(int signo) 22 | { 23 | stop = 1; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | struct fentry_bpf *skel; 29 | int err; 30 | 31 | /* Set up libbpf errors and debug info callback */ 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | /* Open load and verify BPF application */ 35 | skel = fentry_bpf__open_and_load(); 36 | if (!skel) { 37 | fprintf(stderr, "Failed to open BPF skeleton\n"); 38 | return 1; 39 | } 40 | 41 | /* Attach tracepoint handler */ 42 | err = fentry_bpf__attach(skel); 43 | if (err) { 44 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 45 | goto cleanup; 46 | } 47 | 48 | if (signal(SIGINT, sig_int) == SIG_ERR) { 49 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | while (!stop) { 57 | fprintf(stderr, "."); 58 | sleep(1); 59 | } 60 | 61 | cleanup: 62 | fentry_bpf__destroy(skel); 63 | return -err; 64 | } 65 | -------------------------------------------------------------------------------- /examples/c/kprobe.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | SEC("kprobe/do_unlinkat") 11 | int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name) 12 | { 13 | pid_t pid; 14 | const char *filename; 15 | 16 | pid = bpf_get_current_pid_tgid() >> 32; 17 | filename = BPF_CORE_READ(name, name); 18 | bpf_printk("KPROBE ENTRY pid = %d, filename = %s\n", pid, filename); 19 | return 0; 20 | } 21 | 22 | SEC("kretprobe/do_unlinkat") 23 | int BPF_KRETPROBE(do_unlinkat_exit, long ret) 24 | { 25 | pid_t pid; 26 | 27 | pid = bpf_get_current_pid_tgid() >> 32; 28 | bpf_printk("KPROBE EXIT: pid = %d, ret = %ld\n", pid, ret); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /examples/c/kprobe.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Sartura 3 | * Based on minimal.c by Facebook */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "kprobe.skel.h" 13 | 14 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 15 | { 16 | return vfprintf(stderr, format, args); 17 | } 18 | 19 | static volatile sig_atomic_t stop; 20 | 21 | static void sig_int(int signo) 22 | { 23 | stop = 1; 24 | } 25 | 26 | int main(int argc, char **argv) 27 | { 28 | struct kprobe_bpf *skel; 29 | int err; 30 | 31 | /* Set up libbpf errors and debug info callback */ 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | /* Open load and verify BPF application */ 35 | skel = kprobe_bpf__open_and_load(); 36 | if (!skel) { 37 | fprintf(stderr, "Failed to open BPF skeleton\n"); 38 | return 1; 39 | } 40 | 41 | /* Attach tracepoint handler */ 42 | err = kprobe_bpf__attach(skel); 43 | if (err) { 44 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 45 | goto cleanup; 46 | } 47 | 48 | if (signal(SIGINT, sig_int) == SIG_ERR) { 49 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | while (!stop) { 57 | fprintf(stderr, "."); 58 | sleep(1); 59 | } 60 | 61 | cleanup: 62 | kprobe_bpf__destroy(skel); 63 | return -err; 64 | } 65 | -------------------------------------------------------------------------------- /examples/c/ksyscall.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define TASK_COMM_LEN 16 7 | 8 | SEC("ksyscall/tgkill") 9 | int BPF_KSYSCALL(tgkill_entry, pid_t tgid, pid_t tid, int sig) 10 | { 11 | char comm[TASK_COMM_LEN]; 12 | __u32 caller_pid = bpf_get_current_pid_tgid() >> 32; 13 | 14 | if (sig == 0) { 15 | /* 16 | If sig is 0, then no signal is sent, but existence and permission 17 | checks are still performed; this can be used to check for the 18 | existence of a process ID or process group ID that the caller is 19 | permitted to signal. 20 | */ 21 | return 0; 22 | } 23 | 24 | bpf_get_current_comm(&comm, sizeof(comm)); 25 | bpf_printk( 26 | "tgkill syscall called by PID %d (%s) for thread id %d with pid %d and signal %d.", 27 | caller_pid, comm, tid, tgid, sig); 28 | return 0; 29 | } 30 | 31 | SEC("ksyscall/kill") 32 | int BPF_KSYSCALL(entry_probe, pid_t pid, int sig) 33 | { 34 | char comm[TASK_COMM_LEN]; 35 | __u32 caller_pid = bpf_get_current_pid_tgid() >> 32; 36 | 37 | if (sig == 0) { 38 | /* 39 | If sig is 0, then no signal is sent, but existence and permission 40 | checks are still performed; this can be used to check for the 41 | existence of a process ID or process group ID that the caller is 42 | permitted to signal. 43 | */ 44 | return 0; 45 | } 46 | 47 | bpf_get_current_comm(&comm, sizeof(comm)); 48 | bpf_printk("KILL syscall called by PID %d (%s) for PID %d with signal %d.", caller_pid, 49 | comm, pid, sig); 50 | return 0; 51 | } 52 | 53 | char _license[] SEC("license") = "GPL"; 54 | -------------------------------------------------------------------------------- /examples/c/ksyscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ksyscall.skel.h" 9 | 10 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 11 | { 12 | return vfprintf(stderr, format, args); 13 | } 14 | 15 | static volatile sig_atomic_t stop; 16 | 17 | static void sig_int(int signo) 18 | { 19 | stop = 1; 20 | } 21 | 22 | int main(int argc, char **argv) 23 | { 24 | struct ksyscall_bpf *skel; 25 | int err; 26 | 27 | /* Set up libbpf errors and debug info callback */ 28 | libbpf_set_print(libbpf_print_fn); 29 | 30 | /* Open load and verify BPF application */ 31 | skel = ksyscall_bpf__open_and_load(); 32 | if (!skel) { 33 | fprintf(stderr, "Failed to open BPF skeleton\n"); 34 | return 1; 35 | } 36 | 37 | /* Attach tracepoint handler */ 38 | err = ksyscall_bpf__attach(skel); 39 | if (err) { 40 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 41 | goto cleanup; 42 | } 43 | 44 | if (signal(SIGINT, sig_int) == SIG_ERR) { 45 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 46 | goto cleanup; 47 | } 48 | 49 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 50 | "to see output of the BPF programs.\n"); 51 | 52 | while (!stop) { 53 | fprintf(stderr, "."); 54 | sleep(1); 55 | } 56 | 57 | cleanup: 58 | ksyscall_bpf__destroy(skel); 59 | return -err; 60 | } 61 | -------------------------------------------------------------------------------- /examples/c/lsm.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | 5 | char LICENSE[] SEC("license") = "GPL"; 6 | 7 | #define EPERM 1 8 | 9 | SEC("lsm/bpf") 10 | int BPF_PROG(lsm_bpf, int cmd, union bpf_attr *attr, unsigned int size, int ret) 11 | { 12 | /* ret is the return value from the previous BPF program 13 | * or 0 if it's the first hook. 14 | */ 15 | if (ret != 0) 16 | return ret; 17 | 18 | bpf_printk("LSM: block bpf() worked"); 19 | return -EPERM; 20 | } 21 | -------------------------------------------------------------------------------- /examples/c/lsm.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2024 David Di */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "lsm.skel.h" 8 | 9 | /* Notice: Ensure your kernel version is 5.7 or higher, BTF (BPF Type Format) is enabled, 10 | * and the file '/sys/kernel/security/lsm' includes 'bpf'. 11 | */ 12 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 13 | { 14 | return vfprintf(stderr, format, args); 15 | } 16 | 17 | int main(int argc, char **argv) 18 | { 19 | struct lsm_bpf *skel; 20 | int err; 21 | 22 | /* Set up libbpf errors and debug info callback */ 23 | libbpf_set_print(libbpf_print_fn); 24 | 25 | /* Open, load, and verify BPF application */ 26 | skel = lsm_bpf__open_and_load(); 27 | if (!skel) { 28 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 29 | goto cleanup; 30 | } 31 | 32 | /* Attach lsm handler */ 33 | err = lsm_bpf__attach(skel); 34 | if (err) { 35 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 36 | goto cleanup; 37 | } 38 | 39 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 40 | "to see output of the BPF programs.\n"); 41 | 42 | for (;;) { 43 | /* trigger our BPF program */ 44 | fprintf(stderr, "."); 45 | sleep(1); 46 | } 47 | 48 | cleanup: 49 | lsm_bpf__destroy(skel); 50 | return -err; 51 | } 52 | -------------------------------------------------------------------------------- /examples/c/minimal.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | 6 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 7 | 8 | int my_pid = 0; 9 | 10 | SEC("tp/syscalls/sys_enter_write") 11 | int handle_tp(void *ctx) 12 | { 13 | int pid = bpf_get_current_pid_tgid() >> 32; 14 | 15 | if (pid != my_pid) 16 | return 0; 17 | 18 | bpf_printk("BPF triggered from PID %d.\n", pid); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /examples/c/minimal.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "minimal.skel.h" 8 | 9 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 10 | { 11 | return vfprintf(stderr, format, args); 12 | } 13 | 14 | int main(int argc, char **argv) 15 | { 16 | struct minimal_bpf *skel; 17 | int err; 18 | 19 | /* Set up libbpf errors and debug info callback */ 20 | libbpf_set_print(libbpf_print_fn); 21 | 22 | /* Open BPF application */ 23 | skel = minimal_bpf__open(); 24 | if (!skel) { 25 | fprintf(stderr, "Failed to open BPF skeleton\n"); 26 | return 1; 27 | } 28 | 29 | /* ensure BPF program only handles write() syscalls from our process */ 30 | skel->bss->my_pid = getpid(); 31 | 32 | /* Load & verify BPF programs */ 33 | err = minimal_bpf__load(skel); 34 | if (err) { 35 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 36 | goto cleanup; 37 | } 38 | 39 | /* Attach tracepoint handler */ 40 | err = minimal_bpf__attach(skel); 41 | if (err) { 42 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 43 | goto cleanup; 44 | } 45 | 46 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 47 | "to see output of the BPF programs.\n"); 48 | 49 | for (;;) { 50 | /* trigger our BPF program */ 51 | fprintf(stderr, "."); 52 | sleep(1); 53 | } 54 | 55 | cleanup: 56 | minimal_bpf__destroy(skel); 57 | return -err; 58 | } 59 | -------------------------------------------------------------------------------- /examples/c/minimal_legacy.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #define BPF_NO_GLOBAL_DATA 3 | #include 4 | #include 5 | #include 6 | 7 | typedef unsigned int u32; 8 | typedef int pid_t; 9 | 10 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 11 | 12 | /* Create an array with 1 entry instead of a global variable 13 | * which does not work with older kernels */ 14 | struct { 15 | __uint(type, BPF_MAP_TYPE_ARRAY); 16 | __uint(max_entries, 1); 17 | __type(key, u32); 18 | __type(value, pid_t); 19 | } my_pid_map SEC(".maps"); 20 | 21 | SEC("tp/syscalls/sys_enter_write") 22 | int handle_tp(void *ctx) 23 | { 24 | u32 index = 0; 25 | pid_t pid = bpf_get_current_pid_tgid() >> 32; 26 | pid_t *my_pid = bpf_map_lookup_elem(&my_pid_map, &index); 27 | 28 | if (!my_pid || *my_pid != pid) 29 | return 1; 30 | 31 | bpf_printk("BPF triggered from PID %d.\n", pid); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /examples/c/minimal_legacy.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "minimal_legacy.skel.h" 7 | 8 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 9 | { 10 | return vfprintf(stderr, format, args); 11 | } 12 | 13 | int main(int argc, char **argv) 14 | { 15 | struct minimal_legacy_bpf *skel; 16 | int err; 17 | pid_t pid; 18 | unsigned index = 0; 19 | 20 | /* Set up libbpf errors and debug info callback */ 21 | libbpf_set_print(libbpf_print_fn); 22 | 23 | /* Load and verify BPF application */ 24 | skel = minimal_legacy_bpf__open_and_load(); 25 | if (!skel) { 26 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 27 | return 1; 28 | } 29 | 30 | /* ensure BPF program only handles write() syscalls from our process */ 31 | pid = getpid(); 32 | err = bpf_map__update_elem(skel->maps.my_pid_map, &index, sizeof(index), &pid, 33 | sizeof(pid_t), BPF_ANY); 34 | if (err < 0) { 35 | fprintf(stderr, "Error updating map with pid: %s\n", strerror(err)); 36 | goto cleanup; 37 | } 38 | 39 | /* Attach tracepoint handler */ 40 | err = minimal_legacy_bpf__attach(skel); 41 | if (err) { 42 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 43 | goto cleanup; 44 | } 45 | 46 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 47 | "to see output of the BPF programs.\n"); 48 | 49 | for (;;) { 50 | /* trigger our BPF program */ 51 | fprintf(stderr, "."); 52 | sleep(1); 53 | } 54 | 55 | cleanup: 56 | minimal_legacy_bpf__destroy(skel); 57 | return -err; 58 | } 59 | -------------------------------------------------------------------------------- /examples/c/minimal_ns.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2023 Hosein Bakhtiari */ 3 | #include 4 | #include 5 | #include 6 | 7 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 8 | 9 | int my_pid = 0; 10 | unsigned long long dev; 11 | unsigned long long ino; 12 | 13 | SEC("tp/syscalls/sys_enter_write") 14 | int handle_tp(void *ctx) 15 | { 16 | struct bpf_pidns_info ns; 17 | 18 | bpf_get_ns_current_pid_tgid(dev, ino, &ns, sizeof(ns)); 19 | if (ns.pid != my_pid) 20 | return 0; 21 | 22 | bpf_printk("BPF triggered from PID %d.\n", ns.pid); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /examples/c/minimal_ns.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2023 Hosein Bakhtiari */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "minimal_ns.skel.h" 8 | 9 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 10 | { 11 | return vfprintf(stderr, format, args); 12 | } 13 | 14 | int main(int argc, char **argv) 15 | { 16 | struct minimal_ns_bpf *skel; 17 | int err; 18 | struct stat sb; 19 | 20 | /* Set up libbpf errors and debug info callback */ 21 | libbpf_set_print(libbpf_print_fn); 22 | 23 | /* Open BPF application */ 24 | skel = minimal_ns_bpf__open(); 25 | if (!skel) { 26 | fprintf(stderr, "Failed to open BPF skeleton\n"); 27 | return 1; 28 | } 29 | 30 | /* ensure BPF program only handles write() syscalls from our process */ 31 | if (stat("/proc/self/ns/pid", &sb) == -1) { 32 | fprintf(stderr, "Failed to acquire namespace information"); 33 | return 1; 34 | } 35 | skel->bss->dev = sb.st_dev; 36 | skel->bss->ino = sb.st_ino; 37 | skel->bss->my_pid = getpid(); 38 | 39 | /* Load & verify BPF programs */ 40 | err = minimal_ns_bpf__load(skel); 41 | if (err) { 42 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 43 | goto cleanup; 44 | } 45 | 46 | /* Attach tracepoint handler */ 47 | err = minimal_ns_bpf__attach(skel); 48 | if (err) { 49 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 50 | goto cleanup; 51 | } 52 | 53 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 54 | "to see output of the BPF programs.\n"); 55 | 56 | for (;;) { 57 | /* trigger our BPF program */ 58 | fprintf(stderr, "."); 59 | sleep(1); 60 | } 61 | 62 | cleanup: 63 | minimal_ns_bpf__destroy(skel); 64 | return -err; 65 | } 66 | -------------------------------------------------------------------------------- /examples/c/profile.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #include "profile.h" 9 | 10 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 11 | 12 | struct { 13 | __uint(type, BPF_MAP_TYPE_RINGBUF); 14 | __uint(max_entries, 256 * 1024); 15 | } events SEC(".maps"); 16 | 17 | SEC("perf_event") 18 | int profile(void *ctx) 19 | { 20 | int pid = bpf_get_current_pid_tgid() >> 32; 21 | int cpu_id = bpf_get_smp_processor_id(); 22 | struct stacktrace_event *event; 23 | int cp; 24 | 25 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 26 | if (!event) 27 | return 1; 28 | 29 | event->pid = pid; 30 | event->cpu_id = cpu_id; 31 | 32 | if (bpf_get_current_comm(event->comm, sizeof(event->comm))) 33 | event->comm[0] = 0; 34 | 35 | event->kstack_sz = bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); 36 | 37 | event->ustack_sz = 38 | bpf_get_stack(ctx, event->ustack, sizeof(event->ustack), BPF_F_USER_STACK); 39 | 40 | bpf_ringbuf_submit(event, 0); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /examples/c/profile.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "profile.skel.h" 17 | #include "profile.h" 18 | #include "blazesym.h" 19 | 20 | /* 21 | * This function is from libbpf, but it is not a public API and can only be 22 | * used for demonstration. We can use this here because we statically link 23 | * against the libbpf built from submodule during build. 24 | */ 25 | extern int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz); 26 | 27 | static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, 28 | unsigned long flags) 29 | { 30 | int ret; 31 | 32 | ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); 33 | return ret; 34 | } 35 | 36 | static struct blaze_symbolizer *symbolizer; 37 | 38 | static void print_frame(const char *name, uintptr_t input_addr, uintptr_t addr, uint64_t offset, const blaze_symbolize_code_info* code_info) 39 | { 40 | /* If we have an input address we have a new symbol. */ 41 | if (input_addr != 0) { 42 | printf("%016lx: %s @ 0x%lx+0x%lx", input_addr, name, addr, offset); 43 | if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { 44 | printf(" %s/%s:%u\n", code_info->dir, code_info->file, code_info->line); 45 | } else if (code_info != NULL && code_info->file != NULL) { 46 | printf(" %s:%u\n", code_info->file, code_info->line); 47 | } else { 48 | printf("\n"); 49 | } 50 | } else { 51 | printf("%16s %s", "", name); 52 | if (code_info != NULL && code_info->dir != NULL && code_info->file != NULL) { 53 | printf("@ %s/%s:%u [inlined]\n", code_info->dir, code_info->file, code_info->line); 54 | } else if (code_info != NULL && code_info->file != NULL) { 55 | printf("@ %s:%u [inlined]\n", code_info->file, code_info->line); 56 | } else { 57 | printf("[inlined]\n"); 58 | } 59 | } 60 | } 61 | 62 | static void show_stack_trace(__u64 *stack, int stack_sz, pid_t pid) 63 | { 64 | const struct blaze_symbolize_inlined_fn* inlined; 65 | const struct blaze_syms *syms; 66 | const struct blaze_sym *sym; 67 | int i, j; 68 | 69 | assert(sizeof(uintptr_t) == sizeof(uint64_t)); 70 | 71 | if (pid) { 72 | struct blaze_symbolize_src_process src = { 73 | .type_size = sizeof(src), 74 | .pid = pid, 75 | }; 76 | 77 | syms = blaze_symbolize_process_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); 78 | } else { 79 | struct blaze_symbolize_src_kernel src = { 80 | .type_size = sizeof(src), 81 | }; 82 | 83 | syms = blaze_symbolize_kernel_abs_addrs(symbolizer, &src, (const uintptr_t *)stack, stack_sz); 84 | } 85 | 86 | if (!syms) { 87 | printf(" failed to symbolize addresses: %s\n", blaze_err_str(blaze_err_last())); 88 | return; 89 | } 90 | 91 | for (i = 0; i < stack_sz; i++) { 92 | if (!syms || syms->cnt <= i || syms->syms[i].name == NULL) { 93 | printf("%016llx: \n", stack[i]); 94 | continue; 95 | } 96 | 97 | sym = &syms->syms[i]; 98 | print_frame(sym->name, stack[i], sym->addr, sym->offset, &sym->code_info); 99 | 100 | for (j = 0; j < sym->inlined_cnt; j++) { 101 | inlined = &sym->inlined[j]; 102 | print_frame(inlined->name, 0, 0, 0, &inlined->code_info); 103 | } 104 | } 105 | 106 | blaze_syms_free(syms); 107 | } 108 | 109 | /* Receive events from the ring buffer. */ 110 | static int event_handler(void *_ctx, void *data, size_t size) 111 | { 112 | struct stacktrace_event *event = data; 113 | 114 | if (event->kstack_sz <= 0 && event->ustack_sz <= 0) 115 | return 1; 116 | 117 | printf("COMM: %s (pid=%d) @ CPU %d\n", event->comm, event->pid, event->cpu_id); 118 | 119 | if (event->kstack_sz > 0) { 120 | printf("Kernel:\n"); 121 | show_stack_trace(event->kstack, event->kstack_sz / sizeof(__u64), 0); 122 | } else { 123 | printf("No Kernel Stack\n"); 124 | } 125 | 126 | if (event->ustack_sz > 0) { 127 | printf("Userspace:\n"); 128 | show_stack_trace(event->ustack, event->ustack_sz / sizeof(__u64), event->pid); 129 | } else { 130 | printf("No Userspace Stack\n"); 131 | } 132 | 133 | printf("\n"); 134 | return 0; 135 | } 136 | 137 | static void show_help(const char *progname) 138 | { 139 | printf("Usage: %s [-f ] [--sw-event] [-h]\n", progname); 140 | printf("Options:\n"); 141 | printf(" -f Sampling frequency [default: 1]\n"); 142 | printf(" --sw-event Use software event for triggering stack trace capture\n"); 143 | printf(" -h Print help\n"); 144 | } 145 | 146 | int main(int argc, char *const argv[]) 147 | { 148 | const char *online_cpus_file = "/sys/devices/system/cpu/online"; 149 | int freq = 1, sw_event = 0, pid = -1, cpu; 150 | struct profile_bpf *skel = NULL; 151 | struct perf_event_attr attr; 152 | struct bpf_link **links = NULL; 153 | struct ring_buffer *ring_buf = NULL; 154 | int num_cpus, num_online_cpus; 155 | int *pefds = NULL, pefd; 156 | int argp, i, err = 0; 157 | bool *online_mask = NULL; 158 | 159 | static struct option long_options[] = { 160 | {"sw-event", no_argument, 0, 's'}, 161 | {0, 0, 0, 0} 162 | }; 163 | 164 | while ((argp = getopt_long(argc, argv, "hf:", long_options, NULL)) != -1) { 165 | switch (argp) { 166 | case 'f': 167 | freq = atoi(optarg); 168 | if (freq < 1) 169 | freq = 1; 170 | break; 171 | case 's': 172 | sw_event = 1; 173 | break; 174 | 175 | case 'h': 176 | default: 177 | show_help(argv[0]); 178 | return 1; 179 | } 180 | } 181 | 182 | err = parse_cpu_mask_file(online_cpus_file, &online_mask, &num_online_cpus); 183 | if (err) { 184 | fprintf(stderr, "Fail to get online CPU numbers: %d\n", err); 185 | goto cleanup; 186 | } 187 | 188 | num_cpus = libbpf_num_possible_cpus(); 189 | if (num_cpus <= 0) { 190 | fprintf(stderr, "Fail to get the number of processors\n"); 191 | err = -1; 192 | goto cleanup; 193 | } 194 | 195 | skel = profile_bpf__open_and_load(); 196 | if (!skel) { 197 | fprintf(stderr, "Fail to open and load BPF skeleton\n"); 198 | err = -1; 199 | goto cleanup; 200 | } 201 | 202 | symbolizer = blaze_symbolizer_new(); 203 | if (!symbolizer) { 204 | fprintf(stderr, "Fail to create a symbolizer\n"); 205 | err = -1; 206 | goto cleanup; 207 | } 208 | 209 | /* Prepare ring buffer to receive events from the BPF program. */ 210 | ring_buf = ring_buffer__new(bpf_map__fd(skel->maps.events), event_handler, NULL, NULL); 211 | if (!ring_buf) { 212 | err = -1; 213 | goto cleanup; 214 | } 215 | 216 | pefds = malloc(num_cpus * sizeof(int)); 217 | for (i = 0; i < num_cpus; i++) { 218 | pefds[i] = -1; 219 | } 220 | 221 | links = calloc(num_cpus, sizeof(struct bpf_link *)); 222 | 223 | memset(&attr, 0, sizeof(attr)); 224 | attr.type = sw_event ? PERF_TYPE_SOFTWARE : PERF_TYPE_HARDWARE; 225 | attr.size = sizeof(attr); 226 | attr.config = sw_event ? PERF_COUNT_SW_CPU_CLOCK : PERF_COUNT_HW_CPU_CYCLES; 227 | attr.sample_freq = freq; 228 | attr.freq = 1; 229 | 230 | for (cpu = 0; cpu < num_cpus; cpu++) { 231 | /* skip offline/not present CPUs */ 232 | if (cpu >= num_online_cpus || !online_mask[cpu]) 233 | continue; 234 | 235 | /* Set up performance monitoring on a CPU/Core */ 236 | pefd = perf_event_open(&attr, pid, cpu, -1, PERF_FLAG_FD_CLOEXEC); 237 | if (pefd < 0) { 238 | if (!sw_event && errno == ENOENT) { 239 | fprintf(stderr, 240 | "Fail to set up performance monitor on a CPU/Core.\n" 241 | "Try running the profile example with the `--sw-event` option.\n"); 242 | } else { 243 | fprintf(stderr, "Fail to set up performance monitor on a CPU/Core.\n"); 244 | } 245 | err = -1; 246 | goto cleanup; 247 | } 248 | pefds[cpu] = pefd; 249 | 250 | /* Attach a BPF program on a CPU */ 251 | links[cpu] = bpf_program__attach_perf_event(skel->progs.profile, pefd); 252 | if (!links[cpu]) { 253 | err = -1; 254 | goto cleanup; 255 | } 256 | } 257 | 258 | /* Wait and receive stack traces */ 259 | while (ring_buffer__poll(ring_buf, -1) >= 0) { 260 | } 261 | 262 | cleanup: 263 | if (links) { 264 | for (cpu = 0; cpu < num_cpus; cpu++) 265 | bpf_link__destroy(links[cpu]); 266 | free(links); 267 | } 268 | if (pefds) { 269 | for (i = 0; i < num_cpus; i++) { 270 | if (pefds[i] >= 0) 271 | close(pefds[i]); 272 | } 273 | free(pefds); 274 | } 275 | ring_buffer__free(ring_buf); 276 | profile_bpf__destroy(skel); 277 | blaze_symbolizer_free(symbolizer); 278 | free(online_mask); 279 | return -err; 280 | } 281 | -------------------------------------------------------------------------------- /examples/c/profile.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #ifndef __PROFILE_H_ 4 | #define __PROFILE_H_ 5 | 6 | #ifndef TASK_COMM_LEN 7 | #define TASK_COMM_LEN 16 8 | #endif 9 | 10 | #ifndef MAX_STACK_DEPTH 11 | #define MAX_STACK_DEPTH 128 12 | #endif 13 | 14 | typedef __u64 stack_trace_t[MAX_STACK_DEPTH]; 15 | 16 | struct stacktrace_event { 17 | __u32 pid; 18 | __u32 cpu_id; 19 | char comm[TASK_COMM_LEN]; 20 | __s32 kstack_sz; 21 | __s32 ustack_sz; 22 | stack_trace_t kstack; 23 | stack_trace_t ustack; 24 | }; 25 | 26 | #endif /* __PROFILE_H_ */ 27 | -------------------------------------------------------------------------------- /examples/c/sockfilter.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "sockfilter.h" 11 | 12 | #define IP_MF 0x2000 13 | #define IP_OFFSET 0x1FFF 14 | 15 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_RINGBUF); 19 | __uint(max_entries, 256 * 1024); 20 | } rb SEC(".maps"); 21 | 22 | static inline int ip_is_fragment(struct __sk_buff *skb, __u32 nhoff) 23 | { 24 | __u16 frag_off; 25 | 26 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, frag_off), &frag_off, 2); 27 | frag_off = __bpf_ntohs(frag_off); 28 | return frag_off & (IP_MF | IP_OFFSET); 29 | } 30 | 31 | SEC("socket") 32 | int socket_handler(struct __sk_buff *skb) 33 | { 34 | struct so_event *e; 35 | __u8 verlen; 36 | __u16 proto; 37 | __u32 nhoff = ETH_HLEN; 38 | 39 | bpf_skb_load_bytes(skb, 12, &proto, 2); 40 | proto = __bpf_ntohs(proto); 41 | if (proto != ETH_P_IP) 42 | return 0; 43 | 44 | if (ip_is_fragment(skb, nhoff)) 45 | return 0; 46 | 47 | /* reserve sample from BPF ringbuf */ 48 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 49 | if (!e) 50 | return 0; 51 | 52 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, protocol), &e->ip_proto, 1); 53 | 54 | if (e->ip_proto != IPPROTO_GRE) { 55 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, saddr), &(e->src_addr), 4); 56 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, daddr), &(e->dst_addr), 4); 57 | } 58 | 59 | bpf_skb_load_bytes(skb, nhoff + 0, &verlen, 1); 60 | bpf_skb_load_bytes(skb, nhoff + ((verlen & 0xF) << 2), &(e->ports), 4); 61 | e->pkt_type = skb->pkt_type; 62 | e->ifindex = skb->ifindex; 63 | bpf_ringbuf_submit(e, 0); 64 | 65 | return skb->len; 66 | } 67 | -------------------------------------------------------------------------------- /examples/c/sockfilter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "sockfilter.h" 17 | #include "sockfilter.skel.h" 18 | 19 | static struct env { 20 | const char *interface; 21 | } env; 22 | 23 | const char argp_program_doc[] = 24 | "BPF socket filter demo application.\n" 25 | "\n" 26 | "This program watch network packet of specified interface and print out src/dst\n" 27 | "information.\n" 28 | "\n" 29 | "Currently only IPv4 is supported.\n" 30 | "\n" 31 | "USAGE: ./sockfilter [-i ]\n"; 32 | 33 | static const struct argp_option opts[] = { 34 | { "interface", 'i', "INTERFACE", 0, "Network interface to attach" }, 35 | {}, 36 | }; 37 | 38 | static error_t parse_arg(int key, char *arg, struct argp_state *state) 39 | { 40 | switch (key) { 41 | case 'i': 42 | env.interface = arg; 43 | break; 44 | case ARGP_KEY_ARG: 45 | argp_usage(state); 46 | break; 47 | default: 48 | return ARGP_ERR_UNKNOWN; 49 | } 50 | return 0; 51 | } 52 | 53 | static const struct argp argp = { 54 | .options = opts, 55 | .parser = parse_arg, 56 | .doc = argp_program_doc, 57 | }; 58 | 59 | static const char *ipproto_mapping[IPPROTO_MAX] = { 60 | [IPPROTO_IP] = "IP", [IPPROTO_ICMP] = "ICMP", [IPPROTO_IGMP] = "IGMP", 61 | [IPPROTO_IPIP] = "IPIP", [IPPROTO_TCP] = "TCP", [IPPROTO_EGP] = "EGP", 62 | [IPPROTO_PUP] = "PUP", [IPPROTO_UDP] = "UDP", [IPPROTO_IDP] = "IDP", 63 | [IPPROTO_TP] = "TP", [IPPROTO_DCCP] = "DCCP", [IPPROTO_IPV6] = "IPV6", 64 | [IPPROTO_RSVP] = "RSVP", [IPPROTO_GRE] = "GRE", [IPPROTO_ESP] = "ESP", 65 | [IPPROTO_AH] = "AH", [IPPROTO_MTP] = "MTP", [IPPROTO_BEETPH] = "BEETPH", 66 | [IPPROTO_ENCAP] = "ENCAP", [IPPROTO_PIM] = "PIM", [IPPROTO_COMP] = "COMP", 67 | [IPPROTO_SCTP] = "SCTP", [IPPROTO_UDPLITE] = "UDPLITE", [IPPROTO_MPLS] = "MPLS", 68 | [IPPROTO_RAW] = "RAW" 69 | }; 70 | 71 | static int open_raw_sock(const char *name) 72 | { 73 | struct sockaddr_ll sll; 74 | int sock; 75 | 76 | sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); 77 | if (sock < 0) { 78 | fprintf(stderr, "Failed to create raw socket\n"); 79 | return -1; 80 | } 81 | 82 | memset(&sll, 0, sizeof(sll)); 83 | sll.sll_family = AF_PACKET; 84 | sll.sll_ifindex = if_nametoindex(name); 85 | sll.sll_protocol = htons(ETH_P_ALL); 86 | if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { 87 | fprintf(stderr, "Failed to bind to %s: %s\n", name, strerror(errno)); 88 | close(sock); 89 | return -1; 90 | } 91 | 92 | return sock; 93 | } 94 | 95 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 96 | { 97 | return vfprintf(stderr, format, args); 98 | } 99 | 100 | static inline void ltoa(uint32_t addr, char *dst) 101 | { 102 | snprintf(dst, 16, "%u.%u.%u.%u", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, 103 | (addr >> 8) & 0xFF, (addr & 0xFF)); 104 | } 105 | 106 | static int handle_event(void *ctx, void *data, size_t data_sz) 107 | { 108 | const struct so_event *e = data; 109 | char ifname[IF_NAMESIZE]; 110 | char sstr[16] = {}, dstr[16] = {}; 111 | 112 | if (e->pkt_type != PACKET_HOST) 113 | return 0; 114 | 115 | if (e->ip_proto < 0 || e->ip_proto >= IPPROTO_MAX) 116 | return 0; 117 | 118 | if (!if_indextoname(e->ifindex, ifname)) 119 | return 0; 120 | 121 | ltoa(ntohl(e->src_addr), sstr); 122 | ltoa(ntohl(e->dst_addr), dstr); 123 | 124 | printf("interface: %s\tprotocol: %s\t%s:%d(src) -> %s:%d(dst)\n", ifname, 125 | ipproto_mapping[e->ip_proto], sstr, ntohs(e->port16[0]), dstr, ntohs(e->port16[1])); 126 | 127 | return 0; 128 | } 129 | 130 | static volatile bool exiting = false; 131 | 132 | static void sig_handler(int sig) 133 | { 134 | exiting = true; 135 | } 136 | 137 | int main(int argc, char **argv) 138 | { 139 | struct ring_buffer *rb = NULL; 140 | struct sockfilter_bpf *skel; 141 | int err, prog_fd, sock; 142 | 143 | env.interface = "lo"; 144 | /* Parse command line arguments */ 145 | err = argp_parse(&argp, argc, argv, 0, NULL, NULL); 146 | if (err) 147 | return -err; 148 | 149 | /* Set up libbpf errors and debug info callback */ 150 | libbpf_set_print(libbpf_print_fn); 151 | 152 | /* Cleaner handling of Ctrl-C */ 153 | signal(SIGINT, sig_handler); 154 | signal(SIGTERM, sig_handler); 155 | 156 | /* Load and verify BPF programs*/ 157 | skel = sockfilter_bpf__open_and_load(); 158 | if (!skel) { 159 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 160 | return 1; 161 | } 162 | 163 | /* Set up ring buffer polling */ 164 | rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); 165 | if (!rb) { 166 | err = -1; 167 | fprintf(stderr, "Failed to create ring buffer\n"); 168 | goto cleanup; 169 | } 170 | 171 | /* Create raw socket for localhost interface */ 172 | sock = open_raw_sock(env.interface); 173 | if (sock < 0) { 174 | err = -2; 175 | fprintf(stderr, "Failed to open raw socket\n"); 176 | goto cleanup; 177 | } 178 | 179 | /* Attach BPF program to raw socket */ 180 | prog_fd = bpf_program__fd(skel->progs.socket_handler); 181 | if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) { 182 | err = -3; 183 | fprintf(stderr, "Failed to attach to raw socket\n"); 184 | goto cleanup; 185 | } 186 | 187 | /* Process events */ 188 | while (!exiting) { 189 | err = ring_buffer__poll(rb, 100 /* timeout, ms */); 190 | /* Ctrl-C will cause -EINTR */ 191 | if (err == -EINTR) { 192 | err = 0; 193 | break; 194 | } 195 | if (err < 0) { 196 | fprintf(stderr, "Error polling perf buffer: %d\n", err); 197 | break; 198 | } 199 | sleep(1); 200 | } 201 | 202 | cleanup: 203 | ring_buffer__free(rb); 204 | sockfilter_bpf__destroy(skel); 205 | return -err; 206 | } 207 | -------------------------------------------------------------------------------- /examples/c/sockfilter.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #ifndef __SOCKFILTER_H 4 | #define __SOCKFILTER_H 5 | 6 | struct so_event { 7 | __be32 src_addr; 8 | __be32 dst_addr; 9 | union { 10 | __be32 ports; 11 | __be16 port16[2]; 12 | }; 13 | __u32 ip_proto; 14 | __u32 pkt_type; 15 | __u32 ifindex; 16 | }; 17 | 18 | #endif /* __SOCKFILTER_H */ 19 | -------------------------------------------------------------------------------- /examples/c/task_iter.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2023 Meta */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 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 | } 68 | -------------------------------------------------------------------------------- /examples/c/task_iter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2023 Meta */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 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 ""; 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 | } 111 | -------------------------------------------------------------------------------- /examples/c/task_iter.h: -------------------------------------------------------------------------------- 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 | }; 17 | -------------------------------------------------------------------------------- /examples/c/tc.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define TC_ACT_OK 0 9 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 10 | 11 | SEC("tc") 12 | int tc_ingress(struct __sk_buff *ctx) 13 | { 14 | void *data_end = (void *)(__u64)ctx->data_end; 15 | void *data = (void *)(__u64)ctx->data; 16 | struct ethhdr *l2; 17 | struct iphdr *l3; 18 | 19 | if (ctx->protocol != bpf_htons(ETH_P_IP)) 20 | return TC_ACT_OK; 21 | 22 | l2 = data; 23 | if ((void *)(l2 + 1) > data_end) 24 | return TC_ACT_OK; 25 | 26 | l3 = (struct iphdr *)(l2 + 1); 27 | if ((void *)(l3 + 1) > data_end) 28 | return TC_ACT_OK; 29 | 30 | bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), l3->ttl); 31 | return TC_ACT_OK; 32 | } 33 | 34 | char __license[] SEC("license") = "GPL"; 35 | -------------------------------------------------------------------------------- /examples/c/tc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include "tc.skel.h" 6 | 7 | #define LO_IFINDEX 1 8 | 9 | static volatile sig_atomic_t exiting = 0; 10 | 11 | static void sig_int(int signo) 12 | { 13 | exiting = 1; 14 | } 15 | 16 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 17 | { 18 | return vfprintf(stderr, format, args); 19 | } 20 | 21 | int main(int argc, char **argv) 22 | { 23 | DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, .ifindex = LO_IFINDEX, 24 | .attach_point = BPF_TC_INGRESS); 25 | DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts, .handle = 1, .priority = 1); 26 | bool hook_created = false; 27 | struct tc_bpf *skel; 28 | int err; 29 | 30 | libbpf_set_print(libbpf_print_fn); 31 | 32 | skel = tc_bpf__open_and_load(); 33 | if (!skel) { 34 | fprintf(stderr, "Failed to open BPF skeleton\n"); 35 | return 1; 36 | } 37 | 38 | /* The hook (i.e. qdisc) may already exists because: 39 | * 1. it is created by other processes or users 40 | * 2. or since we are attaching to the TC ingress ONLY, 41 | * bpf_tc_hook_destroy does NOT really remove the qdisc, 42 | * there may be an egress filter on the qdisc 43 | */ 44 | err = bpf_tc_hook_create(&tc_hook); 45 | if (!err) 46 | hook_created = true; 47 | if (err && err != -EEXIST) { 48 | fprintf(stderr, "Failed to create TC hook: %d\n", err); 49 | goto cleanup; 50 | } 51 | 52 | tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_ingress); 53 | err = bpf_tc_attach(&tc_hook, &tc_opts); 54 | if (err) { 55 | fprintf(stderr, "Failed to attach TC: %d\n", err); 56 | goto cleanup; 57 | } 58 | 59 | if (signal(SIGINT, sig_int) == SIG_ERR) { 60 | err = errno; 61 | fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno)); 62 | goto cleanup; 63 | } 64 | 65 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 66 | "to see output of the BPF program.\n"); 67 | 68 | while (!exiting) { 69 | fprintf(stderr, "."); 70 | sleep(1); 71 | } 72 | 73 | tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; 74 | err = bpf_tc_detach(&tc_hook, &tc_opts); 75 | if (err) { 76 | fprintf(stderr, "Failed to detach TC: %d\n", err); 77 | goto cleanup; 78 | } 79 | 80 | cleanup: 81 | if (hook_created) 82 | bpf_tc_hook_destroy(&tc_hook); 83 | tc_bpf__destroy(skel); 84 | return -err; 85 | } 86 | -------------------------------------------------------------------------------- /examples/c/uprobe.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 | 7 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 8 | 9 | SEC("uprobe") 10 | int BPF_KPROBE(uprobe_add, int a, int b) 11 | { 12 | bpf_printk("uprobed_add ENTRY: a = %d, b = %d", a, b); 13 | return 0; 14 | } 15 | 16 | SEC("uretprobe") 17 | int BPF_KRETPROBE(uretprobe_add, int ret) 18 | { 19 | bpf_printk("uprobed_add EXIT: return = %d", ret); 20 | return 0; 21 | } 22 | 23 | SEC("uprobe//proc/self/exe:uprobed_sub") 24 | int BPF_KPROBE(uprobe_sub, int a, int b) 25 | { 26 | bpf_printk("uprobed_sub ENTRY: a = %d, b = %d", a, b); 27 | return 0; 28 | } 29 | 30 | SEC("uretprobe//proc/self/exe:uprobed_sub") 31 | int BPF_KRETPROBE(uretprobe_sub, int ret) 32 | { 33 | bpf_printk("uprobed_sub EXIT: return = %d", ret); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /examples/c/uprobe.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "uprobe.skel.h" 9 | 10 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 11 | { 12 | return vfprintf(stderr, format, args); 13 | } 14 | 15 | /* It's a global function to make sure compiler doesn't inline it. To be extra 16 | * sure we also use "asm volatile" and noinline attributes to prevent 17 | * compiler from local inlining. 18 | */ 19 | __attribute__((noinline)) int uprobed_add(int a, int b) 20 | { 21 | asm volatile (""); 22 | return a + b; 23 | } 24 | 25 | __attribute__((noinline)) int uprobed_sub(int a, int b) 26 | { 27 | asm volatile (""); 28 | return a - b; 29 | } 30 | 31 | int main(int argc, char **argv) 32 | { 33 | struct uprobe_bpf *skel; 34 | int err, i; 35 | LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 36 | 37 | /* Set up libbpf errors and debug info callback */ 38 | libbpf_set_print(libbpf_print_fn); 39 | 40 | /* Load and verify BPF application */ 41 | skel = uprobe_bpf__open_and_load(); 42 | if (!skel) { 43 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 44 | return 1; 45 | } 46 | 47 | /* Attach tracepoint handler */ 48 | uprobe_opts.func_name = "uprobed_add"; 49 | uprobe_opts.retprobe = false; 50 | /* uprobe/uretprobe expects relative offset of the function to attach 51 | * to. libbpf will automatically find the offset for us if we provide the 52 | * function name. If the function name is not specified, libbpf will try 53 | * to use the function offset instead. 54 | */ 55 | skel->links.uprobe_add = bpf_program__attach_uprobe_opts(skel->progs.uprobe_add, 56 | 0 /* self pid */, "/proc/self/exe", 57 | 0 /* offset for function */, 58 | &uprobe_opts /* opts */); 59 | if (!skel->links.uprobe_add) { 60 | err = -errno; 61 | fprintf(stderr, "Failed to attach uprobe: %d\n", err); 62 | goto cleanup; 63 | } 64 | 65 | /* we can also attach uprobe/uretprobe to any existing or future 66 | * processes that use the same binary executable; to do that we need 67 | * to specify -1 as PID, as we do here 68 | */ 69 | uprobe_opts.func_name = "uprobed_add"; 70 | uprobe_opts.retprobe = true; 71 | skel->links.uretprobe_add = bpf_program__attach_uprobe_opts( 72 | skel->progs.uretprobe_add, -1 /* self pid */, "/proc/self/exe", 73 | 0 /* offset for function */, &uprobe_opts /* opts */); 74 | if (!skel->links.uretprobe_add) { 75 | err = -errno; 76 | fprintf(stderr, "Failed to attach uprobe: %d\n", err); 77 | goto cleanup; 78 | } 79 | 80 | /* Let libbpf perform auto-attach for uprobe_sub/uretprobe_sub 81 | * NOTICE: we provide path and symbol info in SEC for BPF programs 82 | */ 83 | err = uprobe_bpf__attach(skel); 84 | if (err) { 85 | fprintf(stderr, "Failed to auto-attach BPF skeleton: %d\n", err); 86 | goto cleanup; 87 | } 88 | 89 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 90 | "to see output of the BPF programs.\n"); 91 | 92 | for (i = 0;; i++) { 93 | /* trigger our BPF programs */ 94 | fprintf(stderr, "."); 95 | uprobed_add(i, i + 1); 96 | uprobed_sub(i * i, i); 97 | sleep(1); 98 | } 99 | 100 | cleanup: 101 | uprobe_bpf__destroy(skel); 102 | return -err; 103 | } 104 | -------------------------------------------------------------------------------- /examples/c/usdt.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | pid_t my_pid = 0; 9 | 10 | SEC("usdt/libc.so.6:libc:setjmp") 11 | int BPF_USDT(usdt_auto_attach, void *arg1, int arg2, void *arg3) 12 | { 13 | pid_t pid = bpf_get_current_pid_tgid() >> 32; 14 | 15 | if (pid != my_pid) 16 | return 0; 17 | 18 | bpf_printk("USDT auto attach to libc:setjmp: arg1 = %lx, arg2 = %d, arg3 = %lx", arg1, arg2, 19 | arg3); 20 | return 0; 21 | } 22 | 23 | SEC("usdt") 24 | int BPF_USDT(usdt_manual_attach, void *arg1, int arg2, void *arg3) 25 | { 26 | bpf_printk("USDT manual attach to libc:setjmp: arg1 = %lx, arg2 = %d, arg3 = %lx", arg1, 27 | arg2, arg3); 28 | return 0; 29 | } 30 | 31 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 32 | -------------------------------------------------------------------------------- /examples/c/usdt.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "usdt.skel.h" 8 | 9 | static volatile sig_atomic_t exiting; 10 | static jmp_buf env; 11 | 12 | static void sig_int(int signo) 13 | { 14 | exiting = 1; 15 | } 16 | 17 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 18 | { 19 | return vfprintf(stderr, format, args); 20 | } 21 | 22 | static void usdt_trigger() 23 | { 24 | setjmp(env); 25 | } 26 | 27 | int main(int argc, char **argv) 28 | { 29 | struct usdt_bpf *skel; 30 | int err; 31 | 32 | libbpf_set_print(libbpf_print_fn); 33 | 34 | skel = usdt_bpf__open(); 35 | if (!skel) { 36 | fprintf(stderr, "Failed to open BPF skeleton\n"); 37 | return 1; 38 | } 39 | 40 | skel->bss->my_pid = getpid(); 41 | 42 | err = usdt_bpf__load(skel); 43 | if (!skel) { 44 | fprintf(stderr, "Failed to load BPF skeleton\n"); 45 | return 1; 46 | } 47 | 48 | /* 49 | * Manually attach to libc.so we find. 50 | * We specify pid here, so we don't have to do pid filtering in BPF program. 51 | */ 52 | skel->links.usdt_manual_attach = bpf_program__attach_usdt( 53 | skel->progs.usdt_manual_attach, getpid(), "libc.so.6", "libc", "setjmp", NULL); 54 | if (!skel->links.usdt_manual_attach) { 55 | err = errno; 56 | fprintf(stderr, "Failed to attach BPF program `usdt_manual_attach`\n"); 57 | goto cleanup; 58 | } 59 | 60 | /* 61 | * Auto attach by libbpf, libbpf should be able to find libc.so in your system. 62 | * By default, auto attach does NOT specify pid, so we do pid filtering in BPF program 63 | */ 64 | err = usdt_bpf__attach(skel); 65 | if (err) { 66 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 67 | goto cleanup; 68 | } 69 | 70 | if (signal(SIGINT, sig_int) == SIG_ERR) { 71 | err = errno; 72 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 73 | goto cleanup; 74 | } 75 | 76 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 77 | "to see output of the BPF programs.\n"); 78 | 79 | while (!exiting) { 80 | /* trigger our BPF programs */ 81 | usdt_trigger(); 82 | fprintf(stderr, "."); 83 | sleep(1); 84 | } 85 | 86 | cleanup: 87 | usdt_bpf__destroy(skel); 88 | return -err; 89 | } 90 | -------------------------------------------------------------------------------- /examples/c/xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.release", "mode.debug") 2 | add_rules("platform.linux.bpf") 3 | set_license("GPL-2.0") 4 | 5 | if xmake.version():satisfies(">=2.5.7 <=2.5.9") then 6 | on_load(function (target) 7 | raise("xmake(%s) has a bug preventing BPF source code compilation. Please run `xmake update -f 2.5.6` to revert to v2.5.6 version or upgrade to xmake v2.6.1 that fixed the issue.", xmake.version()) 8 | end) 9 | end 10 | 11 | option("system-libbpf", {showmenu = true, default = false, description = "Use system-installed libbpf"}) 12 | option("require-bpftool", {showmenu = true, default = false, description = "Require bpftool package"}) 13 | 14 | add_requires("elfutils", "zlib") 15 | if is_plat("android") then 16 | add_requires("ndk >=22.x <26", "argp-standalone") 17 | set_toolchains("@ndk", {sdkver = "23"}) 18 | else 19 | add_requires("llvm >=10.x") 20 | set_toolchains("@llvm") 21 | add_requires("linux-headers") 22 | end 23 | 24 | -- fix error: libbpf: map 'my_pid_map': unsupported map linkage static. for bpftool >= 7.2.0 25 | -- we cannot add `"-fvisibility=hidden"` when compiling *.bpf.c 26 | set_symbols("none") 27 | 28 | if is_arch("arm64", "arm64-v8a") then 29 | add_includedirs("../../vmlinux.h/include/arm64") 30 | elseif is_arch("arm.*") then 31 | add_includedirs("../../vmlinux.h/include/arm") 32 | elseif is_arch("riscv32", "riscv64") then 33 | add_includedirs("../../vmlinux.h/include/riscv") 34 | elseif is_arch("loongarch") then 35 | add_includedirs("../../vmlinux.h/include/loongarch") 36 | elseif is_arch("ppc", "powerpc") then 37 | add_includedirs("../../vmlinux.h/include/powerpc") 38 | elseif is_arch("x86_64", "i386") then 39 | add_includedirs("../../vmlinux.h/include/x86") 40 | else 41 | add_includedirs("../../vmlinux.h/include") 42 | end 43 | 44 | -- we can run `xmake f --require-bpftool=y` to pull bpftool from xmake-repo repository 45 | if has_config("require-bpftool") then 46 | add_requires("linux-tools", {configs = {bpftool = true}}) 47 | add_packages("linux-tools") 48 | else 49 | before_build(function (target) 50 | os.addenv("PATH", path.join(os.scriptdir(), "..", "..", "tools")) 51 | end) 52 | end 53 | 54 | -- we use the vendored libbpf sources for libbpf-bootstrap. 55 | -- for some projects you may want to use the system-installed libbpf, so you can run `xmake f --system-libbpf=y` 56 | if has_config("system-libbpf") then 57 | add_requires("libbpf", {system = true}) 58 | else 59 | target("libbpf") 60 | set_kind("static") 61 | set_basename("bpf") 62 | add_files("../../libbpf/src/*.c") 63 | add_includedirs("../../libbpf/include") 64 | add_includedirs("../../libbpf/include/uapi", {public = true}) 65 | add_includedirs("$(buildir)", {interface = true}) 66 | add_configfiles("../../libbpf/src/(*.h)", {prefixdir = "bpf"}) 67 | add_packages("elfutils", "zlib") 68 | if is_plat("android") then 69 | add_defines("__user=", "__force=", "__poll_t=uint32_t") 70 | end 71 | end 72 | 73 | target("minimal") 74 | set_kind("binary") 75 | add_files("minimal.c", "minimal.bpf.c") 76 | add_packages("linux-headers") 77 | if not has_config("system-libbpf") then 78 | add_deps("libbpf") 79 | end 80 | 81 | target("minimal_legacy") 82 | set_kind("binary") 83 | add_files("minimal_legacy.c", "minimal_legacy.bpf.c") 84 | add_packages("linux-headers") 85 | if not has_config("system-libbpf") then 86 | add_deps("libbpf") 87 | end 88 | 89 | target("bootstrap") 90 | set_kind("binary") 91 | add_files("bootstrap.c", "bootstrap.bpf.c") 92 | add_packages("linux-headers") 93 | if not has_config("system-libbpf") then 94 | add_deps("libbpf") 95 | end 96 | if is_plat("android") then 97 | add_packages("argp-standalone") 98 | end 99 | 100 | target("fentry") 101 | set_kind("binary") 102 | add_files("fentry.c", "fentry.bpf.c") 103 | add_packages("linux-headers") 104 | if not has_config("system-libbpf") then 105 | add_deps("libbpf") 106 | end 107 | 108 | target("uprobe") 109 | set_kind("binary") 110 | add_files("uprobe.c", "uprobe.bpf.c") 111 | add_packages("linux-headers") 112 | if not has_config("system-libbpf") then 113 | add_deps("libbpf") 114 | end 115 | 116 | target("kprobe") 117 | set_kind("binary") 118 | add_files("kprobe.c", "kprobe.bpf.c") 119 | add_packages("linux-headers") 120 | if not has_config("system-libbpf") then 121 | add_deps("libbpf") 122 | end 123 | if is_plat("android") then 124 | -- TODO we need fix vmlinux.h to support android 125 | set_default(false) 126 | end 127 | -------------------------------------------------------------------------------- /examples/rust/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /examples/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "tracecon", 5 | "xdp", 6 | "profile", 7 | ] 8 | -------------------------------------------------------------------------------- /examples/rust/profile/.gitignore: -------------------------------------------------------------------------------- 1 | /src/bpf/.output 2 | /target 3 | -------------------------------------------------------------------------------- /examples/rust/profile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "profile" 3 | version = "0.1.0" 4 | authors = ["Kuifeng Lee "] 5 | license = "GPL-2.0 OR BSD-3-Clause" 6 | edition = "2021" 7 | rust-version = "1.71" 8 | 9 | [dependencies] 10 | blazesym = { path = "../../../blazesym", features = ["tracing"] } 11 | clap = { version = "4.5", features = ["derive"] } 12 | libbpf-rs = "0.24" 13 | libc = "*" 14 | nix = "0.29.0" 15 | tracing = "0.1" 16 | tracing-subscriber = {version = "0.3", features = ["ansi", "env-filter", "fmt"]} 17 | 18 | [build-dependencies] 19 | libbpf-cargo = "0.24" 20 | -------------------------------------------------------------------------------- /examples/rust/profile/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::ffi::OsStr; 3 | use std::path::Path; 4 | use std::path::PathBuf; 5 | 6 | use libbpf_cargo::SkeletonBuilder; 7 | 8 | const SRC: &str = "src/bpf/profile.bpf.c"; 9 | 10 | fn main() { 11 | let mut out = 12 | PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR must be set in build script")); 13 | out.push("profile.skel.rs"); 14 | 15 | let arch = env::var("CARGO_CFG_TARGET_ARCH") 16 | .expect("CARGO_CFG_TARGET_ARCH must be set in build script"); 17 | 18 | SkeletonBuilder::new() 19 | .source(SRC) 20 | .clang_args([ 21 | OsStr::new("-I"), 22 | Path::new("../../../vmlinux.h/include").join(arch).as_os_str() 23 | ]) 24 | .build_and_generate(out) 25 | .expect("bpf compilation failed"); 26 | println!("cargo:rerun-if-changed={}", SRC); 27 | } 28 | -------------------------------------------------------------------------------- /examples/rust/profile/src/bpf/profile.bpf.c: -------------------------------------------------------------------------------- 1 | ../../../../c/profile.bpf.c -------------------------------------------------------------------------------- /examples/rust/profile/src/bpf/profile.h: -------------------------------------------------------------------------------- 1 | ../../../../c/profile.h -------------------------------------------------------------------------------- /examples/rust/profile/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::mem; 3 | use std::mem::MaybeUninit; 4 | use std::time::Duration; 5 | 6 | use blazesym::symbolize; 7 | 8 | use clap::ArgAction; 9 | use clap::Parser; 10 | 11 | use libbpf_rs::skel::OpenSkel as _; 12 | use libbpf_rs::skel::SkelBuilder as _; 13 | use libbpf_rs::ErrorExt as _; 14 | 15 | use nix::unistd::close; 16 | 17 | use tracing::subscriber::set_global_default as set_global_subscriber; 18 | use tracing_subscriber::filter::LevelFilter; 19 | use tracing_subscriber::fmt::format::FmtSpan; 20 | use tracing_subscriber::fmt::time::SystemTime; 21 | use tracing_subscriber::FmtSubscriber; 22 | 23 | mod profile { 24 | include!(concat!(env!("OUT_DIR"), "/profile.skel.rs")); 25 | } 26 | mod syscall; 27 | 28 | use profile::*; 29 | 30 | const MAX_STACK_DEPTH: usize = 128; 31 | const TASK_COMM_LEN: usize = 16; 32 | const ADDR_WIDTH: usize = 16; 33 | 34 | // A Rust version of stacktrace_event in profile.h 35 | #[repr(C)] 36 | struct stacktrace_event { 37 | pid: u32, 38 | cpu_id: u32, 39 | comm: [u8; TASK_COMM_LEN], 40 | kstack_size: i32, 41 | ustack_size: i32, 42 | kstack: [u64; MAX_STACK_DEPTH], 43 | ustack: [u64; MAX_STACK_DEPTH], 44 | } 45 | 46 | fn init_perf_monitor(freq: u64, sw_event: bool) -> Result, libbpf_rs::Error> { 47 | let nprocs = libbpf_rs::num_possible_cpus().unwrap(); 48 | let pid = -1; 49 | let attr = syscall::perf_event_attr { 50 | _type: if sw_event { 51 | syscall::PERF_TYPE_SOFTWARE 52 | } else { 53 | syscall::PERF_TYPE_HARDWARE 54 | }, 55 | size: mem::size_of::() as u32, 56 | config: if sw_event { 57 | syscall::PERF_COUNT_SW_CPU_CLOCK 58 | } else { 59 | syscall::PERF_COUNT_HW_CPU_CYCLES 60 | }, 61 | sample: syscall::sample_un { sample_freq: freq }, 62 | flags: 1 << 10, // freq = 1 63 | ..Default::default() 64 | }; 65 | (0..nprocs) 66 | .map(|cpu| { 67 | let fd = syscall::perf_event_open(&attr, pid, cpu as i32, -1, 0) as i32; 68 | if fd == -1 { 69 | let mut error_context = "Failed to open perf event."; 70 | let os_error = io::Error::last_os_error(); 71 | if !sw_event && os_error.kind() == io::ErrorKind::NotFound { 72 | error_context = "Failed to open perf event.\n\ 73 | Try running the profile example with the `--sw-event` option."; 74 | } 75 | Err(libbpf_rs::Error::from(os_error)).context(error_context) 76 | } else { 77 | Ok(fd) 78 | } 79 | }) 80 | .collect() 81 | } 82 | 83 | fn attach_perf_event( 84 | pefds: &[i32], 85 | prog: &libbpf_rs::ProgramMut, 86 | ) -> Vec> { 87 | pefds 88 | .iter() 89 | .map(|pefd| prog.attach_perf_event(*pefd)) 90 | .collect() 91 | } 92 | 93 | fn print_frame( 94 | name: &str, 95 | addr_info: Option<(blazesym::Addr, blazesym::Addr, usize)>, 96 | code_info: &Option, 97 | ) { 98 | let code_info = code_info.as_ref().map(|code_info| { 99 | let path = code_info.to_path(); 100 | let path = path.display(); 101 | 102 | match (code_info.line, code_info.column) { 103 | (Some(line), Some(col)) => format!(" {path}:{line}:{col}"), 104 | (Some(line), None) => format!(" {path}:{line}"), 105 | (None, _) => format!(" {path}"), 106 | } 107 | }); 108 | 109 | if let Some((input_addr, addr, offset)) = addr_info { 110 | // If we have various address information bits we have a new symbol. 111 | println!( 112 | "{input_addr:#0width$x}: {name} @ {addr:#x}+{offset:#x}{code_info}", 113 | code_info = code_info.as_deref().unwrap_or(""), 114 | width = ADDR_WIDTH 115 | ) 116 | } else { 117 | // Otherwise we are dealing with an inlined call. 118 | println!( 119 | "{:width$} {name}{code_info} [inlined]", 120 | " ", 121 | code_info = code_info 122 | .map(|info| format!(" @{info}")) 123 | .as_deref() 124 | .unwrap_or(""), 125 | width = ADDR_WIDTH 126 | ) 127 | } 128 | } 129 | 130 | // Pid 0 means a kernel space stack. 131 | fn show_stack_trace(stack: &[u64], symbolizer: &symbolize::Symbolizer, pid: u32) { 132 | let converted_stack; 133 | // The kernel always reports `u64` addresses, whereas blazesym uses `Addr`. 134 | // Convert the stack trace as necessary. 135 | let stack = if mem::size_of::() != mem::size_of::() { 136 | converted_stack = stack 137 | .iter() 138 | .copied() 139 | .map(|addr| addr as blazesym::Addr) 140 | .collect::>(); 141 | converted_stack.as_slice() 142 | } else { 143 | // SAFETY: `Addr` has the same size as `u64`, so it can be trivially and 144 | // safely converted. 145 | unsafe { mem::transmute::<_, &[blazesym::Addr]>(stack) } 146 | }; 147 | 148 | let src = if pid == 0 { 149 | symbolize::source::Source::from(symbolize::source::Kernel::default()) 150 | } else { 151 | symbolize::source::Source::from(symbolize::source::Process::new(pid.into())) 152 | }; 153 | 154 | let syms = match symbolizer.symbolize(&src, symbolize::Input::AbsAddr(stack)) { 155 | Ok(syms) => syms, 156 | Err(err) => { 157 | eprintln!(" failed to symbolize addresses: {err:#}"); 158 | return; 159 | } 160 | }; 161 | 162 | for (input_addr, sym) in stack.iter().copied().zip(syms) { 163 | match sym { 164 | symbolize::Symbolized::Sym(symbolize::Sym { 165 | name, 166 | addr, 167 | offset, 168 | code_info, 169 | inlined, 170 | .. 171 | }) => { 172 | print_frame(&name, Some((input_addr, addr, offset)), &code_info); 173 | for frame in inlined.iter() { 174 | print_frame(&frame.name, None, &frame.code_info); 175 | } 176 | } 177 | symbolize::Symbolized::Unknown(..) => { 178 | println!("{input_addr:#0width$x}: ", width = ADDR_WIDTH) 179 | } 180 | } 181 | } 182 | } 183 | 184 | fn event_handler(symbolizer: &symbolize::Symbolizer, data: &[u8]) -> ::std::os::raw::c_int { 185 | if data.len() != mem::size_of::() { 186 | eprintln!( 187 | "Invalid size {} != {}", 188 | data.len(), 189 | mem::size_of::() 190 | ); 191 | return 1; 192 | } 193 | 194 | let event = unsafe { &*(data.as_ptr() as *const stacktrace_event) }; 195 | 196 | if event.kstack_size <= 0 && event.ustack_size <= 0 { 197 | return 1; 198 | } 199 | 200 | let comm = std::str::from_utf8(&event.comm).unwrap_or(""); 201 | println!("COMM: {} (pid={}) @ CPU {}", comm, event.pid, event.cpu_id); 202 | 203 | if event.kstack_size > 0 { 204 | println!("Kernel:"); 205 | show_stack_trace( 206 | &event.kstack[0..(event.kstack_size as usize / mem::size_of::())], 207 | symbolizer, 208 | 0, 209 | ); 210 | } else { 211 | println!("No Kernel Stack"); 212 | } 213 | 214 | if event.ustack_size > 0 { 215 | println!("Userspace:"); 216 | show_stack_trace( 217 | &event.ustack[0..(event.ustack_size as usize / mem::size_of::())], 218 | symbolizer, 219 | event.pid, 220 | ); 221 | } else { 222 | println!("No Userspace Stack"); 223 | } 224 | 225 | println!(); 226 | 0 227 | } 228 | 229 | #[derive(Parser, Debug)] 230 | struct Args { 231 | /// Sampling frequency 232 | #[arg(short, default_value_t = 1)] 233 | freq: u64, 234 | /// Increase verbosity (can be supplied multiple times). 235 | #[arg(short = 'v', long = "verbose", global = true, action = ArgAction::Count)] 236 | verbosity: u8, 237 | /// Use software event for triggering stack trace capture. 238 | /// 239 | /// This can be useful for compatibility reasons if hardware event is not available 240 | /// (which could happen in a virtual machine, for example). 241 | #[arg(long = "sw-event")] 242 | sw_event: bool, 243 | } 244 | 245 | fn main() -> Result<(), libbpf_rs::Error> { 246 | let args = Args::parse(); 247 | let level = match args.verbosity { 248 | 0 => LevelFilter::WARN, 249 | 1 => LevelFilter::INFO, 250 | 2 => LevelFilter::DEBUG, 251 | _ => LevelFilter::TRACE, 252 | }; 253 | 254 | let subscriber = FmtSubscriber::builder() 255 | .with_max_level(level) 256 | .with_span_events(FmtSpan::FULL) 257 | .with_timer(SystemTime) 258 | .finish(); 259 | let () = set_global_subscriber(subscriber).expect("failed to set tracing subscriber"); 260 | 261 | let freq = if args.freq < 1 { 1 } else { args.freq }; 262 | 263 | let symbolizer = symbolize::Symbolizer::new(); 264 | 265 | let skel_builder = ProfileSkelBuilder::default(); 266 | let mut open_object = MaybeUninit::uninit(); 267 | let open_skel = skel_builder.open(&mut open_object).unwrap(); 268 | let skel = open_skel.load().unwrap(); 269 | 270 | let pefds = init_perf_monitor(freq, args.sw_event)?; 271 | let _links = attach_perf_event(&pefds, &skel.progs.profile); 272 | 273 | let mut builder = libbpf_rs::RingBufferBuilder::new(); 274 | builder 275 | .add(&skel.maps.events, move |data| { 276 | event_handler(&symbolizer, data) 277 | }) 278 | .unwrap(); 279 | let ringbuf = builder.build().unwrap(); 280 | while ringbuf.poll(Duration::MAX).is_ok() {} 281 | 282 | for pefd in pefds { 283 | close(pefd) 284 | .map_err(io::Error::from) 285 | .map_err(libbpf_rs::Error::from) 286 | .context("failed to close perf event")?; 287 | } 288 | 289 | Ok(()) 290 | } 291 | -------------------------------------------------------------------------------- /examples/rust/profile/src/syscall.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | extern crate libc; 4 | 5 | #[repr(C)] 6 | pub union sample_un { 7 | pub sample_period: u64, 8 | pub sample_freq: u64, 9 | } 10 | 11 | #[repr(C)] 12 | pub union wakeup_un { 13 | pub wakeup_events: u32, 14 | pub wakeup_atermark: u32, 15 | } 16 | 17 | #[repr(C)] 18 | pub union bp_1_un { 19 | pub bp_addr: u64, 20 | pub kprobe_func: u64, 21 | pub uprobe_path: u64, 22 | pub config1: u64, 23 | } 24 | 25 | #[repr(C)] 26 | pub union bp_2_un { 27 | pub bp_len: u64, 28 | pub kprobe_addr: u64, 29 | pub probe_offset: u64, 30 | pub config2: u64, 31 | } 32 | 33 | #[repr(C)] 34 | pub struct perf_event_attr { 35 | pub _type: u32, 36 | pub size: u32, 37 | pub config: u64, 38 | pub sample: sample_un, 39 | pub sample_type: u64, 40 | pub read_format: u64, 41 | pub flags: u64, 42 | pub wakeup: wakeup_un, 43 | pub bp_type: u32, 44 | pub bp_1: bp_1_un, 45 | pub bp_2: bp_2_un, 46 | pub branch_sample_type: u64, 47 | pub sample_regs_user: u64, 48 | pub sample_stack_user: u32, 49 | pub clockid: i32, 50 | pub sample_regs_intr: u64, 51 | pub aux_watermark: u32, 52 | pub sample_max_stack: u16, 53 | pub __reserved_2: u16, 54 | pub aux_sample_size: u32, 55 | pub __reserved_3: u32, 56 | } 57 | 58 | impl Default for perf_event_attr { 59 | fn default() -> Self { 60 | unsafe { mem::zeroed() } 61 | } 62 | } 63 | 64 | pub const PERF_TYPE_HARDWARE: u32 = 0; 65 | pub const PERF_TYPE_SOFTWARE: u32 = 1; 66 | pub const PERF_COUNT_HW_CPU_CYCLES: u64 = 0; 67 | pub const PERF_COUNT_SW_CPU_CLOCK: u64 = 0; 68 | 69 | extern "C" { 70 | fn syscall(number: libc::c_long, ...) -> libc::c_long; 71 | } 72 | 73 | pub fn perf_event_open( 74 | hw_event: &perf_event_attr, 75 | pid: libc::pid_t, 76 | cpu: libc::c_int, 77 | group_fd: libc::c_int, 78 | flags: libc::c_ulong, 79 | ) -> libc::c_long { 80 | unsafe { 81 | syscall( 82 | libc::SYS_perf_event_open, 83 | hw_event as *const perf_event_attr, 84 | pid, 85 | cpu, 86 | group_fd, 87 | flags, 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/rust/tracecon/.gitignore: -------------------------------------------------------------------------------- 1 | /src/bpf/.output 2 | /target 3 | -------------------------------------------------------------------------------- /examples/rust/tracecon/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "ansi_term" 22 | version = "0.11.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 25 | dependencies = [ 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.45" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" 34 | 35 | [[package]] 36 | name = "atty" 37 | version = "0.2.14" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 40 | dependencies = [ 41 | "hermit-abi", 42 | "libc", 43 | "winapi", 44 | ] 45 | 46 | [[package]] 47 | name = "autocfg" 48 | version = "1.0.1" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "1.3.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 57 | 58 | [[package]] 59 | name = "cargo-platform" 60 | version = "0.1.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" 63 | dependencies = [ 64 | "serde", 65 | ] 66 | 67 | [[package]] 68 | name = "cargo_metadata" 69 | version = "0.12.3" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" 72 | dependencies = [ 73 | "cargo-platform", 74 | "semver 0.11.0", 75 | "semver-parser", 76 | "serde", 77 | "serde_json", 78 | ] 79 | 80 | [[package]] 81 | name = "cc" 82 | version = "1.0.72" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 85 | 86 | [[package]] 87 | name = "cfg-if" 88 | version = "1.0.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 91 | 92 | [[package]] 93 | name = "clap" 94 | version = "2.33.3" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 97 | dependencies = [ 98 | "ansi_term", 99 | "atty", 100 | "bitflags", 101 | "strsim", 102 | "textwrap", 103 | "unicode-width", 104 | "vec_map", 105 | ] 106 | 107 | [[package]] 108 | name = "crc32fast" 109 | version = "1.2.1" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 112 | dependencies = [ 113 | "cfg-if", 114 | ] 115 | 116 | [[package]] 117 | name = "ctrlc" 118 | version = "3.2.1" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" 121 | dependencies = [ 122 | "nix 0.23.0", 123 | "winapi", 124 | ] 125 | 126 | [[package]] 127 | name = "derivative" 128 | version = "2.2.0" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 131 | dependencies = [ 132 | "proc-macro2", 133 | "quote", 134 | "syn", 135 | ] 136 | 137 | [[package]] 138 | name = "flate2" 139 | version = "1.0.22" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" 142 | dependencies = [ 143 | "cfg-if", 144 | "crc32fast", 145 | "libc", 146 | "miniz_oxide", 147 | ] 148 | 149 | [[package]] 150 | name = "getrandom" 151 | version = "0.2.3" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 154 | dependencies = [ 155 | "cfg-if", 156 | "libc", 157 | "wasi", 158 | ] 159 | 160 | [[package]] 161 | name = "heck" 162 | version = "0.3.3" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 165 | dependencies = [ 166 | "unicode-segmentation", 167 | ] 168 | 169 | [[package]] 170 | name = "hermit-abi" 171 | version = "0.1.19" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 174 | dependencies = [ 175 | "libc", 176 | ] 177 | 178 | [[package]] 179 | name = "itoa" 180 | version = "0.4.8" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 183 | 184 | [[package]] 185 | name = "lazy_static" 186 | version = "1.4.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 189 | 190 | [[package]] 191 | name = "libbpf-cargo" 192 | version = "0.9.3" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "23536bf02ff6387e7bb68055f6ad52d75c20d77b2ab6535977fc1e771041286c" 195 | dependencies = [ 196 | "anyhow", 197 | "cargo_metadata", 198 | "libbpf-sys", 199 | "memmap2", 200 | "num_enum", 201 | "regex", 202 | "scroll", 203 | "scroll_derive", 204 | "semver 1.0.4", 205 | "serde", 206 | "serde_json", 207 | "structopt", 208 | "tempfile", 209 | "thiserror", 210 | ] 211 | 212 | [[package]] 213 | name = "libbpf-rs" 214 | version = "0.14.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "87d1f46d8d976bba68a9b772138e2307cfd7432bf66f3e1c83b3944e83ab9002" 217 | dependencies = [ 218 | "bitflags", 219 | "libbpf-sys", 220 | "nix 0.22.0", 221 | "num_enum", 222 | "strum_macros", 223 | "thiserror", 224 | "vsprintf", 225 | ] 226 | 227 | [[package]] 228 | name = "libbpf-sys" 229 | version = "0.5.0-2" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "97982339699ecfda9742ae39311fac5a6cce5ac7330bcf714cb702a49ecc8e2a" 232 | dependencies = [ 233 | "cc", 234 | "pkg-config", 235 | ] 236 | 237 | [[package]] 238 | name = "libc" 239 | version = "0.2.107" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" 242 | 243 | [[package]] 244 | name = "memchr" 245 | version = "2.4.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 248 | 249 | [[package]] 250 | name = "memmap2" 251 | version = "0.3.1" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" 254 | dependencies = [ 255 | "libc", 256 | ] 257 | 258 | [[package]] 259 | name = "memoffset" 260 | version = "0.6.4" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 263 | dependencies = [ 264 | "autocfg", 265 | ] 266 | 267 | [[package]] 268 | name = "miniz_oxide" 269 | version = "0.4.4" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 272 | dependencies = [ 273 | "adler", 274 | "autocfg", 275 | ] 276 | 277 | [[package]] 278 | name = "nix" 279 | version = "0.22.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" 282 | dependencies = [ 283 | "bitflags", 284 | "cc", 285 | "cfg-if", 286 | "libc", 287 | "memoffset", 288 | ] 289 | 290 | [[package]] 291 | name = "nix" 292 | version = "0.23.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" 295 | dependencies = [ 296 | "bitflags", 297 | "cc", 298 | "cfg-if", 299 | "libc", 300 | "memoffset", 301 | ] 302 | 303 | [[package]] 304 | name = "num_enum" 305 | version = "0.5.4" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" 308 | dependencies = [ 309 | "derivative", 310 | "num_enum_derive", 311 | ] 312 | 313 | [[package]] 314 | name = "num_enum_derive" 315 | version = "0.5.4" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" 318 | dependencies = [ 319 | "proc-macro-crate", 320 | "proc-macro2", 321 | "quote", 322 | "syn", 323 | ] 324 | 325 | [[package]] 326 | name = "object" 327 | version = "0.25.3" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" 330 | dependencies = [ 331 | "flate2", 332 | "memchr", 333 | ] 334 | 335 | [[package]] 336 | name = "pest" 337 | version = "2.1.3" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 340 | dependencies = [ 341 | "ucd-trie", 342 | ] 343 | 344 | [[package]] 345 | name = "pkg-config" 346 | version = "0.3.22" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" 349 | 350 | [[package]] 351 | name = "plain" 352 | version = "0.2.3" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" 355 | 356 | [[package]] 357 | name = "ppv-lite86" 358 | version = "0.2.15" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 361 | 362 | [[package]] 363 | name = "proc-macro-crate" 364 | version = "1.1.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" 367 | dependencies = [ 368 | "thiserror", 369 | "toml", 370 | ] 371 | 372 | [[package]] 373 | name = "proc-macro-error" 374 | version = "1.0.4" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 377 | dependencies = [ 378 | "proc-macro-error-attr", 379 | "proc-macro2", 380 | "quote", 381 | "syn", 382 | "version_check", 383 | ] 384 | 385 | [[package]] 386 | name = "proc-macro-error-attr" 387 | version = "1.0.4" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 390 | dependencies = [ 391 | "proc-macro2", 392 | "quote", 393 | "version_check", 394 | ] 395 | 396 | [[package]] 397 | name = "proc-macro2" 398 | version = "1.0.32" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" 401 | dependencies = [ 402 | "unicode-xid", 403 | ] 404 | 405 | [[package]] 406 | name = "quote" 407 | version = "1.0.10" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 410 | dependencies = [ 411 | "proc-macro2", 412 | ] 413 | 414 | [[package]] 415 | name = "rand" 416 | version = "0.8.4" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 419 | dependencies = [ 420 | "libc", 421 | "rand_chacha", 422 | "rand_core", 423 | "rand_hc", 424 | ] 425 | 426 | [[package]] 427 | name = "rand_chacha" 428 | version = "0.3.1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 431 | dependencies = [ 432 | "ppv-lite86", 433 | "rand_core", 434 | ] 435 | 436 | [[package]] 437 | name = "rand_core" 438 | version = "0.6.3" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 441 | dependencies = [ 442 | "getrandom", 443 | ] 444 | 445 | [[package]] 446 | name = "rand_hc" 447 | version = "0.3.1" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 450 | dependencies = [ 451 | "rand_core", 452 | ] 453 | 454 | [[package]] 455 | name = "redox_syscall" 456 | version = "0.2.10" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 459 | dependencies = [ 460 | "bitflags", 461 | ] 462 | 463 | [[package]] 464 | name = "regex" 465 | version = "1.5.4" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 468 | dependencies = [ 469 | "aho-corasick", 470 | "memchr", 471 | "regex-syntax", 472 | ] 473 | 474 | [[package]] 475 | name = "regex-syntax" 476 | version = "0.6.25" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 479 | 480 | [[package]] 481 | name = "remove_dir_all" 482 | version = "0.5.3" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 485 | dependencies = [ 486 | "winapi", 487 | ] 488 | 489 | [[package]] 490 | name = "ryu" 491 | version = "1.0.5" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 494 | 495 | [[package]] 496 | name = "scroll" 497 | version = "0.10.2" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 500 | 501 | [[package]] 502 | name = "scroll_derive" 503 | version = "0.10.5" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" 506 | dependencies = [ 507 | "proc-macro2", 508 | "quote", 509 | "syn", 510 | ] 511 | 512 | [[package]] 513 | name = "semver" 514 | version = "0.11.0" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 517 | dependencies = [ 518 | "semver-parser", 519 | "serde", 520 | ] 521 | 522 | [[package]] 523 | name = "semver" 524 | version = "1.0.4" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" 527 | 528 | [[package]] 529 | name = "semver-parser" 530 | version = "0.10.2" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 533 | dependencies = [ 534 | "pest", 535 | ] 536 | 537 | [[package]] 538 | name = "serde" 539 | version = "1.0.130" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" 542 | dependencies = [ 543 | "serde_derive", 544 | ] 545 | 546 | [[package]] 547 | name = "serde_derive" 548 | version = "1.0.130" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" 551 | dependencies = [ 552 | "proc-macro2", 553 | "quote", 554 | "syn", 555 | ] 556 | 557 | [[package]] 558 | name = "serde_json" 559 | version = "1.0.69" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" 562 | dependencies = [ 563 | "itoa", 564 | "ryu", 565 | "serde", 566 | ] 567 | 568 | [[package]] 569 | name = "strsim" 570 | version = "0.8.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 573 | 574 | [[package]] 575 | name = "structopt" 576 | version = "0.3.25" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" 579 | dependencies = [ 580 | "clap", 581 | "lazy_static", 582 | "structopt-derive", 583 | ] 584 | 585 | [[package]] 586 | name = "structopt-derive" 587 | version = "0.4.18" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 590 | dependencies = [ 591 | "heck", 592 | "proc-macro-error", 593 | "proc-macro2", 594 | "quote", 595 | "syn", 596 | ] 597 | 598 | [[package]] 599 | name = "strum_macros" 600 | version = "0.21.1" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" 603 | dependencies = [ 604 | "heck", 605 | "proc-macro2", 606 | "quote", 607 | "syn", 608 | ] 609 | 610 | [[package]] 611 | name = "syn" 612 | version = "1.0.81" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" 615 | dependencies = [ 616 | "proc-macro2", 617 | "quote", 618 | "unicode-xid", 619 | ] 620 | 621 | [[package]] 622 | name = "tempfile" 623 | version = "3.2.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 626 | dependencies = [ 627 | "cfg-if", 628 | "libc", 629 | "rand", 630 | "redox_syscall", 631 | "remove_dir_all", 632 | "winapi", 633 | ] 634 | 635 | [[package]] 636 | name = "textwrap" 637 | version = "0.11.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 640 | dependencies = [ 641 | "unicode-width", 642 | ] 643 | 644 | [[package]] 645 | name = "thiserror" 646 | version = "1.0.30" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 649 | dependencies = [ 650 | "thiserror-impl", 651 | ] 652 | 653 | [[package]] 654 | name = "thiserror-impl" 655 | version = "1.0.30" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 658 | dependencies = [ 659 | "proc-macro2", 660 | "quote", 661 | "syn", 662 | ] 663 | 664 | [[package]] 665 | name = "toml" 666 | version = "0.5.8" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 669 | dependencies = [ 670 | "serde", 671 | ] 672 | 673 | [[package]] 674 | name = "tracecon" 675 | version = "0.1.0" 676 | dependencies = [ 677 | "anyhow", 678 | "ctrlc", 679 | "libbpf-cargo", 680 | "libbpf-rs", 681 | "libc", 682 | "object", 683 | "plain", 684 | "structopt", 685 | ] 686 | 687 | [[package]] 688 | name = "ucd-trie" 689 | version = "0.1.3" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 692 | 693 | [[package]] 694 | name = "unicode-segmentation" 695 | version = "1.8.0" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 698 | 699 | [[package]] 700 | name = "unicode-width" 701 | version = "0.1.9" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 704 | 705 | [[package]] 706 | name = "unicode-xid" 707 | version = "0.2.2" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 710 | 711 | [[package]] 712 | name = "vec_map" 713 | version = "0.8.2" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 716 | 717 | [[package]] 718 | name = "version_check" 719 | version = "0.9.3" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 722 | 723 | [[package]] 724 | name = "vsprintf" 725 | version = "2.0.0" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "aec2f81b75ca063294776b4f7e8da71d1d5ae81c2b1b149c8d89969230265d63" 728 | dependencies = [ 729 | "cc", 730 | "libc", 731 | ] 732 | 733 | [[package]] 734 | name = "wasi" 735 | version = "0.10.2+wasi-snapshot-preview1" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 738 | 739 | [[package]] 740 | name = "winapi" 741 | version = "0.3.9" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 744 | dependencies = [ 745 | "winapi-i686-pc-windows-gnu", 746 | "winapi-x86_64-pc-windows-gnu", 747 | ] 748 | 749 | [[package]] 750 | name = "winapi-i686-pc-windows-gnu" 751 | version = "0.4.0" 752 | source = "registry+https://github.com/rust-lang/crates.io-index" 753 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 754 | 755 | [[package]] 756 | name = "winapi-x86_64-pc-windows-gnu" 757 | version = "0.4.0" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 760 | -------------------------------------------------------------------------------- /examples/rust/tracecon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tracecon" 3 | version = "0.1.0" 4 | authors = ["Magnus Kulke "] 5 | edition = "2021" 6 | license = "GPL-2.0 OR BSD-3-Clause" 7 | rust-version = "1.71" 8 | 9 | [dependencies] 10 | anyhow = "1.0" 11 | libbpf-rs = "0.24" 12 | libc = "0.2" 13 | structopt = "0.3" 14 | ctrlc = "3.4" 15 | object = "0.36" 16 | plain = "0.2" 17 | 18 | [build-dependencies] 19 | libbpf-cargo = "0.24" 20 | -------------------------------------------------------------------------------- /examples/rust/tracecon/README.md: -------------------------------------------------------------------------------- 1 | # tracecon 2 | 3 | An eBPF sample application, written in C & Rust using 4 | [libbpf-rs](https://github.com/libbpf/libbpf-rs). It will output all 5 | TCPv4 connections that have been established on the host as ips and 6 | hostnames by probing `tcp_v4_connect` in kernel and glibc's `getaddrinfo` 7 | in userland. On a successful host lookup the first result will be stored in 8 | a hashmap, which can be used as a lookup table to retrieve a hostname for 9 | ip_v4 connections. 10 | 11 | ## Requirements 12 | 13 | ### Kernel 14 | 15 | The project is built on technology like `CO-RE` and `BTF`, which is only 16 | available in more recent kernels (5.0-ish). Ubuntu 20.10 has configured and 17 | packaged all the required dependencies. 18 | 19 | ### Compilers 20 | 21 | The project has been tested with LLVM v11 and Rust v1.52.1. 22 | 23 | ### Generate `vmlinux.h` 24 | 25 | ```bash 26 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > src/bpf/vmlinux.h 27 | ``` 28 | 29 | You can verify whether your kernel was built with BTF enabled: 30 | 31 | ```bash 32 | cat /boot/config-$(uname -r) | grep CONFIG_DEBUG_INFO_BTF 33 | ``` 34 | 35 | ## Build 36 | 37 | ### Vagrant 38 | 39 | eBPF is a low-level technology on the Linux kernel. Docker is not a good fit 40 | to build eBPF code on MacOS or Windows environments. On those platforms 41 | Docker ships its own kernel (e.g. linuxkit) and BTF might not be enabled. 42 | 43 | There is a `Vagrantfile` to provision a Ubuntu 20.10 VM including the 44 | necessary dependencies to build the project. To install Vagrant with a 45 | VirtualBox backend and provision the VM on a MacOS host machine run: 46 | 47 | ``` 48 | brew cask install virtualbox 49 | brew cask install vagrant 50 | vagrant up 51 | ``` 52 | 53 | Log in to the machine. The current host workdir is mounted to `/vagrant`: 54 | 55 | ``` 56 | vagrant ssh 57 | sudo su - 58 | cd /vagrant 59 | ``` 60 | 61 | ### Cargo 62 | 63 | ```bash 64 | cargo build 65 | ``` 66 | 67 | ## Run 68 | 69 | Start the program to instrument the eBPF probe and listen to events: 70 | 71 | ```bash 72 | cargo run --release 73 | ``` 74 | 75 | In another shell perform some http calls: 76 | 77 | ```bash 78 | curl -s www.jsonplaceholder.com > /dev/null 79 | # Do not use a dns lookup 80 | curl -s -H "Host: www.jsonplaceholder.com" 172.67.201.157 > /dev/null 81 | ``` 82 | 83 | The other shell should show the respective events: 84 | 85 | ```bash 86 | host event: www.jsonplaceholder.com 87 | ip event: 172.67.201.157 88 | ``` 89 | -------------------------------------------------------------------------------- /examples/rust/tracecon/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure("2") do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://vagrantcloud.com/search. 15 | config.vm.box = "ubuntu/groovy64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | # Create a forwarded port mapping which allows access to a specific port 23 | # within the machine from a port on the host machine. In the example below, 24 | # accessing "localhost:8080" will access port 80 on the guest machine. 25 | # NOTE: This will enable public access to the opened port 26 | # config.vm.network "forwarded_port", guest: 80, host: 8080 27 | 28 | # Create a forwarded port mapping which allows access to a specific port 29 | # within the machine from a port on the host machine and only allow access 30 | # via 127.0.0.1 to disable public access 31 | # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" 32 | 33 | # Create a private network, which allows host-only access to the machine 34 | # using a specific IP. 35 | # config.vm.network "private_network", ip: "192.168.33.10" 36 | 37 | # Create a public network, which generally matched to bridged network. 38 | # Bridged networks make the machine appear as another physical device on 39 | # your network. 40 | # config.vm.network "public_network" 41 | 42 | # Share an additional folder to the guest VM. The first argument is 43 | # the path on the host to the actual folder. The second argument is 44 | # the path on the guest to mount the folder. And the optional third 45 | # argument is a set of non-required options. 46 | # config.vm.synced_folder ".", "/src", type: "rsync" 47 | 48 | # Provider-specific configuration so you can fine-tune various 49 | # backing providers for Vagrant. These expose provider-specific options. 50 | # Example for VirtualBox: 51 | # 52 | config.vm.provider "virtualbox" do |vb| 53 | # Display the VirtualBox GUI when booting the machine 54 | vb.gui = false 55 | 56 | # Customize the amount of memory on the VM: 57 | vb.memory = "16384" 58 | vb.cpus = 4 59 | end 60 | 61 | # 62 | # View the documentation for the provider you are using for more 63 | # information on available options. 64 | 65 | # Enable provisioning with a shell script. Additional provisioners such as 66 | # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the 67 | # documentation for more information about their specific syntax and use. 68 | config.vm.provision "shell", inline: <<-SHELL 69 | apt-get update -y 70 | apt-get install -y \ 71 | build-essential \ 72 | curl \ 73 | libbpf-dev \ 74 | clang \ 75 | libelf-dev \ 76 | linux-tools-$(uname -r) 77 | curl https://sh.rustup.rs -sSf | sh -s -- -y 78 | bpftool btf dump file \ 79 | /sys/kernel/btf/vmlinux \ 80 | format c > /vagrant/src/bpf/vmlinux.h 81 | SHELL 82 | end 83 | -------------------------------------------------------------------------------- /examples/rust/tracecon/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::ffi::OsStr; 3 | use std::path::Path; 4 | use std::path::PathBuf; 5 | 6 | use libbpf_cargo::SkeletonBuilder; 7 | 8 | const SRC: &str = "src/bpf/tracecon.bpf.c"; 9 | 10 | fn main() { 11 | let mut out = 12 | PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR must be set in build script")); 13 | out.push("tracecon.skel.rs"); 14 | 15 | let arch = env::var("CARGO_CFG_TARGET_ARCH") 16 | .expect("CARGO_CFG_TARGET_ARCH must be set in build script"); 17 | 18 | SkeletonBuilder::new() 19 | .source(SRC) 20 | .clang_args([ 21 | OsStr::new("-I"), 22 | Path::new("../../../vmlinux.h/include") 23 | .join(arch) 24 | .as_os_str(), 25 | ]) 26 | .build_and_generate(&out) 27 | .expect("bpf compilation failed"); 28 | println!("cargo:rerun-if-changed={}", SRC); 29 | } 30 | -------------------------------------------------------------------------------- /examples/rust/tracecon/src/bpf/tracecon.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | #include "vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) 8 | #define HOSTNAME_LEN 84 9 | 10 | const volatile pid_t target_pid = 0; 11 | 12 | /* Copied from: include/netdb.h */ 13 | struct addrinfo { 14 | int ai_flags; /* Input flags. */ 15 | int ai_family; /* Protocol family for socket. */ 16 | int ai_socktype; /* Socket type. */ 17 | int ai_protocol; /* Protocol for socket. */ 18 | u32 ai_addrlen; /* Length of socket address. */ 19 | struct sockaddr *ai_addr; /* Socket address for socket. */ 20 | char *ai_canonname; /* Canonical name for service location. */ 21 | struct addrinfo *ai_next; /* Pointer to next in list. */ 22 | }; 23 | 24 | struct lookup { 25 | char c[84]; 26 | struct addrinfo **results; 27 | }; 28 | 29 | struct { 30 | __uint(type, BPF_MAP_TYPE_HASH); 31 | __uint(max_entries, 10240); 32 | __type(key, u32); 33 | __type(value, struct lookup); 34 | } lookups SEC(".maps"); 35 | 36 | struct { 37 | __uint(type, BPF_MAP_TYPE_HASH); 38 | __uint(max_entries, 10240); 39 | __type(key, u32); 40 | __type(value, struct lookup); 41 | } hostnames SEC(".maps"); 42 | 43 | struct { 44 | __uint(type, BPF_MAP_TYPE_HASH); 45 | __uint(max_entries, 10240); 46 | __type(key, u32); 47 | __type(value, struct sock *); 48 | } sockets SEC(".maps"); 49 | 50 | struct { 51 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 52 | __uint(key_size, sizeof(u32)); 53 | __uint(value_size, sizeof(u32)); 54 | } events SEC(".maps"); 55 | 56 | enum tag { IP = 0, HOSTNAME = 1 }; 57 | 58 | struct event { 59 | u8 tag; 60 | u8 ip[4]; 61 | u8 hostname[HOSTNAME_LEN]; 62 | }; 63 | 64 | /* trigger creation of event struct in skeleton code */ 65 | struct event _event = {}; 66 | 67 | static u32 get_tid() 68 | { 69 | u64 tgid = bpf_get_current_pid_tgid(); 70 | pid_t pid = tgid >> 32; 71 | 72 | if (target_pid != 0 && target_pid != pid) 73 | return 0; 74 | return (u32)tgid; 75 | } 76 | 77 | SEC("uprobe/getaddrinfo") 78 | int BPF_KPROBE(getaddrinfo_enter, const char *hostname, const char *service, 79 | const struct addrinfo *hints, struct addrinfo **res) 80 | { 81 | u32 tid = get_tid(); 82 | struct lookup lookup = {}; 83 | 84 | if (!tid) 85 | return 0; 86 | bpf_probe_read_user_str(&lookup.c, sizeof(lookup.c), hostname); 87 | lookup.results = res; 88 | bpf_map_update_elem(&lookups, &tid, &lookup, BPF_ANY); 89 | return 0; 90 | } 91 | 92 | SEC("uretprobe/getaddrinfo") 93 | int BPF_KRETPROBE(getaddrinfo_exit, int ret) 94 | { 95 | u32 tid = get_tid(); 96 | struct lookup *lookup; 97 | struct addrinfo *result; 98 | struct sockaddr_in *addr; 99 | struct in_addr ipv4_addr; 100 | 101 | if (!tid) 102 | return 0; 103 | if (ret != 0) 104 | goto cleanup; 105 | lookup = bpf_map_lookup_elem(&lookups, &tid); 106 | if (!lookup) 107 | return 0; 108 | bpf_probe_read_user(&result, sizeof(result), lookup->results); 109 | bpf_probe_read_user(&addr, sizeof(addr), &result->ai_addr); 110 | bpf_probe_read_user(&ipv4_addr, sizeof(ipv4_addr), &addr->sin_addr); 111 | bpf_map_update_elem(&hostnames, &ipv4_addr.s_addr, lookup, BPF_ANY); 112 | cleanup: 113 | bpf_map_delete_elem(&lookups, &tid); 114 | return 0; 115 | } 116 | 117 | 118 | SEC("kprobe/tcp_v4_connect") 119 | int BPF_KPROBE(tcp_v4_connect_enter, struct sock *sk, struct sockaddr *uaddr, int addr_len) 120 | { 121 | u32 tid = get_tid(); 122 | 123 | if (!tid) 124 | return 0; 125 | bpf_map_update_elem(&sockets, &tid, &sk, 0); 126 | return 0; 127 | }; 128 | 129 | SEC("kretprobe/tcp_v4_connect") 130 | int BPF_KRETPROBE(tcp_v4_connect_exit, int ret) 131 | { 132 | u32 tid = get_tid(); 133 | struct sock **sockpp; 134 | struct lookup *lookup; 135 | struct event event = {}; 136 | u32 ip; 137 | 138 | if (!tid) 139 | return 0; 140 | if (ret != 0) 141 | goto cleanup; 142 | sockpp = bpf_map_lookup_elem(&sockets, &tid); 143 | if (!sockpp) 144 | return 0; 145 | ip = BPF_CORE_READ(*sockpp, __sk_common.skc_daddr); 146 | lookup = bpf_map_lookup_elem(&hostnames, &ip); 147 | if (!lookup) { 148 | event.tag = IP; 149 | memcpy(&event.ip, &ip, sizeof(event.ip)); 150 | } else { 151 | event.tag = HOSTNAME; 152 | memcpy(&event.hostname, &lookup->c, sizeof(lookup->c)); 153 | bpf_map_delete_elem(&hostnames, &ip); 154 | } 155 | /* ctx is implied in the signature macro */ 156 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); 157 | cleanup: 158 | bpf_map_delete_elem(&sockets, &tid); 159 | return 0; 160 | } 161 | 162 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 163 | -------------------------------------------------------------------------------- /examples/rust/tracecon/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, bail, Context, Result}; 2 | use core::time::Duration; 3 | use std::mem::MaybeUninit; 4 | use libbpf_rs::PerfBufferBuilder; 5 | use libbpf_rs::skel::SkelBuilder as _; 6 | use libbpf_rs::skel::OpenSkel as _; 7 | use object::Object; 8 | use object::ObjectSymbol; 9 | use plain::Plain; 10 | use std::fs; 11 | use std::net::Ipv4Addr; 12 | use std::path::Path; 13 | use std::sync::atomic::{AtomicBool, Ordering}; 14 | use std::sync::Arc; 15 | use structopt::StructOpt; 16 | 17 | mod tracecon { 18 | include!(concat!(env!("OUT_DIR"), "/tracecon.skel.rs")); 19 | } 20 | use tracecon::*; 21 | 22 | type Event = tracecon::types::event; 23 | unsafe impl Plain for Event {} 24 | 25 | #[derive(Debug, StructOpt)] 26 | struct Command { 27 | /// verbose output 28 | #[structopt(long, short)] 29 | verbose: bool, 30 | /// glibc path 31 | #[structopt(long, short, default_value = "/lib/x86_64-linux-gnu/libc.so.6")] 32 | glibc: String, 33 | #[structopt(long, short)] 34 | /// pid to observe 35 | pid: Option, 36 | } 37 | 38 | fn bump_memlock_rlimit() -> Result<()> { 39 | let rlimit = libc::rlimit { 40 | rlim_cur: 128 << 20, 41 | rlim_max: 128 << 20, 42 | }; 43 | 44 | if unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlimit) } != 0 { 45 | bail!("Failed to increase rlimit"); 46 | } 47 | 48 | Ok(()) 49 | } 50 | 51 | fn get_symbol_address(so_path: &str, fn_name: &str) -> Result { 52 | let path = Path::new(so_path); 53 | let buffer = 54 | fs::read(path).with_context(|| format!("could not read file `{}`", path.display()))?; 55 | let file = object::File::parse(buffer.as_slice())?; 56 | 57 | let mut symbols = file.dynamic_symbols(); 58 | let symbol = symbols 59 | .find(|symbol| { 60 | if let Ok(name) = symbol.name() { 61 | return name == fn_name; 62 | } 63 | false 64 | }) 65 | .ok_or(anyhow!("symbol not found"))?; 66 | 67 | Ok(symbol.address() as usize) 68 | } 69 | 70 | fn handle_event(_cpu: i32, data: &[u8]) { 71 | let mut event = Event::default(); 72 | plain::copy_from_bytes(&mut event, data).expect("Event data buffer was too short"); 73 | 74 | match event.tag { 75 | 0 => println!("ip event: {}", Ipv4Addr::from(event.ip)), 76 | 1 => println!("host event: {}", String::from_utf8_lossy(&event.hostname)), 77 | _ => {} 78 | } 79 | } 80 | 81 | fn main() -> Result<()> { 82 | let opts = Command::from_args(); 83 | 84 | let mut skel_builder = TraceconSkelBuilder::default(); 85 | if opts.verbose { 86 | skel_builder.obj_builder.debug(true); 87 | } 88 | 89 | bump_memlock_rlimit()?; 90 | let mut open_object = MaybeUninit::uninit(); 91 | let open_skel = skel_builder.open(&mut open_object)?; 92 | if let Some(pid) = opts.pid { 93 | open_skel.maps.rodata_data.target_pid = pid; 94 | } 95 | let skel = open_skel.load()?; 96 | let address = get_symbol_address(&opts.glibc, "getaddrinfo")?; 97 | 98 | let _uprobe = 99 | skel.progs 100 | .getaddrinfo_enter 101 | .attach_uprobe(false, -1, &opts.glibc, address)?; 102 | 103 | let _uretprobe = 104 | skel.progs 105 | .getaddrinfo_exit 106 | .attach_uprobe(true, -1, &opts.glibc, address)?; 107 | 108 | let _kprobe = skel 109 | .progs 110 | .tcp_v4_connect_enter 111 | .attach_kprobe(false, "tcp_v4_connect")?; 112 | 113 | let _kretprobe = skel 114 | .progs 115 | .tcp_v4_connect_exit 116 | .attach_kprobe(true, "tcp_v4_connect")?; 117 | 118 | let perf = PerfBufferBuilder::new(&skel.maps.events) 119 | .sample_cb(handle_event) 120 | .build()?; 121 | 122 | let running = Arc::new(AtomicBool::new(true)); 123 | let r = running.clone(); 124 | ctrlc::set_handler(move || { 125 | r.store(false, Ordering::SeqCst); 126 | })?; 127 | 128 | while running.load(Ordering::SeqCst) { 129 | perf.poll(Duration::from_millis(100))?; 130 | } 131 | 132 | Ok(()) 133 | } 134 | -------------------------------------------------------------------------------- /examples/rust/xdp/.gitignore: -------------------------------------------------------------------------------- 1 | /src/bpf/.output 2 | /target 3 | -------------------------------------------------------------------------------- /examples/rust/xdp/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.11.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "anyhow" 25 | version = "1.0.40" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" 28 | 29 | [[package]] 30 | name = "atty" 31 | version = "0.2.14" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 34 | dependencies = [ 35 | "hermit-abi", 36 | "libc", 37 | "winapi", 38 | ] 39 | 40 | [[package]] 41 | name = "autocfg" 42 | version = "1.0.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 45 | 46 | [[package]] 47 | name = "bitflags" 48 | version = "1.3.2" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 51 | 52 | [[package]] 53 | name = "cargo-platform" 54 | version = "0.1.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" 57 | dependencies = [ 58 | "serde", 59 | ] 60 | 61 | [[package]] 62 | name = "cargo_metadata" 63 | version = "0.12.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f" 66 | dependencies = [ 67 | "cargo-platform", 68 | "semver 0.11.0", 69 | "semver-parser", 70 | "serde", 71 | "serde_json", 72 | ] 73 | 74 | [[package]] 75 | name = "cc" 76 | version = "1.0.67" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" 79 | 80 | [[package]] 81 | name = "cfg-if" 82 | version = "1.0.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 85 | 86 | [[package]] 87 | name = "clap" 88 | version = "2.33.3" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 91 | dependencies = [ 92 | "ansi_term", 93 | "atty", 94 | "bitflags", 95 | "strsim", 96 | "textwrap", 97 | "unicode-width", 98 | "vec_map", 99 | ] 100 | 101 | [[package]] 102 | name = "ctrlc" 103 | version = "3.1.9" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "232295399409a8b7ae41276757b5a1cc21032848d42bff2352261f958b3ca29a" 106 | dependencies = [ 107 | "nix 0.20.0", 108 | "winapi", 109 | ] 110 | 111 | [[package]] 112 | name = "derivative" 113 | version = "2.2.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" 116 | dependencies = [ 117 | "proc-macro2", 118 | "quote", 119 | "syn", 120 | ] 121 | 122 | [[package]] 123 | name = "getrandom" 124 | version = "0.2.3" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 127 | dependencies = [ 128 | "cfg-if", 129 | "libc", 130 | "wasi", 131 | ] 132 | 133 | [[package]] 134 | name = "heck" 135 | version = "0.3.2" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 138 | dependencies = [ 139 | "unicode-segmentation", 140 | ] 141 | 142 | [[package]] 143 | name = "hermit-abi" 144 | version = "0.1.18" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 147 | dependencies = [ 148 | "libc", 149 | ] 150 | 151 | [[package]] 152 | name = "itoa" 153 | version = "0.4.8" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" 156 | 157 | [[package]] 158 | name = "lazy_static" 159 | version = "1.4.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 162 | 163 | [[package]] 164 | name = "libbpf-cargo" 165 | version = "0.9.3" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "23536bf02ff6387e7bb68055f6ad52d75c20d77b2ab6535977fc1e771041286c" 168 | dependencies = [ 169 | "anyhow", 170 | "cargo_metadata", 171 | "libbpf-sys", 172 | "memmap2", 173 | "num_enum", 174 | "regex", 175 | "scroll", 176 | "scroll_derive", 177 | "semver 1.0.4", 178 | "serde", 179 | "serde_json", 180 | "structopt", 181 | "tempfile", 182 | "thiserror", 183 | ] 184 | 185 | [[package]] 186 | name = "libbpf-rs" 187 | version = "0.14.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "87d1f46d8d976bba68a9b772138e2307cfd7432bf66f3e1c83b3944e83ab9002" 190 | dependencies = [ 191 | "bitflags", 192 | "libbpf-sys", 193 | "nix 0.22.0", 194 | "num_enum", 195 | "strum_macros", 196 | "thiserror", 197 | "vsprintf", 198 | ] 199 | 200 | [[package]] 201 | name = "libbpf-sys" 202 | version = "0.5.0-2" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "97982339699ecfda9742ae39311fac5a6cce5ac7330bcf714cb702a49ecc8e2a" 205 | dependencies = [ 206 | "cc", 207 | "pkg-config", 208 | ] 209 | 210 | [[package]] 211 | name = "libc" 212 | version = "0.2.107" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" 215 | 216 | [[package]] 217 | name = "memchr" 218 | version = "2.4.1" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 221 | 222 | [[package]] 223 | name = "memmap2" 224 | version = "0.3.1" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" 227 | dependencies = [ 228 | "libc", 229 | ] 230 | 231 | [[package]] 232 | name = "memoffset" 233 | version = "0.6.4" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" 236 | dependencies = [ 237 | "autocfg", 238 | ] 239 | 240 | [[package]] 241 | name = "nix" 242 | version = "0.20.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" 245 | dependencies = [ 246 | "bitflags", 247 | "cc", 248 | "cfg-if", 249 | "libc", 250 | ] 251 | 252 | [[package]] 253 | name = "nix" 254 | version = "0.22.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" 257 | dependencies = [ 258 | "bitflags", 259 | "cc", 260 | "cfg-if", 261 | "libc", 262 | "memoffset", 263 | ] 264 | 265 | [[package]] 266 | name = "num_enum" 267 | version = "0.5.1" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" 270 | dependencies = [ 271 | "derivative", 272 | "num_enum_derive", 273 | ] 274 | 275 | [[package]] 276 | name = "num_enum_derive" 277 | version = "0.5.1" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" 280 | dependencies = [ 281 | "proc-macro-crate", 282 | "proc-macro2", 283 | "quote", 284 | "syn", 285 | ] 286 | 287 | [[package]] 288 | name = "pest" 289 | version = "2.1.3" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 292 | dependencies = [ 293 | "ucd-trie", 294 | ] 295 | 296 | [[package]] 297 | name = "pkg-config" 298 | version = "0.3.22" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" 301 | 302 | [[package]] 303 | name = "ppv-lite86" 304 | version = "0.2.15" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" 307 | 308 | [[package]] 309 | name = "proc-macro-crate" 310 | version = "0.1.5" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" 313 | dependencies = [ 314 | "toml", 315 | ] 316 | 317 | [[package]] 318 | name = "proc-macro-error" 319 | version = "1.0.4" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 322 | dependencies = [ 323 | "proc-macro-error-attr", 324 | "proc-macro2", 325 | "quote", 326 | "syn", 327 | "version_check", 328 | ] 329 | 330 | [[package]] 331 | name = "proc-macro-error-attr" 332 | version = "1.0.4" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 335 | dependencies = [ 336 | "proc-macro2", 337 | "quote", 338 | "version_check", 339 | ] 340 | 341 | [[package]] 342 | name = "proc-macro2" 343 | version = "1.0.26" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" 346 | dependencies = [ 347 | "unicode-xid", 348 | ] 349 | 350 | [[package]] 351 | name = "quote" 352 | version = "1.0.9" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 355 | dependencies = [ 356 | "proc-macro2", 357 | ] 358 | 359 | [[package]] 360 | name = "rand" 361 | version = "0.8.4" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 364 | dependencies = [ 365 | "libc", 366 | "rand_chacha", 367 | "rand_core", 368 | "rand_hc", 369 | ] 370 | 371 | [[package]] 372 | name = "rand_chacha" 373 | version = "0.3.1" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 376 | dependencies = [ 377 | "ppv-lite86", 378 | "rand_core", 379 | ] 380 | 381 | [[package]] 382 | name = "rand_core" 383 | version = "0.6.3" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 386 | dependencies = [ 387 | "getrandom", 388 | ] 389 | 390 | [[package]] 391 | name = "rand_hc" 392 | version = "0.3.1" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 395 | dependencies = [ 396 | "rand_core", 397 | ] 398 | 399 | [[package]] 400 | name = "redox_syscall" 401 | version = "0.2.10" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 404 | dependencies = [ 405 | "bitflags", 406 | ] 407 | 408 | [[package]] 409 | name = "regex" 410 | version = "1.5.4" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 413 | dependencies = [ 414 | "aho-corasick", 415 | "memchr", 416 | "regex-syntax", 417 | ] 418 | 419 | [[package]] 420 | name = "regex-syntax" 421 | version = "0.6.25" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 424 | 425 | [[package]] 426 | name = "remove_dir_all" 427 | version = "0.5.3" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 430 | dependencies = [ 431 | "winapi", 432 | ] 433 | 434 | [[package]] 435 | name = "ryu" 436 | version = "1.0.5" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 439 | 440 | [[package]] 441 | name = "scroll" 442 | version = "0.10.2" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" 445 | 446 | [[package]] 447 | name = "scroll_derive" 448 | version = "0.10.5" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" 451 | dependencies = [ 452 | "proc-macro2", 453 | "quote", 454 | "syn", 455 | ] 456 | 457 | [[package]] 458 | name = "semver" 459 | version = "0.11.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 462 | dependencies = [ 463 | "semver-parser", 464 | "serde", 465 | ] 466 | 467 | [[package]] 468 | name = "semver" 469 | version = "1.0.4" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" 472 | 473 | [[package]] 474 | name = "semver-parser" 475 | version = "0.10.2" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 478 | dependencies = [ 479 | "pest", 480 | ] 481 | 482 | [[package]] 483 | name = "serde" 484 | version = "1.0.125" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" 487 | dependencies = [ 488 | "serde_derive", 489 | ] 490 | 491 | [[package]] 492 | name = "serde_derive" 493 | version = "1.0.125" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" 496 | dependencies = [ 497 | "proc-macro2", 498 | "quote", 499 | "syn", 500 | ] 501 | 502 | [[package]] 503 | name = "serde_json" 504 | version = "1.0.69" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" 507 | dependencies = [ 508 | "itoa", 509 | "ryu", 510 | "serde", 511 | ] 512 | 513 | [[package]] 514 | name = "strsim" 515 | version = "0.8.0" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 518 | 519 | [[package]] 520 | name = "structopt" 521 | version = "0.3.21" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" 524 | dependencies = [ 525 | "clap", 526 | "lazy_static", 527 | "structopt-derive", 528 | ] 529 | 530 | [[package]] 531 | name = "structopt-derive" 532 | version = "0.4.14" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" 535 | dependencies = [ 536 | "heck", 537 | "proc-macro-error", 538 | "proc-macro2", 539 | "quote", 540 | "syn", 541 | ] 542 | 543 | [[package]] 544 | name = "strum_macros" 545 | version = "0.21.1" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" 548 | dependencies = [ 549 | "heck", 550 | "proc-macro2", 551 | "quote", 552 | "syn", 553 | ] 554 | 555 | [[package]] 556 | name = "syn" 557 | version = "1.0.69" 558 | source = "registry+https://github.com/rust-lang/crates.io-index" 559 | checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" 560 | dependencies = [ 561 | "proc-macro2", 562 | "quote", 563 | "unicode-xid", 564 | ] 565 | 566 | [[package]] 567 | name = "tempfile" 568 | version = "3.2.0" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 571 | dependencies = [ 572 | "cfg-if", 573 | "libc", 574 | "rand", 575 | "redox_syscall", 576 | "remove_dir_all", 577 | "winapi", 578 | ] 579 | 580 | [[package]] 581 | name = "textwrap" 582 | version = "0.11.0" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 585 | dependencies = [ 586 | "unicode-width", 587 | ] 588 | 589 | [[package]] 590 | name = "thiserror" 591 | version = "1.0.24" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" 594 | dependencies = [ 595 | "thiserror-impl", 596 | ] 597 | 598 | [[package]] 599 | name = "thiserror-impl" 600 | version = "1.0.24" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" 603 | dependencies = [ 604 | "proc-macro2", 605 | "quote", 606 | "syn", 607 | ] 608 | 609 | [[package]] 610 | name = "toml" 611 | version = "0.5.8" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" 614 | dependencies = [ 615 | "serde", 616 | ] 617 | 618 | [[package]] 619 | name = "ucd-trie" 620 | version = "0.1.3" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 623 | 624 | [[package]] 625 | name = "unicode-segmentation" 626 | version = "1.7.1" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 629 | 630 | [[package]] 631 | name = "unicode-width" 632 | version = "0.1.8" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 635 | 636 | [[package]] 637 | name = "unicode-xid" 638 | version = "0.2.1" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 641 | 642 | [[package]] 643 | name = "vec_map" 644 | version = "0.8.2" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 647 | 648 | [[package]] 649 | name = "version_check" 650 | version = "0.9.3" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 653 | 654 | [[package]] 655 | name = "vsprintf" 656 | version = "2.0.0" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "aec2f81b75ca063294776b4f7e8da71d1d5ae81c2b1b149c8d89969230265d63" 659 | dependencies = [ 660 | "cc", 661 | "libc", 662 | ] 663 | 664 | [[package]] 665 | name = "wasi" 666 | version = "0.10.2+wasi-snapshot-preview1" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 669 | 670 | [[package]] 671 | name = "winapi" 672 | version = "0.3.9" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 675 | dependencies = [ 676 | "winapi-i686-pc-windows-gnu", 677 | "winapi-x86_64-pc-windows-gnu", 678 | ] 679 | 680 | [[package]] 681 | name = "winapi-i686-pc-windows-gnu" 682 | version = "0.4.0" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 685 | 686 | [[package]] 687 | name = "winapi-x86_64-pc-windows-gnu" 688 | version = "0.4.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 691 | 692 | [[package]] 693 | name = "xdp" 694 | version = "0.1.0" 695 | dependencies = [ 696 | "anyhow", 697 | "ctrlc", 698 | "libbpf-cargo", 699 | "libbpf-rs", 700 | "libc", 701 | "structopt", 702 | ] 703 | -------------------------------------------------------------------------------- /examples/rust/xdp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xdp" 3 | version = "0.1.0" 4 | authors = ["Hengqi Chen "] 5 | edition = "2021" 6 | rust-version = "1.71" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | anyhow = "1.0" 12 | ctrlc = { version = "3.4", features = ["termination"] } 13 | libc = "0.2" 14 | libbpf-rs = "0.24" 15 | structopt = "0.3" 16 | 17 | [build-dependencies] 18 | libbpf-cargo = "0.24" 19 | -------------------------------------------------------------------------------- /examples/rust/xdp/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::ffi::OsStr; 3 | use std::path::Path; 4 | use std::path::PathBuf; 5 | 6 | use libbpf_cargo::SkeletonBuilder; 7 | 8 | const SRC: &str = "src/bpf/xdppass.bpf.c"; 9 | 10 | fn main() { 11 | let mut out = 12 | PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR must be set in build script")); 13 | out.push("xdppass.skel.rs"); 14 | 15 | let arch = env::var("CARGO_CFG_TARGET_ARCH") 16 | .expect("CARGO_CFG_TARGET_ARCH must be set in build script"); 17 | 18 | SkeletonBuilder::new() 19 | .source(SRC) 20 | .clang_args([ 21 | OsStr::new("-I"), 22 | Path::new("../../../vmlinux.h/include") 23 | .join(arch) 24 | .as_os_str(), 25 | ]) 26 | .build_and_generate(out) 27 | .unwrap(); 28 | println!("cargo:rerun-if-changed={}", SRC); 29 | } 30 | -------------------------------------------------------------------------------- /examples/rust/xdp/src/bpf/xdppass.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | SEC("xdp") 5 | int xdp_pass(struct xdp_md *ctx) 6 | { 7 | void *data = (void *)(long)ctx->data; 8 | void *data_end = (void *)(long)ctx->data_end; 9 | int pkt_sz = data_end - data; 10 | 11 | bpf_printk("packet size: %d", pkt_sz); 12 | return XDP_PASS; 13 | } 14 | 15 | char __license[] SEC("license") = "GPL"; 16 | -------------------------------------------------------------------------------- /examples/rust/xdp/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use std::sync::atomic::{AtomicBool, Ordering}; 3 | use std::sync::Arc; 4 | use std::{thread, time}; 5 | 6 | use anyhow::{bail, Result}; 7 | use libbpf_rs::skel::OpenSkel as _; 8 | use libbpf_rs::skel::SkelBuilder as _; 9 | use structopt::StructOpt; 10 | 11 | mod xdppass { 12 | include!(concat!(env!("OUT_DIR"), "/xdppass.skel.rs")); 13 | } 14 | use xdppass::*; 15 | 16 | #[derive(Debug, StructOpt)] 17 | struct Command { 18 | /// Interface index to attach XDP program 19 | #[structopt(default_value = "0")] 20 | ifindex: i32, 21 | } 22 | 23 | fn bump_memlock_rlimit() -> Result<()> { 24 | let rlimit = libc::rlimit { 25 | rlim_cur: 128 << 20, 26 | rlim_max: 128 << 20, 27 | }; 28 | 29 | if unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlimit) } != 0 { 30 | bail!("Failed to increase rlimit"); 31 | } 32 | 33 | Ok(()) 34 | } 35 | 36 | fn main() -> Result<()> { 37 | let opts = Command::from_args(); 38 | 39 | bump_memlock_rlimit()?; 40 | 41 | let skel_builder = XdppassSkelBuilder::default(); 42 | let mut open_object = MaybeUninit::uninit(); 43 | let open_skel = skel_builder.open(&mut open_object)?; 44 | let mut skel = open_skel.load()?; 45 | let link = skel.progs.xdp_pass.attach_xdp(opts.ifindex)?; 46 | skel.links = XdppassLinks { 47 | xdp_pass: Some(link), 48 | }; 49 | 50 | let running = Arc::new(AtomicBool::new(true)); 51 | let r = running.clone(); 52 | ctrlc::set_handler(move || { 53 | r.store(false, Ordering::SeqCst); 54 | })?; 55 | 56 | while running.load(Ordering::SeqCst) { 57 | eprint!("."); 58 | thread::sleep(time::Duration::from_secs(1)); 59 | } 60 | 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /tools/cmake/FindBpfObject.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | #[=======================================================================[.rst: 4 | FindBpfObject 5 | -------- 6 | 7 | Find BpfObject 8 | 9 | This module finds if all the dependencies for eBPF Compile-Once-Run-Everywhere 10 | programs are available and where all the components are located. 11 | 12 | The caller may set the following variables to disable automatic 13 | search/processing for the associated component: 14 | 15 | ``BPFOBJECT_BPFTOOL_EXE`` 16 | Path to ``bpftool`` binary 17 | 18 | ``BPFOBJECT_CLANG_EXE`` 19 | Path to ``clang`` binary 20 | 21 | ``LIBBPF_INCLUDE_DIRS`` 22 | Path to ``libbpf`` development headers 23 | 24 | ``LIBBPF_LIBRARIES`` 25 | Path to `libbpf` library 26 | 27 | ``BPFOBJECT_VMLINUX_H`` 28 | Path to ``vmlinux.h`` generated by ``bpftool``. If unset, this module will 29 | attempt to automatically generate a copy. 30 | 31 | This module sets the following result variables: 32 | 33 | :: 34 | 35 | BpfObject_FOUND = TRUE if all components are found 36 | 37 | 38 | This module also provides the ``bpf_object()`` macro. This macro generates a 39 | cmake interface library for the BPF object's generated skeleton as well 40 | as the associated dependencies. 41 | 42 | .. code-block:: cmake 43 | 44 | bpf_object( [
...]) 45 | 46 | Given an abstract ```` for a BPF object and the associated ```` 47 | file, generates an interface library target, ``_skel``, that may be 48 | linked against by other cmake targets. Additional headers may be provided to 49 | the macro to ensure that the generated skeleton is up-to-date. 50 | 51 | Example Usage: 52 | 53 | :: 54 | 55 | find_package(BpfObject REQUIRED) 56 | bpf_object(myobject myobject.bpf.c myobject.h) 57 | add_executable(myapp myapp.c) 58 | target_link_libraries(myapp myobject_skel) 59 | 60 | #]=======================================================================] 61 | 62 | if(NOT BPFOBJECT_BPFTOOL_EXE) 63 | find_program(BPFOBJECT_BPFTOOL_EXE NAMES bpftool DOC "Path to bpftool executable") 64 | endif() 65 | 66 | if(NOT BPFOBJECT_CLANG_EXE) 67 | find_program(BPFOBJECT_CLANG_EXE NAMES clang DOC "Path to clang executable") 68 | 69 | execute_process(COMMAND ${BPFOBJECT_CLANG_EXE} --version 70 | OUTPUT_VARIABLE CLANG_version_output 71 | ERROR_VARIABLE CLANG_version_error 72 | RESULT_VARIABLE CLANG_version_result 73 | OUTPUT_STRIP_TRAILING_WHITESPACE) 74 | 75 | # Check that clang is new enough 76 | if(${CLANG_version_result} EQUAL 0) 77 | if("${CLANG_version_output}" MATCHES "clang version ([^\n]+)\n") 78 | # Transform X.Y.Z into X;Y;Z which can then be interpreted as a list 79 | set(CLANG_VERSION "${CMAKE_MATCH_1}") 80 | string(REPLACE "." ";" CLANG_VERSION_LIST ${CLANG_VERSION}) 81 | list(GET CLANG_VERSION_LIST 0 CLANG_VERSION_MAJOR) 82 | 83 | # Anything older than clang 10 doesn't really work 84 | string(COMPARE LESS ${CLANG_VERSION_MAJOR} 10 CLANG_VERSION_MAJOR_LT10) 85 | if(${CLANG_VERSION_MAJOR_LT10}) 86 | message(FATAL_ERROR "clang ${CLANG_VERSION} is too old for BPF CO-RE") 87 | endif() 88 | 89 | message(STATUS "Found clang version: ${CLANG_VERSION}") 90 | else() 91 | message(FATAL_ERROR "Failed to parse clang version string: ${CLANG_version_output}") 92 | endif() 93 | else() 94 | message(FATAL_ERROR "Command \"${BPFOBJECT_CLANG_EXE} --version\" failed with output:\n${CLANG_version_error}") 95 | endif() 96 | endif() 97 | 98 | if(NOT LIBBPF_INCLUDE_DIRS OR NOT LIBBPF_LIBRARIES) 99 | find_package(LibBpf) 100 | endif() 101 | 102 | if(BPFOBJECT_VMLINUX_H) 103 | get_filename_component(GENERATED_VMLINUX_DIR ${BPFOBJECT_VMLINUX_H} DIRECTORY) 104 | elseif(BPFOBJECT_BPFTOOL_EXE) 105 | # Generate vmlinux.h 106 | set(GENERATED_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR}) 107 | set(BPFOBJECT_VMLINUX_H ${GENERATED_VMLINUX_DIR}/vmlinux.h) 108 | add_custom_command(OUTPUT ${BPFOBJECT_VMLINUX_H} 109 | COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c > ${BPFOBJECT_VMLINUX_H} 110 | DEPENDS ${BPFOBJECT_BPFTOOL_EXE} 111 | VERBATIM 112 | COMMENT "[vmlinux] Generating header: ${BPFOBJECT_VMLINUX_H}" 113 | ) 114 | endif() 115 | 116 | include(FindPackageHandleStandardArgs) 117 | find_package_handle_standard_args(BpfObject 118 | REQUIRED_VARS 119 | BPFOBJECT_BPFTOOL_EXE 120 | BPFOBJECT_CLANG_EXE 121 | LIBBPF_INCLUDE_DIRS 122 | LIBBPF_LIBRARIES 123 | BPFOBJECT_VMLINUX_H 124 | GENERATED_VMLINUX_DIR) 125 | 126 | # Get clang bpf system includes 127 | execute_process( 128 | COMMAND bash -c "${BPFOBJECT_CLANG_EXE} -v -E - < /dev/null 2>&1 | 129 | sed -n '/<...> search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }'" 130 | OUTPUT_VARIABLE CLANG_SYSTEM_INCLUDES_output 131 | ERROR_VARIABLE CLANG_SYSTEM_INCLUDES_error 132 | RESULT_VARIABLE CLANG_SYSTEM_INCLUDES_result 133 | OUTPUT_STRIP_TRAILING_WHITESPACE) 134 | if(${CLANG_SYSTEM_INCLUDES_result} EQUAL 0) 135 | separate_arguments(CLANG_SYSTEM_INCLUDES UNIX_COMMAND ${CLANG_SYSTEM_INCLUDES_output}) 136 | message(STATUS "BPF system include flags: ${CLANG_SYSTEM_INCLUDES}") 137 | else() 138 | message(FATAL_ERROR "Failed to determine BPF system includes: ${CLANG_SYSTEM_INCLUDES_error}") 139 | endif() 140 | 141 | # Get target arch 142 | execute_process(COMMAND uname -m 143 | COMMAND sed -e "s/x86_64/x86/" -e "s/aarch64/arm64/" -e "s/ppc64le/powerpc/" -e "s/mips.*/mips/" -e "s/riscv64/riscv/" 144 | OUTPUT_VARIABLE ARCH_output 145 | ERROR_VARIABLE ARCH_error 146 | RESULT_VARIABLE ARCH_result 147 | OUTPUT_STRIP_TRAILING_WHITESPACE) 148 | if(${ARCH_result} EQUAL 0) 149 | set(ARCH ${ARCH_output}) 150 | message(STATUS "BPF target arch: ${ARCH}") 151 | else() 152 | message(FATAL_ERROR "Failed to determine target architecture: ${ARCH_error}") 153 | endif() 154 | 155 | # Public macro 156 | macro(bpf_object name input) 157 | set(BPF_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${input}) 158 | foreach(arg ${ARGN}) 159 | list(APPEND BPF_H_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${arg}) 160 | endforeach() 161 | set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.bpf.o) 162 | set(BPF_SKEL_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.skel.h) 163 | set(OUTPUT_TARGET ${name}_skel) 164 | 165 | # Build BPF object file 166 | add_custom_command(OUTPUT ${BPF_O_FILE} 167 | COMMAND ${BPFOBJECT_CLANG_EXE} -g -O2 -target bpf -D__TARGET_ARCH_${ARCH} 168 | ${CLANG_SYSTEM_INCLUDES} -I${GENERATED_VMLINUX_DIR} 169 | -isystem ${LIBBPF_INCLUDE_DIRS} -c ${BPF_C_FILE} -o ${BPF_O_FILE} 170 | COMMAND_EXPAND_LISTS 171 | VERBATIM 172 | DEPENDS ${BPF_C_FILE} ${BPF_H_FILES} ${BPFOBJECT_VMLINUX_H} 173 | COMMENT "[clang] Building BPF object: ${name}") 174 | 175 | # Build BPF skeleton header 176 | add_custom_command(OUTPUT ${BPF_SKEL_FILE} 177 | COMMAND bash -c "${BPFOBJECT_BPFTOOL_EXE} gen skeleton ${BPF_O_FILE} > ${BPF_SKEL_FILE}" 178 | VERBATIM 179 | DEPENDS ${BPF_O_FILE} 180 | COMMENT "[skel] Building BPF skeleton: ${name}") 181 | 182 | add_library(${OUTPUT_TARGET} INTERFACE) 183 | target_sources(${OUTPUT_TARGET} INTERFACE ${BPF_SKEL_FILE}) 184 | target_include_directories(${OUTPUT_TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) 185 | target_include_directories(${OUTPUT_TARGET} SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS}) 186 | target_link_libraries(${OUTPUT_TARGET} INTERFACE ${LIBBPF_LIBRARIES} -lelf -lz) 187 | endmacro() 188 | -------------------------------------------------------------------------------- /tools/cmake/FindLibBpf.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | find_path(LIBBPF_INCLUDE_DIRS 4 | NAMES 5 | bpf/bpf.h 6 | bpf/btf.h 7 | bpf/libbpf.h 8 | PATHS 9 | /usr/include 10 | /usr/local/include 11 | /opt/local/include 12 | /sw/include 13 | ENV CPATH) 14 | 15 | find_library(LIBBPF_LIBRARIES 16 | NAMES 17 | bpf 18 | PATHS 19 | /usr/lib 20 | /usr/local/lib 21 | /opt/local/lib 22 | /sw/lib 23 | ENV LIBRARY_PATH 24 | ENV LD_LIBRARY_PATH) 25 | 26 | include (FindPackageHandleStandardArgs) 27 | 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf "Please install the libbpf development package" 29 | LIBBPF_LIBRARIES 30 | LIBBPF_INCLUDE_DIRS) 31 | 32 | mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES) 33 | -------------------------------------------------------------------------------- /tools/gen_vmlinux_h.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | $(dirname "$0")/bpftool btf dump file ${1:-/sys/kernel/btf/vmlinux} format c 4 | --------------------------------------------------------------------------------