目錄

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 -GNinjaninja 提升編譯時間。

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++):

  1. 指定標頭檔 (-I./build/include/api)
  2. 連結共享庫 (-L 與 -l),用 -L./build/lib/api 指定庫的搜尋路徑,使用 -lwasmedge 告訴編譯器連結 libwasmedge.so
  3. 設定執行路徑 (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