Files
embedded-app-pc-debug/references/application-debugging.md
T

120 lines
13 KiB
Markdown

# 应用模块、启动流程与参数调试
仅在任务涉及应用模块部署、NFS、`DxMain`、开机进程、工厂模式或 param 文件时读取本文件。所有主机、用户名、设备、路径和进程名都以当前任务实查为准,示例不能变成跨用户或跨型号默认值。
## 连接与身份
1. 新设备任务开始前枚举 Windows 串口,让用户选择串口或 Telnet;即使只有一个 USB 串口也不自动打开。串口默认 `115200 8N1`、无流控,但允许用户覆盖。
2. 用户选择串口时复用 `serial_web_console.ps1` 的单进程收发服务。部署工具只能连接状态为 `isOpen=true` 且 COM 口与本次选择一致的网页控制台,不能再次直接打开串口。
3. 串口打不开、无数据、乱码或无法登录时先报告事实,再建议 Telnet。不要静默切换;Telnet 前重新广播核对完整 SN、型号和当前 IP。
4. 网络故障时,用户确认 COM 口后允许串口只读诊断。写文件、停止进程、修改启动脚本或重启前,还需用户确认这个 COM 口对应的完整 SN 和型号。固件升级始终需要协议身份核验。
5. Telnet 登录名和密码从准确的当前工程 `product.json` 读取,并在可用时核对固件生成的 `/etc/passwd` 或 `/etc/shadow`。不要混用 Web、ONVIF、RTSP、Wi-Fi 或烧录平台密码;认证失败后停止,不连续猜测。
## NFS 与存储
NFS 服务器 IP 和用户名按用户或任务变化。当前任务尚未确认时询问;已经确认且仍可达时可以继续复用。常见形式仅用于帮助识别:
```sh
mount -t nfs <服务器IP>:/nfs/<用户名> /mnt/nfs -o nolock
```
挂载前确认服务器可达、导出路径正确、挂载点存在,并检查是否已有不同来源挂载。复制前记录源文件的绝对路径、大小、权限、`file` 结果和 SHA-256。
只有 `/proc/mounts` 或 `findmnt` 明确显示为独立可写挂载时才把目录称为 SD 卡。存在 `/mnt/sdcard` 空目录不等于存在 SD 卡;真实 SD 卡可用时优先作为调试目录,否则选择具有足够空间的 eMMC 等可写分区。默认保留 NFS 挂载、调试文件、备份和日志,只有用户明确要求恢复或清理时才删除或卸载,并列出所有遗留文件。
## 普通模块部署
1. 确认源二进制与设备的 ELF 架构、ABI、动态加载器和动态库要求匹配。服务器本机 x86 编译成功不能作为设备可运行证据。
2. 在目标文件同目录写入唯一临时文件,复制后校验 SHA-256 和权限,再用同文件系统内的原子重命名替换。覆盖现有文件前保存带时间戳的备份。
3. 只停止任务明确指定的进程。部署后检查程序实际启动、观察期内没有立即退出或崩溃,并验证用户指定的功能或日志标记。普通一次性 demo 可以退出码 0 和预期输出为成功;常驻进程还需持续存活。
4. `EmxCoreServer`、`EmxMediaServer`、`EmxModulesServer` 等可能触发设备强制重启的关键服务默认拒绝在线替换;必须由任务明确指定并单独确认。
5. 使用 `scripts/module_debug.py` 时默认只预检。实际挂载、复制、停止或启动进程必须显式传入执行开关;脚本的开关不能替代用户授权。
6. Telnet 模式会先调用同目录的 V2 搜索工具,从所有可用活动 IPv4 网卡重新广播(包含虚拟/VPN 网卡,排除回环和链路本地地址),完整 SN、型号和广播返回 IP 唯一匹配后才允许登录;已知 IP 的 30101 查询是第二道核验,不能替代广播。
7. NFS 已挂载时必须与本次确认的服务器和导出路径完全一致。源程序必须通过 ELF 架构和解释器检查;设备缺少 `readelf` 时默认停止,只有人工看过有限检查结果后才可显式放行。
8. 在线停止已有进程时必须提供从当前设备实查得到的失败恢复命令。脚本会在替换失败时尝试恢复原文件和原进程,并在设备 `/tmp/codex-module-<模块>-<动作>-<时间>.state` 记录事务阶段;出现命令超时、会话中断、掉电或 `ROLLBACK_FAILED` 时状态未知,禁止直接重试,必须重新核对设备身份后读取事务状态,检查进程、目标哈希和备份,不能宣称已自动恢复。
### NFS 联调的配置与运行证据
- 替换前记录现有程序路径、PID、启动参数、配置工作目录、必要环境开关、动态库来源和恢复方式。用户手工调整的上线条件、持续运行设置、服务器地址等配置保持不变;不能因重新部署二进制而用工程默认文件覆盖。仅在用户明确要求修改某字段时最小化更改该字段,记录配置哈希与差异,不连带修改其他设置。
- 新版本沿用当前任务确认的启动方式和配置目录;配置依赖相对路径时,不能只切换到 NFS 程序目录启动。核对动态加载器、库路径、必要 `.so` 和符号链接,不能因文件已复制或程序短暂出现就认为依赖齐全。
- 启动后记录 PID,并在可用时通过 `/proc/<pid>/exe`、`cmdline`、`cwd` 核对实际运行文件、参数和工作目录;计算对应部署文件的 SHA-256,必要时检查 `maps` 中实际加载的库来源。不能仅凭进程名、旧日志标记或启动命令返回 0 判断新程序已生效。
- 观察常驻进程持续存活及目标业务结果,区分构建、拷贝、启动和功能验证的结论。已有配置未要求变更时核对前后哈希一致;未验证的功能明确列为待验证,不扩张为整机或全部业务通过。只重启明确授权的模块,不把整机重启或其他关键服务重启作为默认联调步骤。
## DxMain 与 daemon.sh
不要在 `daemon.sh` 或其后台软件看门狗仍运行时停止 `DxMain`。守护逻辑可能重新启动 `DxMain`,关键服务丢失时还可能执行强制重启。
在线替换顺序:
1. 从当前设备脚本和进程表精确定位 `daemon.sh` 主进程及其后台 `SoftwareWatchdog` 子进程。
2. 先发送 `TERM`,等待并检查;仍残留时记录 PID 后发送 `KILL`。父子守护进程全部消失前不能停止 `DxMain`。
3. 对 `DxMain` 使用相同的 `TERM`、等待、必要时 `KILL` 顺序。发现设备重启、会话断开或守护/应用重新出现时立即停止部署并保留日志。
4. 备份、原子部署并运行调试版本。调试结束后恢复原程序,重新启动正确参数的守护脚本,并确认 `DxMain` 与守护进程均恢复。
使用部署脚本在线停止 `DxMain` 时,必须同时传入本次实查的 `daemon.sh` 绝对路径和完整恢复命令。脚本按绝对路径最多接受一个 daemon 匹配,并且最多接受一个 `SoftwareWatchdog`;看门狗必须以该 daemon 为父进程,父子关系不匹配或出现多个候选时都拒绝结束任何进程,先人工核对进程树。
开机调试前读取当前设备的 `appinit.sh` 和 `allstart.sh`。部分工程会优先运行 `/root/configs/allstart.sh`、其次 `/mnt/emmc/allstart.sh`、最后固件默认脚本,也可能原生支持 `/mnt/emmc/DxMain`;这些都是按工程确认的实现,不是通用常量。两个启动覆盖文件同时存在时停止并报告遮蔽关系,不自行选择。
## 开机进程与重启验证
默认固件分区可能是只读 SquashFS。需要改变开机进程时,优先使用当前 `appinit.sh` 已支持的可写覆盖路径,并遵守:
1. 记录默认脚本、现有覆盖文件、挂载属性、权限和 SHA-256。
2. 修改现有覆盖文件前保存时间戳备份;从默认脚本创建覆盖时保留权限。
3. 只修改目标启动项,新脚本先执行 `sh -n`,写入后校验 SHA-256。
4. 如果两个候选覆盖文件会互相遮蔽,停止并让用户选择保留哪一个。
5. 重启前串口必须已经打开并确实收到数据,同时给出可以通过串口执行的恢复命令。完成修改不等于取得重启授权;集中展示变更、备份和预期后等待明确的“确认重启”。
6. 重启后通过串口观察完整启动过程,核对目标进程、守护进程、工厂程序、异常退出、panic、看门狗和循环重启;网络恢复后再使用协议或 Telnet 辅助验证。
## 运行中恢复网络服务
网卡在开机阶段缺失时,依赖它的服务可能已经退出,也可能作为其他主进程内的线程存在。不要只用 `ps | grep <服务名>` 判断服务是否存在;同时检查启动日志、监听端口、Launcher 配置和当前工程实现。
在已经运行的整机上单独启动或重启 NetServer 等网络服务前,先读取 `daemon.sh`、WatchDog 逻辑和关键进程关系。消息总线重连、关键服务短暂断开或守护检测可能触发整机重启。未经明确授权不要用在线重启服务试探;需要恢复升级通道时,优先采用影响范围更小且可回退的临时步骤,例如加载已验证驱动、恢复已确认的产品 MAC、关联和 DHCP。
临时网络恢复也要遵守以下边界:
1. 先通过串口确认设备身份、当前版本和产品网络配置,再执行会改变接口状态的命令。
2. 加密网络 param 只在唯一 `/tmp` 副本上解密;从副本提取当前设备已有的 SSID、PSK 和 MAC,不把明文打印到终端摘要、任务消息或 skill。
3. 只创建临时 wpa_supplicant 配置,任务结束时删除;需要持久写回配置时按 param 修改流程重新取得授权。
4. 记录每个阶段的证据:驱动加载、接口创建、MAC、关联、租约、路由、监听端口和 PC 可达性。
5. 临时恢复后的升级仍必须通过标准协议身份预检;不得把串口身份作为跳过 `30101` 的理由。
6. 新固件升级后必须重启验证自动恢复;如果还需要任何手工步骤,修复不能判为通过。
## 烧录状态与工厂模式
未烧录设备可能自动进入产测流程;已烧录设备通常进入正常流程,只有强制工厂标志生效时才运行产测程序。具体判断可能使用 MAC/SN 文件、文件大小、加密烧录数据、环境变量或标志文件,必须从当前工程和设备的 `function.sh`、`appinit.sh`、`allstart.sh` 实查。
不要把某个版本的文件名、大小阈值、返回码或产测程序写成所有设备的规则。检查到未定义变量、错误文件路径或判断反转时报告证据并给出补丁,不自动修改设备。只有用户明确要求进入或退出工厂模式时,才允许创建、修改或删除工厂标志、烧录信息或启动覆盖文件。
## 加密 param 文件
设备 param 所在分区可能可读写,但内容需要 `DevTools` 解密。先识别格式,不能根据 `.json` 后缀直接调用解密:
- 文件以 `{` 或 `[` 开头且能解析为 JSON:已经是明文,直接读取。
- 内容符合当前工程加密参数格式:使用当前固件匹配的 `DevTools`。
- 格式未知:查找对应模块的专用工具和源码,不试错解密。
部分实测版本的命令为:
```text
DevTools param dec <文件路径>
DevTools param enc <文件路径>
```
命令必须从当前设备的帮助、二进制字符串或工程源码重新核实。不要只看退出码;某些版本对不支持的命令仍返回 0,输入格式错误时还可能段错误并把目标截断为空文件。
只读检查默认流程:
1. 记录原始加密文件的路径、权限、大小和 SHA-256。
2. 复制到唯一的 `/tmp` 文件并用 `cmp` 确认副本一致。
3. 只对副本执行解密。成功后验证文件非空且为合法 JSON,再读取内容。
4. 原文件前后重新计算 SHA-256,必须一致。解密失败、段错误或产生空文件时立即停止,不用同一源文件连续重试。
5. 明文副本默认保留到当前调试任务结束,随后删除并记录;用户明确要求保留时不删除。
如果工具只能原地工作,解密读取后必须立即重新加密,并通过备份和哈希确认恢复;能使用 `/tmp` 副本时不要原地解密。
修改 param 时全程操作临时副本:解密、只改指定字段、验证 JSON 和字段差异、重新加密,再复制一份加密结果解密复查。写回前展示设备身份、原路径、备份路径、字段差异和校验值,等待明确“确认写入”。确认后备份原始加密文件,在同目录写临时文件并原子替换,恢复权限、`sync`,再解密复查。失败时保留原文件,不继续服务重载或重启。
同目录的 active、`.def` 和 `.bak` 文件作用不同。默认只操作任务明确的 active 文件;默认值与备份只用于比对,除非用户明确要求同步修改。参数写回、服务重载和整机重启是三个独立授权阶段。优先调查进程是否支持重新加载;必须重启时仍需单独确认。