From 41ae095271531bf0d47adac6d816b3d5bbda094b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E5=BE=AE?= Date: Sun, 11 Aug 2024 11:09:58 +1000 Subject: [PATCH] CI: fix vm test and add llvm ir generate (#1) * update llvm ir example * fix test * update CI * fix input * add * fix --- .github/workflows/test-vm.yml | 41 +++++++++++--- .github/workflows/unit-test.yml | 2 +- .gitignore | 1 + CMakeLists.txt | 2 +- README.md | 42 +++++++++++++- cli/main.cpp | 55 +++++++++++++------ src/llvm_jit_context.cpp | 10 ++-- test/CMakeLists.txt | 1 + test/bpf_conformance_runner/CMakeLists.txt | 4 +- .../main-bpf-conformance.cpp | 15 ++--- {unit-test => test/unit-test}/CMakeLists.txt | 0 {unit-test => test/unit-test}/bpf_progs.h | 0 {unit-test => test/unit-test}/llvm-aot.cpp | 0 13 files changed, 129 insertions(+), 44 deletions(-) rename {unit-test => test/unit-test}/CMakeLists.txt (100%) rename {unit-test => test/unit-test}/bpf_progs.h (100%) rename {unit-test => test/unit-test}/llvm-aot.cpp (100%) diff --git a/.github/workflows/test-vm.yml b/.github/workflows/test-vm.yml index 5554bf5..fad6c6c 100644 --- a/.github/workflows/test-vm.yml +++ b/.github/workflows/test-vm.yml @@ -1,30 +1,53 @@ -name: Build and Test VM +name: Build and Test VM input and output on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true jobs: build: + runs-on: ubuntu-latest + strategy: + matrix: + container: + - ubuntu-2204 + - fedora-39 + container: + image: "manjusakalza/bpftime-base-image:${{matrix.container}}" + options: --privileged + steps: + + - name: cache dependencies + uses: actions/cache@v2 + id: cache + with: + path: ${{ github.workspace }}/${{ env.INSTALL_LOCATION }} + key: ${{ runner.os }}-dependencies + - uses: actions/checkout@v2 with: submodules: 'recursive' + - uses: actions/setup-python@v4 if: startsWith(matrix.container,'ubuntu') with: python-version: '3.8' + - name: build run: | - sudo apt install llvm-15-dev - cmake -B build -DCMAKE_BUILD_TYPE=Debug + cmake -B build -DCMAKE_BUILD_TYPE=Debug -DBPFTIME_ENABLE_UNIT_TESTING=1 cmake --build build --target all -j + - name: run testsuit x86 shell: bash run: | - python3.8 -m venv vm/test - source vm/test/bin/activate - pip install -r vm/test/requirements.txt + python3.8 -m venv ./test + source test/bin/activate + pip install -r test/requirements.txt # make build # or build-arm32 build-arm64 - make -C vm test-vm -j \ No newline at end of file + pytest -v -s test/test_framework \ No newline at end of file diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index d891b26..7bf8e80 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -36,7 +36,7 @@ jobs: - name: Run tests run: | - ./build/unit-test/llvm_jit_tests + ./build/test/unit-test/llvm_jit_tests - name: build llvm JIT/AOT release as a standalone library run: | diff --git a/.gitignore b/.gitignore index 4de53f5..363e75d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ compile_commands.json libbpftime_llvm_jit_vm.a test.o test.bin +*.ll diff --git a/CMakeLists.txt b/CMakeLists.txt index ea11aee..daded01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,7 @@ add_dependencies(bpftime_llvm_jit_vm spdlog::spdlog) if(BPFTIME_ENABLE_UNIT_TESTING) message(STATUS "Build unit tests for the project. Tests should always be found in the test folder\n") - add_subdirectory(unit-test) + add_subdirectory(test) endif() add_subdirectory(example) diff --git a/README.md b/README.md index e773fe4..7d6faff 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,50 @@ void run_ebpf_prog(const void *code, size_t code_len) ## Use llvmbpf as a AOT compiler +You can use the cli to generate the LLVM IR from eBPF bytecode: + +```console +# ./build/cli/bpftime-vm build .github/assets/sum.bpf.o -emit-llvm > test.bpf.ll +# opt -O3 -S test.bpf.ll -opaque-pointers -o test.opt.ll +# cat test.opt.ll +; ModuleID = 'test.bpf.ll' +source_filename = "bpf-jit" + +; Function Attrs: nofree norecurse nosync nounwind memory(read, inaccessiblemem: none) +define i64 @bpf_main(ptr %0, i64 %1) local_unnamed_addr #0 { +setupBlock: + %2 = ptrtoint ptr %0 to i64 + %3 = load i32, ptr %0, align 4 + %4 = icmp slt i32 %3, 1 + br i1 %4, label %bb_inst_30, label %bb_inst_15 + +bb_inst_15: ; preds = %setupBlock, %bb_inst_15 + %storemerge32 = phi i32 [ %11, %bb_inst_15 ], [ 1, %setupBlock ] + %stackBegin29.sroa.2.031 = phi i32 [ %10, %bb_inst_15 ], [ 0, %setupBlock ] + %5 = sext i32 %storemerge32 to i64 + %6 = shl nsw i64 %5, 2 + %7 = add i64 %6, %2 + %8 = inttoptr i64 %7 to ptr + %9 = load i32, ptr %8, align 4 + %10 = add i32 %9, %stackBegin29.sroa.2.031 + %11 = add i32 %storemerge32, 1 + %12 = icmp sgt i32 %11, %3 + br i1 %12, label %bb_inst_30, label %bb_inst_15 + +bb_inst_30: ; preds = %bb_inst_15, %setupBlock + %stackBegin29.sroa.2.0.lcssa = phi i32 [ 0, %setupBlock ], [ %10, %bb_inst_15 ] + %13 = zext i32 %stackBegin29.sroa.2.0.lcssa to i64 + ret i64 %13 +} + +attributes #0 = { nofree norecurse nosync nounwind memory(read, inaccessiblemem: none) } +``` + AOT Compile a eBPF program: ```console # ./build/cli/bpftime-vm build .github/assets/sum.bpf.o [2024-08-10 14:54:06.453] [info] [main.cpp:56] Processing program test -[2024-08-10 14:54:06.454] [info] [llvm_jit_context.cpp:242] Initializing llvm -[2024-08-10 14:54:06.477] [info] [llvm_jit_context.cpp:342] AOT: done, received 544 bytes [2024-08-10 14:54:06.479] [info] [main.cpp:69] Program test written to ./test.o ``` @@ -66,7 +103,6 @@ Load and run a AOTed eBPF program: ```console # echo "AwAAAAEAAAACAAAAAwAAAA==" | base64 -d > test.bin # ./build/cli/bpftime-vm run test.o test.bin -[2024-08-10 14:57:16.986] [info] [llvm_jit_context.cpp:242] Initializing llvm [2024-08-10 14:57:16.986] [info] [llvm_jit_context.cpp:392] LLVM-JIT: Loading aot object [2024-08-10 14:57:16.991] [info] [main.cpp:136] Program executed successfully. Return value: 6 ``` diff --git a/cli/main.cpp b/cli/main.cpp index 1fd271a..590627c 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -13,15 +13,15 @@ #include "llvmbpf.hpp" extern "C" { - struct bpf_object; - struct bpf_program; - struct bpf_insn; - void bpf_object__close(bpf_object *obj); - bpf_program *bpf_object__next_program(const bpf_object *obj, bpf_program *prog); - const char *bpf_program__name(const bpf_program *prog); - bpf_object *bpf_object__open(const char *path); - const bpf_insn *bpf_program__insns(const bpf_program *prog); - size_t bpf_program__insn_cnt(const bpf_program *prog); +struct bpf_object; +struct bpf_program; +struct bpf_insn; +void bpf_object__close(bpf_object *obj); +bpf_program *bpf_object__next_program(const bpf_object *obj, bpf_program *prog); +const char *bpf_program__name(const bpf_program *prog); +bpf_object *bpf_object__open(const char *path); +const bpf_insn *bpf_program__insns(const bpf_program *prog); +size_t bpf_program__insn_cnt(const bpf_program *prog); } using namespace bpftime; @@ -31,8 +31,9 @@ static void print_usage(const std::string &program_name) std::cerr << "Usage: " << program_name << " [options]\n" << "Commands:\n" - << " build [-o ]\n" + << " build [-o ] [-emit-llvm]\n" << " Build native ELF(s) from eBPF ELF. Each program in the eBPF ELF will be built into a single native ELF.\n" + << " If -emit-llvm is specified, the LLVM IR will be printed to stdout.\n" << " run [MEMORY]\n" << " Run a native eBPF program.\n"; } @@ -47,8 +48,19 @@ parse_optional_argument(int argc, const char **argv, int &i, return std::nullopt; } +static bool has_argument(int argc, const char **argv, const std::string &option) +{ + for (int i = 0; i < argc; ++i) { + if (std::string(argv[i]) == option) { + return true; + } + } + return false; +} + static int build_ebpf_program(const std::string &ebpf_elf, - const std::filesystem::path &output) + const std::filesystem::path &output, + bool emit_llvm) { bpf_object *obj = bpf_object__open(ebpf_elf.c_str()); if (!obj) { @@ -61,8 +73,9 @@ static int build_ebpf_program(const std::string &ebpf_elf, for ((prog) = bpf_object__next_program((elf.get()), __null); (prog) != __null; (prog) = bpf_object__next_program((elf.get()), (prog))) { - const char* name = bpf_program__name(prog); - SPDLOG_INFO("Processing program {}", name); + const char *name = bpf_program__name(prog); + if (!emit_llvm) + SPDLOG_INFO("Processing program {}", name); bpftime_llvm_jit_vm vm; if (vm.load_code((const void *)bpf_program__insns(prog), @@ -73,11 +86,19 @@ static int build_ebpf_program(const std::string &ebpf_elf, name, vm.get_error_message()); return 1; } - auto result = vm.do_aot_compile(false); + // add 1000 pesudo helpers so it can be used with helpers + for (int i = 0; i < 1000; i++) { + vm.register_external_function( + i, "helper_" + std::to_string(i), nullptr); + } + auto result = vm.do_aot_compile(emit_llvm); + auto out_path = output / (std::string(name) + ".o"); std::ofstream ofs(out_path, std::ios::binary); ofs.write((const char *)result.data(), result.size()); - SPDLOG_INFO("Program {} written to {}", name, out_path.c_str()); + if (!emit_llvm) + SPDLOG_INFO("Program {} written to {}", name, + out_path.c_str()); } return 0; } @@ -185,7 +206,9 @@ int main(int argc, const char **argv) } } - return build_ebpf_program(ebpf_elf, output); + bool emit_llvm = has_argument(argc, argv, "-emit-llvm"); + + return build_ebpf_program(ebpf_elf, output, emit_llvm); } else if (command == "run") { if (argc < 3) { print_usage(argv[0]); diff --git a/src/llvm_jit_context.cpp b/src/llvm_jit_context.cpp index 6e4a516..a61fd81 100644 --- a/src/llvm_jit_context.cpp +++ b/src/llvm_jit_context.cpp @@ -233,13 +233,13 @@ extern "C" void __aeabi_unwind_cpp_pr1(); static int llvm_initialized = 0; -llvm_bpf_jit_context::llvm_bpf_jit_context(bpftime_llvm_jit_vm& vm) : vm(vm) +llvm_bpf_jit_context::llvm_bpf_jit_context(bpftime_llvm_jit_vm &vm) : vm(vm) { using namespace llvm; int zero = 0; if (__atomic_compare_exchange_n(&llvm_initialized, &zero, 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) { - SPDLOG_INFO("Initializing llvm"); + SPDLOG_DEBUG("Initializing llvm"); InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); } @@ -286,7 +286,7 @@ std::vector llvm_bpf_jit_context::do_aot_compile( return module->withModuleDo([&](auto &module) -> std::vector { if (print_ir) { - module.print(errs(), nullptr); + module.print(llvm::outs(), nullptr); } optimizeModule(module); module.setTargetTriple(defaultTargetTriple); @@ -339,8 +339,8 @@ std::vector llvm_bpf_jit_context::do_aot_compile( } pass.run(module); - SPDLOG_INFO("AOT: done, received {} bytes", - objStream.size()); + SPDLOG_DEBUG("AOT: done, received {} bytes", + objStream.size()); std::vector result(objStream.begin(), objStream.end()); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index aa13aae..5780112 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,6 +9,7 @@ project( ) add_subdirectory(bpf_conformance_runner) +add_subdirectory(unit-test) message(DEBUG "Adding tests under ${CMAKE_PROJECT_NAME}Tests...") diff --git a/test/bpf_conformance_runner/CMakeLists.txt b/test/bpf_conformance_runner/CMakeLists.txt index 04edcd2..2f26452 100644 --- a/test/bpf_conformance_runner/CMakeLists.txt +++ b/test/bpf_conformance_runner/CMakeLists.txt @@ -2,7 +2,7 @@ add_executable(bpftime_vm_bpf_conformance_runner main-bpf-conformance.cpp ) -add_dependencies(bpftime_vm_bpf_conformance_runner bpftime_vm) -target_link_libraries(bpftime_vm_bpf_conformance_runner bpftime_vm) +add_dependencies(bpftime_vm_bpf_conformance_runner bpftime_llvm_jit_vm) +target_link_libraries(bpftime_vm_bpf_conformance_runner bpftime_llvm_jit_vm) set_target_properties(bpftime_vm_bpf_conformance_runner PROPERTIES CXX_STANDARD 20) diff --git a/test/bpf_conformance_runner/main-bpf-conformance.cpp b/test/bpf_conformance_runner/main-bpf-conformance.cpp index add4cc6..aae4d55 100644 --- a/test/bpf_conformance_runner/main-bpf-conformance.cpp +++ b/test/bpf_conformance_runner/main-bpf-conformance.cpp @@ -1,4 +1,4 @@ -#include "bpftime_vm_compat.hpp" +#include "llvmbpf.hpp" #include "ebpf_inst.h" #include #include @@ -7,6 +7,8 @@ #include #include +using namespace bpftime; + /** * @brief Read in a string of hex bytes and return a vector of bytes. * @@ -102,17 +104,16 @@ int main(int argc, char **argv) std::string log; int err; - auto vm = create_vm_instance(); - assert(vm); - err = vm->load_code(&program[0], program.size() * 8); + auto vm = bpftime_llvm_jit_vm(); + err = vm.load_code(&program[0], program.size() * sizeof(ebpf_inst)); if (err < 0) { - std::cerr << "Error: " << vm->get_error_message() << std::endl; + std::cerr << "Error: " << vm.get_error_message() << std::endl; return -1; } - auto func = vm->compile(); + auto func = vm.compile(); assert(func); uint64_t res; - vm->exec(&memory[0], memory.size(), res); + vm.exec(&memory[0], memory.size(), res); std::cout << std::hex << res << std::endl; return 0; } diff --git a/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt similarity index 100% rename from unit-test/CMakeLists.txt rename to test/unit-test/CMakeLists.txt diff --git a/unit-test/bpf_progs.h b/test/unit-test/bpf_progs.h similarity index 100% rename from unit-test/bpf_progs.h rename to test/unit-test/bpf_progs.h diff --git a/unit-test/llvm-aot.cpp b/test/unit-test/llvm-aot.cpp similarity index 100% rename from unit-test/llvm-aot.cpp rename to test/unit-test/llvm-aot.cpp