SDK: 内置 mipsel o32 时钟/睡眠 ABI 修复(meow_ui_time.c)+ 宿主单测
zig 0.17 内置 musl 在 mipsel 上头声明 64 位 time_t、实现走 time32 系统调用
(__ILP32__ 守卫),导致 meow_ui_time_ms() 毫秒位恒 0、高 32 位随机,
usleep(16000) 立即返回(帧节拍/轮询变忙等空转)。主仓已验证的修复逐字节
搬入公开仓:取时按内核 o32 ABI(8 字节 {int32 sec; int32 nsec})再升 64 位,
同 TU 强定义 __clock_gettime/64 与睡眠入口,链接期接管 libc。
- meow_ui_time.c / CLOCK_ABI.md:cp 搬运,md5 与主仓一致(含原注释与依据链)
- meow_ui_gfx.c:删掉 now_ms/meow_ui_time_ms(避免与 meow_ui_time.o 重复符号),
6 处内部调用改外部 meow_ui_time_ms;<time.h> 不再使用
- meow_ui_internal.h:补 meow_time_ms_from_parts 声明
- build_sdk.sh:OBJS 首位加 meow_ui_time,头注释写明归档须排在 -lc 之前
- test_meow_time.c:适配公开仓构建方式,加负 sec 回绕两例,输出断言数(15)
- sdk/README.md:公开说明本次修复、链接顺序要求与已知限制(2038 前有效)
- tuku/test/run_tests.sh、run_webtoon_tests.sh:$SDK 源文件清单补 meow_ui_time.c
(宿主集成测试直接编 SDK 源,否则链接期 undefined symbol)
验证:sdk/build_sdk.sh 0 warning;宿主 test_meow_time == PASS == checks=15;
tuku 两套测试全 PASS(run_tests 462+106+325 断言、webtoon 240 断言,0 failed);
探针/ picbrowser 反汇编确认 __clock_gettime 取自本 TU(8 字节 time32 + 升位),
无 libc VDSO 前导;picbrowser 仍为 MIPS32r2 静态 ELF。
This commit is contained in:
1 parent
4773ec1a61
commit
a07f9752d3
9 files changed
+531
-23
No files matched your search
@@ -0,0 +1,187 @@
|
|||||||
|
# SDK 时钟 ABI 说明(meow_ui_time_ms 真机故障的根因与修复)
|
||||||
|
|
||||||
|
面向对象:维护 SDK / 工具链 / 重链 App 的人。**结论先行**:这不是 SDK 代码写错,
|
||||||
|
是 zig 0.17 自带 musl 在 mipsel 上的 time64 ABI 自相矛盾;修复落在 SDK 链上
|
||||||
|
(`patch_v2/sdk/meow_ui_time.c` + 链接期强定义),改后需重编 `meow_ui.a` 并重链四 App。
|
||||||
|
|
||||||
|
## 1. 现象(真机)
|
||||||
|
|
||||||
|
M10 抓到的日志首列(`meow_ui_time_ms()`,64 位)可拆成:
|
||||||
|
|
||||||
|
```
|
||||||
|
6607363222717682072 → lo=1135000 hi=1538396632
|
||||||
|
8754846870717682072 → lo=1135000 hi=2038396632
|
||||||
|
18138485921460348192 → lo=1140000 hi=4223195352
|
||||||
|
```
|
||||||
|
|
||||||
|
- 低 32 位 = 开机秒数 × 1000,**恒为 1000 的倍数 → 毫秒位丢失**;
|
||||||
|
- 高 32 位每次调用随机、无单调性。
|
||||||
|
|
||||||
|
同进程另一份取时(旧容器编的 core)正常 → 问题只在「zig 0.17 编的那部分」。
|
||||||
|
|
||||||
|
## 2. 根因链(三段证据)
|
||||||
|
|
||||||
|
### 2.1 zig 头文件:`time_t` 是 64 位,`struct timespec` 16 字节
|
||||||
|
|
||||||
|
`<zig lib>/libc/include/mips-linux-musl/bits/alltypes.h`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define _REDIR_TIME64 1
|
||||||
|
#define __USE_TIME_BITS64 1
|
||||||
|
typedef _Int64 time_t; /* 无条件 64 位 */
|
||||||
|
struct timespec { time_t tv_sec; ...; long tv_nsec; ... }; /* tv_sec@0(8B)、tv_nsec@8 */
|
||||||
|
```
|
||||||
|
|
||||||
|
`include/generic-musl/time.h` 因此在 `_REDIR_TIME64` 下把 `clock_gettime` 重定向到
|
||||||
|
`__clock_gettime64`。用该链编译的 C 代码一律按「16 字节、tv_sec 8 字节」取数
|
||||||
|
(`patch_v2/sdk/meow_ui.a(meow_ui_gfx.o)` 的旧 `meow_ui_time_ms` 反汇编:`lw 0x10/0x14`
|
||||||
|
取 tv_sec、`lw 0x18` 取 tv_nsec,正是这个布局)。
|
||||||
|
|
||||||
|
### 2.2 zig 的 clang 给 mipsel 预定义了 `__ILP32__`
|
||||||
|
|
||||||
|
```
|
||||||
|
$ echo | zig cc --target=mipsel-linux-musleabi -dM -E - | grep __ILP32__
|
||||||
|
#define __ILP32__ 1
|
||||||
|
```
|
||||||
|
|
||||||
|
(arm-linux-musleabi 同样为 1;x86-linux-musl / aarch64 为 0。容器里的 gcc 对 mips o32
|
||||||
|
**不定义** `__ILP32__`。)
|
||||||
|
|
||||||
|
### 2.3 musl 的 time64 回退分支被这句 `!__ILP32__` 整体编译掉
|
||||||
|
|
||||||
|
`<zig lib>/libc/musl/src/time/clock_gettime.c`:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#if defined(SYS_clock_gettime64) && !defined(__ILP32__)
|
||||||
|
... __syscall(SYS_clock_gettime64, clk, ts); /* 4403 */
|
||||||
|
... 失败(ENOSYS) 再 __syscall(SYS_clock_gettime, clk, ts32); /* 4263 + 宽度转换 */
|
||||||
|
#else
|
||||||
|
r = __syscall(SYS_clock_gettime, clk, ts); /* 直接把 8 字节 time32 写进调用者缓冲 */
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
这句 `!__ILP32__` 本意只排除 x32(`__ILP32__` 是 x32 的标记),但 clang 把它当成
|
||||||
|
「32 位数据模型」的通名。于是 zig 的 mipsel libc 走进 `#else`:
|
||||||
|
`__clock_gettime64` 实际执行 **SYS_clock_gettime(4263)**,只写 8 字节
|
||||||
|
`{ int32 tv_sec; int32 tv_nsec; }`。
|
||||||
|
|
||||||
|
**证据(反汇编,当前 zig 0.17 静态链接的探针)**:
|
||||||
|
|
||||||
|
```
|
||||||
|
0002576c <__clock_gettime64>:
|
||||||
|
257c8: addiu $2,$zero,0x10a7 ; 4263 = SYS_clock_gettime(time32)
|
||||||
|
257cc: syscall ; $5 直接是调用者的 16 字节 struct 指针,无 4403 尝试、无转换
|
||||||
|
```
|
||||||
|
|
||||||
|
同一符号在**旧容器产物** `patch_v2/ncm_app/ncm`(已入库,gcc 构建)里是:
|
||||||
|
|
||||||
|
```
|
||||||
|
115748: addiu $2,$zero,0x1133 ; 4403 = SYS_clock_gettime64
|
||||||
|
11574c: syscall
|
||||||
|
...
|
||||||
|
115774: addiu $2,$zero,0x10a7 ; ENOSYS 后回退 4263,写入本地 ts32
|
||||||
|
...
|
||||||
|
; 再把 ts32 升成 64 位写回调用者(tv_sec=tv_sec、tv_nsec=tv_nsec)
|
||||||
|
```
|
||||||
|
|
||||||
|
两者对比即根因:**头文件 ABI(64 位 time_t)与 libc 实现 ABI(32 位 time32)不一致**。
|
||||||
|
|
||||||
|
### 2.4 数值解释(为什么「低 32 = 秒×1000、高 32 随机」)
|
||||||
|
|
||||||
|
libc 把 `{sec, nsec}` 当两个 int32 写进 16 字节缓冲的前 8 字节;SDK 按 64 位读:
|
||||||
|
|
||||||
|
```
|
||||||
|
tv_sec = sec | (nsec << 32) // 低字正确、高字=纳秒
|
||||||
|
tv_nsec = 缓冲偏移 8 的未初始化值 // 毫秒项≈0
|
||||||
|
result = tv_sec*1000 + tv_nsec/1e6
|
||||||
|
= sec*1000 + (nsec*1000 mod 2^32)<<32 + 垃圾/1e6
|
||||||
|
```
|
||||||
|
|
||||||
|
- 低 32 位只剩 `sec*1000`(nsec 项左移 32 位不落到低字)→ 恒为 1000 的倍数;
|
||||||
|
- 高 32 位 = `nsec*1000 mod 2^32` → 每次随机、不单调;
|
||||||
|
- `tv_nsec` 来自越界读 → 通常是小值/0(真机上恰好 0,所以没有额外尾数)。
|
||||||
|
|
||||||
|
## 3. 修复(`patch_v2/sdk/meow_ui_time.c`)
|
||||||
|
|
||||||
|
1. **取数走内核 o32 ABI**:`meow_ui_time_ms()` 用
|
||||||
|
`syscall(SYS_clock_gettime32, CLOCK_MONOTONIC, &{int32 sec; int32 nsec;})`,
|
||||||
|
再 `meow_time_ms_from_parts(sec, nsec)` 升成 64 位毫秒。完全不依赖 libc 头声明的
|
||||||
|
`struct timespec` 宽度 —— 即使将来 zig 修好(或换回容器链),内核 time32 入口始终存在。
|
||||||
|
2. **链接期修 libc 入口**:同一 TU 强定义 `__clock_gettime` / `__clock_gettime64`
|
||||||
|
(musl 的 `time()`、`gettimeofday()`、`__timedwait_cp()`(pthread 定时等待)内部都调
|
||||||
|
`__clock_gettime`)。应用链接顺序是 `... meow_ui.a ... -lc`,归档先解析符号,libc 里那份
|
||||||
|
坏实现(及其 VDSO 前导)不会被拉入 —— 用 `nm` 验证链接产物里没有 `cgt_init/__vdsosym`,
|
||||||
|
且 `__time64`/`__timedwait_cp` 的 `jal` 指向本 TU 的实现。
|
||||||
|
3. **睡眠入口一并修正(同源缺陷)**:`__nanosleep_time64` / `__clock_nanosleep(_time64)`
|
||||||
|
先按 time32 转换再发 `SYS_nanosleep`/`SYS_clock_nanosleep`,`rem` 再扩回 64 位。
|
||||||
|
修复前 zig libc 把 16 字节 time64 结构直接交给 time32 系统调用,内核读到的 nsec 恒为 0 →
|
||||||
|
SDK 里 `usleep(16000)`(帧节拍、输入轮询、IME 冷却)全部变成忙等空转。
|
||||||
|
4. **去掉四处重复的 `now_ms()`**:`meow_ui_gfx.c` / `meow_ui_sys.c` / `meow_ui_input.c` /
|
||||||
|
`meow_cachegov.c` 统一调 `meow_ui_time_ms()`(cachegov 保留宿主测试注入桩)。
|
||||||
|
5. 构建脚本 `build_sdk.sh` 的 OBJS 增加 `meow_ui_time`;宿主单测 `test_meow_time.c`
|
||||||
|
固定换算边界与「单调且前进」;探针 `tools/clock_abi_probe.c` 用于宿主/真机一眼验收。
|
||||||
|
|
||||||
|
非 mips 目标(宿主单测、aarch64 宿主)走普通 `clock_gettime`,行为不变。
|
||||||
|
|
||||||
|
## 4. 验证
|
||||||
|
|
||||||
|
宿主:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd patch_v2/sdk
|
||||||
|
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o /tmp/meow_ui_time.o
|
||||||
|
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c /tmp/meow_ui_time.o -o /tmp/test_meow_time
|
||||||
|
/tmp/test_meow_time # == PASS ==(换算边界 + 单调前进)
|
||||||
|
```
|
||||||
|
|
||||||
|
交叉:
|
||||||
|
|
||||||
|
```
|
||||||
|
bash patch_v2/sdk/build_sdk_host.sh --verify # 成员清单/架构自检 + 0 warning
|
||||||
|
llvm-objdump -d meow_ui.a(meow_ui_time.o) # meow_ui_time_ms: syscall 4263, lw 0x18 取 nsec、sra 扩展 tv_sec
|
||||||
|
```
|
||||||
|
|
||||||
|
链接期接管(用真实归档链一个探针,检查符号来源):
|
||||||
|
|
||||||
|
```
|
||||||
|
# 不应出现 libc 的 VDSO 前导(cgt_init/__vdsosym)→ libc 的取时实现未被拉入
|
||||||
|
strings -a clock_abi_probe_sdk | grep -c __vdso # 期望 0
|
||||||
|
llvm-nm clock_abi_probe_sdk | grep -E 'clock_gettime|nanosleep'
|
||||||
|
# __time64 / __timedwait_cp 的 jal 应指向本 TU 的 __clock_gettime;睡眠走 SYS_nanosleep(4166)/clock_nanosleep(4265)
|
||||||
|
```
|
||||||
|
|
||||||
|
探针 `tools/clock_abi_probe.c`(宿主 `gcc` 或 mipsel 交叉;加 `-DMEOW_PROBE_WITH_SDK`
|
||||||
|
并链 `meow_ui.a` 可同时验收 `meow_ui_time_ms`):把 `struct timespec` 预填 0xAA 后取时,
|
||||||
|
若偏移 8..11 仍是 0xAA 即说明 libc 只写了 8 字节(ABI 不一致),退出码 2。
|
||||||
|
|
||||||
|
真机(塔台批准后):日志首列应连续、出现非整千差;`meow_ui_time_ms()` 两次取样差 = 真实毫秒。
|
||||||
|
本次设备已离线,真机确认**待办**。
|
||||||
|
|
||||||
|
## 5. 影响面
|
||||||
|
|
||||||
|
- **SDK 内**:`meow_ui_time_ms()` 及其上层 —— 动画 tween(`meow_ui_anim.c`)、滚动/fling
|
||||||
|
(`meow_ui_scroll.c`/`meow_select.c`)、IME 冷却(`meow_ui_ime.c`)、状态栏动画
|
||||||
|
(`meow_ui_gfx.c`)、通知 TTL(`meow_ui_notify.c`)、输入层 tick/启动冷却
|
||||||
|
(`meow_ui_input.c`)、cachegov 回退窗口(`meow_cachegov.c`)、日志首列(`meow_ui_log.c`)。
|
||||||
|
- **App 侧**:`on_tick` 入参 `now`、`meow_anim_*`、以及任何自行调用 `meow_ui_time_ms()` 的
|
||||||
|
App(hello/reader/files/ncm…)。
|
||||||
|
- **产物**:`meow_ui.a` 变更 → 需**重编并重链四 App**(ncm/reader/files/hello);
|
||||||
|
`time()`/`gettimeofday()` 的修正也在同一份归档里生效(链接期覆盖)。
|
||||||
|
- 真机写入红线未触碰;本单只改 SDK 源与构建脚本。
|
||||||
|
|
||||||
|
## 6. 同源残余(未在本单修复,需单独安排)
|
||||||
|
|
||||||
|
同一句 `!__ILP32__` 还打穿了以下路径(睡眠族已在上文一并修掉,余下这些症状不同、
|
||||||
|
修复面更大):
|
||||||
|
|
||||||
|
| 路径 | 症状 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `futex` 相对超时 | 亚秒 timed wait 立即超时 | `__timedwait_cp` 的 `__futex4_cp` 同款 `!__ILP32__` 守卫;`pthread_cond_timedwait` 的「now」已由本单修正,但相对超时仍按 8 字节读(>1s 的整秒等待基本可用,亚秒等待失效) |
|
||||||
|
| `timerfd_settime` | 到期时间错乱 | `struct itimerspec`(32 字节)当 16 字节读;当前 SDK 未用 timerfd |
|
||||||
|
| `clock_settime`/`clock_adjtime`/`pselect`/`ppoll`/`sigtimedwait`/`semtimedop` 等 | 未验证 | 同族守卫;SDK 目前未直接使用 |
|
||||||
|
|
||||||
|
建议:向 zig 上游报 musl `__ILP32__` 守卫问题(clang 对 32 位目标统一定义 `__ILP32__`,
|
||||||
|
而该守卫本意只排除 x32);根治应换用容器旧链(gcc,不定义 `__ILP32__`)编 SDK,或
|
||||||
|
downgrade/修补 zig 内置 musl。**不要**指望 `-D_TIME_BITS=32`:zig 的
|
||||||
|
`alltypes.h` 是无条件 `#define __USE_TIME_BITS64 1`,命令行改不动;而只改用户侧头文件会让
|
||||||
|
用户结构(8 字节)与 libc 内部(16 字节,pthread 等)再次错配。
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# sdk/ — meow_ui 应用框架(源码 + 构建)
|
||||||
|
|
||||||
|
本目录是 `meow_ui.a` 的**自包含**源码树:公共头在 `include/`(伞头 `meow_ui.h`),
|
||||||
|
构建脚本 `build_sdk.sh`,宿主逻辑测试 `test_*.c`。上手指南见仓库根
|
||||||
|
[README.md](../README.md) 与 [docs/APP_DEV_GUIDE.md](../docs/APP_DEV_GUIDE.md)。
|
||||||
|
|
||||||
|
## 构建
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd sdk
|
||||||
|
./build_sdk.sh # 产出 sdk/meow_ui.a(mipsel 静态库)
|
||||||
|
```
|
||||||
|
|
||||||
|
交叉编译器默认 `$HOME/Project/R3ProII/.tools/zig-mipsel-cc`,可用 `MEOW_CC` 覆盖。
|
||||||
|
|
||||||
|
## 时钟:mipsel o32 时钟/睡眠 ABI 修复(已内置)
|
||||||
|
|
||||||
|
**公开 SDK 现已内置 mipsel o32 时钟/睡眠 ABI 修复(见 [CLOCK_ABI.md](CLOCK_ABI.md))**,
|
||||||
|
源码在 `meow_ui_time.c`。
|
||||||
|
|
||||||
|
- 背景:zig 内置 musl 在 mipsel 上「头文件声明 64 位 `time_t`、实现却走 time32 系统调用」,
|
||||||
|
导致 `meow_ui_time_ms()` 毫秒位恒 0、高 32 位随机;`usleep()` 亚秒等待立即返回(忙等空转)。
|
||||||
|
- 修复:`meow_ui_time_ms()` 直接按**内核 o32 ABI**(8 字节 `{int32 sec; int32 nsec;}`)取时,
|
||||||
|
再自行升成 64 位毫秒;同一 TU 强定义 `__clock_gettime` / `__clock_gettime64` /
|
||||||
|
`__clock_nanosleep` / `__clock_nanosleep_time64` / `__nanosleep_time64`,在**链接期接管**
|
||||||
|
libc 里那份 ABI 不一致的实现。
|
||||||
|
- **链接顺序要求**:应用必须把 `meow_ui.a` 放在 `-lc` **之前**(`cc ... meow_ui.a ... -lc`),
|
||||||
|
强定义才能生效。仓库内 `examples/hello/build_hello.sh` 与 `tuku/build_tuku.sh` 均如此;
|
||||||
|
自定义构建脚本请照抄这个顺序,否则取时/睡眠会静默回到坏实现。
|
||||||
|
- 非 mipsel 目标(宿主单测、aarch64 等)走普通 `clock_gettime`,行为不变。
|
||||||
|
|
||||||
|
### 已知限制
|
||||||
|
|
||||||
|
- **2038 前有效**:内核侧仍是 time32(`tv_sec` 为 int32),秒数超出 `INT32_MAX` 即溢出;
|
||||||
|
本修复只保证「头/实现的宽度一致」,不把内核升到 time64。
|
||||||
|
- 同源缺陷**未修**:futex 相对超时(`pthread_cond_timedwait` 的亚秒等待)、`timerfd_settime`、
|
||||||
|
`clock_settime`/`clock_adjtime`、`pselect`/`ppoll` 等仍受同一句 `!__ILP32__` 影响;
|
||||||
|
清单与影响见 [CLOCK_ABI.md](CLOCK_ABI.md) §6。SDK 自身不直接使用这些路径。
|
||||||
|
- 真机验收:本修复的证据是反汇编 + 宿主单测(本机无 qemu-mipsel),设备侧仍需实测确认。
|
||||||
|
|
||||||
|
## 宿主测试
|
||||||
|
|
||||||
|
无硬件即可跑(`gcc` 直接编译,不链 `meow_ui.a`):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd sdk
|
||||||
|
# 时钟换算与单调性
|
||||||
|
gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o meow_ui_time_host.o
|
||||||
|
gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c meow_ui_time_host.o -o test_meow_time
|
||||||
|
./test_meow_time # == PASS == / checks=N
|
||||||
|
|
||||||
|
# 伞头与其余模块测试(示例)
|
||||||
|
gcc -I. -Iinclude test_umbrella.c <各 sdk 源 .c> test_host_stub.c -lpthread -o test_umbrella
|
||||||
|
./test_umbrella
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:宿主编译时 `meow_ui_time.c` 走 `MEOW_TIME32_KERNEL_ABI 0` 分支(普通 `clock_gettime`),
|
||||||
|
因此**宿主测试不能证明 mipsel o32 路径正确** —— 那条路径的正确性靠 `CLOCK_ABI.md §4` 的
|
||||||
|
反汇编证据(`nm` 符号来源 + `syscall 4263/4265` 的 8 字节转换)。
|
||||||
+8
-1
@@ -6,6 +6,11 @@
|
|||||||
#
|
#
|
||||||
# 交叉编译器:默认 MeowOS 开发机路径 $HOME/Project/R3ProII/.tools/zig-mipsel-cc;
|
# 交叉编译器:默认 MeowOS 开发机路径 $HOME/Project/R3ProII/.tools/zig-mipsel-cc;
|
||||||
# 可用环境变量 MEOW_CC 覆盖(或用你自己装好的 zig 交叉 cc,--target=mipsel-linux-musleabi soft-float)。
|
# 可用环境变量 MEOW_CC 覆盖(或用你自己装好的 zig 交叉 cc,--target=mipsel-linux-musleabi soft-float)。
|
||||||
|
#
|
||||||
|
# 时钟(meow_ui_time):mipsel 上 zig 内置 musl 的 time64 ABI 自相矛盾(头声明 64 位 time_t、
|
||||||
|
# 实现走 time32 取数),该 TU 按内核 o32 ABI 取时并**强定义** __clock_gettime*/睡眠入口,靠链接期
|
||||||
|
# 接管 libc 那份坏实现。因此应用链接顺序必须是 `... meow_ui.a ... -lc`(归档在 libc 之前,
|
||||||
|
# 现有 build_tuku.sh 即如此);根因与证据链见 sdk/CLOCK_ABI.md。
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
CC="${MEOW_CC:-$HOME/Project/R3ProII/.tools/zig-mipsel-cc}"
|
CC="${MEOW_CC:-$HOME/Project/R3ProII/.tools/zig-mipsel-cc}"
|
||||||
@@ -16,7 +21,9 @@ rm -f meow_ui.a *.o
|
|||||||
# meow_font.c = 预渲染位图字体运行时(自包含:只含 meow_font.h + 标准头;最小 zlib ABI 自声明,
|
# meow_font.c = 预渲染位图字体运行时(自包含:只含 meow_font.h + 标准头;最小 zlib ABI 自声明,
|
||||||
# 链接时用 sysroot 的 libz.a),随 SDK 一并 vendored。新模块(box/anim/scroll/str + cachegov/lazygrid/
|
# 链接时用 sysroot 的 libz.a),随 SDK 一并 vendored。新模块(box/anim/scroll/str + cachegov/lazygrid/
|
||||||
# select/net_ipc)随主仓增量并入。
|
# select/net_ipc)随主仓增量并入。
|
||||||
OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_pinyin meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media meow_ui_audio stb_image meow_font meow_ui_box meow_ui_anim meow_ui_scroll meow_ui_str meow_cachegov meow_lazygrid meow_select meow_net_ipc"
|
# meow_ui_time.c 必须**编进归档**且排在前面:它提供 meow_ui_time_ms(gfx 已不再实现)与
|
||||||
|
# __clock_gettime* 强定义,归档成员随 meow_ui_time_ms 的引用被拉入,从而在 -lc 之前解析。
|
||||||
|
OBJS="meow_ui_time meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_pinyin meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config meow_ui_draw meow_ui_view meow_ui_nav meow_ui_media meow_ui_audio stb_image meow_font meow_ui_box meow_ui_anim meow_ui_scroll meow_ui_str meow_cachegov meow_lazygrid meow_select meow_net_ipc"
|
||||||
CFLAGS="-Os -static -Iinclude -I."
|
CFLAGS="-Os -static -Iinclude -I."
|
||||||
for f in $OBJS; do
|
for f in $OBJS; do
|
||||||
echo "== cc $f =="
|
echo "== cc $f =="
|
||||||
|
|||||||
+9
-16
@@ -20,7 +20,6 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
|
||||||
@@ -172,15 +171,9 @@ static void refresh_governor(unsigned long long cost_ms, int period)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 工具 ---- */
|
/* ---- 工具 ---- */
|
||||||
static uint64_t now_ms(void)
|
/* 时钟统一由 meow_ui_time.c 提供(meow_ui_time_ms,声明见 include/meow_ui_draw.h):
|
||||||
{
|
* mipsel 上 zig musl 的 time64 ABI 自相矛盾,取时必须走内核 o32 ABI 再升位,
|
||||||
struct timespec ts;
|
* 故本 TU 不再保留取时实现(详见 sdk/CLOCK_ABI.md)。 */
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t meow_ui_time_ms(void) { return now_ms(); }
|
|
||||||
|
|
||||||
static uint16_t rgb565(uint32_t c)
|
static uint16_t rgb565(uint32_t c)
|
||||||
{
|
{
|
||||||
return (uint16_t)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F));
|
return (uint16_t)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F));
|
||||||
@@ -895,7 +888,7 @@ static void statusbar_collapse_step(void)
|
|||||||
int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0;
|
int tgt = g_sbar_collapsed ? MUI_SBAR_H : 0;
|
||||||
if (g_sbar_collapse_h == tgt) return;
|
if (g_sbar_collapse_h == tgt) return;
|
||||||
if (g_sbar_collapse_anim_ms <= 0) { g_sbar_collapse_h = tgt; return; }
|
if (g_sbar_collapse_anim_ms <= 0) { g_sbar_collapse_h = tgt; return; }
|
||||||
unsigned long long now = now_ms();
|
unsigned long long now = meow_ui_time_ms();
|
||||||
unsigned long long el = now - g_sbar_collapse_start_ms;
|
unsigned long long el = now - g_sbar_collapse_start_ms;
|
||||||
int span = g_sbar_collapse_anim_ms;
|
int span = g_sbar_collapse_anim_ms;
|
||||||
if ((int)el >= span) { g_sbar_collapse_h = tgt; return; }
|
if ((int)el >= span) { g_sbar_collapse_h = tgt; return; }
|
||||||
@@ -932,7 +925,7 @@ void meow_ui_statusbar_set_collapsed(int on)
|
|||||||
if (tgt == g_sbar_collapsed) return;
|
if (tgt == g_sbar_collapsed) return;
|
||||||
g_sbar_collapsed = tgt;
|
g_sbar_collapsed = tgt;
|
||||||
g_sbar_collapse_from_h = g_sbar_collapse_h;
|
g_sbar_collapse_from_h = g_sbar_collapse_h;
|
||||||
g_sbar_collapse_start_ms = now_ms();
|
g_sbar_collapse_start_ms = meow_ui_time_ms();
|
||||||
dirty_full(); /* 顶栏显隐/上收变化 → 全帧重画 */
|
dirty_full(); /* 顶栏显隐/上收变化 → 全帧重画 */
|
||||||
meow_ui_sbar_write_mode(); /* App 进程上报顶条模式(launcher 感知全屏/收合,agent-146) */
|
meow_ui_sbar_write_mode(); /* App 进程上报顶条模式(launcher 感知全屏/收合,agent-146) */
|
||||||
}
|
}
|
||||||
@@ -1012,7 +1005,7 @@ int meow_ui_overlay_suppressed_active(void)
|
|||||||
snprintf(s_path, sizeof s_path, "%s", (e && e[0]) ? e : MEOW_NOTIF_ACTIVE_DEFAULT);
|
snprintf(s_path, sizeof s_path, "%s", (e && e[0]) ? e : MEOW_NOTIF_ACTIVE_DEFAULT);
|
||||||
s_path_init = 1;
|
s_path_init = 1;
|
||||||
}
|
}
|
||||||
unsigned long long now = now_ms();
|
unsigned long long now = meow_ui_time_ms();
|
||||||
if (s_last_chk && now - s_last_chk < MEOW_OVERLAY_CHK_MS) return s_last_res;
|
if (s_last_chk && now - s_last_chk < MEOW_OVERLAY_CHK_MS) return s_last_res;
|
||||||
s_last_chk = now;
|
s_last_chk = now;
|
||||||
{
|
{
|
||||||
@@ -1066,7 +1059,7 @@ static void present(void)
|
|||||||
}
|
}
|
||||||
/* 帧率门控(A-1/A-2,从 launcher 迁入):present 距上次 ≥ period(1000/eff_rate) 才呈现;
|
/* 帧率门控(A-1/A-2,从 launcher 迁入):present 距上次 ≥ period(1000/eff_rate) 才呈现;
|
||||||
* 未满周期则保留脏区(不 dirty_reset),下一轮再试。refresh_always_max=1 时跳过门控(恒满速)。 */
|
* 未满周期则保留脏区(不 dirty_reset),下一轮再试。refresh_always_max=1 时跳过门控(恒满速)。 */
|
||||||
unsigned long long now = now_ms();
|
unsigned long long now = meow_ui_time_ms();
|
||||||
int period = (g_eff_rate > 0) ? (1000 / g_eff_rate) : 16;
|
int period = (g_eff_rate > 0) ? (1000 / g_eff_rate) : 16;
|
||||||
if (!g_refresh_always_max) {
|
if (!g_refresh_always_max) {
|
||||||
if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return;
|
if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return;
|
||||||
@@ -1096,7 +1089,7 @@ static void present(void)
|
|||||||
}
|
}
|
||||||
if (g_fb_mem) {
|
if (g_fb_mem) {
|
||||||
ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */
|
ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */
|
||||||
unsigned long long tbg = now_ms();
|
unsigned long long tbg = meow_ui_time_ms();
|
||||||
/* 「同一个状态栏」闭环:App 进程不渲染状态栏(meow_ui_sys_render_enabled()==0),也绝不向
|
/* 「同一个状态栏」闭环:App 进程不渲染状态栏(meow_ui_sys_render_enabled()==0),也绝不向
|
||||||
* 真实 fb 写顶条 inset 区(顶条由 launcher 系统进程叠加绘制)。否则 App 首帧全量拷贝
|
* 真实 fb 写顶条 inset 区(顶条由 launcher 系统进程叠加绘制)。否则 App 首帧全量拷贝
|
||||||
* (meow_ui_init 置 dirty_full)会把 launcher 刚叠画的顶条冲成未初始化数据——真机
|
* (meow_ui_init 置 dirty_full)会把 launcher 刚叠画的顶条冲成未初始化数据——真机
|
||||||
@@ -1133,7 +1126,7 @@ static void present(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refresh_governor(now_ms() - tbg, period);
|
refresh_governor(meow_ui_time_ms() - tbg, period);
|
||||||
}
|
}
|
||||||
g_last_present_ms = now;
|
g_last_present_ms = now;
|
||||||
g_frames_presented++;
|
g_frames_presented++;
|
||||||
|
|||||||
@@ -284,4 +284,8 @@ int meow_ime_handle_event(const meow_input_ev *e);
|
|||||||
/* 当前 IME 是否拼音模式(供 meow_ui_input.c 决定是否喂长按轮询)。 */
|
/* 当前 IME 是否拼音模式(供 meow_ui_input.c 决定是否喂长按轮询)。 */
|
||||||
int meow_ime_mode_pinyin(void);
|
int meow_ime_mode_pinyin(void);
|
||||||
|
|
||||||
|
/* ---- 时钟(meow_ui_time.c)---- */
|
||||||
|
/* 秒/纳秒 → 单调毫秒的唯一换算点(meow_ui_time_ms 与宿主单测共用)。 */
|
||||||
|
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec);
|
||||||
|
|
||||||
#endif /* MEOW_UI_INTERNAL_H */
|
#endif /* MEOW_UI_INTERNAL_H */
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/* meow_ui_time.c — SDK 单调时钟(meow_ui_time_ms)+ mipsel o32 时间 ABI 兜底。
|
||||||
|
*
|
||||||
|
* 根因(M11;真机日志 + 反汇编证据链见 sdk/CLOCK_ABI.md):
|
||||||
|
* zig 0.17 的 clang 前端给 32 位 mips 目标预定义了 __ILP32__;zig 自带 musl 的
|
||||||
|
* time64 回退分支统一写作 `#if defined(SYS_x_time64) && !defined(__ILP32__)`
|
||||||
|
* (该 `!__ILP32__` 本意只排除 x32),于是 mipsel 的 libc 把「time64 入口」编译成了
|
||||||
|
* 老 32 位实现:__clock_gettime64 直接做 SYS_clock_gettime(4263),只写 8 字节
|
||||||
|
* `{ int32 tv_sec; int32 tv_nsec; }`。而 zig 头文件又无条件把 time_t 声明成 64 位
|
||||||
|
* (struct timespec 16 字节、tv_sec 8 字节),两边 ABI 不一致:
|
||||||
|
* tv_sec 低字 = 真 sec,高字 = 真 nsec → 64 位结果高 32 位随机、非单调;
|
||||||
|
* tv_nsec(结构偏移 8)根本没被写 → 毫秒位恒 0(真机实测全是 1000 的倍数)。
|
||||||
|
* 容器旧链(gcc)不定义 __ILP32__,time64 回退正确——对照 patch_v2/ncm_app/ncm:
|
||||||
|
* 同一函数里先 SYS_clock_gettime64(4403)、ENOSYS 后走 4263 并做宽度转换。
|
||||||
|
*
|
||||||
|
* 修复:不依赖 libc 头声明的 struct timespec 宽度,直接按**内核 o32 ABI**
|
||||||
|
* (8 字节 time32:int32 sec + int32 nsec)取时,再自行升成 64 位毫秒;
|
||||||
|
* 同时在本 TU 给出 __clock_gettime / __clock_gettime64 的强定义。应用链接顺序是
|
||||||
|
* `... meow_ui.a ... -lc`,归档先参与解析,因此链接期整体替换掉 libc 里那份
|
||||||
|
* ABI 不一致的实现:time()、gettimeofday()、pthread 定时等待的内部取时
|
||||||
|
* (musl 的 time()/__timedwait_cp 都调 __clock_gettime)随之修正。
|
||||||
|
* 上层时间位宽仍是 64 位,只有内核取数按 o32 time32;2038 前有效。
|
||||||
|
*
|
||||||
|
* 同一句 `!__ILP32__` 还打穿了睡眠:__nanosleep_time64/__clock_nanosleep 走 `#else`
|
||||||
|
* 把 16 字节 time64 结构直接交给 time32 系统调用,内核读到的 nsec 恒为 0 →
|
||||||
|
* `usleep(16000)` 立即返回(SDK 里帧节拍/轮询会变忙等空转)。这里一并按 time32
|
||||||
|
* 转换后重发,恢复亚秒睡眠。
|
||||||
|
*
|
||||||
|
* 残余(未修):futex 相对超时(pthread 定时等待的剩余时间按 8 字节读)、timerfd_settime、
|
||||||
|
* clock_settime/adjtime、pselect/ppoll 超时——同为该守卫的受害者,见 CLOCK_ABI.md §6。
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "meow_ui.h"
|
||||||
|
|
||||||
|
#if defined(__mips__) && !defined(__mips64) && (__SIZEOF_LONG__ == 4)
|
||||||
|
#define MEOW_TIME32_KERNEL_ABI 1
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#define MEOW_TIME32_KERNEL_ABI 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t meow_time_ms_from_parts(int64_t sec, int64_t nsec)
|
||||||
|
{
|
||||||
|
return (uint64_t)sec * 1000u + (uint64_t)nsec / 1000000u;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t meow_ui_time_ms(void)
|
||||||
|
{
|
||||||
|
#if MEOW_TIME32_KERNEL_ABI
|
||||||
|
struct meow_time32 { int32_t tv_sec; int32_t tv_nsec; } t;
|
||||||
|
t.tv_sec = 0;
|
||||||
|
t.tv_nsec = 0;
|
||||||
|
if (syscall(SYS_clock_gettime32, CLOCK_MONOTONIC, &t) != 0) return 0;
|
||||||
|
return meow_time_ms_from_parts(t.tv_sec, t.tv_nsec);
|
||||||
|
#else
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0;
|
||||||
|
return meow_time_ms_from_parts((int64_t)ts.tv_sec, (int64_t)ts.tv_nsec);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MEOW_TIME32_KERNEL_ABI
|
||||||
|
/* 内核 o32 time32 布局(与 libc 头的 time_t 宽度无关)。 */
|
||||||
|
struct meow_time32 {
|
||||||
|
int32_t tv_sec;
|
||||||
|
int32_t tv_nsec;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int32_t time32_clamp(int64_t v)
|
||||||
|
{
|
||||||
|
if (v > INT32_MAX) return INT32_MAX;
|
||||||
|
if (v < 0) return 0;
|
||||||
|
return (int32_t)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- libc 取时入口的强定义 ----
|
||||||
|
* 返回约定与 musl 一致:0 成功 / -1 失败(errno)。
|
||||||
|
* 说明:<time.h> 在 _REDIR_TIME64 下把标识符 clock_gettime 的引用改名为
|
||||||
|
* __clock_gettime64,故本 TU 不定义 clock_gettime 这个名字;musl 里它只是
|
||||||
|
* __clock_gettime 的 weak alias,同一份语义已由下面的强定义接管。 */
|
||||||
|
int __clock_gettime(clockid_t clk, struct timespec *ts);
|
||||||
|
int __clock_gettime64(clockid_t clk, struct timespec *ts);
|
||||||
|
|
||||||
|
int __clock_gettime(clockid_t clk, struct timespec *ts)
|
||||||
|
{
|
||||||
|
struct meow_time32 t;
|
||||||
|
if (!ts) { errno = EFAULT; return -1; }
|
||||||
|
t.tv_sec = 0;
|
||||||
|
t.tv_nsec = 0;
|
||||||
|
if (syscall(SYS_clock_gettime32, clk, &t) != 0) return -1;
|
||||||
|
ts->tv_sec = t.tv_sec;
|
||||||
|
ts->tv_nsec = t.tv_nsec;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __clock_gettime64(clockid_t clk, struct timespec *ts)
|
||||||
|
{
|
||||||
|
return __clock_gettime(clk, ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 睡眠入口(同源缺陷:亚秒 usleep 变空转)----
|
||||||
|
* __clock_nanosleep 按 musl 内部约定返回 0 或正 errno;__nanosleep_time64 是
|
||||||
|
* nanosleep/usleep 的重定向目标,按公共约定返回 0/-1(errno)。 */
|
||||||
|
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem);
|
||||||
|
int __clock_nanosleep_time64(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem);
|
||||||
|
int __nanosleep_time64(const struct timespec *req, struct timespec *rem);
|
||||||
|
|
||||||
|
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
|
||||||
|
{
|
||||||
|
struct meow_time32 rq, rm;
|
||||||
|
long r;
|
||||||
|
|
||||||
|
if (clk == CLOCK_THREAD_CPUTIME_ID) return EINVAL;
|
||||||
|
if (!req) return EFAULT;
|
||||||
|
if (req->tv_nsec < 0 || req->tv_nsec >= 1000000000L) return EINVAL;
|
||||||
|
rq.tv_sec = time32_clamp(req->tv_sec);
|
||||||
|
rq.tv_nsec = (int32_t)req->tv_nsec;
|
||||||
|
rm.tv_sec = 0;
|
||||||
|
rm.tv_nsec = 0;
|
||||||
|
|
||||||
|
if (clk == CLOCK_REALTIME && !flags)
|
||||||
|
r = syscall(SYS_nanosleep, &rq, rem ? &rm : 0);
|
||||||
|
else
|
||||||
|
r = syscall(SYS_clock_nanosleep_time32, clk, flags, &rq, rem ? &rm : 0);
|
||||||
|
if (r != 0) return errno;
|
||||||
|
if (rem) {
|
||||||
|
rem->tv_sec = rm.tv_sec;
|
||||||
|
rem->tv_nsec = rm.tv_nsec;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __clock_nanosleep_time64(clockid_t clk, int flags, const struct timespec *req, struct timespec *rem)
|
||||||
|
{
|
||||||
|
return __clock_nanosleep(clk, flags, req, rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __nanosleep_time64(const struct timespec *req, struct timespec *rem)
|
||||||
|
{
|
||||||
|
int r = __clock_nanosleep(CLOCK_REALTIME, 0, req, rem);
|
||||||
|
if (r) { errno = r; return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/* test_meow_time.c — 宿主单测:SDK 时钟换算(meow_ui_time.c / meow_time_ms_from_parts)。
|
||||||
|
*
|
||||||
|
* 背景(M11/M21):mipsel 真机上 meow_ui_time_ms() 曾返回「低 32 位=秒×1000、高 32 位随机」,
|
||||||
|
* 根因是 zig musl 头(time_t 64 位)与 libc 实现(o32 time32 取数)ABI 不一致,见
|
||||||
|
* sdk/CLOCK_ABI.md。修复后取数统一走 meow_time_ms_from_parts(sec, nsec),本测试把
|
||||||
|
* 换算边界(毫秒进位/截断/大秒数/负值回绕)与「真时钟单调且前进」固定在宿主上。
|
||||||
|
*
|
||||||
|
* 注意:宿主(非 mips)编译时 meow_ui_time.c 走 MEOW_TIME32_KERNEL_ABI=0 分支,即普通
|
||||||
|
* clock_gettime —— 本测试**不能**证明 mipsel o32 路径正确(那条路径只靠反汇编证据)。
|
||||||
|
*
|
||||||
|
* 编译(在 sdk/ 目录下):
|
||||||
|
* gcc -Os -Wall -Wextra -I. -Iinclude -c meow_ui_time.c -o meow_ui_time_host.o
|
||||||
|
* gcc -Os -Wall -Wextra -I. -Iinclude test_meow_time.c meow_ui_time_host.o -o test_meow_time
|
||||||
|
* 运行:./test_meow_time (exit 0=PASS / 1=FAIL)
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "meow_ui.h"
|
||||||
|
#include "meow_ui_internal.h"
|
||||||
|
|
||||||
|
static int g_fail = 0;
|
||||||
|
static unsigned long g_checks = 0;
|
||||||
|
|
||||||
|
static void eq_ms(const char *name, int64_t sec, int64_t nsec, uint64_t want)
|
||||||
|
{
|
||||||
|
uint64_t got = meow_time_ms_from_parts(sec, nsec);
|
||||||
|
g_checks++;
|
||||||
|
if (got != want) {
|
||||||
|
printf("FAIL %s: sec=%lld nsec=%lld got=%llu want=%llu\n",
|
||||||
|
name, (long long)sec, (long long)nsec,
|
||||||
|
(unsigned long long)got, (unsigned long long)want);
|
||||||
|
g_fail = 1;
|
||||||
|
} else {
|
||||||
|
printf("ok %s = %llu\n", name, (unsigned long long)got);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
/* ---- 1) 换算边界:毫秒 = sec*1000 + nsec/1e6(整除截断) ---- */
|
||||||
|
eq_ms("零", 0, 0, 0);
|
||||||
|
eq_ms("(sec,0) 整秒", 1, 0, 1000);
|
||||||
|
eq_ms("999999ns 截断", 0, 999999, 0);
|
||||||
|
eq_ms("1ms 进位", 0, 1000000, 1);
|
||||||
|
eq_ms("1ns 截断", 0, 1, 0);
|
||||||
|
eq_ms("开机 1135.008s", 1135, 8000000, 1135008);
|
||||||
|
eq_ms("跨秒 1.999999999s", 1, 999999999, 1999);
|
||||||
|
eq_ms("开机 1204.999999999s", 1204, 999999999, 1204999);
|
||||||
|
eq_ms("接近 2038", 2147483647, 999999999, 2147483647999ULL);
|
||||||
|
|
||||||
|
/* ---- 2) 负值:from_parts 只做无符号升位((uint64_t)sec*1000 按二补回绕),不钳制。
|
||||||
|
* 真正把内核 time32 的负 sec 钳成 0 的是 meow_ui_time.c 的 time32_clamp(),它只在
|
||||||
|
* mipsel 分支编译、宿主不可达;这里把 from_parts 的回绕语义钉住,防止有人悄悄把它
|
||||||
|
* 理解成「负值 = 0」而改掉换算。 ---- */
|
||||||
|
eq_ms("负 sec 回绕(-1s)", -1, 0, 0xFFFFFFFFFFFFFC18ULL);
|
||||||
|
eq_ms("负 sec+1ms 回绕", -1, 1000000, 0xFFFFFFFFFFFFFC19ULL);
|
||||||
|
|
||||||
|
/* ---- 3) 真时钟:非零、单调不减、量级合理(不上溢成「高 32 随机」) ---- */
|
||||||
|
uint64_t prev = meow_ui_time_ms();
|
||||||
|
g_checks++;
|
||||||
|
if (prev == 0) {
|
||||||
|
printf("FAIL 时钟返回 0(clock_gettime 失败?)\n");
|
||||||
|
g_fail = 1;
|
||||||
|
}
|
||||||
|
g_checks++;
|
||||||
|
if (prev >= (1ULL << 40)) {
|
||||||
|
printf("FAIL 时钟量级异常(>=2^40 ms):%llu\n", (unsigned long long)prev);
|
||||||
|
g_fail = 1;
|
||||||
|
}
|
||||||
|
/* 有界采样(避免时钟停走时测试挂死):要求单调不减且确实前进 */
|
||||||
|
int advanced = 0;
|
||||||
|
for (int i = 0; i < 200000 && !advanced; i++) {
|
||||||
|
uint64_t now = meow_ui_time_ms();
|
||||||
|
if (now < prev) {
|
||||||
|
printf("FAIL 单调倒退:%llu -> %llu\n",
|
||||||
|
(unsigned long long)prev, (unsigned long long)now);
|
||||||
|
g_fail = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
advanced = now > prev;
|
||||||
|
prev = now;
|
||||||
|
}
|
||||||
|
g_checks++;
|
||||||
|
if (!advanced) {
|
||||||
|
printf("FAIL 时钟未前进(200000 次采样内无变化)\n");
|
||||||
|
g_fail = 1;
|
||||||
|
} else {
|
||||||
|
printf("ok 时钟前进且单调\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 4) 动画常用差值:两次取样的差 = 毫秒(不能是 0 恒等/整千) ---- */
|
||||||
|
uint64_t t0 = meow_ui_time_ms();
|
||||||
|
int waited = 0;
|
||||||
|
for (int i = 0; i < 200000 && !waited; i++)
|
||||||
|
waited = meow_ui_time_ms() != t0;
|
||||||
|
uint64_t t1 = meow_ui_time_ms();
|
||||||
|
g_checks++;
|
||||||
|
if (!waited || t1 <= t0 || t1 - t0 > 5000) {
|
||||||
|
printf("FAIL 取样差值异常:%llu -> %llu\n",
|
||||||
|
(unsigned long long)t0, (unsigned long long)t1);
|
||||||
|
g_fail = 1;
|
||||||
|
} else {
|
||||||
|
printf("ok 取样差值 %llu ms\n", (unsigned long long)(t1 - t0));
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(g_fail ? "== FAIL ==\n" : "== PASS ==\n");
|
||||||
|
printf("checks=%lu\n", g_checks);
|
||||||
|
return g_fail;
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
|||||||
gcc -Wall -Wextra -O1 -DMEOW_UI_HOST_TEST -I. -I"$SDK" -I"$SDK/include" \
|
gcc -Wall -Wextra -O1 -DMEOW_UI_HOST_TEST -I. -I"$SDK" -I"$SDK/include" \
|
||||||
test/test_settings_lock_host.c ui/settings/settings.c ui/lock/lock.c \
|
test/test_settings_lock_host.c ui/settings/settings.c ui/lock/lock.c \
|
||||||
logic/pin.c logic/reader.c \
|
logic/pin.c logic/reader.c \
|
||||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||||
@@ -81,7 +81,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
|||||||
test/test_browse_ui_host.c ui/library/library.c logic/library.c \
|
test/test_browse_ui_host.c ui/library/library.c logic/library.c \
|
||||||
logic/reader.c logic/reader/page_source.c logic/reader/archive_source.c \
|
logic/reader.c logic/reader/page_source.c logic/reader/archive_source.c \
|
||||||
logic/reader/page_cache.c logic/zip/zip.c \
|
logic/reader/page_cache.c logic/zip/zip.c \
|
||||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||||
@@ -99,7 +99,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
|||||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||||
logic/reader/nav.c \
|
logic/reader/nav.c \
|
||||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||||
@@ -116,7 +116,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
|||||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||||
logic/reader/nav.c \
|
logic/reader/nav.c \
|
||||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||||
@@ -133,7 +133,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
|||||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||||
logic/reader/nav.c \
|
logic/reader/nav.c \
|
||||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ if [ -f "$SDK/meow_ui_view.c" ] && [ -f "$SDK/meow_ui_config.c" ]; then
|
|||||||
logic/library.c logic/reader.c logic/reader/page_source.c \
|
logic/library.c logic/reader.c logic/reader/page_source.c \
|
||||||
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
logic/reader/archive_source.c logic/reader/page_cache.c logic/zip/zip.c \
|
||||||
logic/reader/nav.c \
|
logic/reader/nav.c \
|
||||||
"$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
"$SDK"/meow_ui_time.c "$SDK"/meow_ui_gfx.c "$SDK"/meow_ui_sys.c "$SDK"/meow_ui_theme.c \
|
||||||
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
"$SDK"/meow_ui_vol.c "$SDK"/meow_ui_log.c "$SDK"/meow_ui_widget.c \
|
||||||
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
"$SDK"/meow_ui_input.c "$SDK"/meow_ui_activity.c "$SDK"/meow_ui_dialog.c \
|
||||||
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
"$SDK"/meow_ui_ime.c "$SDK"/meow_ui_str.c "$SDK"/meow_pinyin.c \
|
||||||
|
|||||||
Reference in new issue
Block a user