Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ tinywasm
venv
wasmtime-as-lib
image_content/*
**/__pycache__/
*.py[cod]
reports/latency_report.md
reports/latency_report_results.json

.VSCodeCounter
.gdbinit
Expand All @@ -13,4 +17,4 @@ _members.txt

**/*.img
**/fake*txt
**/*.bin
**/*.bin
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "nom-uri"]
path = nom-uri
url = https://github.com/Skasselbard/nom-uri.git
[submodule "third_party/musl"]
path = third_party/musl
url = https://git.musl-libc.org/git/musl
[submodule "third_party/cpython"]
path = third_party/cpython
url = https://github.com/python/cpython.git
116 changes: 116 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# AlloyStack 协作指南

## 项目定位

AlloyStack 是面向 Serverless workflow 的 Rust LibOS 运行时。它在单一地址空间中:

- 按需加载用户函数及系统服务,以降低冷启动开销。
- 通过引用传递 workflow 的中间数据,减少序列化与复制。
- 让 Rust 用户函数以 `#![no_std]` 方式依赖 `as_std`,并直接链接到 LibOS,而不是 libc。
- 通过定制的 `wasmtime-as-lib` 执行 WebAssembly,并由 `wasmtime_wasi_api` 将 WASI 调用接入 LibOS,从而承载 C/CPython workload。

注意:仓库实现使用 **Wasmtime**,不是 Wasmer。讨论或修改 WebAssembly 路径时,以代码中的 `wasmtime_wasi_api` 和 `wasmtime-as-lib` 为准。

## 目录职责

- `bins/asvisor/`:主要运行时命令行入口。
- `libasvisor/`:隔离实例、模块加载、workflow 调度、指标和 hostcall 接入。
- `as_hostcall/`:LibOS hostcall 及文件、内存、网络等后端。
- `as_std/`:Rust 用户函数使用的 `no_std` 接口、分配器、panic 和数据传递支持。
- `as_std_proc_macro/`:用户函数相关过程宏。
- `common_service/`:可独立构建并按需加载的 LibOS 服务。
- `wasmtime_wasi_api/`:Wasmtime/WASI 到 LibOS 的适配层。
- `user/`:用户函数及 Wasmtime/CPython workload;不属于根 Cargo workspace。
- `isol_config/`:workflow JSON 定义,是运行时装配 workflow 的事实来源。
- `libasvisor/tests/`:运行时集成测试。
- `baseline/`:对比实现和实验脚本,不属于根 Cargo workspace。
- `fs_images/`、`image_content/`:文件系统镜像及挂载内容。

根 workspace 刻意排除了 `user/`、`common_service/` 和 `baseline/`。不要仅凭根目录 `cargo test` 判断这些组件已经构建或验证。

## 工具链与构建

项目基准环境是 Ubuntu 22.04、GCC 11.4 和 `nightly-2023-12-01`。部分实验依赖 Intel MPK、sudo、TAP 设备或已挂载的 FAT 镜像。

常用命令:

```bash
just init
just asvisor
just libos <service_name>
just rust_func <function_name>
just all_libos
target/release/asvisor --files isol_config/<workflow>.json
```

`justfile` 默认以 release 模式构建。可通过变量覆盖功能开关,例如:

```bash
just enable_mpk=1 rust_func <function_name>
just enable_file_buffer=1 rust_func <function_name>
just enable_release=0 asvisor
```

相关 feature 约束:

- `mpk`/`enable_mpk` 启用内存保护键;需要兼容硬件和运行环境。
- `pkey_per_func` 建立在 MPK 之上。
- `file-based` 用文件中转代替引用传递,主要用于消融实验。
- `as_std` 的 `unwinding` 与 `panic_def` 互斥,不得同时启用。

不要擅自运行需要 sudo、修改网络设备、挂载镜像或执行完整性能评测的 recipe。此类命令包括但不限于 `cold_start_latency`、`end_to_end_latency`、`breakdown`、`p99` 和 `resource_consume`。

## Workflow 配置

workflow 在 `isol_config/*.json` 中定义:

