diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c744194 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,66 @@ +name: ci +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build + runs-on: ubuntu-22.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Setup Java 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'adopt' + + - name: mvn package + run: mvn package -DskipTests + + - name: upload bcc/target/bcc.jar + uses: actions/upload-artifact@v4 + with: + name: bcc.jar + path: bcc/target/bcc.jar + + vm-test: + name: Run tests on pre-built kernel + runs-on: ubuntu-22.04 + needs: build + timeout-minutes: 10 + strategy: + matrix: + version: ["6.6"] + env: + HBT_KERNEL_VERSION: "${{ matrix.version }}" + steps: + - uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'adopt' + + - run: sudo pip3 install https://github.com/amluto/virtme/archive/beb85146cd91de37ae455eccb6ab67c393e6e290.zip + - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu-system-x86 bpfcc-tools libbpfcc libbpfcc-dev linux-headers-$(uname -r) linux-cloud-tools-generic + + - name: Test + run: | + mvn test -Djvm=testutil/bin/java + + - name: Publish Test Report + uses: mikepenz/action-junit-report@v4 + if: always() + with: + check_name: 'Test Report (Kernel ${{ matrix.KERNEL_VERSION }})' + report_paths: '**/build/test-results/test/TEST-*.xml' \ No newline at end of file diff --git a/README.md b/README.md index 2c8b1b5..dfa1c5b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Hello eBPF ========== +[![ci](https://github.com/parttimenerd/hello-ebpf/actions/workflows/ci.yml/badge.svg)](https://github.com/parttimenerd/hello-ebpf/actions/workflows/ci.yml) + There are [user land libraries](https://ebpf.io/what-is-ebpf/#development-toolchains) for [eBPF](https://ebpf.io) that allow you to write eBPF applications in C++, Rust, Go, Python and even Lua. But there are none for Java, which is a pity. @@ -221,6 +223,7 @@ This requires [virtme](https://github.com/ezequielgarcia/virtme) (`apt install v You can run custom commands in the container using `testutil/run-in-container.sh`. Read more in the [testutil/README.md](testutil/README.md). +I'm unable to get it running in the CI, so I'm currently running the tests locally. Contributing ------------ diff --git a/bcc/pom.xml b/bcc/pom.xml index ce1cb76..4b49533 100644 --- a/bcc/pom.xml +++ b/bcc/pom.xml @@ -120,7 +120,7 @@ me.bechberger rawbcc - 0.1.1 + 0.1.2 org.junit.jupiter diff --git a/bcc/src/main/java/me/bechberger/ebpf/bcc/BPF.java b/bcc/src/main/java/me/bechberger/ebpf/bcc/BPF.java index 58f4137..3fbe0c3 100644 --- a/bcc/src/main/java/me/bechberger/ebpf/bcc/BPF.java +++ b/bcc/src/main/java/me/bechberger/ebpf/bcc/BPF.java @@ -142,10 +142,13 @@ private BPF(String text, String fileName, @Nullable Path hdrFile, boolean allowR cflags_array, len(cflags_array), allow_rlimit, device) */ - var maybeModule = bpf_module_create_c_from_string(arena, textNative, debug, MemorySegment.NULL, 0, allowRLimit ? 1 : 0, MemorySegment.NULL); + var maybeModule = bpf_module_create_c_from_string(arena, textNative, -1, MemorySegment.NULL, 0, allowRLimit ? 1 : 0, MemorySegment.NULL); if (maybeModule.err() != 0 && maybeModule.err() != 2) { throw new BPFCallException(STR."Failed to compile BPF module: \{PanamaUtil.errnoString(maybeModule.err())}"); } + if (maybeModule.err() != 0) { + System.err.println(STR."Warning BPF constructor: \{fileName} \{maybeModule.err()} \{PanamaUtil.errnoString(maybeModule.err())}"); + } module = maybeModule.result(); if (module == null) throw new RuntimeException(STR."Failed to compile BPF module \{fileName}"); diff --git a/bcc/src/test/java/me/bechberger/ebpf/bcc/structs/HelloBufferTest.java b/bcc/src/test/java/me/bechberger/ebpf/bcc/structs/HelloBufferTest.java index af0086f..ad6c76e 100644 --- a/bcc/src/test/java/me/bechberger/ebpf/bcc/structs/HelloBufferTest.java +++ b/bcc/src/test/java/me/bechberger/ebpf/bcc/structs/HelloBufferTest.java @@ -63,7 +63,7 @@ int hello(void *ctx) { return 0; } - """).build()) { + """).withDebug(-1).build()) { var syscall = b.get_syscall_fnname("execve"); b.attach_kprobe(syscall, "hello"); diff --git a/rawbcc/CHANGELOG b/rawbcc/CHANGELOG index 2867307..db54650 100644 --- a/rawbcc/CHANGELOG +++ b/rawbcc/CHANGELOG @@ -1,5 +1,13 @@ # Changelog +## 0.1.2 + +### Fixed +- Fix loading libraries if the library ends with a version number, like `libbcc.so.0` + +### Changed +- Delegate loading of libraries to `System.load` if possible + ## 0.1.1 ### Fixed diff --git a/rawbcc/README.md b/rawbcc/README.md index 5de6b0c..dbb6210 100644 --- a/rawbcc/README.md +++ b/rawbcc/README.md @@ -12,7 +12,7 @@ These bindings are regurlarly updated and published on Maven Central: me.bechberger rawbcc - 0.1.1 + 0.1.2 ``` diff --git a/rawbcc/pom.xml b/rawbcc/pom.xml index ce4bc6c..2a61659 100644 --- a/rawbcc/pom.xml +++ b/rawbcc/pom.xml @@ -8,7 +8,7 @@ me.bechberger Raw bindings for libbcc - 0.1.1 + 0.1.2 https://github.com/parttimenerd/hello-ebpf diff --git a/rawbcc/src/main/java/me/bechberger/ebpf/bcc/raw/LibraryLoader.java b/rawbcc/src/main/java/me/bechberger/ebpf/bcc/raw/LibraryLoader.java index 7c3936a..b05b129 100644 --- a/rawbcc/src/main/java/me/bechberger/ebpf/bcc/raw/LibraryLoader.java +++ b/rawbcc/src/main/java/me/bechberger/ebpf/bcc/raw/LibraryLoader.java @@ -5,32 +5,36 @@ import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; import java.util.Optional; +import java.util.stream.Stream; /** * Loads the BCC library */ public class LibraryLoader { + + private static Optional findLibInFolder(Path folder, int depth) { + try (var stream = Files.walk(folder, depth, FileVisitOption.FOLLOW_LINKS)) { + return stream.filter(p -> p.getFileName().toString().startsWith("libbcc.so")).findFirst(); + } catch (IOException e) { + return Optional.empty(); + } + } private static Optional findBCCLibrary() { var javaLibraryPath = System.getProperty("java.library.path"); if (javaLibraryPath == null) { return Optional.empty(); } - var paths = javaLibraryPath.split(":"); - for (var path : paths) { - var libPath = Path.of(path + "/libbcc.so"); - if (libPath.toFile().exists()) { - return Optional.of(libPath); - } - } - - try (var stream = Files.walk(Path.of("/lib"), 2, FileVisitOption.FOLLOW_LINKS)) { - return stream.filter(p -> p.endsWith("libbcc.so")).findFirst(); - } catch (IOException e) { - return Optional.empty(); - } + return Arrays.stream(javaLibraryPath.split(":")) + .map(f -> findLibInFolder(Path.of(f), 1)) + .filter(Optional::isPresent).map(Optional::get).findFirst() + .or(() -> Stream.of("/lib", "/usr/lib", "/lib64", "/usr/lib64") + .map(Path::of) + .map(p -> findLibInFolder(p, 2)) + .filter(Optional::isPresent).map(Optional::get).findFirst()); } /** @@ -44,13 +48,21 @@ public static boolean isInstalled() { * Loads the BCC library and {@code System.exit(1)} if it is not available */ public static void load() { - var lib = findBCCLibrary(); - if (lib.isPresent()) { - System.load(lib.get().toString()); - return; + try { + System.loadLibrary("bcc"); + } catch (UnsatisfiedLinkError e) { + var lib = findBCCLibrary(); + if (lib.isPresent()) { + System.load(lib.get().toString()); + return; + } + System.err.println("Failed to load libbcc.so, pass the location of the lib folder " + + "via -Djava.library.path after you installed it"); + System.exit(1); } - System.err.println("Failed to load libbcc.so, pass the location of the lib folder " + - "via -Djava.library.path after you installed it"); - System.exit(1); + } + + public static void main(String[] args) { + System.out.println(isInstalled()); } } \ No newline at end of file diff --git a/testutil/bin/java b/testutil/bin/java index f72eaa6..e1fe4fe 100755 --- a/testutil/bin/java +++ b/testutil/bin/java @@ -6,7 +6,7 @@ # # Configuration per environment variables: # - `HBT_JAVA_BINARY` - the java binary to use (default: `java`) -# - `HBT_KERNEL_VERSION` - the kernel version to use (default: `6.7`) +# - `HBT_KERNEL_VERSION` - the kernel version to use (default: `6.6`) # see https://ghcr.io/cilium/ci-kernels for available versions # # Has to reside in bin/java so it can be passed to the `-Djvm` maven option. @@ -20,8 +20,7 @@ set -e HBT_JAVA_BINARY=${HBT_JAVA_BINARY:-java} # The kernel version to use -HBT_KERNEL_VERSION=${HBT_KERNEL_VERSION:-6.7} +HBT_KERNEL_VERSION=${HBT_KERNEL_VERSION:-6.6} testutil_dir=$(dirname "$0")/.. -$testutil_dir/run-in-container.sh $HBT_KERNEL_VERSION $HBT_JAVA_BINARY $@ -exit 0 \ No newline at end of file +$testutil_dir/run-in-container.sh $HBT_KERNEL_VERSION $HBT_JAVA_BINARY $@ \ No newline at end of file diff --git a/testutil/find_and_get_kernel.py b/testutil/find_and_get_kernel.py index c21af27..9c1f4d8 100755 --- a/testutil/find_and_get_kernel.py +++ b/testutil/find_and_get_kernel.py @@ -135,8 +135,6 @@ def copy_headers_into_dest(arch: str, version: str, dest_root: Path): argparse.add_argument("version", help="Kernel version") argparse.add_argument("destination", help="Destination folder") args = argparse.parse_args() - print( - f"Downloading headers for {args.version} (arch {get_arch()}) into {args.destination}") copy_headers_into_dest(get_arch(), args.version, Path(args.destination)) diff --git a/testutil/run-in-container.sh b/testutil/run-in-container.sh index 2d87c08..1be35d6 100755 --- a/testutil/run-in-container.sh +++ b/testutil/run-in-container.sh @@ -72,6 +72,12 @@ fi # get exact kernel version which is "$input/lib/modules//updates" kernel_version=$(basename "$(find "${input}/lib/modules" -maxdepth 1 -type d)") +# /lib is a symlink which causes problems +mkdir "$input"/lib2 +cp -r "$input"/lib/modules "$input"/lib2 +rm -r "$input"/lib +mv $input/lib2 $input/lib + "$script_folder"/find_and_get_kernel.py "${kernel_version}" "$input" mkdir -p "$input"/root diff --git a/testutil/setup-and-run.sh b/testutil/setup-and-run.sh index 8b4dd6b..844d90a 100644 --- a/testutil/setup-and-run.sh +++ b/testutil/setup-and-run.sh @@ -13,4 +13,8 @@ if [[ -d "/run/input/lib/modules" ]]; then find /run/input/lib/modules -type f -name bpf_testmod.ko -exec insmod {} \; fi +# used for debugging to check if bcc works at all +#script_dir="$(dirname "$(realpath "$0")")" +#timeout 5 python3 $script_dir/../pysamples/bcc/hello_world.py + $* && touch /run/output/status \ No newline at end of file