# Clash Meta 故障排查指南 ## 常见运行时问题 ### 问题 1: VPN 连接失败,日志显示 "PermissionMonitor error 22 (EINVAL)" **症状:** ``` E/VPNService: PermissionMonitor error 22 (EINVAL) E/Clash: TUN 设备启动失败 ``` **原因:** - VPN Builder 配置的路由规则无效 - 缺少 bypass-LAN 路由配置 - 路由 CIDR 格式错误 **解决方案:** 1. 检查 `getAndroidVpnOptions()` 返回的路由列表: ```dart final options = await KRClashImp().getAndroidVpnOptions(); print('路由数量: ${options?['routeAddress']?.length}'); print('路由列表: ${options?['routeAddress']}'); ``` 2. 确认配置包含完整的 bypass-LAN 路由: ```yaml # clash_config.yaml tun: route-exclude-address: - 10.0.0.0/8 - 100.64.0.0/10 - 127.0.0.0/8 - 169.254.0.0/16 - 172.16.0.0/12 - 192.0.0.0/24 - 192.0.2.0/24 - 192.88.99.0/24 - 192.168.0.0/16 - 198.18.0.0/15 - 198.51.100.0/24 - 203.0.113.0/24 - 224.0.0.0/3 - fc00::/7 - fe80::/10 - ff00::/8 ``` 3. 验证 VPN Builder 配置: ```kotlin // VPNService.kt builder .addAddress("172.19.0.1/30") .addRoute("0.0.0.0/1") // ✅ 拆分默认路由 .addRoute("128.0.0.0/1") // ✅ 避免 0.0.0.0/0 .addRoute("10.0.0.0/8") // ✅ bypass-LAN // ... ``` **参考:** `lib/app/services/clash_imp/kr_clash_imp.dart:192` --- ### 问题 2: FFI 初始化崩溃 "Go runtime already initialized" **症状:** ``` F/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR) E/Clash: Go runtime already initialized ``` **原因:** - 多线程并发调用 `_ensureInitialized()` - Go 运行时被重复初始化 **解决方案:** 已在 `kr_clash_imp.dart` 中修复,使用 `Completer` 实现并发安全: ```dart Future _ensureInitialized() async { if (_initialized) return; if (_initLock != null) { await _initLock!.future; // ✅ 等待其他初始化完成 return; } _initLock = Completer(); // ... 执行初始化 } ``` **验证修复:** ```dart // 并发测试 await Future.wait([ KRClashImp().start(...), KRClashImp().getAndroidVpnOptions(), KRClashImp().getTraffic(), ]); // ✅ 应该不会崩溃 ``` **参考:** `lib/app/services/clash_imp/kr_clash_imp.dart:43` --- ### 问题 3: 模拟器无法联网 "Network unreachable" **症状:** ``` E/Clash: 网络不可达 W/VPNService: 底层网络丢失 ``` **原因:** - 模拟器需要显式设置底层网络 - 缺少 `setUnderlyingNetworks()` 调用 **解决方案:** 已在 `VPNService.kt` 中实现: ```kotlin // Android P+ 需要设置底层网络 private val defaultNetworkCallback by lazy { object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { setUnderlyingNetworks(arrayOf(network)) // ✅ 关键! } } } // 注册回调 connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback) ``` **验证修复:** ```bash # 在 MuMu 模拟器测试 adb logcat | grep "底层网络" # 应该看到: "底层网络可用: NetworkIdentity{...}" ``` **参考:** `android/.../bg/VPNService.kt:32-66` --- ### 问题 4: 配置文件解析失败 "invalid YAML" **症状:** ``` E/Clash: 启动失败: yaml: unmarshal errors: line 10: cannot unmarshal !!str `8388` into int ``` **原因:** - YAML 类型不匹配 (字符串 vs 整数) - 配置格式错误 **解决方案:** 检查 `ClashConfigGenerator.generate()`: ```dart proxies: - name: "Server-1" type: ss server: "example.com" port: 8388 # ✅ 整数,不要引号 password: "password" # ✅ 字符串,使用引号 cipher: "aes-256-gcm" ``` **调试方法:** ```dart // 打印生成的配置 final config = ClashConfigGenerator.generate(...); print('配置预览:\n$config'); // 保存到文件手动检查 File('/tmp/clash_debug.yaml').writeAsStringSync(config); ``` **参考:** `lib/app/services/clash_imp/clash_config_generator.dart` --- ### 问题 5: "quickStart timeout" 启动超时 **症状:** ``` W/Clash: 启动超时 (10秒) E/Clash: quickStart 未收到回调 ``` **原因:** - Go 核心启动耗时过长 - ReceivePort 未正确监听 - 回调被阻塞 **解决方案:** 1. 增加超时时间: ```dart _isRunning = await completer.future.timeout( const Duration(seconds: 20), // ✅ 增加到 20 秒 onTimeout: () { print('⚠️ 启动超时'); return false; }, ); ``` 2. 检查 Go 核心日志: ```kotlin // ClashService.kt - 添加日志回调 tempMethodChannel.setMethodCallHandler { call, result -> if (call.method == "log") { Log.d(TAG, "Go 核心日志: ${call.arguments}") } } ``` 3. 使用 Service Isolate 避免阻塞: ```kotlin // ClashService.kt:102 - 已实现 serviceEngine?.dartExecutor?.executeDartEntrypoint(entrypoint) ``` **参考:** `lib/app/services/clash_imp/kr_clash_imp.dart:145` --- ### 问题 6: 流量统计不更新 **症状:** ``` I/Clash: 流量统计: {"upload": 0, "download": 0} # 始终为 0 ``` **原因:** - TUN 设备未正确启动 - 流量未通过代理 **解决方案:** 1. 确认 TUN 启动成功: ```dart final tunStarted = await KRClashImp().startTun(fd, protectCallback); print('TUN 启动状态: $tunStarted'); // 应该为 true ``` 2. 检查路由配置: ```bash # 在设备上检查路由表 adb shell "ip route show table all | grep tun" ``` 3. 测试代理连接: ```bash # 使用 curl 测试 adb shell "curl -v http://www.google.com" # 应该看到代理日志 ``` **参考:** `lib/app/services/clash_imp/kr_clash_imp.dart:261` --- ## 日志分析 ### 启用详细日志 ```dart // lib/app/services/clash_imp/kr_clash_imp.dart // 添加更详细的日志 print('📨 [Clash] 收到消息: ${jsonEncode(message)}'); print('📋 [Clash] 配置完整内容:\n$config'); ``` ### Android Logcat 过滤 ```bash # 只看 Clash 相关日志 adb logcat -s "A/Clash" "E/Clash" "W/Clash" # 实时监控 FFI 调用 adb logcat | grep -E "(ClashFFI|quickStart|getAndroidVpnOptions)" # 保存日志到文件 adb logcat -d > clash_debug.log ``` ### Dart Observatory 调试 ```bash # 启用 Dart Observatory flutter run --observatory-port=8181 # 在浏览器打开 open http://localhost:8181 # 查看 Isolate 状态 # - Main Isolate (UI) # - Service Isolate (Clash 后台) ``` --- ## 性能问题 ### 内存泄漏 **症状:** - 应用内存持续增长 - 最终 OOM 崩溃 **排查:** ```dart // 确保调用 dispose() @override void dispose() { KRClashImp().dispose(); super.dispose(); } // 检查 ReceivePort 是否关闭 receivePort.listen((message) { // ... receivePort.close(); // ✅ 必须关闭! }); ``` ### CPU 占用过高 **症状:** - 设备发热 - 电池消耗快 **排查:** ```bash # 查看 CPU 占用 adb shell "top -m 10" # 使用 Profiler flutter run --profile # DevTools → CPU Profiler ``` **优化:** ```dart // 降低流量统计频率 Timer.periodic(const Duration(seconds: 2), (timer) { // ✅ 2秒而非1秒 final traffic = await KRClashImp().getTraffic(); // ... }); ``` --- ## 崩溃分析 ### 获取崩溃堆栈 ```bash # Native 崩溃 adb logcat -d | grep "backtrace" # 使用 ndk-stack 解析 adb logcat | ndk-stack -sym android/app/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a ``` ### 常见崩溃模式 #### SIGSEGV (段错误) **原因:** FFI 指针使用错误 **示例:** ```dart // ❌ 错误: 释放后使用 final ptr = 'hello'.toNativeUtf8(); malloc.free(ptr); _ffi!.someFunction(ptr); // 崩溃! // ✅ 正确: 使用后释放 final ptr = 'hello'.toNativeUtf8(); _ffi!.someFunction(ptr); malloc.free(ptr); ``` #### SIGABRT (异常终止) **原因:** Go panic 未恢复 **解决:** 在 Go 侧添加 recover: ```go //export quickStart func quickStart(...) { defer func() { if r := recover(); r != nil { log.Println("Panic recovered:", r) } }() // ... } ``` --- ## 获取帮助 ### 收集诊断信息 创建 Bug 报告时请包含: 1. **系统信息:** ```bash adb shell "getprop ro.build.version.release" # Android 版本 adb shell "getprop ro.product.cpu.abi" # CPU 架构 ``` 2. **应用日志:** ```bash adb logcat -d > full_log.txt ``` 3. **配置文件:** ```dart // 脱敏后的 clash_config.yaml print(config.replaceAll(RegExp(r'password:.*'), 'password: ***')); ``` 4. **复现步骤:** - 详细操作流程 - 预期结果 vs 实际结果 - 复现概率 ### 相关资源 - [GitHub Issues](https://github.com/your-repo/issues) - [Clash Meta Wiki](https://wiki.metacubex.one/) - [Flutter FFI 文档](https://dart.dev/guides/libraries/c-interop)