- `services`:需加载的 LibOS 服务及其 `.so` 文件。
- `apps`:用户函数及其 `.so` 文件。
- `groups`:按顺序执行的阶段;同一 group 中的函数可并行执行。
- group 级 `args` 会作为默认参数,单个 app 的 `args` 会覆盖同名值。
- 运行时会为 group 中每个 app 注入字符串形式的 `id`。
- 可选字段包括 `fs_image` 和 `with_libos`。

新增或修改 workflow 时,应同步确认:

1. 所有 service 和 app 名称与构建产物名称一致。
2. 所需 service 已列入 `services`,并可通过 `just libos <name>` 构建。
3. app 可通过 `just rust_func <name>` 或对应的 `wasm_func` recipe 构建。
4. group 参数均为字符串,并与函数实际读取的键一致。
5. workflow 所需数据确实存在于对应文件系统镜像中。

## 修改原则

- 保持单地址空间、按需加载和引用传递这三条核心路径的语义;不要无意中引入 libc 依赖或跨边界复制。
- 修改用户函数接口时,同时检查 `as_std`、`as_hostcall`、运行时装载逻辑和相关 workflow 配置。
- 修改 WASI 实现时,检查 `wasmtime_wasi_api/src/lib.rs` 的 linker 注册及 `wasi.rs` 中对应实现。
- 新增用户函数时遵循相邻 `user/*` crate 的布局、crate type、build script 和 feature 传递方式。
- 不要把 `user/` 或 `common_service/` 草率加入根 workspace;现有排除与 `as_std` 的冲突 feature 组合有关。
- 不要提交生成物、挂载内容、实验日志或大型数据文件,除非任务明确要求。
- 仓库含来源于 RuxOS 的代码;修改相关实现时保留既有来源和许可证信息。

## 验证要求

验证范围应与修改范围匹配,优先使用最小且有针对性的命令:

```bash
cargo fmt --all -- --check
cargo test -p libasvisor
cargo build -p asvisor
just libos <changed_service>
just rust_func <changed_function>
target/release/asvisor --files isol_config/<affected_workflow>.json
```

注意:

- 根 workspace 构建可能需要获取定制 git 依赖;网络不可用时应明确报告,不能把依赖获取失败描述成代码失败。
- 集成测试和 workflow 执行可能依赖 MPK、镜像、网络权限或已构建的动态库。报告验证结果时区分代码错误与环境前置条件不足。
- 性能相关修改不能只证明“能运行”;应使用相同构建模式、feature、数据集和 workflow 配置进行前后对比。
- 未实际执行的测试不得声称通过。

## 沟通约定

