# MeowOS devkit — 静态 MIPS32r2 调试工具集 TF 卡 `MeowOS/opt/devkit/bin/` 里那批静态二进制的构建说明、可行性实测、许可证留痕。 构建入口:`bash scripts/build/build_devkit.sh`(产物在 `fw_apps/devkit/`,已被 `.gitignore` 的 `fw_apps/` 规则覆盖,不入库)。 ## 1. 为什么要有这个 设备(HiBy R3 Pro II / Ingenic X1600 / MIPS32r2 / Linux 4.4.94+ / 单核 / 56 MB)自带 busybox 1.31.1,`free / ps / top / lsof` 都是指向 `/bin/busybox` 的软链,字段残缺; `vmstat`、`slabtop`、`pmap` 干脆没有。做内存分析时最缺的正是: - `slabtop` —— Slab 总量 ~11 MB 到底被谁占了(buffer_head / dentry / squashfs_inode…) - `vmstat` —— 系统级趋势(r/b、si/so、in/cs、us/sy/id/wa),busybox 无替代 - 真版 `lsof` —— busybox lsof 只有 pid/fd/设备号,没有 SIZE/OFF、TCP 状态、NAME 所以本工具集只围绕「这台机器的真实调试痛点」,不做大而全。 ## 2. 产物清单 全部:`ELF 32-bit LSB executable, MIPS, MIPS32 rel2, statically linked, stripped`, `readelf -d` 零 `NEEDED`(musl libc 静态链入),可直接从 exFAT 卡上跑。 | 工具 | 来源版本 | 许可证 | 体积(B) | 用途 / 相对 busybox 的增量 | |---|---|---|---|---| | `slabtop` | procps-ng 4.0.4 | GPL-2.0-or-later | 394 408 | Slab 明细;`-o` 一次性输出不需要 TTY/terminfo | | `vmstat` | procps-ng 4.0.4 | GPL-2.0-or-later | 216 924 | 系统趋势;busybox 无 | | `ps` | procps-ng 4.0.4 | GPL-2.0-or-later | 324 980 | `-eo pid,rss,vsz,comm --sort=-rss` 等完整字段 | | `pmap` | procps-ng 4.0.4 | GPL-2.0-or-later | 173 460 | 地址空间映射(读 `/proc/pid/maps`);busybox 无 | | `free` | procps-ng 4.0.4 | GPL-2.0-or-later | 94 312 | 拆 buff/cache、available | | `lsof` | lsof 4.99.4 | Purdue/BSD-style | 789 496 | SIZE/OFF、TCP 状态、NAME、`-i` 网络视图 | | | | **合计** | **1 993 580 ≈ 1.9 MB** | | 同目录还有构建时生成的 `MANIFEST.txt`(每个二进制的 sha256)、`licenses/`(上游 COPYING)、 `README.txt`(上卡说明)。`--with-sources` 会额外生成 `src/`(源码包,GPL 合规用)。 ## 3. 上卡后怎么用 ```sh # 临时(SSH 会话) export PATH=/usr/data/mnt/sd_0/MeowOS/opt/devkit/bin:$PATH # 持久化:rootfs 的 /etc/profile.d/meow_card_path.sh 只前插了 MeowOS/opt/bin, # devkit 是独立目录,需要再补一行(由塔/维护者决定是否进固件): if [ -d /usr/data/mnt/sd_0/MeowOS/opt/devkit/bin ]; then export PATH="/usr/data/mnt/sd_0/MeowOS/opt/devkit/bin:$PATH" fi ``` **与 busybox 同名命令的先后关系**(前插 PATH 时): | 名字 | 现状 | 前插后 | |---|---|---| | `free` `ps` `lsof` | `/usr/bin/*` 均是指向 `/bin/busybox` 的软链 | 解析到 devkit 版(功能更多) | | `vmstat` `slabtop` `pmap` | 不存在 | 新增 | | `top` | busybox 软链;**本工具集不提供 top**(见 §7) | 仍为 busybox | 要临时用回 busybox 版:`/bin/busybox free`、`busybox ps`、`busybox lsof`。 不要覆盖 `/usr/bin` 里的软链:rootfs 是 ubifs,随固件回刷即被还原,而且这些软链是基线的一部分。 ## 4. exFAT 语义(权限 / 软链) 设备挂载:`/dev/mmcblk0 on /usr/data/mnt/sd_0 type exfat (rw, fmask=0022, dmask=0022, symlink=0, noexec 未设置)`。 - **无符号链接**(`symlink=0`):所以本工具集全部是实体文件,不靠 `argv[0]` 分派, 也不提供任何 symlink(不像 busybox 那样一个多合一二进制 + 一堆软链)。 - **无 Unix 权限位**:`fmask=0022` 让卡上新文件恒为 `0755`,因此**不需要 chmod**; 反过来说,卡上也表达不了 `4755` 之类,任何需要 setuid 的工具都不适合放这。 - **可执行性**:挂载参数里没有 `noexec`,且卡上现有 `MeowOS/opt/MeowOS…/meowpanel` 就是 0755 直接执行的。真机验证时我们把同一批二进制放在设备 `/tmp` 跑通(见 §9)。 未在卡上实测执行,是因为本次任务约定「不往卡上写任何东西」(塔来推)。 ## 5. 重跑代价与可复现性 - **联网次数**:冷启动 **3 次**(ncurses / procps-ng / lsof 各一个 tarball,共 5.5 MB); 源码包按 sha256 校验后落在 `$MEOW_DEVKIT_WORK/src/`,此后**重跑 0 次联网**。 - **耗时**(8 核宿主实测):冷启动(含 3 次下载)**80 秒**(两次实测 80s / 86s); 源码已缓存时全量重编约 **85 秒**;`stamp` 命中(脚本与版本都没变)时 **1 秒**直接跳过; `--verify-only` 秒级,且不需要 zig。 - **可复现(逐字节)**:在两个不同工作区各建一次,六个二进制 **sha256 完全相同** (`ps` 用 `-ffile-prefix-map` 抹掉绝对源码路径;`lsof` 用裸名 `CC=zigcc` + 相对源码路径, 不再把工具链/构建目录写进 `LSOF_CC/LSOF_CCFLAGS`)。同一份 `MANIFEST.txt` 里的 sha256 与设备上 `sha256sum` 的结果一致,可用来核对「跑的到底是哪个二进制」。 - **可复现性**:三份源码都固定 `版本 + sha256 + URL`,校验不符直接失败;`zig 0.17.x` 版本 也做硬检查。不依赖任何「这次网络恰好成功」的偶然性 —— 离线环境可先拷 `src/*.tar.*` 进工作区再用 `--offline`。 - 工作区默认 `~/.cache/meow-devkit`(`MEOW_DEVKIT_WORK` 可改),不污染仓库。 ## 6. 许可证与分发合规(重点) | 上游 | 许可证 | 分发二进制时的义务 | |---|---|---| | procps-ng 4.0.4 | **GPL-2.0-or-later**(`COPYING`=GPLv2;`library/` 为 LGPL-2.1+) | **必须提供完整对应源码**(GPLv2 §3),或书面的三年有效要约 | | ncurses 6.5 | MIT 风格(X11 许可) | 保留版权与许可声明;不单独分发(只静态链入 slabtop) | | lsof 4.99.4 | Purdue Research Foundation 许可(BSD 风格 4 条) | 文档与源码中保留作者/Purdue 署名;改过的版本要标注 | **结论(不要含糊)**:`build_devkit.sh` 里的固定版本 + 下载 URL **不构成 GPLv2 §3 的合规**。 URL 作者随时可能撤、镜像可能消失,而且 GPL 要求的是*分发者*提供的源码或要约。所以: 1. 若这批二进制随卡交到第三方手上 → 构建时加 **`--with-sources`**,把 `ncurses-6.5.tar.gz` / `procps-ng-4.0.4.tar.xz` / `lsof-4.99.4.tar.xz` 一起放到卡上 `MeowOS/opt/devkit/src/`(合计约 5.5 MB,TF 卡的容量代价可忽略); 2. 或者随卡附**书面要约**(有效期内提供源码,GPLv2 §3(b)); 3. `licenses/` 里的上游 COPYING 只是「许可声明留痕」,**不能替代源码义务**。 内部自用(只有自己插卡)不触发 §3 的分发义务,但既然这台机器的产物可能被拷来拷去, 建议一律 `--with-sources`。 ## 7. 未交付清单(以及为什么) | 候选 | 结论 | 原因 | |---|---|---| | `top`(procps 4.0.4) | **不交付** | 真机实测输出不可信:表头时长字段溢出成 `-343597384:00`,`Mem` 显示 `59.4/0.1 GiB`(MemTotal 只有 55 MB)。同时它在无 TTY 的批处理下会静默 `exit 1`(strace 显示 `ioctl(1,TIOCGWINSZ)=ENOTTY` 后直接 `exit_group(1)`),即使批处理可用也拿不到正确数据。卡上已有 htop 3.5.3,`top` 属重复且不可信 → 不凑数 | | `htop` | 不构建 | 卡上 `MeowOS/opt/bin/htop` 已经是 **3.5.3**(673 744 B)。我们实测用 zig + 自编 ncursesw 也能编出来,但 strip 后 **756 908 B**,比现有版本更大(静态链入 musl + ncurses),没有增量价值 | | `watch` | 不构建 | busybox 1.31 已有 `watch`;procps 版 403 KB,收益不抵体积 | | `pgrep`/`pkill` | 不构建 | busybox 已有;procps 版各 260 KB(链接整份 libproc) | | `uptime`/`tload`/`pidof`/`pidwait`/`w` | 不构建 | busybox 已有 `uptime`/`pidof`;`tload`/`w` 在本机无调试价值 | | `sysctl` | 不构建 | busybox 已有 `sysctl`(读写 `/proc/sys` 足够) | | `perf` / `valgrind` / `gdb` / `ltrace` / `sysstat` / `smem` | 不可行 | 见 §8 | `pmap` 的低价值要说明白:本机 `CONFIG_PROC_PAGE_MONITOR=n`,`/proc//smaps` 不存在, 所以 `pmap -x` 拿不到 PSS/私有脏页。它仍有用的一点是 `/proc//maps` **存在**, `pmap ` 能列出地址段 + 映射文件名(busybox 没有等价物),排查「谁映射了这块内存」够用。 ## 8. 可行性实测记录(编不出来的卡在哪) 宿主:Termux/Android aarch64,zig 0.17.0(自带 musl libc 与头文件),`make`、`curl`、`pkg-config`。 **没有** autoconf/automake/libtool,**没有**目标内核头,sysroot 里只有 zlib(本工具集不需要它)。 - **procps-ng 4.0.4** ✅ 可编。两个必须处理的坑: 1. `slabtop`(`src/slabtop.c` 直接 `#include `)与 `top` 硬依赖 ncurses; `configure --without-ncurses` 会把它们整个跳过 → 要 `slabtop` 就必须先交叉编 ncurses。 2. 交叉编译下 autoconf 的 `malloc(0)/realloc(0)` 运行期探测无法执行,不缓存 `ac_cv_func_malloc_0_nonnull=yes ac_cv_func_realloc_0_nonnull=yes` 会引入 `rpl_malloc/rpl_realloc` 未定义引用,链接失败。 3. 另外必须把 `PKG_CONFIG_LIBDIR` 收紧到自编 prefix,否则会捡到宿主 aarch64 的 `ncursesw.pc`,结果是「musl 头 + bionic 头」混编(`watch` 直接编不过)。 - **ncurses 6.5** ✅ 可编(`--without-shared --with-normal --enable-widec --without-progs` 等)。 **必须** `--with-default-terminfo-dir=/usr/share/terminfo --with-terminfo-dirs=...`: 否则交互式 `slabtop` 会 `Error opening terminal: xterm.`(默认值是编译期 prefix,设备上不存在)。 体积代价:`libncursesw.a` 3.3 MB(未 strip,不单独分发),链进 `slabtop` 后只增加约 100 KB。 - **lsof 4.99.4** ✅ 可编。坑: 1. 4.99 源码包**不带** `configure`(autotools),但带老式 `Configure` 脚本,`./Configure -n linux` 可用。 2. 版本探测读宿主 `/usr/include/linux/version.h` + `uname`,交叉时拿不到 4.4.94, 会退化成 `LINUXV=00000 / LSOF_VSTR="0.0.0"`(Configure 打 "unsupported dialect or version")。 这两个宏在 linux dialect 里只影响 `lsof -v` 显示,仍用 `-DLINUXV/LSOF_VSTR` 覆盖成 4.4.94。 3. zig 的 musl 头没有 glibc 侧经由 `linux/tcp.h` 间接带来的 `TCP_ESTABLISHED` 等 11 个 状态宏,`dsock.c` 会编不过 → 脚本里 `-D` 补齐(值取自 `linux/tcp.h`)。 4. zig cc **不带 `-O` 时按 Debug 编**,会插入 UBSan 桩(`__ubsan_handle_negate_overflow`) 导致链接失败 → 所有编译都显式 `-O2`。 - **htop 3.5.3** ✅ 可编(用自编 ncursesw,`--disable-hwloc/sensors/delayacct`),strip 后 756 908 B —— 比卡上现有版本大,故不交付。 - **perf** ❌ 不可行:依赖目标内核源码树(`tools/perf` 要按内核版本配 `perf_event_attr`), 且设备内核未必开了 `CONFIG_PERF_EVENTS` 相关选项;没有内核头也是硬阻塞。 - **valgrind** ❌ 不可行:没有 MIPS32 后端(valgrind 官方支持 x86/amd64/arm/aarch64/ppc/s390, 无 mips),不是交叉编译问题,是根本没有后端。 - **gdb** ❌ 未尝试/不建议:设备已带 `gdbserver`,宿主侧可以用交叉 gdb 连;在 56 MB 单核设备上 跑 gdb 本体收益极低、体积巨大。 - **ltrace** ❌ 不可行:需要解析目标 ELF 动态符号(`PT_DYNAMIC` + PLT),本机程序多为 静态/精简,且 ltrace 的构建依赖 libelf + 复杂 autoconf 探测,投入产出不划算。 - **sysstat(sar/iostat/mpstat)** ❌ 未做:autoconf 探测多、依赖 `gettext`/`libsensors` 等, 且本机是单核、无块设备统计(`/proc/diskstats` 对 TF 卡意义有限),收益低。 - **smem** ⚠️ 价值为零:依赖 `/proc//smaps` 的 PSS,本机 `CONFIG_PROC_PAGE_MONITOR=n`, 没有 smaps → 拿不到任何它要的数据。 ## 9. 真机验证证据(2026-11-06,root@10.76.208.50) 产物拷到设备 `/tmp/devkit`(tmpfs)执行,验证完已删除;**没有**往 TF 卡写过任何东西。 ``` # free -m total used free shared buff/cache available Mem: 55 30 2 1 27 24 Swap: 0 0 0 # vmstat 1 3 procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu------- r b swpd free buff cache si so bi bo in cs us sy id wa st gu 1 0 0 2584 8764 19388 0 0 0 0 449 6 3 3 94 0 0 0 0 0 0 2644 8764 19388 0 0 0 0 340 566 1 5 94 0 0 0 0 0 0 2644 8764 19388 0 0 0 0 332 526 1 2 97 0 0 0 # ps -eo pid,rss,vsz,comm --sort=-rss PID RSS VSZ COMMAND 1368 4832 16516 meow_launcherd 4241 1608 3012 sh ... # slabtop -o | head -6 Active / Total Objects (% used) : 55852 / 61357 (91.0%) Active / Total Slabs (% used) : 2794 / 2794 (100.0%) Active / Total Caches (% used) : 57 / 81 (70.4%) Active / Total Size (% used) : 11024.86K / 11593.48K (95.1%) Minimum / Average / Maximum Object : 0.02K / 0.19K / 32768.00K OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 12474 10746 86% 0.06K 198 63 792K buffer_head # pmap 1 1: init 00400000 864K r-x-- busybox 004e8000 4K rw--- busybox 00969000 132K rwx-- [ anon ] # lsof -nP -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME dropbear 1354 root 3u inet 2196 0t0 TCP *:22 (LISTEN) meow_netm 1372 root 5u inet 2611 0t0 UDP 127.0.0.1:53 meow_mdns 1375 root 3u inet 2906 0t0 UDP *:5353 meowpanel 1395 root 3u inet 2587 0t0 TCP *:8080 (LISTEN) ``` - 六个二进制 `--help`/`-V` 全部 rc=0(`lsof --help` 不是合法选项,`lsof -v` 正常打印 4.99.4)。 - 交互式 `slabtop`(无 `-o`)在 `TERM=xterm` 下正常初始化 curses(stdout 写出 1599 字节转义序列); 对照 `TERM=bogus-term` 报 `Error opening terminal: bogus-term.` —— 说明 terminfo 路径确实命中 `/usr/share/terminfo`,不是内嵌 fallback。 - 宿主侧静态/架构自检(每个产物):`file` = `statically linked, stripped`; `readelf -h` = `ELF32` / `Machine: MIPS R3000` / `Flags: ... mips32r2`;`readelf -d` 无 `NEEDED`。 可用 `bash scripts/build/build_devkit.sh --verify-only` 复跑。 ## 10. 已知限制 - `pmap -x` 无 PSS(无 smaps),只有 maps 级别的段/文件信息。 - 不提供 `top`(原因见 §7);需要交互式总览请用卡上已有的 `htop`。 - 二进制按设备内核 4.4.94 编 `LINUXV`;换内核/换卡不影响运行(这些宏只影响显示)。 - 产物是 32 位 MIPS32r2(o32 ABI,软浮点/无 FPU 依赖),只适用于 X1600 这类 MIPS32r2 目标。