2. QEMU 日志系统初探:日志级别详解与输出重定向
调试 RISC-V 模拟环境,第一件事就是学会跟 QEMU “对话”。
QEMU 的日志系统,说白了就是它内部运行状态的“黑匣子”。你告诉它记录什么,它就记什么。我个人习惯,拿到一个新镜像,第一件事就是打开日志,看看 CPU 到底在干什么。
2.1 -d 参数:日志级别的“开关”
QEMU 的日志控制,核心就是 -d 参数。它的语法很简单:
qemu-system-riscv64 -d [日志项1],[日志项2],... -D [输出文件]
这里 -d 后面跟的是日志类别,多个类别用逗号隔开。-D 则是把日志写到文件里。如果不加 -D,日志默认会打印到终端(stderr)。
我刚开始用的时候,经常忘了加 -D,结果终端被日志刷屏,连命令都输不了。嗯,这是个坑,后面会讲怎么避。
2.2 核心日志级别一览
QEMU 支持的日志级别很多,但常用的就那么几个。我把它们整理成了表格,方便你查阅:
| 日志项 | 含义 | 我的使用场景 |
|---|---|---|
cpu |
CPU 执行状态(PC、指令、寄存器) | 跟踪程序执行流,看它跑到了哪里 |
int |
中断和异常 | 排查中断没响应、异常向量表配置错误 |
mmu |
内存管理单元(页表、地址翻译) | 调试页表配置、虚拟地址映射问题 |
guest_errors |
客户机错误(非法指令、对齐错误等) | 快速定位 Guest OS 或裸机程序的 bug |
page |
内存页分配与释放 | 检查内存泄漏或内存布局异常 |
trace |
QEMU 内部事件跟踪(需编译时开启) | 性能分析、设备模型行为追踪 |
all |
开启所有日志(慎用!) | 只有万不得已才用,输出量巨大 |
cpu,guest_errors。这两个组合能覆盖 80% 的启动问题。等定位到具体模块,再针对性加 int 或 mmu。
2.3 实战:启动一个 RISC-V 内核并查看日志
光说不练假把式。我们直接上手,用 QEMU 启动一个 RISC-V 64 位 Linux 内核,看看日志长什么样。
假设你已经有编译好的内核镜像 Image 和设备树 fw_dynamic.elf。执行:
qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-kernel Image \
-append "console=ttyS0" \
-serial stdio \
-d cpu,guest_errors \
-D qemu_trace.log
这条命令做了几件事:
- 启动 RISC-V 64 位模拟器,机器模型是
virt。 - 加载内核镜像
Image。 - 把串口输出重定向到当前终端(
-serial stdio)。 - 开启
cpu和guest_errors日志。 - 把日志写入
qemu_trace.log文件。
运行后,你会看到终端里内核的启动信息。同时,当前目录下会生成一个 qemu_trace.log 文件。打开它,你会看到类似这样的内容:
CPU #0:
pc 0x0000000080000000
insn 0x00000097
priv 1
Guest errors:
- Misaligned load at 0x0000000080001000
每一行都记录了 CPU 的当前状态。PC 值、指令编码、特权级……这些信息在排查“程序跑飞”或“死循环”时,就是救命稻草。
all),QEMU 的性能会急剧下降。我曾经有一次忘了关日志,结果模拟器跑得比乌龟还慢,排查了半天才发现是日志在疯狂写盘。所以,调试完记得关掉不必要的日志项。
2.4 日志输出重定向:从终端到文件
除了用 -D 指定文件,QEMU 还支持其他重定向方式。我整理了一下:
| 方式 | 命令示例 | 说明 |
|---|---|---|
| 默认输出到 stderr | qemu-system-riscv64 -d cpu |
日志和错误信息混在一起,适合快速查看 |
| 重定向到文件 | qemu-system-riscv64 -d cpu -D log.txt |
最常用,日志单独保存,方便后续分析 |
| Shell 重定向 | qemu-system-riscv64 -d cpu 2>log.txt |
把 stderr 重定向到文件,但会混入其他错误信息 |
| 同时输出到终端和文件 | qemu-system-riscv64 -d cpu -D log.txt 2>&1 | tee log.txt |
实时查看 + 持久化保存,调试时很方便 |
我个人最推荐第二种:-D 指定文件。它干净、隔离,不会跟终端输出打架。
2.5 避坑指南:日志文件太大怎么办?
这个问题我遇到过好几次。尤其是调试 MMU 或中断时,日志量会爆炸式增长。一个不小心,硬盘就满了。
我的经验是:
- 不要开
all,除非你确定需要所有信息。 - 用
guest_errors代替cpu,前者只记录错误,后者记录每条指令。 - 配合
-singlestep使用,让 QEMU 单步执行,这样日志量可控。 - 写个脚本定期清理,或者用
logrotate管理日志文件。
mmu 日志,结果 10 秒钟生成了 2GB 的日志文件。当时我以为是 QEMU 死循环了,后来才发现是日志在疯狂输出。从那以后,我每次开日志前都会先估算一下输出量。
2.6 知识体系:日志系统核心逻辑
为了让你更直观地理解 QEMU 日志系统的工作流程,我画了一张图:
这张图展示了从输入参数到最终输出的完整链路。你只需要记住:先选类别,再定输出。就这么简单。
2.7 小结
这一章我们聊了 QEMU 日志系统的核心:-d 参数和 -D 输出重定向。你学会了:
- 如何用
-d开启不同级别的日志。 - 如何用
-D把日志保存到文件。 - 如何避免日志文件过大导致性能问题。