- 回答使用中文,保持结构化、直接和可核验。
- 对项目描述、代码行为和实验结论都保持审慎;发现文档与实现冲突时,以代码和可复现实验为依据,并明确指出冲突。
- 不做过度夸赞。信息不足且会影响技术结论时,主动索要配置、日志、复现步骤或实验数据。
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ members = [
# In current implementation, as_std has conflict features (e.g. `panic_def` and
# `unwinding`). Of course, if add "user/" to members, will also have problem of
# feature gate.
exclude = ["user/", "common_service/", "baseline/"]
#
# as_musl enables as_std's application-side allocator and panic definitions, so
# it must also be built separately through the musl user crates.
exclude = ["user/", "common_service/", "baseline/", "as_musl/"]
default-members = ["bins/asvisor"]
resolver = "2"

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ AlloyStack/
To run a new test application on AlloyStack, user need to develop functions in the `user/` directory. Then, edit the workflow specification files in the `isol_config/` directory to declare how functions compose the workflow, specify dependencies on LibOS modules, and define input parameters for functions. If the workflow involves reading datasets from files, the datasets must also be added to the file system image. Please use the following command to extract the provided image archive, which contains the source code for the Python benchmarks.

For detailed documentation, please refer to [AlloyStack User Guide](./doc/).
For source-compatible C functions backed by musl, see
[Running C functions with musl](./doc/musl_functions.md).

## Evaluation
### Cold start latency
Expand Down
1 change: 1 addition & 0 deletions as_hostcall/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ thiserror-no-std = "2.0.2"
fatfs = []
socket = []
signal = []
sys = []

fdtab = ["fatfs", "socket"]
mmap_file_backend = ["fdtab"]
Expand Down
3 changes: 3 additions & 0 deletions as_hostcall/src/fatfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ pub type FatfsWriteFunc = fn(Fd, &[u8]) -> FatfsResult<Size>;
pub type FatfsReadFunc = fn(Fd, &mut [u8]) -> FatfsResult<Size>;
pub type FatfsCloseFunc = fn(Fd) -> FatfsResult<()>;
pub type FatfsSeekFunc = fn(Fd, u32) -> FatfsResult<()>;
pub type FatfsSeek64Func = fn(Fd, i64, i32) -> FatfsResult<i64>;
pub type FatfsStatFunc = fn(Fd) -> FatfsResult<Stat>;
pub type FatfsPathStatFunc = fn(&str) -> FatfsResult<Stat>;
pub type FatfsReadDirFunc = fn(&str, &mut [u8]) -> FatfsResult<Size>;

pub type FatfsResult<T> = Result<T, FatfsError>;

Expand Down
6 changes: 5 additions & 1 deletion as_hostcall/src/fdtab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ use thiserror_no_std::Error;
use crate::{
fatfs::FatfsError,
socket::SmoltcpError,
types::{Fd, OpenFlags, OpenMode, Size, SockFd, Stat, DirEntry},
types::{DirEntry, Fd, OpenFlags, OpenMode, Size, SockFd, Stat},
};

pub type OpenFunc = fn(&str, OpenFlags, OpenMode) -> FdtabResult<Fd>;
pub type OpenDirFunc = fn(&str) -> FdtabResult<Fd>;
pub type GetDentsFunc = fn(Fd, &mut [u8]) -> FdtabResult<Size>;
pub type WriteFunc = fn(Fd, &[u8]) -> FdtabResult<Size>;
pub type ReadFunc = fn(Fd, &mut [u8]) -> FdtabResult<Size>;
pub type CloseFunc = fn(Fd) -> FdtabResult<()>;
pub type LseekFunc = fn(Fd, u32) -> FdtabResult<()>;
pub type Lseek64Func = fn(Fd, i64, i32) -> FdtabResult<i64>;
pub type StatFunc = fn(Fd) -> FdtabResult<Stat>;
pub type PathStatFunc = fn(&str) -> FdtabResult<Stat>;
pub type ReadDirFunc = fn(&str) -> FdtabResult<Vec<DirEntry>>;
pub type ConnectFunc = fn(SocketAddrV4) -> FdtabResult<Fd>;
pub type BindFunc = fn(SocketAddrV4) -> FdtabResult<Fd>;
Expand Down
51 changes: 50 additions & 1 deletion as_hostcall/src/lib.rs

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不需要borrow_buffer的语义

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub mod mpk;
pub mod signal;
#[cfg(feature = "socket")]
pub mod socket;
#[cfg(feature = "sys")]
pub mod sys;
pub mod types;

use alloc::{borrow::ToOwned, string::String};
Expand All @@ -43,12 +45,20 @@ pub enum CommonHostCall {
Read,
#[display(fmt = "open")]
Open,
#[display(fmt = "open_dir")]
OpenDir,
#[display(fmt = "getdents")]
GetDents,
#[display(fmt = "close")]
Close,
#[display(fmt = "lseek")]
Lseek,
#[display(fmt = "lseek64")]
Lseek64,
#[display(fmt = "stat")]
Stat,
#[display(fmt = "path_stat")]
PathStat,
#[display(fmt = "readdir")]
ReadDir,
#[display(fmt = "connect")]
Expand All @@ -73,8 +83,14 @@ pub enum CommonHostCall {
FatfsClose,
#[display(fmt = "fatfs_seek")]
FatfsSeek,
#[display(fmt = "fatfs_seek64")]
FatfsSeek64,
#[display(fmt = "fatfs_stat")]
FatfsStat,
#[display(fmt = "fatfs_path_stat")]
FatfsPathStat,
#[display(fmt = "fatfs_readdir")]
FatfsReadDir,

#[display(fmt = "addrinfo")]
SmoltcpAddrInfo,
Expand All @@ -93,6 +109,10 @@ pub enum CommonHostCall {

#[display(fmt = "buffer_alloc")]
BufferAlloc,
#[display(fmt = "buffer_alloc_raw")]
BufferAllocRaw,
#[display(fmt = "buffer_register")]
BufferRegister,
#[display(fmt = "access_buffer")]
AccessBuffer,
#[display(fmt = "buffer_dealloc")]
Expand All @@ -118,6 +138,20 @@ pub enum CommonHostCall {

#[display(fmt = "libos_sigaction")]
SigAction,

#[display(fmt = "host_futex")]
Futex,
#[display(fmt = "host_gettid")]
GetTid,
#[display(fmt = "host_getrandom")]
GetRandom,

// Keep new hostcalls at the end to preserve existing discriminants used by
// prebuilt user modules.
#[display(fmt = "buffer_set_len")]
BufferSetLen,
#[display(fmt = "buffer_len")]
BufferLen,
}

#[derive(Debug, Display)]
Expand All @@ -136,10 +170,14 @@ impl HostCallID {

CommonHostCall::Write
| CommonHostCall::Open
| CommonHostCall::OpenDir
| CommonHostCall::GetDents
| CommonHostCall::Read
| CommonHostCall::Close
| CommonHostCall::Lseek
| CommonHostCall::Lseek64
| CommonHostCall::Stat
| CommonHostCall::PathStat
| CommonHostCall::ReadDir
| CommonHostCall::Connect
| CommonHostCall::Socket
Expand All @@ -153,7 +191,10 @@ impl HostCallID {
| CommonHostCall::FatfsRead
| CommonHostCall::FatfsClose
| CommonHostCall::FatfsSeek
| CommonHostCall::FatfsStat => "fatfs".to_owned(),
| CommonHostCall::FatfsSeek64
| CommonHostCall::FatfsStat
| CommonHostCall::FatfsPathStat
| CommonHostCall::FatfsReadDir => "fatfs".to_owned(),

CommonHostCall::SmoltcpAddrInfo
| CommonHostCall::SmoltcpConnect
Expand All @@ -164,6 +205,10 @@ impl HostCallID {
| CommonHostCall::SmoltcpClose => "socket".to_owned(),

CommonHostCall::BufferAlloc
| CommonHostCall::BufferAllocRaw
| CommonHostCall::BufferRegister
| CommonHostCall::BufferSetLen
| CommonHostCall::BufferLen
| CommonHostCall::AccessBuffer
| CommonHostCall::BufferDealloc
| CommonHostCall::Mmap
Expand All @@ -177,6 +222,10 @@ impl HostCallID {
CommonHostCall::SigAction => "signal".to_owned(),

CommonHostCall::GetTime | CommonHostCall::NanoSleep => "time".to_owned(),

CommonHostCall::Futex | CommonHostCall::GetTid | CommonHostCall::GetRandom => {
"sys".to_owned()
}
},
HostCallID::Custom(_) => todo!(),
}
Expand Down
10 changes: 9 additions & 1 deletion as_hostcall/src/mm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ bitflags! {
}

pub type BufferAllocFunc = fn(&str, Layout, u64) -> MMResult<usize>;
pub type AccessBufferFunc = fn(&str) -> Option<(usize, u64)>;
pub type BufferAllocRawFunc = fn(Layout) -> MMResult<usize>;
pub type BufferRegisterFunc = fn(&str, usize, u64, usize) -> MMResult<()>;
pub type BufferSetLenFunc = fn(&str, usize) -> MMResult<()>;
pub type BufferLenFunc = fn(&str) -> Option<usize>;
pub type AccessBufferFunc = fn(&str) -> Option<(usize, u64, usize)>;
pub type BufferDeallocFunc = fn(usize, Layout);
pub type MemmapFunc = fn(usize, usize, ProtFlags, Fd) -> MMResult<usize>;
pub type MemunmapFunc = fn(&mut [u8], bool) -> MMResult<()>;
Expand All @@ -34,4 +38,8 @@ pub enum MMError {
FileBackendErr(#[from] MmapFileErr),
#[error("libc api error: {0}")]
LibcErr(String),
#[error("buffer allocation failed")]
NoMemory,
#[error("buffer slot already exists")]
BufferSlotExists,
}
3 changes: 3 additions & 0 deletions as_hostcall/src/sys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub type FutexFunc = unsafe extern "C" fn(*mut i32, i32, i32, *const u8, *mut i32, i32) -> isize;
pub type GetTidFunc = extern "C" fn() -> isize;
pub type GetRandomFunc = unsafe extern "C" fn(*mut u8, usize, u32) -> isize;
Loading
Loading