Files
MeowOS-For-R3P2/scripts/build/DEVKIT.md
T
AskaEth b52e6a08bc feat(devkit): M24 静态 MIPS32r2 调试工具集 — vmstat/slabtop/free/ps/pmap/lsof
TF 卡 MeowOS/opt/devkit 用的交叉编译流水线:zig 0.17 musl 静态链接,
产物 fw_apps/devkit/{bin,MANIFEST.txt,licenses,README.txt}(gitignore 内)。

- build_devkit.sh:源码固定 版本+sha256+URL(ncurses 6.5 / procps-ng 4.0.4 /
  lsof 4.99.4),缓存命中零联网;冷启动 3 次下载约 80s,重跑 0 次联网约 65s,
  stamp 命中 1s 跳过;--force/--clean/--offline/--with-sources/--verify-only
- 自编静态 ncursesw 只为 slabtop(procps 的 slabtop 硬依赖 ncurses);
  terminfo 目录钉到设备 /usr/share/terminfo,交互式 slabtop 可用
- 逐项自检 file/readelf(static、ELF32、mips32r2、0 NEEDED),任一不过即失败
- 不交付 top(真机输出不可信)、htop(卡上已有 3.5.3 且我们的更大)
- DEVKIT.md:许可证留痕(procps=GPL-2+ 需提供源码、ncurses=MIT、lsof=Purdue)、
  可行性实测、上卡 PATH 与 busybox 同名关系、真机验证输出
2026-11-06 19:05:15 +08:00

15 KiB
Raw Blame History

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. 上卡后怎么用

# 临时(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/<pid>/smaps 不存在, 所以 pmap -x 拿不到 PSS/私有脏页。它仍有用的一点是 /proc/<pid>/maps 存在, pmap <pid> 能列出地址段 + 映射文件名(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 <ncurses.h>)与 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/<pid>/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 目标。