15 KiB
调试与日志
bring-up 需要四类证据:串口输出、屏幕诊断页、panic 屏幕和构建报告。把它们一起收集。
USB 调试终端
来自 giulia 移植的电脑端工具已整合为 scripts/debug_terminal.py 和 Windows CMD 启动器 scripts/debug-terminal.cmd,支持 CDC 日志采集、有界诊断命令、按主题导出日志及 GSC1 帧缓冲解码。需要 Python 3.10 或更新版本。交互编辑与 PTY 键盘输入需要 Windows CMD;脚本采集可在 Windows、Linux 和 macOS 上运行。
python -m pip install -r scripts/terminal-requirements.txt
python scripts/debug_terminal.py --list
python scripts/debug_terminal.py --port COM21 --command collect --seconds 20
# Linux 示例,请使用实际端口:
python3 scripts/debug_terminal.py --port /dev/ttyACM0 --command status --seconds 5
Windows 交互模式运行 scripts\debug-terminal.cmd --port COM21,输入 :help 查看命令。可以显式指定端口,或同时提供完整 USB 身份参数:giulia 开发设备使用 --vid 0x1209 --pid 0x0001 --usb-serial ACE5DBG001。未提供完整身份或匹配到多个设备时不会自动连接。工具使用 115200 波特率并置位 DTR;连接前关闭其他占用端口的程序。
| 命令 | 结果 |
|---|---|
all / collect |
排队发送有界查询;collect 额外请求 replay/sample,至少 15 秒后保存 |
logs wifi / logs gpu / logs panic |
筛选保留的会话日志,记录对应会话的硬件观测 |
save |
导出主题文本与会话元数据 |
watch status 2 / unwatch |
轮询只读状态查询 / 停止轮询 |
screen.start / screen.stop |
打开本机浏览器投屏 / 停止帧传输 |
screen.snapshot |
设备提供帧缓冲后保存真实 PNG |
shell |
进入设备 PTY;Ctrl+] 返回诊断模式 |
reboot.fastboot |
请求设备同步磁盘后重启 |
会话保存到 out/terminal/<时间戳>/,包含轮转的 device-*.log、主题文本、session.json 和独立屏幕图片。主题导出保留最近 20,000 条完整日志行,屏幕数据包与文本日志分离。浏览器保留最后一张真实画面,启用可选鼠标/键盘控制前检查设备输入能力。
python scripts/debug_terminal.py --port COM21 --command screen.start --seconds 60 --max-size 695 --max-fps 1
python scripts/debug_terminal.py --port COM21 --command reboot.fastboot --fastboot-serial DEVICE_SERIAL --seconds 20
Fastboot 验证需要 PATH 中有 fastboot,收到内核重启确认且 CDC 断开后,精确匹配指定的设备序列号。
设备兼容性
这些工具运行在电脑端,不会升级设备内核。初始后端实现 giulia 协议 2 命令集、GIULIA shell/输入/重启标记和 GSC1 屏幕格式(最多 1264 × 2780 RGB565 像素)。其他板型需要实现兼容协议或增加对应后端,修改 USB ID 本身不提供协议兼容性。板级驱动、地址、启动参数和守护进程安装保留在设备仓库,参见 giulia 终端实现与配置。
基础内核可能仅支持 help、ping、status、replay;扩展查询需要匹配的终端实现。投屏需要帧缓冲生产者,远程输入需要设备 HID 提供者。PTY shell 需要开发启动参数和已安装的守护进程/shell,尚未实机验证。MMIO 命令保留 giulia 后端的低地址外设窗口及显式开发开关;时钟、映射和支持操作以设备说明为准。GPU/HFI 命令需要对应开发内核。设备会报告不支持的命令,安装电脑工具不会启用这些能力。
导入代码来自 UniXNU-OnePlus-Ace5 的 694b39e,并包含本地终端/投屏改动。电脑端验证:
python -m unittest discover -s tests/terminal -v
可选 C 发送端互操作测试需要设置 GIULIA_SCREEN_FIXTURE,指向设备仓库屏幕测试脚本生成的样本。默认测试使用合成数据包和模拟串口。硬件状态通过设备检查记录。
日志来源
| 来源 | 通道 | 用途 |
|---|---|---|
| UEFI 串口 | UART | 固件、UniXNU、DarwinBoot |
| 内核串口 | 由 serial= 选择的 UART |
XNU 控制台和 panic 输出 |
| 诊断页 | framebuffer | 控制台停止后的实时状态 |
| 脉冲条 | framebuffer 底部 | 诊断页占用面板时的心跳 |
| panic 屏幕 | framebuffer | panic 文本和寄存器 |
| QEMU 日志 | 文件 | UniXNU 和加载器测试 |
| 构建报告 | JSON | 输入哈希和阶段结果 |
串口
把参数加入负载:
[payload]
kernel_args = ["-v", "serial=3", "debug=0x14e"]
UniXNU 启动管理器写到第一个 EFI_SERIAL_IO_PROTOCOL,行以 UNIXNU: 开头。DarwinBoot 的行以 DARWINBOOT: 开头。
抓取串口:
tio -b 115200 -d 8 -p none -s 1 /dev/tty.usbserial-0001 -L out/serial.log
其他工具:
picocom -b 115200 /dev/tty.usbserial-0001
screen -L /dev/tty.usbserial-0001 115200
minicom -D /dev/tty.usbserial-0001 -b 115200
设备页面记录 UART 引脚和波特率。支持 USB device 模式的板子可以暴露 CDC ACM 串口。
常用的早期输出:
UNIXNU: UniXNU 0.1.0 starting as firmware boot option
UNIXNU: config: loaded \EFI\UniXNU\unixnu.toml
UNIXNU: driver ...: started
UNIXNU: menu: shown 2 entries, default 0
DARWINBOOT: loader v2 installed EL1
DARWINBOOT: ARM64 va=... pa=... args=... entry=... owned=...
DARWINBOOT: inputs validated; exiting boot services
屏幕诊断页
诊断页是绘制到 XNU framebuffer 映射上的全屏文本覆盖层。启用方式:
boot-progress=1
级别 1 启动监视器。30 秒没有阶段或存储活动,或达到 120 秒观察上限时,页面冻结并保留屏幕,便于拍照。
boot-progress=2
级别 2 连续运行。超过时限只报告,系统继续运行。
页面使用 48 行、120 列。面板至少 2160x1400 时缩放为 3。
| 行区域 | 内容 |
|---|---|
| 标题 | 构建标签、live 或 stop 状态、原因 |
| 1 | 时间、空闲、IRQ 计数、监视器计数、root 状态、进程数、空闲页 |
| 2 | 存储请求、已验证块、失败、完整性计数 |
| 3 到 30 | 阶段历史、进程状态、存储细节、最后 PC 和寄存器 |
| 31 到 47 | 缓存存储输出、UFS 计时、内存、进程采样、UI trace、触控行 |
| 47 | 独立脉冲条 |
给镜像加标签,拍照后可以确认构建:
sheng-build-tag=ks-001
标题栏会以 [ks-001] 开头。
用户态接管显示并停止扫描输出后,可以收回面板:
boot-diag-resume=10
10 秒没有 scanout 后页面重绘。新的有效 scanout 会再次让出面板。
页面字段:
| 字段 | 含义 |
|---|---|
time |
监视器启动后的秒数 |
idle |
距上次阶段或 I/O 进展的秒数 |
IRQ |
监视器统计的中断入口数 |
monitor |
监视器线程迭代数 |
lost |
丢弃的记录更新数 |
root |
owner root 接受状态 |
procs |
进程数 |
free |
空闲 4 KiB 页 |
requests |
存储请求 |
verified4K |
已验证 4 KiB 块 |
failures |
存储失败 |
integrity |
存储完整性失败 |
last PC |
最后中断入口 PC |
TCR、TTBR、SCTLR |
翻译控制和页表基址寄存器 |
脉冲条
脉冲条位于面板底部,高 32 像素,2 倍缩放,绿色文字,由独立高优先级监视线程每秒更新。
P<turn> I<irq> M<monitor>/<seconds> B<bind>:<result> d<depth> @<base> lock<busy>/<render> PC<pc>
| 字段 | 含义 |
|---|---|
P |
脉冲轮次 |
I |
中断入口数 |
M |
监视器计数和距上次推进的秒数 |
B |
framebuffer 绑定次数、结果和深度 |
@ |
framebuffer 基址 |
lock |
记录器和渲染器忙碌标志 |
PC |
最后中断入口 PC |
诊断页占用面板时脉冲条持续存在。OS 接管显示后它关闭。boot-diag-resume 会重新打开诊断页和脉冲条。
panic 屏幕
负载可以把 panic 屏幕打进内核副本:
[payload]
panic_screen = true
负载步骤运行:
python3 scripts/patch-xnu-entry-probe.py <kernel> --panic-screen --no-reset
panic 屏幕把格式化后的 panic 消息和寄存器写到 framebuffer,并保持显示。--no-reset 停止自动复位,屏幕保留到拍照完成。
交接时的绿色条
顶部绿色条由 DarwinBoot 的 DarwinEnter 在 ExitBootServices 之后绘制,随后才执行缓存维护、关闭 MMU 和跳转 XNU。画面停在这里时,需要入口探针或串口日志定位后续阶段。仅凭绿色条,原因未查明。
- 记录最后的
DARWINBOOT:日志,包括内核入口地址、显示几何信息、命令行和 CPU 状态。确认所选内核和 ADT 对应同一设备及 OS 配置。 - 对
scripts/patch-xnu-entry-probe.py支持的内核,在设备配置中设置[payload] panic_screen = true,运行./unixnu payload和./unixnu bootimg,再启动新镜像。检查logs/payload-patch.log中的探针安装结果。 - 查看绿色条下方的蓝色和品红色条。蓝色表示进入 MMU 关闭状态下的内核入口,品红色表示到达开启 MMU 前的位置。缺少探针画面时,检查记录的 framebuffer 基址和行跨度。
- 使用
entry_probe_reset_delay = 6设置开启 MMU 后的定时复位检查点。在设备移植记录中保存色条、复位时间、串口输出和payload.json。
配置修改未体现在启动镜像中时,按应用启动参数检查。
颜色条
早期启动会在 framebuffer 上画三条 1/16 屏幕高度的色条。正常方向下从上到下:
| 色条 | 绘制者 | 含义 |
|---|---|---|
| 绿色 | DarwinBoot DarwinEnter |
已退出 boot services,进入跳转内核前的加载器交接阶段 |
| 蓝色 | 入口探针阶段 1 | 内核入口代码已执行,MMU 仍关闭 |
| 品红 | 入口探针阶段 2 | 内核到达打开 MMU 之前的位置 |
入口探针把每条画在绿色条下方。阶段 3 没有色条:它在 MMU 打开后等待 --reset-delay 秒再复位。过了这段时间设备重启,说明 MMU 已打开。
入口探针复位
内核还不能绘图之前,重启是唯一的信号。entry_probe_reset_delay 保留入口探针的复位:打过补丁的内核在 MMU 打开后等待这么多秒,然后用 PSCI SYSTEM_RESET 复位。过了这段时间设备自己重启,说明 CPU 已经通过 MMU 打开这一步。
[payload]
entry_probe_reset_delay = 6
entry_probe_reset_after = "_arm_vm_init" # 可选:改为在这个调用之后复位
entry_probe_reset_in = "_arm_init" # 发出调用的函数(默认值)
entry_probe_reset_occurrence = 1 # 第几次调用(默认 1)
entry_probe_reset_before = false # 在调用点复位,不执行调用,也不等待
设置 entry_probe_reset_after 后,复位移到该调用之后,一次运行只回答一个问题:内核有没有走过这个调用。这些键对应板级 patch-xnu-entry-probe.py 的 --reset-delay、--reset-after、--in、--occurrence 和 --reset-before,可以和 panic_screen 同时使用。不设置 entry_probe_reset_delay 时,负载仍传 --no-reset。
第一条 panic 消息也会复制到 boot_progress_panic_text。secondary core 上的 panic 只停住该 core,实时横幅在屏幕重绘后仍保留文本。
设备树 panic 区域需要:
CONFIG_XNU_DT_PANIC_REGION=y
内核读取 /chosen 的 embedded-panic-log-size 和区域的 reg 属性,映射前先校验。
用户态故障证据
故障探针在异常投递前记录 EL0 breakpoint 和 abort 状态:
| 字段 | 含义 |
|---|---|
pid、name |
进程 |
PC、LR、SP、FP |
故障时寄存器 |
ESR、FAR |
异常综合征和故障地址 |
slide |
shared region slide |
x0 到 x28 |
通用寄存器 |
| frames | 最多 12 个返回地址 |
捕获在 trap 路径中完成,不分配内存。匹配的进程退出时,记录随信号打印。未捕获异常的原因会从退出进程的可写 malloc 区域中搜索并打印到崩溃行。
UI 栈追踪
UI trace 采样指定进程,打印 PC、LR、FP 和栈切片:
boot-ui-focus=launchd
boot-ui-focus2=SpringBoard
boot-ui-focus3=backboardd
boot-ui-focus4=Setup
boot-ui-chrono=1
地址使用 64 进制字母表编码,以适配行宽。把抄录的行交给解码器:
python3 scripts/decode-boot-ui-trace.py trace.txt
解码器为每个线程组输出一个 JSON 对象,包含 pc、lr、帧和错误码。
trace 每 8 秒轮换一页。间隔几秒拍三到四张照片,覆盖全部页面。
拍照
诊断页按拍照场景设计。建议流程:
- 把
sheng-build-tag设为简短的镜像标签。 - 冻结页面用
boot-progress=1,实时页面用boot-progress=2。 - 用户态会清屏时设置
boot-diag-resume。 - 把设备放在支架上,相机与面板平行。
- 关闭闪光灯,从侧面打光,避免反射。
- 对标题栏锁定焦点和曝光。
- 让面板填满画面,包含四个角。
- 先拍标题栏,再拍整页。
- UI trace 页面间隔 8 秒拍三到四张。
- 拍底部脉冲条。
- 记录启动镜像 SHA-256 和拍摄时间。
| 检查项 | 原因 |
|---|---|
| 标题栏可读 | 构建标签和状态标识镜像 |
| 文字区域无眩光 | 第 3 到 30 行承载阶段历史 |
| 四角可见 | 确认完整面板和缩放 |
| 脉冲条可见 | 显示拍摄时的心跳 |
| 至少两张 | 变化的计数器说明屏幕在实时更新 |
相机照片是面板显示内容的证据。保留原始文件。只抄录需要的行,并把照片与抄录一起保存。
QEMU 抓取
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/qemu.png \
--expect 'UNIXNU: menu: shown'
运行器把去掉 ANSI 的日志写到 out/qemu.clean.log。--screenshot 在菜单出现时截屏。--keys 在菜单后发送 QEMU 键码。
停止辅助脚本在指定符号处记录寄存器和内存:
python3 scripts/qemu-entry/gdb-stop.py --help
调试参数
| 参数 | 用途 |
|---|---|
-v |
详细内核输出 |
serial=3 |
串口控制台 |
debug=0x14e |
调试输出掩码 |
kextlog=0xfff |
kext 日志 |
boot-progress=1 |
诊断页,30 秒停滞后冻结 |
boot-progress=2 |
诊断页连续运行 |
sheng-build-tag=<tag> |
标题栏镜像标签 |
boot-diag-resume=<seconds> |
OS 停止 scanout 后收回面板 |
sheng-platform-stage=<n> |
在平台专家阶段停住 |
sheng-touch=<n> |
触控探针阶段 |
offline-legacy-phase=<n> |
显示探针阶段 |
-no_slto_panic |
关闭 SLTO panic |
cs_debug=<mask> |
代码签名输出 |
证据包
收集:
| 项目 | 来源 |
|---|---|
| 串口日志 | UART 抓取文件 |
| 诊断照片 | 标题栏、整页、trace 页、脉冲条 |
| panic 照片 | panic 屏幕 |
| 启动报告 | out/<device>/<os_profile>/<config>/boot/boot.json |
| 负载报告 | out/<device>/<os_profile>/<config>/payload/payload.json |
| 构建日志 | out/<device>/<os_profile>/<config>/logs/ |
每张照片都记录启动镜像哈希:
shasum -a 256 out/<device>/<os_profile>/<config>/boot/boot.img
jq . out/<device>/<os_profile>/<config>/boot/boot.json