diff --git a/tests/unit/shared-heap/CMakeLists.txt b/tests/unit/shared-heap/CMakeLists.txt index 5de15b4428..740c2bda6b 100644 --- a/tests/unit/shared-heap/CMakeLists.txt +++ b/tests/unit/shared-heap/CMakeLists.txt @@ -64,4 +64,20 @@ add_executable(shared_heap_test ${unit_test_sources}) target_link_libraries(shared_heap_test gtest_main) +# Copy wasm/aot files from ExternalProject build dirs to the test working directory +set(WASM_APPS_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/shared_heap_wasm_apps-prefix/src/shared_heap_wasm_apps-build) +add_custom_command(TARGET shared_heap_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${WASM_APPS_BUILD_DIR}/test.wasm + ${WASM_APPS_BUILD_DIR}/test.aot + ${WASM_APPS_BUILD_DIR}/test_chain.aot + ${WASM_APPS_BUILD_DIR}/test_addr_conv.wasm + ${WASM_APPS_BUILD_DIR}/test_addr_conv.aot + ${WASM_APPS_BUILD_DIR}/test_addr_conv_chain.aot + ${WASM_APPS_BUILD_DIR}/test_sandbox.wasm + ${WASM_APPS_BUILD_DIR}/test_runtime.wasm + ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Copying wasm/aot test files to test working directory" +) + gtest_discover_tests(shared_heap_test) diff --git a/tests/unit/shared-heap/shared_heap_test.cc b/tests/unit/shared-heap/shared_heap_test.cc index a66cfc68b1..ffe91081b5 100644 --- a/tests/unit/shared-heap/shared_heap_test.cc +++ b/tests/unit/shared-heap/shared_heap_test.cc @@ -3,16 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#include "platform_internal.h" #include "test_helper.h" #include "gtest/gtest.h" #include "bh_read_file.h" #include "wasm_runtime_common.h" -#include "bh_platform.h" #include + +#include +#include #include +#include +#include class shared_heap_test : public testing::Test { @@ -46,6 +49,7 @@ load_wasm(const char *wasm_file_tested, unsigned int app_heap_size, ret_module_env.wasm_file_buf = (unsigned char *)bh_read_file_to_buffer(wasm_file, &wasm_file_size); if (!ret_module_env.wasm_file_buf) { + ADD_FAILURE() << "Failed to read wasm file buffer: " << wasm_file; goto fail; } @@ -54,6 +58,8 @@ load_wasm(const char *wasm_file_tested, unsigned int app_heap_size, error_buf, sizeof(error_buf)); if (!ret_module_env.wasm_module) { memcpy(ret_module_env.error_buf, error_buf, 128); + ADD_FAILURE() << "Failed to load wasm module: " << wasm_file + << " with error: " << error_buf; goto fail; } @@ -62,12 +68,16 @@ load_wasm(const char *wasm_file_tested, unsigned int app_heap_size, heap_size, error_buf, sizeof(error_buf)); if (!ret_module_env.wasm_module_inst) { memcpy(ret_module_env.error_buf, error_buf, 128); + ADD_FAILURE() << "Failed to instantiate wasm module: " << wasm_file + << " with error: " << error_buf; goto fail; } ret_module_env.exec_env = wasm_runtime_create_exec_env( ret_module_env.wasm_module_inst, stack_size); if (!ret_module_env.exec_env) { + ADD_FAILURE() << "Failed to create wasm execution environment: " + << wasm_file; goto fail; } @@ -263,7 +273,7 @@ TEST_F(shared_heap_test, test_preallocated_shared_runtime_api) } size = (uint64_t)UINT32_MAX + 0x2000; - printf("offset %llx size: %llx\n", offset, size); + printf("offset %" PRIx64 " size: %" PRIx64 "\n", offset, size); ASSERT_EQ(false, wasm_runtime_validate_app_addr( tmp_module_env.wasm_module_inst, offset, size)); @@ -1372,3 +1382,562 @@ TEST_F(shared_heap_test, test_shared_heap_chain_addr_conv_oob) "test_preallocated", 1, argv), "Exception: out of bounds memory access"); } + +/* Test the behavior of the shared heap in a multi-threaded environment to + * ensure thread safety and data consistency. */ + +TEST_F(shared_heap_test, test_shared_heap_multithread_access) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 argv[1] = { 0 }; + const int num_threads = 2; + std::vector threads; + + args.size = 1024; + shared_heap = wasm_runtime_create_shared_heap(&args); + if (!shared_heap) { + FAIL() << "Failed to create shared heap"; + } + + auto thread_func = [&shared_heap]() { + wasm_runtime_init_thread_env(); + uint32 local_argv[1] = { 0 }; + test_shared_heap(shared_heap, "test.wasm", "test", 0, local_argv); + wasm_runtime_destroy_thread_env(); + EXPECT_EQ(10, local_argv[0]); + }; + + for (int i = 0; i < num_threads; ++i) { + threads.emplace_back(thread_func); + } + + for (auto &thread : threads) { + thread.join(); + } +} + +TEST_F(shared_heap_test, test_shared_heap_concurrent_access) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 BUF_SIZE = os_getpagesize(); + uint8 *preallocated_buf_ptr = nullptr; + + ASSERT_GT(BUF_SIZE, 0u); + std::vector preallocated_buf(BUF_SIZE); + preallocated_buf_ptr = preallocated_buf.data(); + ASSERT_NE(preallocated_buf_ptr, nullptr); + + args.pre_allocated_addr = preallocated_buf_ptr; + args.size = BUF_SIZE; + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + auto thread_func = [&](uint8 *buf, int value) { + for (uint32 i = 0; i < BUF_SIZE; ++i) { + buf[i] = value; + } + }; + + // Using std::ref to solve array parameter passing problem + std::thread t1(thread_func, preallocated_buf_ptr, 0xAA); + std::thread t2(thread_func, preallocated_buf_ptr, 0x55); + + t1.join(); + t2.join(); + + // Verify shared memory consistency (last thread wins) + for (uint32 i = 0; i < BUF_SIZE; ++i) { + EXPECT_TRUE(preallocated_buf[i] == 0xAA || preallocated_buf[i] == 0x55) + << "Data corruption detected"; + } +} + +TEST_F(shared_heap_test, test_shared_heap_cross_instance) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 BUF_SIZE = os_getpagesize(); + uint8 *preallocated_buf_ptr = nullptr; + uint32 start1; + + ASSERT_GT(BUF_SIZE, 0u); + std::vector preallocated_buf(BUF_SIZE); + preallocated_buf_ptr = preallocated_buf.data(); + ASSERT_NE(preallocated_buf_ptr, nullptr); + + args.pre_allocated_addr = preallocated_buf_ptr; + args.size = BUF_SIZE; + + /* app addr for shared heap */ + start1 = UINT32_MAX - BUF_SIZE + 1; + + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + struct ret_env module_env1, module_env2; + + ASSERT_TRUE(load_wasm((char *)"test.wasm", 0, module_env1)) + << "Failed to load wasm file for instance 1"; + ASSERT_TRUE(load_wasm((char *)"test.wasm", 0, module_env2)) + << "Failed to load wasm file for instance 2"; + + ASSERT_TRUE(wasm_runtime_attach_shared_heap(module_env1.wasm_module_inst, + shared_heap)); + ASSERT_TRUE(wasm_runtime_attach_shared_heap(module_env2.wasm_module_inst, + shared_heap)); + + // Instance 1 writes to shared memory + uint32 argv1[2] = { start1, 123 }; + test_shared_heap(shared_heap, "test.wasm", "read_modify_write_8", 2, argv1); + + // Instance 2 reads from shared memory + uint32 argv2[2] = { start1, 0 }; + test_shared_heap(shared_heap, "test.wasm", "read_modify_write_8", 2, argv2); + + EXPECT_EQ(argv2[0], 123); + + wasm_runtime_detach_shared_heap(module_env1.wasm_module_inst); + wasm_runtime_detach_shared_heap(module_env2.wasm_module_inst); + destroy_module_env(module_env1); + destroy_module_env(module_env2); +} + +TEST_F(shared_heap_test, test_shared_heap_lifecycle) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 BUF_SIZE = os_getpagesize(); + uint8 *preallocated_buf_ptr = nullptr; + + ASSERT_GT(BUF_SIZE, 0u); + std::vector preallocated_buf(BUF_SIZE); + preallocated_buf_ptr = preallocated_buf.data(); + ASSERT_NE(preallocated_buf_ptr, nullptr); + + args.pre_allocated_addr = preallocated_buf_ptr; + args.size = BUF_SIZE; + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + preallocated_buf[0] = 42; + EXPECT_EQ(preallocated_buf[0], 42); + + // Manually zeroing preallocated memory to simulate deallocation + memset(preallocated_buf_ptr, 0, BUF_SIZE); + + // Check if it is still accessible after clearing + EXPECT_EQ(preallocated_buf[0], 0) + << "Memory not properly cleared after manual cleanup"; +} + +// Unaligned memory access +TEST_F(shared_heap_test, test_shared_heap_unaligned_access) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 BUF_SIZE = os_getpagesize(); + uint8 *preallocated_buf_ptr = nullptr; + + ASSERT_GT(BUF_SIZE, 0u); + std::vector preallocated_buf(BUF_SIZE); + preallocated_buf_ptr = preallocated_buf.data(); + ASSERT_NE(preallocated_buf_ptr, nullptr); + + args.pre_allocated_addr = preallocated_buf_ptr; + args.size = BUF_SIZE; + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + // Write unaligned data + uint16 *unaligned_ptr = (uint16 *)(preallocated_buf_ptr + 1); + *unaligned_ptr = 0xABCD; + + // Read back unaligned data + uint16 result = *(uint16 *)(preallocated_buf_ptr + 1); + EXPECT_EQ(result, 0xABCD); +} + +// test_sandbox.wasm +TEST_F(shared_heap_test, test_memory_size_and_growth) +{ + uint32 argv[1] = { 0 }; + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + + args.size = 1024; + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + // Load the WebAssembly module + struct ret_env module_env; + ASSERT_TRUE(load_wasm((char *)"test_sandbox.wasm", 0, module_env)) + << "Failed to load wasm file"; + + ASSERT_TRUE(wasm_runtime_attach_shared_heap(module_env.wasm_module_inst, + shared_heap)) + << "Failed to attach shared heap"; + + // Test initial memory size + WASMFunctionInstanceCommon *func_memsize = wasm_runtime_lookup_function( + module_env.wasm_module_inst, "memory_size"); + ASSERT_NE(func_memsize, nullptr) << "Failed to find 'memory_size' function"; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_memsize, 0, argv)); + EXPECT_EQ(argv[0], 1) << "Initial memory size should be 1 page"; + + // Test growing memory by 1 page + argv[0] = 1; + WASMFunctionInstanceCommon *func_grow = wasm_runtime_lookup_function( + module_env.wasm_module_inst, "grow_memory"); + ASSERT_NE(func_grow, nullptr) << "Failed to find 'grow_memory' function"; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_grow, 1, argv)); + EXPECT_EQ(argv[0], 1) + << "Memory growth result should be the previous size (1 page)"; + + // Verify new memory size (2 pages) + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_memsize, 0, argv)); + EXPECT_EQ(argv[0], 2) << "Memory size should now be 2 pages"; + + // Test growing memory to maximum size + argv[0] = 2; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_grow, 1, argv)); + EXPECT_EQ(argv[0], 2) + << "Memory growth result should be the previous size (2 pages)"; + + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_memsize, 0, argv)); + EXPECT_EQ(argv[0], 4) << "Memory size should now be 4 pages (maximum)"; + + // Test exceeding maximum memory size + argv[0] = 1; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_grow, 1, argv)); + EXPECT_EQ(int32_t(argv[0]), -1) + << "Memory growth should fail when exceeding max size"; + + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_memsize, 0, argv)); + EXPECT_EQ(argv[0], 4) << "Memory size should remain 4 pages"; + + wasm_runtime_detach_shared_heap(module_env.wasm_module_inst); + destroy_module_env(module_env); +} + +TEST_F(shared_heap_test, test_store_and_load) +{ + uint32 argv[2] = { 0 }; + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + + args.size = 1024; + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + // Load the WebAssembly module + struct ret_env module_env; + ASSERT_TRUE(load_wasm((char *)"test_sandbox.wasm", 0, module_env)) + << "Failed to load wasm file"; + + ASSERT_TRUE(wasm_runtime_attach_shared_heap(module_env.wasm_module_inst, + shared_heap)) + << "Failed to attach shared heap"; + + // Store value 100 at address 4 + argv[0] = 4; // Address + argv[1] = 100; // Value + WASMFunctionInstanceCommon *func_store = + wasm_runtime_lookup_function(module_env.wasm_module_inst, "store"); + ASSERT_NE(func_store, nullptr) << "Failed to find 'store' function"; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_store, 2, argv)); + + // Load value from address 4 + argv[0] = 4; // Address + WASMFunctionInstanceCommon *func_load = + wasm_runtime_lookup_function(module_env.wasm_module_inst, "load"); + ASSERT_NE(func_load, nullptr) << "Failed to find 'load' function"; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_load, 1, argv)); + EXPECT_EQ(argv[0], 100) << "Loaded value should be 100"; + + wasm_runtime_detach_shared_heap(module_env.wasm_module_inst); + destroy_module_env(module_env); +} + +TEST_F(shared_heap_test, test_unaligned_store_and_load) +{ + uint32 argv[2] = { 0 }; + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + + args.size = 1024; + shared_heap = wasm_runtime_create_shared_heap(&args); + ASSERT_NE(shared_heap, nullptr) << "Failed to create shared heap"; + + // Load the WebAssembly module + struct ret_env module_env; + ASSERT_TRUE(load_wasm((char *)"test_sandbox.wasm", 0, module_env)) + << "Failed to load wasm file"; + + ASSERT_TRUE(wasm_runtime_attach_shared_heap(module_env.wasm_module_inst, + shared_heap)) + << "Failed to attach shared heap"; + + // Store value 42 at unaligned address 1 + argv[0] = 1; // Address + argv[1] = 42; // Value + WASMFunctionInstanceCommon *func_store = + wasm_runtime_lookup_function(module_env.wasm_module_inst, "store"); + ASSERT_NE(func_store, nullptr) << "Failed to find 'store' function"; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_store, 2, argv)); + + // Load value from unaligned address 1 + argv[0] = 1; // Address + WASMFunctionInstanceCommon *func_load = + wasm_runtime_lookup_function(module_env.wasm_module_inst, "load"); + ASSERT_NE(func_load, nullptr) << "Failed to find 'load' function"; + ASSERT_TRUE( + wasm_runtime_call_wasm(module_env.exec_env, func_load, 1, argv)); + EXPECT_EQ(argv[0], 42) << "Loaded value should be 42"; + + wasm_runtime_detach_shared_heap(module_env.wasm_module_inst); + destroy_module_env(module_env); +} + +// Test Case: Invalid size parameter +TEST_F(shared_heap_test, test_shared_heap_invalid_size) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + + // Test size = 0 + args.size = 0; + shared_heap = wasm_runtime_create_shared_heap(&args); + EXPECT_EQ(shared_heap, nullptr) + << "Expected shared_heap to be NULL for size=0"; + + // Test size < APP_HEAP_SIZE_MIN + args.size = APP_HEAP_SIZE_MIN - 1; + shared_heap = wasm_runtime_create_shared_heap(&args); + EXPECT_NE(shared_heap, nullptr) + << "Expected shared_heap will not be NULL even Size < " + "APP_HEAP_SIZE_MIN due to align_uint"; + + // Test size > APP_HEAP_SIZE_MAX + args.size = APP_HEAP_SIZE_MAX + 1; + shared_heap = wasm_runtime_create_shared_heap(&args); + EXPECT_EQ(shared_heap, nullptr) + << "Expected shared_heap to be NULL for Size > APP_HEAP_SIZE_MAX"; + + // Test size not aligned to system page size + args.size = os_getpagesize() - 1; // Non-aligned size + shared_heap = wasm_runtime_create_shared_heap(&args); + EXPECT_NE(shared_heap, nullptr) << "Expected shared_heap will not be NULL " + "for Non-aligned size due to align_uint"; +} + +// Test Case: Invalid pre-allocated address parameter +TEST_F(shared_heap_test, test_shared_heap_invalid_pre_allocated_addr) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 BUF_SIZE = os_getpagesize(); // System page size + uint8 *preallocated_buf_ptr = nullptr; + + ASSERT_GT(BUF_SIZE, 0u); + std::vector preallocated_buf(BUF_SIZE); + preallocated_buf_ptr = preallocated_buf.data(); + ASSERT_NE(preallocated_buf_ptr, nullptr); + + // Test NULL pre-allocated address with invalid size + args.pre_allocated_addr = nullptr; + args.size = 0; // Invalid size + shared_heap = wasm_runtime_create_shared_heap(&args); + EXPECT_EQ(shared_heap, nullptr) + << "Expected shared_heap to be NULL for NULL pre_allocated_addr and " + "invalid size"; + + // Test mismatched size with pre-allocated address + args.pre_allocated_addr = preallocated_buf_ptr; + args.size = BUF_SIZE - 1; // Size not aligned + shared_heap = wasm_runtime_create_shared_heap(&args); + EXPECT_EQ(shared_heap, nullptr) + << "Expected shared_heap to be NULL for pre_allocated_addr with " + "mismatched size"; +} + +TEST_F(shared_heap_test, test_divide_by_zero) +{ + struct ret_env module_env; + uint32 argv[1] = { 42 }; + bool ret = false; + + if (!load_wasm((char *)"test_runtime.wasm", 0, module_env)) { + FAIL() << "Failed to load test_runtime.wasm file"; + return; + } + + WASMFunctionInstanceCommon *func_test = wasm_runtime_lookup_function( + module_env.wasm_module_inst, "divide_by_zero"); + if (!func_test) { + FAIL() << "Failed to find divide_by_zero function"; + destroy_module_env(module_env); + return; + } + + ret = wasm_runtime_call_wasm(module_env.exec_env, func_test, 1, argv); + if (!ret) { + const char *exception = + wasm_runtime_get_exception(module_env.wasm_module_inst); + if (exception && strstr(exception, "integer divide by zero")) { + SUCCEED() << "Caught expected divide by zero exception: " + << exception; + } + else { + FAIL() << "Unexpected exception occurred: " + << (exception ? exception : "unknown"); + } + } + else { + FAIL() << "Expected divide by zero exception, but function executed " + "successfully"; + } + + destroy_module_env(module_env); +} + +TEST_F(shared_heap_test, test_shared_heap_multithread_performance) +{ + SharedHeapInitArgs args = {}; + WASMSharedHeap *shared_heap = nullptr; + uint32 argv[1] = { 0 }; + const int num_threads = 20; + std::vector threads; + + args.size = 1024; + shared_heap = wasm_runtime_create_shared_heap(&args); + if (!shared_heap) { + FAIL() << "Failed to create shared heap"; + } + + auto thread_func = [&shared_heap]() { + wasm_runtime_init_thread_env(); + uint32 local_argv[1] = { 0 }; + test_shared_heap(shared_heap, "test.wasm", "test", 0, local_argv); + EXPECT_EQ(10, local_argv[0]); + wasm_runtime_destroy_thread_env(); + }; + + for (int i = 0; i < num_threads; ++i) { + threads.emplace_back(thread_func); + } + + for (auto &thread : threads) { + thread.join(); + } + // Declare a mem_alloc_info_t struct to store memory allocation details + mem_alloc_info_t info; + + // Call the function to fetch memory allocation details + wasm_runtime_get_mem_alloc_info(&info); + + // Print memory allocation details + std::cout << "After inst" << std::endl; + std::cout << "Total size: " << info.total_size << std::endl; + std::cout << "Total free size: " << info.total_free_size << std::endl; + std::cout << "Highmark size: " << info.highmark_size << std::endl; +} + +#include // Include this for std::random_device and std::mt19937 + +std::vector +generate_random_data(size_t size) +{ + std::vector data(size); + std::random_device rd; // Random number generator + std::mt19937 gen(rd()); // Mersenne Twister engine + std::uniform_int_distribution dis(0, 255); // Uniform distribution + + for (size_t i = 0; i < size; i++) { + data[i] = dis(gen); + } + return data; +} + +// Fuzzing test for wasm_runtime_load +TEST_F(shared_heap_test, test_FuzzWasmRuntimeLoad) +{ + const size_t max_size = 1024 * 1024; // 1 MB max size + auto random_data = generate_random_data(max_size); + + char error_buf[128]; + wasm_module_t module = wasm_runtime_load( + random_data.data(), random_data.size(), error_buf, sizeof(error_buf)); + + if (module != NULL) { + wasm_runtime_unload(module); + } + else { + printf("Error message: %s\n", error_buf); + } +} + +// Fuzzing test for wasm_runtime_instantiate +TEST_F(shared_heap_test, test_FuzzWasmRuntimeInstantiate) +{ + const size_t max_size = 1024 * 1024; // 1 MB max size + auto random_data = generate_random_data(max_size); + + char error_buf[128]; + wasm_module_t module = wasm_runtime_load( + random_data.data(), random_data.size(), error_buf, sizeof(error_buf)); + + if (module != NULL) { + wasm_module_inst_t module_inst = wasm_runtime_instantiate( + module, 8092, 8092, error_buf, sizeof(error_buf)); + if (module_inst != NULL) { + wasm_runtime_deinstantiate(module_inst); + } + else { + printf("Error message: %s\n", error_buf); + } + wasm_runtime_unload(module); + } +} + +// Fuzzing test for wasm_runtime_call_wasm +TEST_F(shared_heap_test, test_FuzzWasmRuntimeCallWasm) +{ + const size_t max_size = 1024 * 1024; // 1 MB max size + auto random_data = generate_random_data(max_size); + + char error_buf[128]; + wasm_module_t module = wasm_runtime_load( + random_data.data(), random_data.size(), error_buf, sizeof(error_buf)); + + if (module != NULL) { + wasm_module_inst_t module_inst = wasm_runtime_instantiate( + module, 8092, 8092, error_buf, sizeof(error_buf)); + if (module_inst != NULL) { + wasm_exec_env_t exec_env = + wasm_runtime_create_exec_env(module_inst, 8092); + if (exec_env != NULL) { + wasm_function_inst_t func = wasm_runtime_lookup_function( + module_inst, "some_function_name"); + if (func != NULL) { + uint32_t wasm_argv[4] = { 0 }; + wasm_runtime_call_wasm(exec_env, func, 4, wasm_argv); + } + wasm_runtime_destroy_exec_env(exec_env); + } + wasm_runtime_deinstantiate(module_inst); + } + wasm_runtime_unload(module); + } +} \ No newline at end of file diff --git a/tests/unit/shared-heap/wasm-apps/CMakeLists.txt b/tests/unit/shared-heap/wasm-apps/CMakeLists.txt index b0482888f9..8e0feeae55 100644 --- a/tests/unit/shared-heap/wasm-apps/CMakeLists.txt +++ b/tests/unit/shared-heap/wasm-apps/CMakeLists.txt @@ -5,67 +5,136 @@ cmake_minimum_required(VERSION 3.14) project(wasm-apps) set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../..) +set(WAMRC_ROOT_DIR ${WAMR_ROOT_DIR}/wamr-compiler/build) -# Find WAMRC -set(WAMRC_ROOT_DIR ${WAMR_ROOT_DIR}/wamr-compiler) -find_program(WAMRC_BIN wamrc HINTS ${WAMRC_ROOT_DIR}/build REQUIRED) +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set(WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set(CMAKE_C_FLAGS "-nostdlib -Qunused-arguments") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=8192 -nostdlib -O0") +set(CMAKE_C_COMPILER_TARGET "wasm32") +set(CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set(DEFINED_SYMBOLS + "${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set(CMAKE_EXE_LINKER_FLAGS + "-Wl,--no-entry \ + -Wl,--initial-memory=65536 \ + -Wl,--export-all \ + -Wl,--allow-undefined" + ) -# Set architecture-specific WAMRC flags if (WAMR_BUILD_TARGET STREQUAL "X86_32") - set(WAMRC_SHARED_HEAP_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-heap --target=i386) - set(WAMRC_SHARED_HEAP_CHAIN_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-chain --target=i386) + set (WAMR_COMPILER_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-heap --target=i386) + set (WAMR_COMPILER_CHAIN_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-chain --target=i386) else () - set(WAMRC_SHARED_HEAP_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-heap) - set(WAMRC_SHARED_HEAP_CHAIN_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-chain) + set (WAMR_COMPILER_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-heap) + set (WAMR_COMPILER_CHAIN_FLAGS --opt-level=3 --bounds-checks=1 --enable-shared-chain) endif () -# -# C -> Wasm -# +function(copy_wasm TARGET_NAME) + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + ${CMAKE_CURRENT_BINARY_DIR}/../ + COMMENT "Copy ${TARGET_NAME} to the same directory of google test" + ) +endfunction() + +function(compile_and_copy_aot_from TARGET_NAME) + string(REPLACE ".wasm" ".aot" AOT_TARGET ${TARGET_NAME}) + string(REPLACE ".wasm" "_chain.aot" AOT_CHAIN_TARGET ${TARGET_NAME}) + + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND ${WAMRC_ROOT_DIR}/wamrc ${WAMR_COMPILER_FLAGS} + -o ${AOT_TARGET} + ${TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/${AOT_TARGET} + ${CMAKE_CURRENT_BINARY_DIR}/../ + COMMAND ${WAMRC_ROOT_DIR}/wamrc ${WAMR_COMPILER_CHAIN_FLAGS} + -o ${AOT_CHAIN_TARGET} + ${TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/${AOT_CHAIN_TARGET} + ${CMAKE_CURRENT_BINARY_DIR}/../ + COMMENT "Compile and copy ${AOT_TARGET} to the same directory of google test" + ) +endfunction() + add_executable(test.wasm test.c) -target_compile_options(test.wasm PUBLIC -nostdlib -O0 -pthread) -target_link_options(test.wasm PRIVATE - -nostdlib - LINKER:--no-entry - LINKER:--initial-memory=65536 - LINKER:--allow-undefined - LINKER:--export-all - -z stack-size=1024 -) +target_link_libraries(test.wasm) +copy_wasm(test.wasm) +compile_and_copy_aot_from(test.wasm) add_executable(test_addr_conv.wasm test_addr_conv.c) -target_compile_options(test_addr_conv.wasm PUBLIC -nostdlib -O0 -pthread) -target_link_options(test_addr_conv.wasm PRIVATE - -nostdlib - LINKER:--no-entry - LINKER:--initial-memory=65536 - LINKER:--allow-undefined - LINKER:--export-all - -z stack-size=1024 +target_link_libraries(test_addr_conv.wasm) +copy_wasm(test_addr_conv.wasm) +compile_and_copy_aot_from(test_addr_conv.wasm) + +# copy and compile aot for bulk memory test +set(SOURCE_WASM ${CMAKE_CURRENT_SOURCE_DIR}/bulk-memory/test_bulk_memory.wasm) +set(BUILD_WASM ${CMAKE_CURRENT_BINARY_DIR}/../test_bulk_memory.wasm) +set(OUTPUT_AOT ${CMAKE_CURRENT_BINARY_DIR}/../test_bulk_memory.aot) +set(OUTPUT_CHAIN_AOT ${CMAKE_CURRENT_BINARY_DIR}/../test_bulk_memory_chain.aot) + +add_custom_command( + OUTPUT ${BUILD_WASM} + COMMAND ${CMAKE_COMMAND} -E copy + ${SOURCE_WASM} + ${BUILD_WASM} + DEPENDS ${SOURCE_WASM} + COMMENT "Copying bulk memory WASM to build directory" +) + +add_custom_command( + OUTPUT ${OUTPUT_AOT} + COMMAND ${WAMRC_ROOT_DIR}/wamrc ${WAMR_COMPILER_FLAGS} + -o ${OUTPUT_AOT} + ${BUILD_WASM} + COMMAND ${WAMRC_ROOT_DIR}/wamrc ${WAMR_COMPILER_CHAIN_FLAGS} + -o ${OUTPUT_CHAIN_AOT} + ${BUILD_WASM} + DEPENDS ${BUILD_WASM} + COMMENT "Compiling bulk memory AOT from copied WASM" ) -# Compile AOT files (combined target) -add_custom_target(compile_aot ALL - COMMAND ${WAMRC_BIN} ${WAMRC_SHARED_HEAP_FLAGS} -o test.aot test.wasm - COMMAND ${WAMRC_BIN} ${WAMRC_SHARED_HEAP_CHAIN_FLAGS} -o test_chain.aot test.wasm - COMMAND ${WAMRC_BIN} ${WAMRC_SHARED_HEAP_FLAGS} -o test_addr_conv.aot test_addr_conv.wasm - COMMAND ${WAMRC_BIN} ${WAMRC_SHARED_HEAP_CHAIN_FLAGS} -o test_addr_conv_chain.aot test_addr_conv.wasm - DEPENDS test.wasm test_addr_conv.wasm - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +add_custom_target(compile_bulk_memory_aot ALL + DEPENDS ${OUTPUT_AOT} ) -# Install WASM files -set(WASM_FILES - ${CMAKE_CURRENT_BINARY_DIR}/test.wasm - ${CMAKE_CURRENT_BINARY_DIR}/test_addr_conv.wasm + +# copy val test +set(SOURCE_SANDBOX_WASM ${CMAKE_CURRENT_SOURCE_DIR}/additional/test_sandbox.wasm) +set(BUILD_SANDBOX_WASM ${CMAKE_CURRENT_BINARY_DIR}/../test_sandbox.wasm) +add_custom_command( + OUTPUT ${BUILD_SANDBOX_WASM} + COMMAND ${CMAKE_COMMAND} -E copy + ${SOURCE_SANDBOX_WASM} + ${BUILD_SANDBOX_WASM} + DEPENDS ${SOURCE_SANDBOX_WASM} + COMMENT "Copying test_sandbox.wasm to build directory" +) +add_custom_target(copy_test_sandbox ALL + DEPENDS ${BUILD_SANDBOX_WASM} ) -install(FILES ${WASM_FILES} DESTINATION .) - -# Install AOT files -set(AOT_FILES - ${CMAKE_CURRENT_BINARY_DIR}/test.aot - ${CMAKE_CURRENT_BINARY_DIR}/test_chain.aot - ${CMAKE_CURRENT_BINARY_DIR}/test_addr_conv.aot - ${CMAKE_CURRENT_BINARY_DIR}/test_addr_conv_chain.aot + + +set(SOURCE_RUNTIME_WASM ${CMAKE_CURRENT_SOURCE_DIR}/additional/test_runtime.wasm) +set(BUILD_RUNTIME_WASM ${CMAKE_CURRENT_BINARY_DIR}/../test_runtime.wasm) +add_custom_command( + OUTPUT ${BUILD_RUNTIME_WASM} + COMMAND ${CMAKE_COMMAND} -E copy + ${SOURCE_RUNTIME_WASM} + ${BUILD_RUNTIME_WASM} + DEPENDS ${SOURCE_RUNTIME_WASM} + COMMENT "Copying test_runtime.wasm to build directory" ) -install(FILES ${AOT_FILES} DESTINATION .) +add_custom_target(copy_test_runtime ALL + DEPENDS ${BUILD_RUNTIME_WASM} +) \ No newline at end of file diff --git a/tests/unit/shared-heap/wasm-apps/additional/test_runtime.wasm b/tests/unit/shared-heap/wasm-apps/additional/test_runtime.wasm new file mode 100644 index 0000000000..dc7e3f8f90 Binary files /dev/null and b/tests/unit/shared-heap/wasm-apps/additional/test_runtime.wasm differ diff --git a/tests/unit/shared-heap/wasm-apps/additional/test_runtime.wat b/tests/unit/shared-heap/wasm-apps/additional/test_runtime.wat new file mode 100644 index 0000000000..b9524f878a --- /dev/null +++ b/tests/unit/shared-heap/wasm-apps/additional/test_runtime.wat @@ -0,0 +1,7 @@ +(module + ;; Export a function for division operation + (func (export "divide_by_zero") (param $numerator i32) (result i32) + ;; Directly perform division by zero + (i32.div_s (local.get $numerator) (i32.const 0)) + ) +) \ No newline at end of file diff --git a/tests/unit/shared-heap/wasm-apps/additional/test_sandbox.wasm b/tests/unit/shared-heap/wasm-apps/additional/test_sandbox.wasm new file mode 100644 index 0000000000..5930731e02 Binary files /dev/null and b/tests/unit/shared-heap/wasm-apps/additional/test_sandbox.wasm differ diff --git a/tests/unit/shared-heap/wasm-apps/additional/test_sandbox.wat b/tests/unit/shared-heap/wasm-apps/additional/test_sandbox.wat new file mode 100644 index 0000000000..c5700fbbcd --- /dev/null +++ b/tests/unit/shared-heap/wasm-apps/additional/test_sandbox.wat @@ -0,0 +1,37 @@ +(module + ;; Define memory with initial size = 1 page (64KiB) and max size = 4 pages + (memory 1 4) + + ;; Function to grow memory dynamically + (func (export "grow_memory") (param $pages i32) (result i32) + (memory.grow (local.get $pages))) + + ;; Function to return current memory size in pages + (func (export "memory_size") (result i32) + (memory.size)) + + ;; Function to store an integer value at a specific address + (func (export "store") (param $addr i32) (param $val i32) + (i32.store align=4 (local.get $addr) (local.get $val))) + + ;; Function to load an integer value from a specific address + (func (export "load") (param $addr i32) (result i32) + (i32.load align=4 (local.get $addr))) + + ;; Data segment initialization (valid offset) + (data (i32.const 0) "valid_data") +) + +;; ;; Test initial memory size +;; (assert_return (invoke "memory_size") (i32.const 1)) + +;; ;; Test memory growth +;; (assert_return (invoke "grow_memory" (i32.const 1)) (i32.const 1)) ;; Grow by 1 page +;; (assert_return (invoke "memory_size") (i32.const 2)) ;; Verify new size +;; (assert_return (invoke "grow_memory" (i32.const 2)) (i32.const 2)) ;; Grow to max size +;; (assert_return (invoke "memory_size") (i32.const 4)) ;; Verify max size +;; (assert_return (invoke "grow_memory" (i32.const 1)) (i32.const -1)) ;; Exceed max size + +;; ;; Test unaligned store and load +;; (invoke "store" (i32.const 4) (i32.const 100)) ;; Store value 100 at address 4 +;; (assert_return (invoke "load" (i32.const 4)) (i32.const 100)) ;; Load value from address 4 \ No newline at end of file