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。
112 lines
4.5 KiB
C
112 lines
4.5 KiB
C
/* 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;
|
||
}
|