使用脚本自动执行 zxdb

Zxdb 提供脚本支持,可让您自动执行重复的调试任务、跳转到特定应用状态,并以自动方式验证调试器行为。脚本是纯文本文件,其中包含一系列 zxdb 命令及其预期输出。

运行脚本

如需在启动 zxdb 时从命令行运行脚本,请使用 -S--script-file 选项。

  • 使用 ffx debug
ffx debug connect -- --script-file=my_script.script
  • 或者直接使用 zxdb(请注意,这仅适用于离线核心文件,例如 minidump,因此您的脚本应首先使用 opendump 命令加载核心文件。如需了解详情,请参阅 help opendump):
zxdb --script-file=my_script.script

脚本语法

脚本按行进行评估,通常会在输入命令和匹配输出行之间交替进行。

命令

[zxdb] 开头的行会被解读为要运行的命令。这些命令的执行方式与在交互式 zxdb 控制台中相同。

[zxdb] attach my_component.cm

您可以使用缩写和任何标准 zxdb 功能:

[zxdb] t * f

这相当于:

[zxdb] thread * frame

输出匹配

如果某行不是以 [zxdb] 开头,也不是注释,则会被视为与之前执行的命令的输出进行匹配的模式。

由于 zxdb 中的许多命令都是异步的,因此调试器需要知道命令何时实际完成输出,然后才能发出下一个命令。Zxdb 会等待输出与指定的行匹配,然后再继续执行下一个命令。这可确保您的脚本与调试器的异步事件保持同步。

[zxdb] attach cobalt.cm
Attached Process
Done.

如果您不关心命令的输出,则无需指定任何匹配行。您可以编写下一个 [zxdb] 命令,系统会允许上一个命令立即完成。

如果命令有任何依赖项,则可能会导致结果不稳定。例如:

[zxdb] b $main
Created Breakpoint 1 @ $main
[zxdb] run-component fuchsia-pkg://fuchsia.com/zxdb_e2e_inferiors#meta/async_rust_multithreaded.cm
Launched Process 1 state=Running koid=?? name=async_rust_multithreaded.cm component=async_rust_multithreaded.cm
🛑
[zxdb] until foo
# This might lead to flaky results
[zxdb] thread

thread 的结果可能是

  7 #[fasync::run(2)]
   8 async fn main() {
   9     let _task_a = fasync::Task::spawn(async {});
🛑 on bp 1 async_rust_multithreaded::main()  async_rust_multithreaded.rs:7
[zxdb] until foo
[zxdb] thread
  # state               koid name
 1 Blocked (Futex) 24769196 initial-thread
  2 Blocked (Port)  24769684 executor_worker
  3 Blocked (Port)  24769691 executor_worker
   19 }
   20
  21 async fn foo(i: u64) {
   22     fasync::Timer::new(std::time::Duration::from_secs(i)).await;
   23 }

  7 #[fasync::run(2)]
   8 async fn main() {
   9     let _task_a = fasync::Task::spawn(async {});
🛑 on bp 1 async_rust_multithreaded::main()  async_rust_multithreaded.rs:7
[zxdb] until foo
   19 }
   20
  21 async fn foo(i: u64) {
   22     fasync::Timer::new(std::time::Duration::from_secs(i)).await;
   23 }
🛑 thread 2 async_rust_multithreaded::foo(u64)  async_rust_multithreaded.rs:21
[zxdb] thread
  # state                   koid name
  1 Suspended           24775467 initial-thread
 2 Blocked (Exception) 24775973 executor_worker
  3 Suspended           24775980 executor_worker

您可以手动快速输入命令或使用脚本运行程序来重现此结果。

这是因为 untilthread 命令是同时运行的。 为避免不稳定,建议使用暂停符号 (🛑) 作为预期输出。例如:

[zxdb] until foo
🛑
[zxdb] thread

通配符

如果您只想匹配行的部分内容,或者某行包含不可预测的数据(例如内存地址、进程 ID 或文件路径),则可以使用 ?? 通配符。它与行的任意部分(最多为整行)匹配。

[zxdb] frame
 0 MyFunction()  my_file.cc:??

评论

# 开头的行是注释,会被忽略。注释对于记录脚本非常有用,但请注意,系统也支持 UTF-8 字符。

# This is a comment explaining what the next command does.
[zxdb] pause
# Wait for the stop event
🛑

无序输出

默认情况下,脚本会期望输出行以指定的精确顺序到达。如果命令生成的输出无法保证顺序,您可以在预期输出块中的任意位置添加 ## allow-out-of-order-output。这可确保所有指定的行都匹配,无论它们以何种顺序打印。此指令必须紧跟在 zxdb 命令行之后,并且在要匹配的任何预期输出之前,以便匹配算法正确考虑所有输出。

[zxdb] thread
## allow-out-of-order-output
  1 State: Running
  2 State: Suspended

退出脚本

脚本完成后,您将自动进入交互式 [zxdb] 命令行。

如果您希望 zxdb 在脚本结束时自动退出,则可以在文件末尾添加 quit 命令。但是,请确保之前的命令已指定匹配的输出,否则 quit 可能会在之前的命令完成之前执行。

[zxdb] quit --force

示例

捕获异步回溯

此脚本会附加到组件、暂停组件、打印异步回溯,并让您进入交互式控制台以进行进一步调查。

# Attach to the component
[zxdb] attach hwinfo.cm
Attached Process
Done.

# Pause execution
[zxdb] pause
🛑

# Output the tree of async tasks.
[zxdb] async-backtrace

此脚本会附加到组件、等待组件停止、打印所有线程的回溯,然后进入交互式控制台。

[zxdb] attach cobalt.cm
Attached Process
Done.

[zxdb] pause
🛑

# Output the backtraces of all threads of the current process.
[zxdb] thread * frame