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

225 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<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 目标。