WasmEdge 基礎介紹跟專案架構
Wasm 基礎背景
檔案格式:
.wat(WebAssembly Text Format): 人眼可讀的純文字格式。類似於組合語言,開發者可以用它來進行底層除錯或理解 Wasm 的運行邏輯.wasm(WebAssembly Binary Format): 二進制格式。這是 Wasm 的標準執行檔,體積小且跨平台,是交給瀏覽器或 WasmEdge 等運行環境執行的正式檔案.aot.wasm(Ahead-of-Time Compiled): 預先編譯後的格式,這是 WasmEdge 特有的優化檔案,將.wasm事先轉譯成機器碼。執行時不需要再經過「直譯」,速度會比純.wasm快非常多
常見工具:
- WABT (WebAssembly Binary Toolkit): 這是一套 Wasm 的基礎工具包,其中最常用的是
wat2wasm,它能將人類看得懂的.wat文字檔轉換成電腦執行的.wasm二進制檔 - WasmEdge Runtime: 有不同高效能的 Wasm 執行引擎
- Interpreter (直譯) 模式: 逐行讀取並執行,靈活性高但速度較慢
- AOT (預先編譯) 模式: 配合編譯後的 .aot.wasm 執行,效能接近原生程式(Native code)
- Emscripten (emcc):這是一個強大的編譯器工具鏈,基於 LLVM,它能讓開發者直接將現有的 C 或 C++ 專案編譯成 WebAssembly。它不僅處理程式碼,還會處理系統呼叫(System Calls)和庫的連結,是 C++ 進入 Wasm 世界的主流工具
WasmEdge
在 WebAssembly 的世界裡,有幾個著名的 Runtime(如 Wasmtime, Wasmer),而 WasmEdge 專攻的是 「高效能、雲原生 (Cloud Native) 與邊緣運算 (Edge)」。
專案架構
- include/: 定義不同標頭檔
- api/: WasmEdge 的 C API 定義(這是外部整合最常用的入口)
- runtime/: 核心組件的定義,包括你正在修改的 store.h (StoreManager) 和 instance
- lib/: 各種核心實作
你可以參考 Build Guide | WasmEdge Developer Guides 來自己編譯專案,基本上使用 CMake 設定 Make config,然後跑 make,或者你可以用 -Bbuild -GNinja 用 ninja 提升編譯時間。
sudo apt update
sudo apt install -y git cmake ninja-build build-essential llvm-15-dev liblld-15-dev
git clone https://github.com/WasmEdge/WasmEdge.git
cd WasmEdge
mkdir -p build && cd build
# 開啟測試選項,這樣才能跑 Unit Test
cmake -GNinja -DWASMEDGE_BUILD_TESTS=ON ..
ninja
ctest最後會在 build 目錄底下看到產生的東西。
kola:~/proj/WasmEdge/build$ ls
build.ninja CPackConfig.cmake include thirdparty
CMakeCache.txt CPackSourceConfig.cmake lib tools
CMakeFiles CTestTestfile.cmake plugins
cmake_install.cmake DartConfiguration.tcl test
compile_commands.json _deps Testing基本上編譯的時候要 (假如用 g++):
- 指定標頭檔 (
-I./build/include/api) - 連結共享庫 (-L 與 -l),用
-L./build/lib/api指定庫的搜尋路徑,使用 -lwasmedge 告訴編譯器連結libwasmedge.so - 設定執行路徑 (
LD_LIBRARY_PATH),LD_LIBRARY_PATH=./build/lib/api添加目錄,讓 Loader 去這個目錄找動態連結的 .so 檔案
測試
如果你有在 camke 使用 -DWASMEDGE_BUILD_TESTS=ON,你可以在 build 目錄使用 ctest 跑測試。
而實際上測試都放在 test/ 目錄底下,底下有包含不同種類的測試,像是 api, aot …
舉理我們看 test/api/APIVMCoreTest.cpp
TEST(WasmEdgeVM, ForceDeleteRegisteredModule) {
WasmEdge_ConfigureContext *Conf = WasmEdge_ConfigureCreate();
WasmEdge_VMContext *VMCxt = WasmEdge_VMCreate(Conf, nullptr);
uint32_t originalCount = WasmEdge_VMListRegisteredModuleLength(VMCxt);
WasmEdge_String ModuleName = WasmEdge_StringCreateByCString("test_module");
WasmEdge_ModuleInstanceContext *ModInst =
WasmEdge_ModuleInstanceCreate(ModuleName);
WasmEdge_Result Res = WasmEdge_VMRegisterModuleFromImport(VMCxt, ModInst);
EXPECT_TRUE(WasmEdge_ResultOK(Res));
EXPECT_EQ(WasmEdge_VMListRegisteredModuleLength(VMCxt), originalCount + 1);
// Force delete
WasmEdge_VMForceDeleteRegisteredModule(VMCxt, ModuleName);
EXPECT_EQ(WasmEdge_VMListRegisteredModuleLength(VMCxt), originalCount);
// Added check to ensure module is no longer accessible
WasmEdge_StoreContext *StoreCxt = WasmEdge_VMGetStoreContext(VMCxt);
const WasmEdge_ModuleInstanceContext *FindResult =
WasmEdge_StoreFindModule(StoreCxt, ModuleName);
EXPECT_EQ(FindResult, nullptr);
// Cleanup
WasmEdge_StringDelete(ModuleName);
WasmEdge_VMDelete(VMCxt);
WasmEdge_ConfigureDelete(Conf);
}它每個測試都是用 TEST() 包裝的,TEST(SuiteName, TestName) 是 gtest 的基本 macro。EXPECT_* 與 ASSERT_*: 用來斷言結果。例如 EXPECT_EQ 檢查相等,EXPECT_TRUE 檢查布林值。
雖然測試程式碼本身不寫釋放檢查,但 WasmEdge 的 CI 會跑 ASan (AddressSanitizer) 。如果你「太早刪除」導致 Use-After-Free,測試就會噴錯。
我們可以用
cd build/test/api/
./wasmedgeAPIVMCoreTests --gtest_filter=WasmEdgeVM.ForceDeleteRegisteredModule跑特定測試,不然預設 ctest 跑所有測試。
官方測試資料
根據 Shipping binary precompiled WASM files is problematic for downstreams #1869,以前測試資料還會有 wasm binary 檔案,現在不回有了。
並且你看測試,他們其實會用 wast2json,官方測試通常是 .wast 格式(S-Expression 腳本)。WasmEdge 使用 wast2json 這個工具把一個 .wast 檔案拆開,變成一個包含指令的 json 檔案和一堆二進制的 wasm 檔案,可以看 WasmEdge-spectest。