/* test/shelf_index_probe.c — M32 真机测量探针(不进发行包/不参与 mpk)。 * * 只调 shelf_model/shelf_index/progress,不初始化 UI;用 /proc/uptime 计时 * (该内核 time32 ABI 下 clock_gettime 不可靠,见 sdk/CLOCK_ABI.md;分辨率 10ms)。 * * 用法(设备): * MEOW_PROBE_CFG=/usr/data/mnt/sd_0/MeowData/m32probe/data \ * ./shelf_index_probe /usr/data/mnt/sd_0/Books * mode: * write 全量扫描 + 写索引(打印 n/索引字节数) * scan 全量扫描(不写索引) * load 索引载入(ts_fn=NULL,只量索引+校验) * loadts 索引载入 + 逐本 rdprog_load(生产口径) * statpaths 冷缓存 stat 指定路径(argv[2..],不做任何枚举,避免预热) * mtime 打印路径的 st_mtime/sec+nsec(验证 exFAT 1 秒粒度) * size 打印索引文件字节数 * 冷缓存:每次运行前 `sync; echo 3 > /proc/sys/vm/drop_caches`。 * 注意:load/level1 本身会按需把 dentry/page cache 焐热,所以冷值必须每次 drop 后单跑一次。 */ #include "shelf_model.h" #include "shelf_index.h" #include "progress.h" #include #include #include #include #include static char g_cfgdir[500]; const char *meow_config_dir(void) { return g_cfgdir; } static int64_t now_us(void) { FILE *f = fopen("/proc/uptime", "r"); if (!f) return 0; long sec = 0, cs = 0; if (fscanf(f, "%ld.%ld", &sec, &cs) != 2) cs = 0; fclose(f); return (int64_t)sec * 1000000 + (int64_t)cs * 10000; } static uint64_t prog_ts(const char *path) { rdprog p; return (rdprog_load(path, &p) == 1) ? p.ts : 0; } static int64_t index_bytes(const char *root) { char ip[500]; struct stat st; if (shelf_index_path(root, ip, sizeof ip) != 0 || stat(ip, &st) != 0) return -1; return (int64_t)st.st_size; } int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "用法: %s \n", argv[0]); return 2; } const char *mode = argv[1]; const char *root = argv[2]; const char *cfg = getenv("MEOW_PROBE_CFG"); snprintf(g_cfgdir, sizeof g_cfgdir, "%s", (cfg && cfg[0]) ? cfg : "/tmp/m32probe_cfg/data"); static shelf_book books[SHELF_MAX_BOOKS]; shelf_scan_report rep; int64_t t0, t1; if (strcmp(mode, "scan") == 0) { t0 = now_us(); int n = shelf_scan_books(root, books, SHELF_MAX_BOOKS, NULL, &rep); t1 = now_us(); printf("PROBE scan_ms %lld n=%d trunc=%d skip=%d root_missing=%d\n", (long long)((t1 - t0) / 1000), n, rep.truncated, rep.depth_skipped, rep.root_missing); } else if (strcmp(mode, "write") == 0) { t0 = now_us(); shelf_idx_writer *w = shelf_index_writer_open(root); int n = shelf_scan_books_ex(root, books, SHELF_MAX_BOOKS, NULL, &rep, w ? shelf_index_writer_dir : NULL, w); int ok = w ? shelf_index_writer_commit(w, books, n, &rep) : 0; t1 = now_us(); printf("PROBE write_ms %lld n=%d commit=%d idx_bytes=%lld\n", (long long)((t1 - t0) / 1000), n, ok, (long long)index_bytes(root)); } else if (strcmp(mode, "load") == 0 || strcmp(mode, "loadts") == 0) { int with_ts = (strcmp(mode, "loadts") == 0); t0 = now_us(); int hit = shelf_index_load(root, books, SHELF_MAX_BOOKS, with_ts ? prog_ts : NULL, &rep); t1 = now_us(); printf("PROBE %s_ms %lld hit=%d n=%d trunc=%d skip=%d idx_bytes=%lld\n", with_ts ? "loadts" : "load", (long long)((t1 - t0) / 1000), hit, rep.nbooks, rep.truncated, rep.depth_skipped, (long long)index_bytes(root)); } else if (strcmp(mode, "statpaths") == 0) { /* 冷缓存 stat 指定路径(不做任何 readdir/枚举,避免预热):argv[2..] 都是路径。 * 设备用法示例:drop_caches 后 probe statpaths ... */ struct stat st; int cnt = argc - 2; t0 = now_us(); for (int i = 2; i < argc; i++) (void)stat(argv[i], &st); t1 = now_us(); printf("PROBE statpaths_ms %lld npaths=%d\n", (long long)((t1 - t0) / 1000), cnt); } else if (strcmp(mode, "mtime") == 0) { struct stat st; int rc = stat(argv[2], &st); printf("PROBE mtime rc=%d sec=%lld nsec=%ld\n", rc, (long long)st.st_mtime, (long)st.st_mtim.tv_nsec); } else if (strcmp(mode, "size") == 0) { printf("PROBE idx_bytes %lld\n", (long long)index_bytes(root)); } else { fprintf(stderr, "未知 mode: %s\n", mode); return 2; } return 0; }