第一部分:问题复盘与解决方案 (From Zero to Hero)

我们按时间线梳理了遇到的 3 个核心问题:

1. Libc SIGILL (Fatal signal 4) 崩溃

  • 现象:App 刚运行到函数调用就闪退,报错 ILL_ILLOPC (非法指令)。
  • 原因
    • 信号冲突:Wasmtime 默认使用系统信号 (SIGBUS/SIGSEGV) 来处理内存越界,这与 Android 的 ART 虚拟机信号处理冲突。
    • JIT 激进优化:默认生成的 ARM64 机器码可能包含你手机 CPU 不支持的指令(如 SIMD 或 BTI)。
  • 解决方案
    • signals_based_traps = false关键修复,强制使用显式的 if 判断做边界检查,不依赖系统信号。
    • opt_level = NONE:关闭优化,生成最保守的代码。
    • simd = false:关闭向量指令,保证兼容性。

2. Wasm Trap: null reference

  • 现象:不闪退了,但报错 Trap,提示空指针。
  • 原因Kotlin 运行时未初始化。Wasm 模块只是加载了,但 Kotlin 的 GC 和内存分配器还没启动。CLI 会自动运行 _start,但嵌入式 API 需要手动调用。
  • 解决方案
    • 在调用 add 之前,手动查找并调用 _initialize (Reactor模式) 或 _start (Command模式)。

3. 看不到 println 日志

  • 现象:运行成功返回 3,但 Logcat 空空如也。
  • 原因:WASI 的标准输出 (stdout) 默认指向 /dev/null
  • 解决方案
    • 使用 wasi_config_set_stdout_custom 注册回调函数,将 stdout 数据流“偷”出来,通过 __android_log_print 转发给 Logcat。

第二部分:配置影响分析 (Trade-offs)

为了修复 SIGILL 和崩溃,我们使用了一些“保守”的配置。以下是这些修改带来的具体影响:

1. 关闭信号陷阱 (signals_based_traps = false)

  • 影响性能略微下降,但稳定性极大提升。
  • 解释
    • 默认模式:Wasmtime 假定内存访问是安全的,不做检查。如果越界,依靠操作系统抛出信号 (Hardware Trap) 来捕获。这很快,但在 Android 这种对信号管控严格的环境极易崩溃。
    • 当前模式:Wasmtime 会在每一条涉及内存读写的指令前,插入一段汇编代码(类似 if (addr > max) throw error)。这增加了 CPU 指令数量,但确保了绝对安全,不会导致 App 闪退,只会返回可捕获的 Trap 错误。

2. 关闭优化 (opt_level = NONE)

  • 影响JIT 编译速度变快,但 Wasm 执行速度变慢。
  • 解释:编译器不再花时间做循环展开、寄存器分配优化等高级操作。对于 add 这种简单函数没感觉,但如果是复杂的算法(如图像处理),性能可能会比开启优化慢 2-5 倍。
  • 未来优化:等环境稳定后,可以尝试改回 WASMTIME_OPT_LEVEL_SPEED,只要保留 signals_based_traps = false,或许能跑通的。

3. 关闭 SIMD (simd = false)

  • 影响无法利用硬件加速进行并行计算。
  • 解释:如果你的 Wasm 模块(比如图像滤镜、加密库)大量使用了向量指令,关闭此选项会导致模块加载失败或回退到标量计算(变慢)。但对于业务逻辑(如 Kotlin UI、数据处理),几乎没有影响。

总结

当前的配置是 “兼容性优先 (Stability First)” 的方案。它牺牲了一点点运行时性能,换取了在各种 Android 机型(无论新旧 CPU、无论系统信号机制如何)上都能不闪退、稳定运行的保障。对于初期集成和调试来说,这是最完美的起步点。

第三部分:优化后的完整 C++ 代码

我对代码进行了重构,特点如下:

  1. 增加配置开关:JNI 函数增加 jboolean enableLogs 参数,由 Kotlin 控制是否开启日志转发。
  2. 结构清晰:将配置、初始化、调用分块注释。
  3. 内存安全:确保所有 JNI 资源和 Wasm 对象都被正确释放。

1. 修改 Kotlin 定义 (MainActivity.kt)

1
2
3
4
5
// 增加一个 boolean 参数控制日志
private external fun runWasmAdd(wasmBytes: ByteArray, enableLogs: Boolean): Int

// 调用时
runWasmAdd(wasmBytes, true) // 开启日志

2. C++ 实现 (wasm_host.cpp)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <jni.h>
#include <android/log.h>
#include <vector>
#include <string>

// Wasmtime Headers
#include "wasm.h"
#include "wasi.h"
#include "wasmtime.h"

#define TAG "KotlinWasm"
// 简化的日志宏
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)

// ============================================================================
// 工具函数:错误打印
// ============================================================================
static void log_and_exit_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) {
wasm_byte_vec_t error_message;
if (error != nullptr) {
wasmtime_error_message(error, &error_message);
wasmtime_error_delete(error);
LOGE("ERROR: %s -> %.*s", message, (int)error_message.size, error_message.data);
} else if (trap != nullptr) {
wasm_trap_message(trap, &error_message);
wasm_trap_delete(trap);
LOGE("TRAP: %s -> %.*s", message, (int)error_message.size, error_message.data);
} else {
LOGE("UNKNOWN ERROR: %s", message);
}
if (error || trap) wasm_byte_vec_delete(&error_message);
}

// ============================================================================
// 回调函数:WASI stdout -> Android Logcat
// ============================================================================
ptrdiff_t android_log_callback(void* data, const unsigned char* buffer, size_t size) {
if (size == 0) return 0;
// 构造字符串并移除末尾换行符(Logcat 自带换行)
std::string msg((const char*)buffer, size);
while (!msg.empty() && (msg.back() == '\n' || msg.back() == '\r')) {
msg.pop_back();
}
if (!msg.empty()) {
__android_log_print(ANDROID_LOG_INFO, TAG, "[WasmOut] %s", msg.c_str());
}
return size;
}

