1

我将基于 FreeBSD 的内核作为 QEMU/KVM 来宾运行。

我正在开发基于 FreeBSD 的操作系统内核 SCSI 驱动程序,并且read系统调用出现问题会产生损坏的数据。

为了解决这个问题,我使用了在 QEMU 中运行的内核,并希望跟踪负责将数据传递到用户提供的缓冲区的 DMA 控制器执行的内存访问。在 QEMU 的情况下,控制器是QEMU SCSI/ATA Disk设备。所以我尝试在用户提供的缓冲区上设置观察点

例子:

设置断点int sys_read(struct thread *td, struct read_args *uap)我从用户那里得到了一些缓冲区:

(gdb) p uap->buf
 $5 = 0x7ffd4593f000 "User buffer initial data"
(gdb) watch *0x7ffd4593f000
 Hardware watchpoint 7: *0x7ffd4593f000
(gdb) c

问题在于观察点永远不会被击中。为什么?我想用它来了解检查从设备传输到内存的数据。

是否有可能从 DMA 控制器观看访问?

升级版:

我设法达到了观察点。它看起来如下:

Thread 3 hit Hardware watchpoint 7: *0x7ffd4593f000                                                                                                                                                                
                                                                                                                                                                                                                   
Old value = 1750343715                                                                                                                                                                                             
New value = 1819043144                                                                                                                                                                                             
0x00005586ec68f146 in ?? ()                                                                                                                                                                                        
(gdb) bt                                                                                                                                                                                                           
#0  0x00005586ec68f146 in ?? ()
#1  0x654b206f6c6c6548 in ?? ()
#2  0x6f7266206c656e72 in ?? ()
#3  0x707372657375206d in ?? ()
#4  0x6574617200656361 in ?? ()
#5  0x6d69562079622064 in ?? ()
#6  0x20230a2e312e3820 in ?? ()
#7  0x2079616d20756f59 in ?? ()
#8  0x2074692074696465 in ?? ()
#9  0x7227756f79206669 in ?? ()
...
#509 0x0a632e6e69616d2f in ?? ()
#510 0x32312c30352c347c in ?? ()
#511 0x323436312c37312c in ?? ()
#512 0x222c323037343132 in ?? ()
#513 0x0000000000000000 in ?? ()

这很有可能是正确的,因为显示为旧值新值的前 4 个字节与我期望读取的内容相匹配。

但奇怪的是,它只在 QEMU 启动时被击中一次。随后read的系统调用不会触发观察点。为了让它被击中,我重新启动 QEMU 并再次设置它。

这个堆栈跟踪可能意味着什么?

4

1 回答 1

4

为 QEMU 的 gdbstub 提供的观察点处理实际上只适用于客户 CPU 完成的访问,而不适用于 DMA 从模拟设备完成的访问。我很惊讶它会成功,而且这种行为有点奇怪并不奇怪。

如果您可以使用纯仿真(即不是 KVM、hvf 或 whpx)在 QEMU 设置上重现此问题,那么我的调试建议是在主机 gdb 中运行 QEMU 本身,并使用主机 gdb 在对应于客户内存的相关位的主机内存。不幸的是,这需要一些 QEMU 内部知识才能找到主机内存,并且通常需要了解正在发生的事情并将 QEMU 正在做的事情与来宾执行的内容联系起来。

补充调试提示:如果您可以在触发错误之前拍摄“快照”,那么这将为您提供更短的重现案例,即“从快照加载并触发错误”而不是“启动整个客户操作系统和用户空间然后触发错误”。此博客文章中的更多详细信息。

补充调试技巧 2:如果您采用“使用主机 gdb 调试 QEMU”的方法,您可以使用反向调试器 rr,这对于内存损坏错误非常方便,因为您可以说“现在向后执行到上次触及的任何内容记忆”。更多信息在这篇文章中。

于 2022-01-28T11:41:31.573 回答