Skip to content

Ticket 02 — Baseline Build (Windows MSVC + NDK r29 + bpf-linker) - #74

Open
Start-Z1314 wants to merge 14 commits into
imacte:mainfrom
Start-Z1314:ticket-02-baseline-build
Open

Start-Z1314 wants to merge 14 commits into
imacte:mainfrom
Start-Z1314:ticket-02-baseline-build

Conversation

@Start-Z1314

Copy link
Copy Markdown

ticket-02: Windows NDK r29 baseline build + webui + verify scripts

简介

yumi v2.0.2 在 Windows + NDK r29 + LLVM 22 上的端到端 baseline 构建链路,以及 webui 完整打包 + on-device zip 校验。

改了什么

构建链路 (NDK r29 / LLVM 22 兼容)

  • Cargo.toml: 为 yumi-ebpf 设置独立 release profile (opt-level=3), 规避 LLVM 22+ 不再支持 -Oz 导致 BPF 编译失败
  • build.rs: Windows 路径探测, 自动复制 bpf-linker.exetarget/, 跳过失败的 cargo install
  • scripts/redownload-ndk.ps1: NDK 重下脚本 (NDK 放仓库外 D:\Android\ndk-cache\android-ndk-r29\, 避免被 git 清理)

WebUI + 打包

  • scripts/webui-build.ps1: 跑 npm install && npm run build
  • scripts/pack-baseline.ps1: 改 webroot 真实复制 webui/dist/* 内容 (原版是占位 HTML)
  • webui/package-lock.json: 同步 npm install

验证工具链

  • scripts/test-bpf-build.ps1: BPF 单独测试
  • scripts/test-core-build.ps1: Core 单独测试
  • scripts/verify-on-device.ps1: adb push + unzip -t + SHA1 对比
  • scripts/check-elf.ps1: host 端 ELF magic/machine 验证
  • scripts/verify-full-build.ps1: BPF + core + zip 三阶段端到端验证

文档

  • .scratch/yumi-personalize/issues/ticket-02-baseline-build.md: ticket-02 修复记录
  • .scratch/yumi-personalize/issues/ticket-03-percpu-perception.md: 8路感知 + 区间寻优 spec (待 grill-me)
  • .scratch/yumi-personalize/issues/ticket-04-disable-core.md: 关核方案 D spec (待 grill-me)

验证

  • ✅ BPF 编译通过: cargo +nightly build -Z build-std -p yumi-ebpf --target bpfel-unknown-none --release
  • ✅ Core 编译通过: cargo +nightly ndk --platform 26 -t arm64-v8a build -Z build-std -r, 1515 KB aarch64 ELF
  • ✅ WebUI 构建通过: npm install + npm run build
  • ✅ Zip 打包: yumi-2.0.2-75-...zip (1008 KB)
  • ✅ Zip integrity: unzip -t 13 files all OK
  • ✅ ELF header 验证: magic \x7fELF, class=64, endianness=little, machine=0xb7 (EM_AARCH64), type=ET_DYN
  • ✅ On-device: 推送 zip 到小米 14 Pro (Android 14 SDK 34), SHA1 与 host 完全一致

风险 / 注意

  1. NDK r29 路径硬编码在 scripts/redownload-ndk.ps1, CI 需要相应路径
  2. WebUI dist 当前被 .gitignore output/ 排除 (但 webui/dist/ 没在 gitignore), 需要时可 commit dist
  3. 未在手机上实跑 module — 用户的设备是 KernelSU Next 3.3.0 而非 Magisk, 验证停在 zip 完整性 + ELF 校验层面. 装机需要 yuki 在有 Magisk 的设备上做

关联

  • Rebase 到 origin/main (5c54e0f Update update.json)
  • 3 commits: 39bf71f + f75271b + b6264d3

下一步 (待 grill-me)

  • ticket-03: 8路 per-cpu 感知 + 用户区间寻优 (spec 已写, 4 个决策点待确认)
  • ticket-04: 关核方案 D (spec 已写, 5 个决策点待确认)

zqk00 and others added 14 commits August 25, 2026 07:58
本提交合并 task imacte#2-imacte#6 的全部产出, 包含 6 大子系统:

1. 八路感知层 (src/sensor/*)
   - snapshot_writer.rs: 周期写 /data/local/tmp/yumi/snapshot.json
   - 采集触摸/GPU/IO/Swap/CPU 频率/温升/帧率/亮屏 八路指标

2. 监控采集器 (src/monitor/{touch,gpu,io,swap,sense_snapshot}.rs)
   - touch_monitor.rs: 触摸事件频率 + down/up 状态
   - gpu_monitor.rs: sysfs 读 GPU 频率 + governor
   - io_monitor.rs: PSI some/full 压力指标
   - swap_monitor.rs: meminfo + zram 容量
   - sense_snapshot.rs: 聚合快照 + TouchState/GpuState/IoState/SwapState

3. FAS 综合压力 PID (src/scheduler/fas/{pid,pid_jank}.rs)
   - pid.rs: 标准 PID 控制器, 根据综合压力输出 gear 调整
   - pid_jank.rs: jank-aware 增强, 帧时间突刺时压低 gear
   - controller.rs: integrate 全部 sensor 输出做 gear state machine

5. 热插拔真关核降级 (src/scheduler/hotplug/*)
   - disable_policy.rs (NEW, ~300行): hyperos 关核逻辑移植
     * MIN_OFFLINE_DURATION_SEC_CPU7=5 / CPU6=8 延迟守门
     * DisableOutcome { SkippedMinDuration, WriteOnline, FreqFloorFallback }
     * decide_disable() 决策函数, 13 个单元测试
   - mod.rs: 应用 apply_disable_with_fallback 替换原 apply_disable 循环
     * to_enable 成功时记录 last_enable_unix_ms[cpu] = now_ms
     * to_disable 按 outcome 三分支处理: skip / write online / freq-floor fallback
   - threshold.rs: ThresholdDecider + mark_online
   - touch_signal.rs: 触摸触发强制开核信号
   - apply_freq_floor_disable(): kernel hotplug 锁拒时降级写 scaling_max_freq

6. App 规则 (src/scheduler/app_rule.rs)
   - 配置驱动: 按包名 + 当前gear + 触摸信号匹配
   - 覆盖 PID 输出, 让特定 App 强制进入 perf/powersave gear

7. WebUI (webui/src/)
   - api/{appRules,hotplug,sense}.ts: REST 客户端封装
   - views/AppRuleManagement.vue: App 规则编辑
   - views/HotplugSettings.vue: 热插拔策略调整 (CPU7/6 min-offline 等)
   - views/SensePanel.vue: 八路感知实时显示
   - components/{CoreMap,HelpTooltip}.vue: 核心拓扑 + 提示
   - i18n/{en,zh}.ts: 双语支持扩展
   - router/index.ts: 新增 /app-rules /hotplug /sense 路由

8. 守护与恢复 (src/watchdog.rs)
   - 主进程 crash 时启动 watchdog 拉起
   - 写 /data/local/tmp/yumi/watchdog.log

build.rs 修复 (前置阻塞):
  - 强制 yumi-ebpf 子 cargo 永远编 release, 消除 debug/release profile mismatch
    导致 release/yumi-ebpf not found 错误. 这是一个 pre-existing bug,
    之前 ticket-02 baseline 也受影响.

cargo ndk 编译验证 (Windows host, NDK r29):
  cargo +nightly ndk --platform 26 -t arm64-v8a build -r
  -> Finished `release` profile [optimized] target(s) in 2m 18s
  -> target/aarch64-linux-android/release/yumi 1,531,432 bytes

未完成:
- 单元测试无法在 Windows host 跑 (aya/inotify 等 Linux-only crate)
- 实机 adb push 验证需要用户接 Android 设备 (本机无 device/emulator)
- eBPF 在 WSL 上因 LLVM 版本不兼容 bpf-linker 0.11 仍无法链接 (pre-existing)
主要变更:
- logger.rs: 解析失败时默认 DEBUG (原 INFO); 单文件 10MB, 保留 3 历史文件 (原 5MB)
- logger.rs: 新增 fallback — 模块目录不可写时降级到 /data/local/tmp/core-pilot/logs/
- logger.rs: 新增 LOG_TO_STDERR=1 env 选项, 同时输出到 stderr (调试用)
- service.sh: 导出 RUST_LOG (默认 debug), 可由 config.yaml 覆盖
- config.yaml: 默认 loglevel 改为 DEBUG
- config.rs: default_loglevel() 改为 DEBUG
- service.sh / config.yaml / .rs 文件: 全部规范化 LF (Windows CRLF drift)
- 新增 .gitattributes: 强制 *.sh/*.yaml/*.prop/*.rs eol=lf

补 debug 日志的关键模块 (用户要求):
- watchdog.rs: run_one_cycle 全字段 + try_recover stdout 摘要
- io_monitor.rs: PSI 读数 + 失败原因
- screen_detect.rs: uevent 子系统 + backlight 状态
- sense_snapshot.rs: sense_now() 8 路聚合摘要 (log_enabled! 守护)
- fas/controller.rs: update_cpu_util/set_game/clear_game/target_pressure/pressure_index
- fas/gear_state.rs: do_gear_switch 切档详情
- fas/policy_mgmt.rs: reload_rules/load_policies
- monitor/mod.rs: 各采集器启动完成
- touch_monitor.rs: 状态变化 / tick push 摘要
- app_detect.rs: detect / debounce / determine_mode

热路径保护: heartbeat_tick() / 每帧 compute_pressure_index() / 每 200ms touch_push 不打 debug, 避免日志洪水.
SizeTrigger counts bytes written by this process only (log4rs semantics), external growth does not roll. Added env overrides so rotation can be verified on-device with a tiny threshold; defaults stay 10MB x3. Verified live: daemon.0.log / daemon.1.log appear at threshold.
Task A (configurable protected cores):
- screen_on_keep_cores [0-5] / screen_off_keep_cores [0-1] in hotplug/config.yaml,
  applied every 200ms tick via decider.set_keep_cores (no daemon restart)
- safety invariants in CpuAllowList::from_keep_cores: cpu0 always kept, at least 2 cores
- whitelist cores force-enabled immediately on screen/mode switch (fix wake-up lag)
- screen state wired into sense_snapshot via screen_push (was dead code)
- fix oscillation: FAS-panic/touch bypass disabled while screen off; 1.5s re-enable
  cooldown after our own disable (offline cpu /proc/stat stats are stale)
- new consumed config keys: min_online_cores, thermal_force_all_on_c;
  state.yaml now exposes screen_on + active_keep_cores for WebUI

Task B (WebUI):
- HomeView rewritten: mode buttons / 8-core grid (green=on gray=off orange=busy) /
  temp+app only; duplicated dashboards removed
- ScheduleSettings replaces ConfigEditor+HotplugSettings: 8 themed cards
  (cpu blue, gpu purple, io orange, mem green, cores red, touch cyan, fas pink,
   thermal dark-red), mobile-first, single-column single-hand friendly
- every parameter shows inline 5-dimension explanation (what/higher/lower/when/suggested)
- keep-cores toggle groups for screen-on/off with >=2 enforcement and cpu0 locked
- fake panels removed (swappiness etc); gpu/mem/touch cards are honest read-only monitoring
- snapshot_writer: temp sampling (1s, hotplug-same thermal source), hz_poller
  (dumpsys display renderFrameRate), current_pkg via app_detect API,
  mem_full_pct from PSI avg10 (was total-us/10000 -> always 100%),
  zram mm_stat fallback, dual-write webroot/sense/snapshot.yaml for WebView
  http relative fetch fallback
- gpu_monitor: kgsl gpu_busy_percentage / gpuclk fallback (Qualcomm devfreq
  has no load file -> was NaN)
- fps_monitor: push measured fps from uprobe frametime window, zero after
  1.2s frame silence
- app_detect: check_cgroup_path now counts per-package procs inside top-app
  and picks the highest vote (tail-pid pick got stuck on background leftovers)
- webui: HomeView absorbs full sense panel (8 metric cards + per-core util
  bars), nav 5->4 tabs, /sense redirects to /, api adds display_hz + link
  status, snapshot fetch falls back to relative http path
…x, config split

- moduleSpecs.ts: single source of truth for all 8 config modules —
  param specs + plain-language five-dimension descriptions
  (what / higher / lower / when-to-tune / suggested)
- HelpTooltip: rewrite — teleport-to-body popup, close via overlay / X /
  button / 10s auto-timeout (old popup nested under @click.stop could jam)
- scheduler store: currentMode as the single source of truth; every page
  reads it live (home switch now reflected on config pages instantly)
- save chain fixes: IO_Settings.Scheduler key case (serde rename, old
  lowercase key wrote a dead field), read_ahead_kb / nomerges kept as
  strings (Number write made serde_yaml reject the whole config),
  writeFile now pipes base64 instead of echo-with-quotes (shell metas
  in yaml corrupted the file)
- config page split: /config becomes 8 module cards with live summaries,
  each opens a subpage (/config/{hotplug,cpu,gpu,touch,frame,io,swap,temp});
  ScheduleSettings.vue removed
- rules page: rule type / strength / disable-burst / offsets get five-dim
  help, all-English tags replaced with Chinese
- HomeView: mode switch gets five-dim help; SoC/Swap labels to Chinese
根因 (真机 2026-08-27, online_mask=0x17): cpu_monitor 只为在线核产出
load 条目, 离线核完全脱离 loads 视野. 拉起路径全部遍历 loads,
白名单内 cpu3/5 在合法摘核窗口被关后永久滞留离线:
- 白名单强制在线分支看不见失明核
- 触摸旁路/thermal/FAS panic/全局禁用 四个全开旁路同样失明
- min_online 只能拦关不能拉, set_keep_cores 承诺的下 tick 接管不会发生

修复:
1. threshold.rs: 上述五处拉起路径全部改遍历 0..=MAX_CPU_ID 全集
   (不用 per_cpu.keys() — 初始空 map 同样失明); 负载 enable 主循环
   保持遍历 loads (离线核无负载语义)
2. mod.rs: 每 tick 读 /sys/devices/system/cpu/online 与 decider 视图
   对账, 外部 (echo/其他 governor) 关核时纠正 entry.online, 下一 tick
   白名单分支接管拉起; 新增 parse_online_list 支持 0-3,5-7 区间格式
3. threshold.rs 暴露 is_cpu_online_view 供对账查询

回归测试 (loads 形状与生产一致 — 只含在线核; 旧测试给离线核造条目,
数据形状不符故漏检):
- whitelist_force_online_sees_blind_offline_cores (用户指定场景 0x17)
- touch_bypass_sees_blind_offline_cores
- screen_off_to_on_restores_whitelist_within_2_ticks + 幂等
- parse_online_list_formats / decider_view_query_reflects_mark_online

host 单测 30/30 通过; 真机验证: 手动关 cpu3/5 同秒 reconcile+enable,
500ms 内回线; 灭屏/亮屏 4 轮核数无递减.
需求 1 (四参数按模式独立): 后端与前端的 {mode}.cpu_load_governor 结构
本已存在, 本次补齐展示与即时保存.

需求 2 (三参数):
- target_load: Mode 加 Option<f32> 字段, Config::target_load_of() 配置值
  优先 / 回落 mode_target_pressure() 硬编码 (40/60/75/85); ModeChange 与
  AppRuleRefresh 两处改用配置化取值. WebUI 处理器页按当前档位显示与修改,
  未配置时按模式显示真实回落默认.
- freq_limits (亮屏/息屏两套 min/max pct, 默认 0/100 不限制):
  FreqLimits 结构 + limits_for(screen_on); CLG 加 set_freq_limits() 运行时
  护栏 (幂等, 与 perf_floor/ceil 叠加取更严者), scheduler 主循环在每次
  SystemLoadUpdate 投喂前按屏幕状态注入 — config 热重载与亮灭屏切换
  自动覆盖. WebUI 处理器页新增"频率护栏"卡片 (四滑条).

需求 3 (即时生效+永久保存+部署不重置):
- ConfigCpu / ConfigIo / ConfigFrame 从手动保存按钮改为 600ms 防抖自动
  保存 (对齐 ConfigHotplug / ConfigTemp), daemon inotify / 200ms tick 热生效.
- _deploy_clean.py: 恢复源改为本次刚备份的旧模块 (MOD_BAK/core-pilot_old),
  cp_bak 兜底; 恢复列表补 config/config.yaml 与 hotplug/config.yaml;
  删除硬编码 hotplug 默认覆盖 (上次部署重置用户 85/65/5 的根因).
  真机实证: 用户 WebUI 修改的 70/80/4 配置在重部署后完整保留.

需求 4 (双恢复默认):
- 新组件 ResetDefaultsBtn (确认弹窗 + reset 事件).
- moduleSpecs.ts 新增 DEFAULTS 默认值中心: CLG_MODE_DEFAULTS (四档位
  五参数, 与包内 config.yaml 逐字对齐) / FREQ_LIMIT_DEFAULTS /
  HOTPLUG_DEFAULTS / IO_DEFAULTS / FAS_DEFAULTS.
- 8 个模块页底部挂"恢复本模块默认值": cpu (当前档位五参数+频率护栏),
  hotplug (全量), frame (FAS 参数, 保留 per-app), io (三参数+总开关),
  temp (保护温度线), gpu/touch/swap (自动管理提示).
- 日志页加"恢复全部默认值" (危险操作样式): 四档位+频率护栏+IO+hotplug
  全量+FAS 参数; 保留 app 规则/当前档位/meta; 三配置文件写盘即热生效.

真机验证: freq limits 息屏 [0,60]/亮屏 [10,100] 双套注入日志实证;
config 热重载实证; powersave target_pressure=40 回落路径实证; 部署
保留用户配置实证; webui 构建通过 (vant Dialog 自动注册).
问题 3 根因 (真机实证): cpu_monitor 的 online_cpus_list 是启动快照,
核被触摸旁路/白名单/外部模块拉起后, loads 对新在线核永久失明 —
负载关核决策 (3b 遍历 loads) 看不到 cpu5/6/7, 滞留全开永不回落
(观测: 触摸拉起 0-7 后 20s 空闲仍全开; CPU Monitor cores 数组恒 5 值).
修复: 采样 loop 每 tick 重读 online_cpus(); 历史向量按 8 槽定长
(启动快照长度会在后上线核索引越界).
真机回归: 触摸拉起 0-7 → 松手 5s 内自动回落 0-4 (白名单 [0..4]).

问题 1 根因: fmtVal 小数位逻辑 (s!==1 && step<1 ? 2 : 0) 对无 scale
参数 (上行/下行平滑 0.05~1, step 0.05) 取 0 位 — 0.05..0.49 全显示
"0", 滑块拖动数值看似不动. 滑块绑定本身正确 (:value+@input 单向受控).
修复: 小数位由显示粒度 step*scale 决定 (>=1→0位, >=0.1→1位, 否则2位):
平滑系数显示 "0.35", 升频阈值仍 "81%".

问题 2: 全站保存文案统一 —
- 模块列表 tag: 保存后生效/改动即生效 → 改动自动生效 (可配置模块)
- 子页 live-tag 同步统一 (6 处)
- 保存成功提示统一 "已自动保存并生效" (5 处)
- 自动管理模块 (gpu/touch/swap) 保持 "自动管理"
问题 1 (首页"守护进程未响应"误报):
daemon 实际运行 (pidof 有值, state.yaml 实时更新), 根因是前端健康检查
exec('pidof core-pilot') — 进程名一直是 yumi, 模块目录才叫 core-pilot.
修复: pidof yumi 优先, 兼容两种名字. 真机 rc=0 实证.

问题 2+3 (全模块亮/息屏双套 + 自动管理模块可配置):

后端 (config.yaml modules.* 统一双套结构 ScreenScoped{screen_on,off}):
- 新增 modules_ctrl.rs: apply_screen_scoped 统一应用 (屏幕切换/config
  热重载/启动三入口); gpu 写 kgsl devfreq min/max_freq (档位表降序
  排序后取值, pct 插值+吸附最近档); swap 写 vm.swappiness; io 息屏套
  写全部块设备 (亮屏套沿用现役 IO_Settings 链路避免双写).
- gpu boost 独立线程: 负载超加速线临时拉满 max, 80% 滞回回落.
- touch: enabled/extra_cores/duration_ms 经全局快照 (utils) 被 hotplug
  每 tick 消费; TOUCH_PROTECT_MS 改运行期 AtomicI64; extra_cores 配额
  过滤触摸唤醒的非白名单核 (大核优先).
- frame: FasController::set_frame_params (jank_margin_ms 换算 fps_margin,
  boost 开关 gate 降档 boost, strength 缩放增量); 屏幕切换/进游戏/启动
  三处应用.
- temp 双套: hotplug/config.yaml 新增 temp_{on,off}_{soft,hard}_c;
  hard 优先于旧 thermal_force_all_on_c (兼容), soft 超限 10s 节流预警.
- CLG 跳过全离线 policy (修复离线核 cpufreq 写入 EBUSY 刷屏);
  utils 全局在线位图由 hotplug 对账循环同步.

死锁修复 (真机 07:37 僵死复现): ipc 线程屏幕事件持 screen 写锁等
config 读锁, 与 watcher (config 写锁 → screen 读锁) 成环. 全进程统一
锁序 "先 config 后 screen".

WebUI:
- 新组件 ScreenScopedModule (亮/息屏 chip + 双套参数 + 防抖自动保存 +
  双套恢复默认); Gpu/Touch/Swap 从说明页升级为完整配置页 (保留实时
  读数), Frame 加双套掉帧参数区块, Io 加息屏套, Temp 升级软/硬双阈值
  双套; MODULES tag 全部改为"改动自动生效".
- moduleSpecs: 各模块双套参数 + MODULE_SCOPED_DEFAULTS 默认值中心;
  全局恢复按钮覆盖 modules.* 与温度双套.
- 列表页摘要改读配置值.

真机验证: pidof yumi rc=0; [modules] gpu limits 0-80%(770MHz 吸附) /
swappiness=80 亮屏套实证; off 套 swappiness=120 + io read_ahead=64KB
on 91 devs 实证; frame params jank=3.0ms strength=1.2 实证; 死锁修复
后日志持续流动.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant