Skip to main content
控制嵌入式开发中的调试器操作,包括烧录、GDB 服务器管理、内存访问和 RTT 日志。

语法

全局选项

命令

命令参考

gdbserver

启动用于远程调试的 GDB 服务器。这是建立调试连接的主要命令。 Box 根据探针选择后端。J-Link 探针使用 JLinkGDBServer,其他受支持的探针使用 OpenOCD。请参阅受支持的调试探针。
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --force / --no-force - 强制建立新连接(默认复用已有连接)
  • --halt / --no-halt - 连接时让设备停住(默认 no-halt)
  • --speed KHZ - SWD/JTAG 速度,单位 kHz(例如 100、4000),或 “adaptive”
  • --quiet - 抑制提示性信息
  • --json - 以 JSON 格式输出结果
  • --rtt - 启动 GDB 服务器之后自动串流 RTT 日志
  • --rtt-reset - 复位设备之后串流 RTT(可以捕获启动过程)
  • -i, --interactive - 双向 RTT:把 stdin 转发到目标的 RTT 下行通道,同时把上行通道串流到 stdout(需要 --rtt 或 --rtt-reset)
  • --rtt-channel N - 要串流的 RTT 通道,两个方向都用它(默认 0)
  • --reset - 启动 GDB 服务器之后复位设备
  • --gdb-port PORT - 覆盖自动分配的 GDB 服务器端口。默认情况下,Box 按探针槽位选择端口(第一个探针为 2331,第二个为 2334)。只有在需要特定端口时才传它。在多探针的 Box 上请避免使用。
  • --rtt-search-addr HEX - 搜索 RTT 控制块的 RAM 起始地址(十六进制,例如 0x20020000)
  • --rtt-search-size HEX - 搜索 RTT 控制块的 RAM 区域大小(十六进制,例如 0x4000)
  • --rtt-chunk-size HEX - RTT 搜索的读取块大小(十六进制,例如 0x1000)
  • --local-port PORT - 在网关之后的 Box 上,隧道使用的本地端口(默认与 Box 上 GDB 服务器的端口相同)
  • --no-tunnel - 在网关之后的 Box 上,启动 GDB 服务器后直接返回,不建立隧道
示例:
用 GDB 连接:
命令会打印应当使用的确切地址。在普通的 Lager Box 上,它打印 Box 的地址,然后返回。 位于网关之后的 Box: 有些 Box 位于做认证的访问网关之后(请参阅 lager login)。网关会检查每个连接的登录状态。这类 Box 的 GDB 端口不对网络开放,因此 GDB 无法直接连接到 Box。 在这类 Box 上,gdbserver 启动 GDB 服务器之后,会通过网关建立一条隧道。隧道只在 localhost 上监听,端口号与 Box 上 GDB 服务器的端口相同。隧道打开期间,命令保持在前台运行:
  • 命令会针对每台 Box 自行判断是否需要隧道,您不需要为此设置任何选项。
  • 隧道打开期间,GDB 可以断开后重新连接,也可以有多个客户端同时连接。
  • Ctrl-C 只关闭隧道,GDB 服务器继续在 Box 上运行。要停止它,请使用 lager debug <NET> disconnect。
  • 如果本地端口已被占用,命令会报错并停止。请用 --local-port 选择其他本地端口。
  • 使用 --rtt、--rtt-reset 或 --interactive 时,隧道在 RTT 串流期间保持打开。RTT 本身不经过隧道,而是作为普通 HTTP 通过网关。
  • 使用 --json 时,输出中带有一个 tunnel 对象,包含 local_host 和 local_port,之后命令保持在前台运行。
  • 如果脚本需要在 GDB 服务器启动后继续执行,可以使用 --no-tunnel。这时命令直接返回,不建立隧道。
  • 如果管理员撤销了您对该 Box 的访问权限,网关会在大约一分钟内关闭隧道,命令会提示您。之后的下一个连接会收到访问错误。
如果该 Box 的网关还不支持调试隧道,命令仍会启动 GDB 服务器并成功退出。命令会警告本机上的调试器无法连接到该服务器,并且不会打印可供连接的地址。需要先更新网关,才能连接调试器。请联系该 Box 的管理员。

disconnect

停止 GDB 服务器(JLinkGDBServer 或 OpenOCD)并释放调试资源。
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --keep-server - 让 GDB 服务器继续运行,供外部连接使用
示例:

flash

把固件烧录到目标。支持 Intel HEX、ELF 和二进制文件格式。
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --hex FILE - Intel HEX 文件的路径
  • --elf FILE - ELF 可执行文件的路径
  • --bin FILE,ADDRESS - 二进制文件及其加载地址,作为一个参数(例如 build/app.bin,0x08000000)
  • --verbose - 显示详细的 J-Link 输出
  • --force-reconnect - 烧录前强制干净重连
  • --no-erase - 跳过擦除步骤(默认情况下 flash 会先擦除再编程,以获得干净状态)
  • --erase-start ADDR - 编程前擦除的起始地址,十六进制(0x16000000)或十进制。必须与 --erase-size 一起给出。
  • --erase-size BYTES - 从 --erase-start 起擦除的字节数:十进制、0x 十六进制,或带 K/M 后缀(例如 2M)。
  • --halt / --no-halt - 烧录之后让设备停住(默认 no-halt)
flash 默认会先擦除再编程,因此不需要任何标志就能得到干净状态(这正是 RTT 初始化所需要的)。只有当您有意保留现有闪存内容时才传 --no-erase。旧的 --erase 标志现在是空操作,仅为向后兼容而保留。
--erase-start 和 --erase-size 设定这次擦除的范围,在两种后端上都有效。不带它们时,flash 擦除整片,在 DA1469x 上则从 0x16000000 擦除 1 MiB。下面的 erase 一节说明了范围的规则,以及它需要的 Box 版本。 失败输出:
  • 如果编程前的擦除失败,flash 会打印 Flash erase failed: 和错误,然后以 1 退出。它不会对目标编程。
  • 如果编程失败,flash 会打印 Flash failed: 和出错的那一行,然后打印 The target was NOT programmed. If this ran without --no-erase it is now erased.,并以 1 退出。
  • 如果回读比对失败,flash 会打印 Flash failed: 和出错的那一行,然后打印 Flash does not read back as the image: the target holds a bad or partial image. Flash it again.,并以 1 退出。
  • 在 J-Link 上,由探针的输出决定结果。以下几类行会使命令失败:连接失败、探针被另一个客户端占用、RAMCode 下载失败。例如 Selected interface (SWD) is not supported by the connected probe. 和 Failed to download RAMCode!。下面的 erase 一节列出了连接失败的行。成功还需要 J-Link 自己的证据:Downloading file 之后要有 J-Link: Flash download: 行(或 O.K.)。没有证据的烧录并没有对目标编程,命令会失败。
  • J-Link 回读比对失败的行也会使命令失败:Verification failed @ address 0x...、ERROR: Verify failed. 或 Error while programming flash: Verify failed.。DA1469x 是例外。它的比对经过带缓存的 XIP 窗口读取,对已经正确编程的器件也可能报告失败。在 DA1469x 上,只有 Box 的无缓存回读才会使命令失败。在 Box 上设置 LAGER_DA1469_UNCACHED_VERIFY=1 即可开启它。
  • J-Link 烧录失败时,会追加 Diagnosis: 行。这些行列出烧录期间使用该探针的其他 J-Link 程序。在 DA1469x 上,还会说明目标在编程期间是否复位。
  • 如果您的目标的 J-Link 输出中没有这些证据行,请在 CLI 的环境中设置 LAGER_JLINK_REQUIRE_EVIDENCE=0。这样 flash 和 erase 就不再要求它们。失败行仍会使命令失败。
示例:

reset

复位目标设备。
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --halt / --no-halt - 复位后让设备停住(默认 no-halt)
  • --force-reconnect - 复位前强制干净重连
示例:

erase

擦除目标上的闪存:全部,或用 --erase-start 和 --erase-size 给定的范围。这是一个破坏性操作。 在 DA1469x 上,erase 只擦除外部 QSPI 闪存的一段范围。请参阅 DA1469x 目标。
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --speed KHZ - SWD/JTAG 速度,单位 kHz(默认 4000)
  • --yes - 跳过确认提示
  • --quiet - 抑制警告信息。它同时也会跳过确认提示。
  • --json - 以 JSON 格式输出结果。它同时也会跳过确认提示。
  • --erase-start ADDR - 擦除的起始地址,十六进制(0x16000000)或十进制。必须与 --erase-size 一起给出。
  • --erase-size BYTES - 从 --erase-start 起擦除的字节数:十进制、0x 十六进制,或带 K/M 后缀(例如 2M)。
  • --halt / --no-halt - 擦除后让设备停住(默认 no-halt)
擦除范围: --erase-start 和 --erase-size 必须一起给出。--erase-start 接受十六进制(0x16000000)或十进制。--erase-size 接受十进制、0x 十六进制,或带 K/M 后缀(例如 2M,以 1024 为基数)。范围必须落在 32 位地址空间内。在 DA1469x 上,范围必须位于 0x16000000–0x17FFFFFF 之内,并且会取代 1 MiB 的默认值和 JLinkScript 中的 LAGER_ERASE_RANGE 行。在其他目标上,该范围取代全片擦除:J-Link 运行 erase <start> <end>,OpenOCD 运行 flash erase_address。无效的范围会在 CLI 联系 Box 之前就被拒绝。 0.50.0 及以上版本的 Box 接受该范围。在较旧的 Box 上,CLI 会在擦除之前拒绝这两个选项,并给出提到 lager update 的信息。Box 不会收到请求,因此也不会改为擦除它的默认范围。 成功时 CLI 会打印 Erase complete: 和 Box 实际擦除的范围,例如 Erase complete: 0x16000000-0x161FFFFF (2 MiB),或 Erase complete: full chip。早于 0.50.0 的 Box 不报告范围,此时 CLI 打印 Erase complete!。使用 --json 时,erase_range 字段包含 start、end(闭区间)、length、source 和 text。source 为 request、script 或 default。全片擦除时该字段为 null。 失败输出: 如果擦除失败,erase 会打印 Erase failed: 和错误,然后以 1 退出。 0.42.0 及以上版本的 Box 会自己识别 J-Link 连接失败并返回错误,CLI 会在 Erase failed: 之后打印该错误。 较旧的 Box 会返回探针输出而不报错。此时 CLI 会逐行检查那段输出。当某一行是、以下列信息开头,或以它们结尾时,该行判定为擦除失败:
  • ERROR: Could not connect to target.
  • Could not connect to target.
  • Could not connect to the target device.
  • Cannot connect to target.
  • Failed to power up DAP
对于这样的行,CLI 会打印 Erase failed: 和该行,然后打印 The target was NOT erased. Check that it is connected and powered. 如果输出中没有这些行,erase 以 0 退出。 示例:

memrd

从目标设备读取内存。
参数:
  • START_ADDR - 起始内存地址(例如 0x20000000)
  • LENGTH - 要读取的字节数
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --json - 以 JSON 格式输出结果
  • --halt / --no-halt - 读取期间让设备停住(默认 no-halt)。--no-halt 会覆盖 DA1469x QSPI XIP 的自动停住
  • --no-reset - 仅 DA1469x。 跳过 Box 在读取之前执行的复位+停住。运行中的 DA1469x 会禁用 SWD,因此不做复位读取就会失败 —— 只有在芯片空白/已唤醒、且您不想重启它时才使用它
示例:
输出:

status

显示调试 Net 的状态和配置信息。
选项:
  • --box TEXT - Lager Box 名称或 IP
示例:
输出:
GDB server running 说的是 Box 上的那个进程。Target attached 说的是芯片本身: Box 会去读取目标来回答它。这两个字段不同,因此 CLI 分开报告。 gdbserver 可能比它驱动的设备活得更久,而烧录或擦除类命令关心的是第二个字段,而不是第一个。 为了回答 Target attached,status 会通过正在运行的 GDB 服务器读取 Cortex-M 的 CPUID 寄存器。这次读取不会让内核停住。探针较慢时, status 可能耗时多达 20 秒。 Target attached 在两种情况下显示 No:
  • 该探针没有运行 GDB 服务器。
  • GDB 服务器的日志记录了一次连接失败。
当 Box 无法得出结论时,Target attached 显示 Unknown。以下情况会得到这个结果:
  • Box 版本早于这个字段。
  • 探针无法运行,因为已有调试器占用了该会话。
  • 读取寄存器超时。
  • OpenOCD 返回了空回复。
Unknown 并不表示目标不存在。

health

检查调试服务的健康状况和资源使用情况。 输出中包含一行 Features:Box 调试服务在原有请求之外支持的能力,例如支持 --erase-start 和 --erase-size 的 erase_range。早于该列表的 Box 会打印 none reported。
选项:
  • --box TEXT - Lager Box 名称或 IP
  • --verbose - 显示详细的健康信息
示例:
输出(verbose):

列出调试 Net

只带 --box 而不带子命令调用时,列出该 Lager Box 上的全部调试 Net:
输出:

RTT(实时传输)日志

RTT 通过调试探针提供低延迟的日志。请在 gdbserver 上使用 --rtt 或 --rtt-reset 标志:

解码 defmt 日志

大多数 Rust 固件(以及不少 C 固件)通过 defmt 打日志,这是一种压缩的二进制格式。 来自 defmt 固件的原始 RTT 字节不是人类可读的。 必须由 defmt-print 解码,而且它需要目标上烧录的那个确切的 ELF 文件。
有两点需要注意:
  • 请重定向 stderr。 RTT 负载写入 stdout,状态信息(JLinkGDBServer started! 等)写入 stderr。请只用管道传 stdout —— 追加 2>/dev/null(或 2>debug.log),这样状态行就绝不会污染 defmt-print 的输入。
  • 这个流不会结束。 --rtt 会一直运行,直到进程被终止。在脚本或非交互式会话中,请用 timeout <seconds> 把它包起来,以捕获一个固定的时间窗口。当您终止 lager 进程时,管道关闭,defmt-print 会在 EOF 时退出。
请在运行该管道的计算机上(也就是保存着 .elf 的那台)用 cargo install defmt-print 安装 defmt-print。

交互式(双向)RTT

加上 --interactive 可以在读取目标数据的同时向目标发送数据。您在 stdin 上输入的任何内容都会被转发到目标的 RTT 下行通道,通过 RTT 提供命令控制台的固件就是这样驱动的:
stdout 仍然是原始的上行通道字节流,因此它与 defmt-print 的组合方式和普通的 --rtt 完全一样:
您的按键由您的终端回显,而不是写入 stdout,因此它们永远不会进入解码器。用 --rtt-channel N 可以使用 0 以外的通道;两个方向使用同一个通道。
固件必须在您所用的通道上声明一个 RTT 下行缓冲区。defmt-rtt crate 只建立上行缓冲区。没有下行缓冲区时,目标会丢弃它收到的数据,并且不给任何提示。这看起来像是主机侧的故障,但其实不是。使用 rtt-target 下行通道,或使用 SEGGER_RTT 下行缓冲区的固件,可以正常工作。
--interactive 需要终端。在脚本和其他非交互式场景中,请继续使用普通的 --rtt 加 timeout。 同一个探针和通道上同一时刻只能连接一个交互式会话,因为底层的 RTT 连接只接受一个客户端。第二次尝试会被拒绝,而不是悄悄地把数据流从第一个会话那里抢走。

典型工作流程

开发循环

RTT 调试

内存检查

清理

JLinkScript 支持

JLinkScript 文件让您可以为特定硬件配置定制 J-Link 调试探针的行为。它们可以处理自定义复位序列、时钟初始化、引脚配置,以及标准 J-Link 连接流程未覆盖的其他设备专有操作。

配置 JLinkScript

有三种方式可以把 J-Link 脚本附加到调试 Net 上: 1. 创建 Net 时:
2. 在已有的 Net 上:
3. 在 .lager 配置中按项目设置:

脚本优先级

当 Net 级脚本(通过 set-script 保存在 Box 上)和项目级脚本(位于 .lager 配置中)同时存在时,项目级脚本优先。这样您就可以为特定项目覆盖 Box 上保存的脚本。

管理脚本

一旦附加,该脚本会在所有调试操作(连接、烧录、擦除、复位)中自动使用,不需要任何额外标志。

DA1469x 目标

设备名称中含有 DA1469 时会启用 DA1469x 专用处理。在这个系列上, flash 和 erase 作用于外部 QSPI 闪存。CPU 把该闪存映射到 0x16000000 以便就地执行(XIP)。
  • 地址。 请传入绝对的 XIP 地址,例如 --bin build/app.bin,0x16000000。不要传闪存偏移量。
  • 擦除。 默认情况下,erase 以及 flash 之前的擦除,在两种后端上都从 0x16000000 擦除 1 MiB。这个系列从不做全片擦除。
  • 擦除范围。 --erase-start 和 --erase-size 在两种后端上设定范围,该范围必须位于 0x16000000–0x17FFFFFF 之内。在 J-Link 上,当这两个选项缺省时,该 Net 的 JLinkScript 中类似 LAGER_ERASE_RANGE: 0x16000000 0x160FFFFF 的一行会设定范围。J-Link 后端随后会擦除这个闭区间。选项优先于脚本行,脚本行优先于默认值。
在 OpenOCD 探针上,主线 OpenOCD 没有这种 QSPI 闪存的驱动。 Box 改为运行一个驻留在 RAM 中的闪存加载器:
  • 超出 0x16000000–0x17FFFFFF 的地址,在 Box 碰探针之前就会被拒绝。
  • 请使用 --bin。加载器原样写入文件中的字节,因此它不解码 .hex 或 .elf 文件。
  • 请把 flash_loader.elf 和 flash_loader.elf.bin 放在 Box 宿主机的 ~/third_party/customer-binaries/openocd/flash-loaders/da1469x/ 中。容器把该目录看作 /home/www-data/customer-binaries/openocd/flash-loaders/da1469x/。
  • 若要使用其他父目录,请用 lager box-config env set 把 LAGER_FLASH_LOADERS_DIR 设为一个容器内路径,然后运行 lager box-config apply。
  • OpenOCD 没有这个系列的内置目标配置。请用 lager nets set-script 或 lager nets add --openocd-config 为该 Net 附加一份配置。
  • 一次丢失的调试读取不会中止加载器。加载器会重试读取,直到该步骤的截止时间。截止时间为:启动 10 秒,擦除每 MiB 60 秒,每个编程块 30 秒。
加载器失败时会指出它打印的最后一行进度信息:
如果信息中说 QSPI 擦除已经执行过,那么板子可能是空白的。请在复位它之前重新烧录一次。

受支持的调试探针

Box 根据探针的 USB 厂商 ID 选择后端。

受支持的设备系列

Lager 支持 70 多个 ARM Cortex-M 设备系列,并能自动识别架构。创建调试 Net 时,设备型号作为通道给出(例如 STM32F407VG、nRF52840)。

Cortex-M0/M0+(ARMv6-M)

Cortex-M3(ARMv7-M)

Cortex-M4/M7(ARMv7E-M)

Cortex-M23(ARMv8-M Base)

Cortex-M33/M55(ARMv8-M Main)

不在上表中的设备默认按 Cortex-M4(ARMv7E-M)架构处理。如果您的设备没有被正确识别,请在创建调试 Net 时给出完整的设备型号(例如 STM32F407VG,而不是只写 STM32F4)。

说明

  • 调试 Net 用 lager nets add <name> debug <device_type> <address> 创建
  • 对 flash 和 reset 之类的命令,系统会在需要时自动连接
  • 在 flash、erase 和 reset 运行之前,CLI 会检查目标是否响应。如果 GDB 服务器已启动但目标没有响应,CLI 会打印 The debug session is up, but the target does not answer; reconnecting... 并开始一个新会话。如果重连失败,命令以 1 退出。
  • flash 默认先擦除再编程,从而为 RTT 初始化提供干净状态(用 --no-erase 退出这一行为)
  • RTT 串流要求设备固件中具备 RTT 支持
  • 用 --halt 暂停 CPU 时,内存读取更可靠
  • 用 lager debug health --verbose 诊断连接问题
  • JLinkScript 文件以 base64 编码保存,并在 Box 上自动解码

参见