4. QEMU 日志系统详解:-d 参数选项、日志类别与输出控制

调试 RISC-V 系统,说白了就是跟日志打交道。QEMU 的 -d 参数就像一把瑞士军刀,能帮你切开模拟器的黑盒子,看到里面到底在干什么。我刚开始用 QEMU 的时候,总觉得这参数太复杂,后来踩了几个坑才明白——用好日志,调试效率能翻好几倍。

4.1 -d 参数:日志的开关

-d 是 QEMU 最核心的调试开关。它的用法很简单:

qemu-system-riscv64 -d [日志类别] [其他参数]

你可以指定一个或多个类别,用逗号隔开。比如:

qemu-system-riscv64 -d in_asm,cpu -kernel vmlinux

这行命令会同时输出指令反汇编和 CPU 状态变化。我个人习惯先开 in_asm 看看指令流,再根据情况加其他类别。

小技巧: 如果你不确定有哪些日志类别,可以用 -d help 查看完整列表。QEMU 会列出所有支持的类别,非常方便。

4.2 日志类别详解:in_asm、cpu、exec 等

日志类别是 -d 参数的核心。我挑几个最常用的给你讲讲,这些都是我在实际项目中反复用到的。

日志类别 输出内容 适用场景
in_asm 每条指令的反汇编(翻译后的代码) 跟踪指令执行流,检查代码是否按预期运行
cpu 每条指令执行前后的 CPU 寄存器状态 调试寄存器值变化,比如 PC、SP、通用寄存器
exec 每条指令的 PC 地址和执行次数 统计指令执行频率,定位热点代码
int 中断和异常事件 调试中断处理流程,检查中断是否被正确触发
mmu MMU 地址转换过程 调试页表、虚拟地址到物理地址的映射
guest_errors 客户机(Guest)的错误信息 快速定位客户机代码中的异常行为

举个例子,我曾经调试一个 RISC-V 的启动代码,发现程序跑到某个地址就卡住了。我开了 in_asm 和 cpu,结果发现 PC 值跳到了一个未初始化的内存区域。嗯,原来是链接脚本把代码段放错了位置。

核心思路: 日志类别不是越多越好。开太多类别会刷屏,反而看不清关键信息。我一般先开 in_asm 看指令流,再根据问题加 cpu 或 int。

4.3 日志级别控制:-d 的进阶用法

你以为 -d 只能开和关?其实它还能控制日志的详细程度。QEMU 支持用 + 和 - 来调整级别。

# 开启 in_asm,并增加详细程度
qemu-system-riscv64 -d in_asm,+in_asm_detail

# 开启 cpu,但减少寄存器输出
qemu-system-riscv64 -d cpu,-cpu_all

你想想看,这有什么用?比如你只想看 PC 和 SP 的变化,不想看所有寄存器,就可以用 -cpu_all 来过滤。我遇到过一种情况:客户机跑着跑着突然崩溃,日志刷了几万行。我加了 -cpu_all 后,只看到 PC 和 SP 的变化,很快就发现 SP 被写成了一个非法地址。

注意: 日志级别控制依赖于具体的 QEMU 版本。有些版本可能不支持所有子类别。建议先用 -d help 确认当前版本支持的选项。

4.4 日志文件输出:-D 参数

默认情况下,日志会直接打印到终端。但调试复杂系统时,终端刷屏根本看不过来。这时候就需要 -D 参数把日志输出到文件。

qemu-system-riscv64 -d in_asm,cpu -D qemu.log -kernel vmlinux

这会把所有日志写入 qemu.log 文件。我个人习惯用 -D 配合 -d,这样可以在调试结束后慢慢分析日志,而不是盯着终端看。

还有一个实用技巧:用 -D 输出到文件后,可以用 grep 或 awk 快速过滤关键信息。比如:

# 只查看 CPU 状态变化
grep "cpu" qemu.log

# 统计指令执行次数
grep "exec" qemu.log | wc -l

我曾经用这个方法定位过一个死循环问题。客户机卡在某个循环里,我开了 exec 日志,然后用 grep 统计 PC 地址的出现次数。结果发现某个地址被执行了上百万次,而其他地址几乎没动。嗯,问题就出在那个循环条件上。

建议: 日志文件可能会很大,尤其是开了 in_asm 或 cpu 后。建议先在小规模测试中确认日志类别,再用于完整系统。

4.5 知识结构图:日志系统的核心逻辑

下面这张图展示了 QEMU 日志系统的整体结构。你可以看到从 -d 参数到日志输出的完整流程。

QEMU 日志系统结构图 QEMU 日志系统核心结构 -d 参数 日志类别选择 in_asm | cpu | exec | int | mmu guest_errors | 等 日志级别控制 +/- 子类别(如 +in_asm_detail) 输出到终端 输出到文件 (-D) 说明:日志类别和级别控制共同决定输出内容,-D 参数决定输出目标

4.6 实战建议:如何高效使用日志

说了这么多,我总结几条实战建议,都是我自己踩坑换来的经验。

  • 先小后大: 先在小规模测试中确认日志类别,再用于完整系统。不然日志文件可能大到让你崩溃。
  • 组合使用: 不要只开一个类别。比如 in_asm 配合 cpu,能同时看到指令和寄存器变化,定位问题更快。
  • 善用 grep: 日志输出到文件后,用 grep 过滤关键信息。比如 grep "PC=" qemu.log 只看 PC 变化。
  • 注意性能: 开日志会降低模拟速度。尤其是 in_asm 和 cpu,会让 QEMU 慢好几倍。调试时尽量用精简的测试用例。
避坑指南: 我曾经在调试一个多核 RISC-V 系统时,开了 in_asm 和 cpu,结果日志文件在几分钟内涨到了几个 GB。后来我改用 exec 只记录 PC 地址,才把问题定位到。记住:日志不是越多越好,够用就行。

好了,关于 QEMU 日志系统,核心就是这些。用好 -d 和 -D,你就能像看 X 光片一样,看清模拟器内部的每一个细节。下次遇到奇怪的问题,不妨先开个日志看看。