diff --git a/.github/workflows/dep_rust.yml b/.github/workflows/dep_rust.yml index ba9d3f2e6..44e9aca46 100644 --- a/.github/workflows/dep_rust.yml +++ b/.github/workflows/dep_rust.yml @@ -85,7 +85,7 @@ jobs: run: just build-and-move-c-guests - name: Build - run: just build-rust ${{ matrix.config }} + run: just build ${{ matrix.config }} - name: Verify MSRV run: ./dev/verify-msrv.sh hyperlight-host hyperlight-guest hyperlight-common @@ -95,10 +95,10 @@ jobs: CARGO_TERM_COLOR: always run: | # with default features - just test-rust ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}} + just test ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}} # with only one driver enabled (driver mshv/kvm feature is ignored on windows) + seccomp + inprocess - just test-rust ${{ matrix.config }} inprocess,seccomp,${{ matrix.hypervisor == 'mshv' && 'mshv2' || matrix.hypervisor == 'mshv3' && 'mshv3' || 'kvm' }} + just test ${{ matrix.config }} inprocess,seccomp,${{ matrix.hypervisor == 'mshv' && 'mshv2' || matrix.hypervisor == 'mshv3' && 'mshv3' || 'kvm' }} # make sure certain cargo features compile cargo check -p hyperlight-host --features crashdump @@ -106,7 +106,7 @@ jobs: cargo check -p hyperlight-host --features gdb # without any driver (shouldn't compile) - just test-rust-feature-compilation-fail ${{ matrix.config }} + just test-compilation-fail ${{ matrix.config }} # One of the examples is flaky on Windows GH runners, so this allows us to disable it for now - name: Run Rust examples - windows diff --git a/Justfile b/Justfile index 5b0209ad4..6d28c861e 100644 --- a/Justfile +++ b/Justfile @@ -1,10 +1,8 @@ import 'c.just' -alias build-rust-debug := build-rust set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"] set dotenv-load := true -set-trace-env-vars := if os() == "windows" { "$env:RUST_LOG='none,hyperlight-host=info';" } else { "RUST_LOG=none,hyperlight-host=info" } set-env-command := if os() == "windows" { "$env:" } else { "export " } bin-suffix := if os() == "windows" { ".bat" } else { ".sh" } @@ -18,28 +16,20 @@ callbackguest_source := "src/tests/rust_guests/callbackguest/target/x86_64-unkno callbackguest_msvc_source := "src/tests/rust_guests/callbackguest/target/x86_64-pc-windows-msvc" rust_guests_bin_dir := "src/tests/rust_guests/bin" -install-vcpkg: - cd .. && git clone https://github.com/Microsoft/vcpkg.git || cd - - cd ../vcpkg && ./bootstrap-vcpkg{{ bin-suffix }} && ./vcpkg integrate install || cd - - -install-flatbuffers-with-vcpkg: install-vcpkg - cd ../vcpkg && ./vcpkg install flatbuffers || cd - +################ +### BUILDING ### +################ +alias b := build +alias rg := build-and-move-rust-guests +alias cg := build-and-move-c-guests -tar-headers: (build-rust-capi) # build-rust-capi is a dependency because we need the hyperlight_guest.h to be built - tar -zcvf include.tar.gz -C {{root}}/src/hyperlight_guest/third_party/ musl/include musl/arch/x86_64 printf/printf.h -C {{root}}/src/hyperlight_guest_capi include - -tar-static-lib: (build-rust-capi "release") (build-rust-capi "debug") - tar -zcvf hyperlight-guest-c-api-windows.tar.gz -C {{root}}/target/x86_64-pc-windows-msvc/ release/hyperlight_guest_capi.lib -C {{root}}/target/x86_64-pc-windows-msvc/ debug/hyperlight_guest_capi.lib - tar -zcvf hyperlight-guest-c-api-linux.tar.gz -C {{root}}/target/x86_64-unknown-none/ release/libhyperlight_guest_capi.a -C {{root}}/target/x86_64-unknown-none/ debug/libhyperlight_guest_capi.a +# build host library +build target=default-target: + cargo build --profile={{ if target == "debug" { "dev" } else { target } }} -# Create release notes for the given tag. The expected format is a v-prefixed version number, e.g. v0.2.0 -# For prereleases, the version should be "dev-latest" -@create-release-notes tag: - echo "## What's Changed" - ./dev/extract-changelog.sh {{ if tag == "dev-latest" { "Prerelease" } else { tag } }} - gh api repos/{owner}/{repo}/releases/generate-notes -f tag_name={{ tag }} | jq -r '.body' | sed '1,/## What'"'"'s Changed/d' +# build testing guest binaries +guests: build-and-move-rust-guests build-and-move-c-guests -# BUILDING build-rust-guests target=default-target: cd src/tests/rust_guests/callbackguest && cargo build --profile={{ if target == "debug" { "dev" } else { target } }} cd src/tests/rust_guests/callbackguest && cargo build --profile={{ if target == "debug" { "dev" } else { target } }} --target=x86_64-pc-windows-msvc @@ -57,18 +47,6 @@ build-rust-guests target=default-target: build-and-move-rust-guests: (build-rust-guests "debug") (move-rust-guests "debug") (build-rust-guests "release") (move-rust-guests "release") build-and-move-c-guests: (build-c-guests "debug") (move-c-guests "debug") (build-c-guests "release") (move-c-guests "release") -# short aliases rg "rust guests", cg "c guests" for less typing -rg: build-and-move-rust-guests -cg: build-and-move-c-guests -guests: rg cg - - -build-rust target=default-target: - cargo build --profile={{ if target == "debug" { "dev" } else { target } }} - -build: build-rust - -# CLEANING clean: clean-rust clean-rust: @@ -78,51 +56,61 @@ clean-rust: cd src/tests/rust_guests/callbackguest && cargo clean git clean -fdx src/tests/c_guests/bin src/tests/rust_guests/bin -# TESTING -# Some tests cannot run with other tests, they are marked as ignored so that cargo test works -# there may be tests that we really want to ignore so we can't just use --ignored and we have to -# Specify the test name of the ignored tests that we want to run -test-rust target=default-target features="": (test-rust-int "rust" target features) (test-rust-int "c" target features) (test-seccomp target features) - # unit tests - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --lib - - # ignored tests - these tests need to run serially or with specific properties - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} test_trace -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} test_drop -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --test integration_test log_message -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} sandbox::uninitialized::tests::test_log_trace -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} hypervisor::hypervisor_handler::tests::create_1000_sandboxes -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact --ignored - {{ set-trace-env-vars }} cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --lib sandbox::outb::tests::test_log_outb_log -- --ignored +################ +### TESTING #### +################ -test-seccomp target=default-target features="": - # run seccomp test with feature "seccomp" on and off - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host test_violate_seccomp_filters --lib {{ if features =="" {''} else { "--features " + features } }} -- --ignored - cargo test --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host test_violate_seccomp_filters --no-default-features {{ if features =~"mshv3" {"--features mshv3"} else {"--features mshv2,kvm" } }} --lib -- --ignored +# Note: most testing recipes take an optional "features" comma separated list argument. If provided, these will be passed to cargo as **THE ONLY FEATURES**, i.e. default features will be disabled. -# rust integration tests. guest can either be "rust" or "c" -test-rust-int guest target=default-target features="": - # integration tests +# runs all tests +test target=default-target features="": (test-unit target features) (test-isolated target features) (test-integration "rust" target features) (test-integration "c" target features) (test-seccomp target features) - # run execute_on_heap test with feature "executable_heap" on and off +# runs unit tests +test-unit target=default-target features="": + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --lib + +# runs tests that requires being run separately, for example due to global state +test-isolated target=default-target features="": + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_trace_trace --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- hypervisor::hypervisor_handler::tests::create_1000_sandboxes --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- mem::shared_mem::tests::test_drop --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --test integration_test -- log_message --exact --ignored + @# metrics tests + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact --ignored + +# runs integration tests. Guest can either be "rust" or "c" +test-integration guest target=default-target features="": + @# run execute_on_heap test with feature "executable_heap" on and off {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --test integration_test execute_on_heap {{ if features =="" {" --features executable_heap"} else {"--features executable_heap," + features} }} -- --ignored {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --test integration_test execute_on_heap {{ if features =="" {""} else {"--features " + features} }} -- --ignored - # run the rest of the integration tests + + @# run the rest of the integration tests {{if os() == "windows" { "$env:" } else { "" } }}GUEST="{{guest}}"{{if os() == "windows" { ";" } else { "" } }} cargo test -p hyperlight-host {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --test '*' -test-rust-feature-compilation-fail target=default-target: +# runs seccomp tests +test-seccomp target=default-target features="": + @# run seccomp test with feature "seccomp" on and off + cargo test --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host test_violate_seccomp_filters --lib {{ if features =="" {''} else { "--features " + features } }} -- --ignored + cargo test --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host test_violate_seccomp_filters --no-default-features {{ if features =~"mshv3" {"--features mshv3"} else {"--features mshv2,kvm" } }} --lib -- --ignored + +# runs tests that ensure compilation fails when it should +test-compilation-fail target=default-target: @# the following should fail on linux because one of kvm, mshv, or mshv3 feature must be specified, which is why the exit code is inverted with an !. {{ if os() == "linux" { "! cargo check -p hyperlight-host --no-default-features 2> /dev/null"} else { "" } }} -# Test rust gdb debugging -test-rust-gdb-debugging target=default-target features="": (build-rust target) - {{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} - {{ set-trace-env-vars }} cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- test_gdb +# runs tests that exercise gdb debugging +test-rust-gdb-debugging target=default-target features="": + cargo test --profile={{ if target == "debug" { "dev" } else { target } }} --example guest-debugging {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} + cargo test --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {'--features gdb'} else { "--features gdb," + features } }} -- test_gdb -test target=default-target: (test-rust target) -# RUST LINTING +################ +### LINTING #### +################ + check: cargo check @@ -157,23 +145,42 @@ clippy-apply-fix-windows: verify-msrv: ./dev/verify-msrv.sh hyperlight-host hyperlight-guest hyperlight-common -# GEN FLATBUFFERS -gen-all-fbs-rust-code: - for fbs in `find src -name "*.fbs"`; do flatc -r --rust-module-root-file --gen-all -o ./src/hyperlight_common/src/flatbuffers/ $fbs; done - just fmt-apply +##################### +### RUST EXAMPLES ### +##################### -# RUST EXAMPLES -run-rust-examples target=default-target features="": (build-rust target) +run-rust-examples target=default-target features="": cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example metrics {{ if features =="" {''} else { "--features " + features } }} cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example metrics {{ if features =="" {"--features function_call_metrics"} else {"--features function_call_metrics," + features} }} - {{ set-trace-env-vars }} cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example logging {{ if features =="" {''} else { "--features " + features } }} + cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example logging {{ if features =="" {''} else { "--features " + features } }} # The two tracing examples are flaky on windows so we run them on linux only for now, need to figure out why as they run fine locally on windows -run-rust-examples-linux target=default-target features="": (build-rust target) (run-rust-examples target features) - {{ set-trace-env-vars }} cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example tracing {{ if features =="" {''} else { "--features " + features } }} - {{ set-trace-env-vars }} cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example tracing {{ if features =="" {"--features function_call_metrics" } else {"--features function_call_metrics," + features} }} +run-rust-examples-linux target=default-target features="": (run-rust-examples target features) + cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example tracing {{ if features =="" {''} else { "--features " + features } }} + cargo run --profile={{ if target == "debug" { "dev" } else { target } }} --example tracing {{ if features =="" {"--features function_call_metrics" } else {"--features function_call_metrics," + features} }} + + +######################### +### ARTIFACT CREATION ### +######################### -# BENCHMARKING +tar-headers: (build-rust-capi) # build-rust-capi is a dependency because we need the hyperlight_guest.h to be built + tar -zcvf include.tar.gz -C {{root}}/src/hyperlight_guest/third_party/ musl/include musl/arch/x86_64 printf/printf.h -C {{root}}/src/hyperlight_guest_capi include + +tar-static-lib: (build-rust-capi "release") (build-rust-capi "debug") + tar -zcvf hyperlight-guest-c-api-windows.tar.gz -C {{root}}/target/x86_64-pc-windows-msvc/ release/hyperlight_guest_capi.lib -C {{root}}/target/x86_64-pc-windows-msvc/ debug/hyperlight_guest_capi.lib + tar -zcvf hyperlight-guest-c-api-linux.tar.gz -C {{root}}/target/x86_64-unknown-none/ release/libhyperlight_guest_capi.a -C {{root}}/target/x86_64-unknown-none/ debug/libhyperlight_guest_capi.a + +# Create release notes for the given tag. The expected format is a v-prefixed version number, e.g. v0.2.0 +# For prereleases, the version should be "dev-latest" +@create-release-notes tag: + echo "## What's Changed" + ./dev/extract-changelog.sh {{ if tag == "dev-latest" { "Prerelease" } else { tag } }} + gh api repos/{owner}/{repo}/releases/generate-notes -f tag_name={{ tag }} | jq -r '.body' | sed '1,/## What'"'"'s Changed/d' + +#################### +### BENCHMARKING ### +#################### # Warning: can overwrite previous local benchmarks, so run this before running benchmarks # Downloads the benchmarks result from the given release tag. @@ -194,9 +201,9 @@ bench-ci baseline target=default-target features="": bench target=default-target features="": cargo bench --profile={{ if target == "debug" { "dev" } else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose -##################################### -# FUZZING -##################################### +############### +### FUZZING ### +############### # Enough memory (4GB) for the fuzzer to run for 5 hours, with address sanitizer turned on fuzz_memory_limit := "4096" @@ -215,3 +222,19 @@ build-fuzzers: (build-fuzzer "fuzz_guest_call") (build-fuzzer "fuzz_host_call") # Builds the given fuzzer build-fuzzer fuzz-target: cargo +nightly fuzz build {{ fuzz-target }} + + +################### +### FLATBUFFERS ### +################### + +gen-all-fbs-rust-code: + for fbs in `find src -name "*.fbs"`; do flatc -r --rust-module-root-file --gen-all -o ./src/hyperlight_common/src/flatbuffers/ $fbs; done + just fmt-apply + +install-vcpkg: + cd .. && git clone https://github.com/Microsoft/vcpkg.git || cd - + cd ../vcpkg && ./bootstrap-vcpkg{{ bin-suffix }} && ./vcpkg integrate install || cd - + +install-flatbuffers-with-vcpkg: install-vcpkg + cd ../vcpkg && ./vcpkg install flatbuffers || cd - \ No newline at end of file diff --git a/src/hyperlight_common/Cargo.toml b/src/hyperlight_common/Cargo.toml index dd8098f60..560a26184 100644 --- a/src/hyperlight_common/Cargo.toml +++ b/src/hyperlight_common/Cargo.toml @@ -31,3 +31,4 @@ hyperlight-testing = { workspace = true } [lib] bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options +doctest = false # reduce noise in test output \ No newline at end of file diff --git a/src/hyperlight_host/examples/guest-debugging/main.rs b/src/hyperlight_host/examples/guest-debugging/main.rs index 9ebde14b4..0e4cb3aff 100644 --- a/src/hyperlight_host/examples/guest-debugging/main.rs +++ b/src/hyperlight_host/examples/guest-debugging/main.rs @@ -155,6 +155,7 @@ mod tests { let mut gdb = Command::new("rust-gdb") .arg("--nw") + .arg("--batch") .arg("-x") .arg(cmd_file_path) .spawn() diff --git a/src/hyperlight_host/examples/logging/main.rs b/src/hyperlight_host/examples/logging/main.rs index 3a4a6edf4..c4b9b6cc2 100644 --- a/src/hyperlight_host/examples/logging/main.rs +++ b/src/hyperlight_host/examples/logging/main.rs @@ -32,7 +32,9 @@ fn fn_writer(_msg: String) -> Result { // by Hyperlight will also be emitted as log messages. fn main() -> Result<()> { - env_logger::init(); + env_logger::builder() + .parse_filters("none,hyperlight=info") + .init(); // Get the path to a simple guest binary. let hyperlight_guest_path = simple_guest_as_string().expect("Cannot find the guest binary at the expected location."); diff --git a/src/hyperlight_host/examples/tracing/main.rs b/src/hyperlight_host/examples/tracing/main.rs index 1778a3d47..f746a270e 100644 --- a/src/hyperlight_host/examples/tracing/main.rs +++ b/src/hyperlight_host/examples/tracing/main.rs @@ -42,7 +42,8 @@ fn main() -> Result<()> { // Set up the tracing subscriber. // tracing_forest uses the tracing subscriber, which, by default, will consume logs as trace events // unless the tracing-log feature is disabled. - let layer = ForestLayer::default().with_filter(EnvFilter::from_default_env()); + let layer = ForestLayer::default() + .with_filter(EnvFilter::builder().parse("none,hyperlight=info").unwrap()); Registry::default().with(layer).init(); run_example() } diff --git a/src/hyperlight_testing/Cargo.toml b/src/hyperlight_testing/Cargo.toml index 40ec4020f..9fb724305 100644 --- a/src/hyperlight_testing/Cargo.toml +++ b/src/hyperlight_testing/Cargo.toml @@ -15,3 +15,6 @@ serde_json = "1.0" [lib] bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options +# reduce noise in test output +test = false +doctest = false \ No newline at end of file