hi-client/docs/CLASH_TROUBLESHOOTING.md
Rust d02eed3bd8 docs: 添加 GitHub Actions 构建文档并锁定 libcore 版本
- 添加完整的 GitHub Actions 构建指南文档
  - BUILD_GUIDE.md: Android 详细构建指南
  - MULTIPLATFORM_GUIDE.md: 多平台构建指南
  - HOW_TO_BUILD.md: 分步操作教程
  - QUICKSTART.md: 3步快速开始指南
  - INDEX.md: 文档总览索引
  - README.md: 基础说明
- 创建 docs/ 目录存放项目文档
- 锁定 libcore 子模块到 f993a57 (v3.1.7)
  - 防止在线编译时使用最新版本
  - 确保构建稳定性和一致性
2025-10-27 23:11:21 +08:00

442 lines
8.4 KiB
Markdown

# 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<void> _ensureInitialized() async {
if (_initialized) return;
if (_initLock != null) {
await _initLock!.future; // ✅ 等待其他初始化完成
return;
}
_initLock = Completer<void>();
// ... 执行初始化
}
```
**验证修复:**
```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)