// ============================================================================
// JNI 主入口
// ============================================================================
extern "C" JNIEXPORT jint JNICALL
Java_crow_wasmedge_wasmline_MainActivity_runWasmAdd(JNIEnv *env, jobject thiz, jbyteArray wasmBytes, jboolean enableLogs) {

// --- 1. 获取 Wasm 字节数据 ---
jsize len = env->GetArrayLength(wasmBytes);
jbyte *bytes = env->GetByteArrayElements(wasmBytes, nullptr);
if (!bytes) return -1;

// --- 2. 配置 Wasmtime (The Silver Bullet Config) ---
wasm_config_t *config = wasm_config_new();

// 开启 Kotlin 所需特性
wasmtime_config_wasm_gc_set(config, true);
wasmtime_config_wasm_function_references_set(config, true);
wasmtime_config_wasm_exceptions_set(config, true);

// 解决 Android SIGILL 崩溃的核心配置
wasmtime_config_cranelift_opt_level_set(config, WASMTIME_OPT_LEVEL_NONE); // 关闭优化
wasmtime_config_wasm_simd_set(config, false); // 关闭 SIMD
wasmtime_config_wasm_relaxed_simd_set(config, false); // 关闭 Relaxed SIMD
wasmtime_config_signals_based_traps_set(config, false); // 关闭信号 Trap,改用显式检查

// --- 3. 创建 Engine & Store ---
wasm_engine_t *engine = wasm_engine_new_with_config(config);
if (!engine) {
env->ReleaseByteArrayElements(wasmBytes, bytes, JNI_ABORT);
LOGE("Failed to create engine");
return -1;
}
wasmtime_store_t *store = wasmtime_store_new(engine, nullptr, nullptr);
wasmtime_context_t *context = wasmtime_store_context(store);

// --- 4. 配置 WASI (含日志重定向) ---
wasi_config_t *wasi_config = wasi_config_new();
wasi_config_inherit_argv(wasi_config);
wasi_config_inherit_env(wasi_config);

if (enableLogs) {
// 如果启用,挂载回调函数
wasi_config_set_stdout_custom(wasi_config, android_log_callback, nullptr, nullptr);
wasi_config_set_stderr_custom(wasi_config, android_log_callback, nullptr, nullptr);
}

wasmtime_error_t *error = wasmtime_context_set_wasi(context, wasi_config);
if (error) {
log_and_exit_error("WASI Config Error", error, nullptr);
return -1;
}

// --- 5. 编译与实例化 ---
wasmtime_linker_t *linker = wasmtime_linker_new(engine);
wasmtime_linker_define_wasi(linker);

wasmtime_module_t *module = nullptr;
error = wasmtime_module_new(engine, (uint8_t *)bytes, len, &module);

// 释放 Java 数组,节省内存
env->ReleaseByteArrayElements(wasmBytes, bytes, JNI_ABORT);

if (error) {
log_and_exit_error("Compile Error", error, nullptr);
return -1;
}

wasmtime_instance_t instance;
wasm_trap_t *trap = nullptr;
error = wasmtime_linker_instantiate(linker, context, module, &instance, &trap);
if (error || trap) {
log_and_exit_error("Instantiate Error", error, trap);
return -1;
}

// --- 6. 核心步骤:初始化 Kotlin 运行时 (_initialize / _start) ---
// 这是解决 null reference 的关键
wasmtime_extern_t init_extern;
wasmtime_func_t init_func;
bool needs_init = false;

if (wasmtime_instance_export_get(context, &instance, "_initialize", 11, &init_extern) &&
init_extern.kind == WASM_EXTERN_FUNC) {
init_func = init_extern.of.func;
needs_init = true;
} else if (wasmtime_instance_export_get(context, &instance, "_start", 6, &init_extern) &&
init_extern.kind == WASM_EXTERN_FUNC) {
init_func = init_extern.of.func;
needs_init = true;
}

if (needs_init) {
error = wasmtime_func_call(context, &init_func, nullptr, 0, nullptr, 0, &trap);
if (error || trap) {
// _start 可能会因为 exit(0) 而 trap,这里简单处理,如果是严重错误才退出
// 实际生产中可能需要检查 trap message 是否包含 "exit"
log_and_exit_error("Runtime Init Error (Ignorable if exit code 0)", error, trap);
}
}

// --- 7. 调用目标函数 (add) ---
int32_t result_val = -1;
wasmtime_extern_t run_extern;
if (wasmtime_instance_export_get(context, &instance, "add", 3, &run_extern) &&
run_extern.kind == WASM_EXTERN_FUNC) {

wasmtime_func_t func_obj = run_extern.of.func;

// 参数:1, 2
wasmtime_val_t args[2];
args[0].kind = WASMTIME_I32; args[0].of.i32 = 1;
args[1].kind = WASMTIME_I32; args[1].of.i32 = 2;

wasmtime_val_t results[1];

error = wasmtime_func_call(context, &func_obj, args, 2, results, 1, &trap);

if (error || trap) {
log_and_exit_error("Function Call Error", error, trap);
} else {
if (results[0].kind == WASMTIME_I32) {
result_val = results[0].of.i32;
if (enableLogs) LOGD("Success! Result: %d", result_val);
}
}
} else {
LOGE("Export 'add' not found");
}

// --- 8. 清理资源 ---
wasmtime_module_delete(module);
wasmtime_linker_delete(linker);
wasmtime_store_delete(store);
wasm_engine_delete(engine);

return result_val;
}