GDB 在 x86 上观察 DMA 控制器内存访问
GDB watch DMA controller memory access on x86
我 运行 基于 FreeBSD 的内核作为 QEMU/KVM 来宾。
我正在开发基于 FreeBSD 的 OS 内核 SCSI 驱动程序,但遇到 read
系统调用产生损坏数据的问题。
为了解决这个问题,我在 QEMU 中使用了内核 运行ning 并想跟踪由负责将数据传送到用户提供的缓冲区的 DMA 控制器执行的内存访问。在 QEMU 的情况下,控制器是 QEMU SCSI/ATA Disk
设备。所以我尝试在用户提供的缓冲区上设置观察点
示例:
在 int sys_read(struct thread *td, struct read_args *uap)
上设置断点 我收到了一些来自用户的缓冲区:
(gdb) p uap->buf
= 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 并重新设置它。
这个堆栈跟踪意味着什么?
为 QEMU 的 gdbstub 提供的观察点处理实际上仅适用于访客 CPU 完成的访问,不适用于 DMA 从模拟设备完成的访问。我对它的成功感到很惊讶,并且对这种行为有点奇怪并不感到惊讶。
如果您可以使用纯仿真(即不是 KVM、hvf 或 whpx)在 QEMU 设置上重现它,那么我的调试建议是 运行 QEMU 本身在主机 gdb 中,并使用主机 gdb 在主机内存上设置一个观察点,该观察点对应于来宾内存的相关位。不幸的是,这需要一些 QEMU 内部知识才能找到主机内存,并且通常要了解正在发生的事情并将 QEMU 正在做什么与来宾执行是什么联系起来。
补充调试提示:如果您可以在错误被触发之前采取 'snapshot',那么您的重现案例会更短,即“从快照加载并触发错误”而不是“启动整个客户机 OS 和用户空间然后触发错误”。 this blog post.
中有更多详细信息
补充调试技巧 2:如果您采用“使用主机 gdb 调试 QEMU”方法,则可以使用 reverse-debugger rr,这对于 memory-corruption 错误非常方便,因为您可以说“现在向后执行到最后触及此内存的任何内容”。 this post 中的更多信息。
我 运行 基于 FreeBSD 的内核作为 QEMU/KVM 来宾。
我正在开发基于 FreeBSD 的 OS 内核 SCSI 驱动程序,但遇到 read
系统调用产生损坏数据的问题。
为了解决这个问题,我在 QEMU 中使用了内核 运行ning 并想跟踪由负责将数据传送到用户提供的缓冲区的 DMA 控制器执行的内存访问。在 QEMU 的情况下,控制器是 QEMU SCSI/ATA Disk
设备。所以我尝试在用户提供的缓冲区上设置观察点
示例:
在 int sys_read(struct thread *td, struct read_args *uap)
上设置断点 我收到了一些来自用户的缓冲区:
(gdb) p uap->buf
= 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 并重新设置它。
这个堆栈跟踪意味着什么?
为 QEMU 的 gdbstub 提供的观察点处理实际上仅适用于访客 CPU 完成的访问,不适用于 DMA 从模拟设备完成的访问。我对它的成功感到很惊讶,并且对这种行为有点奇怪并不感到惊讶。
如果您可以使用纯仿真(即不是 KVM、hvf 或 whpx)在 QEMU 设置上重现它,那么我的调试建议是 运行 QEMU 本身在主机 gdb 中,并使用主机 gdb 在主机内存上设置一个观察点,该观察点对应于来宾内存的相关位。不幸的是,这需要一些 QEMU 内部知识才能找到主机内存,并且通常要了解正在发生的事情并将 QEMU 正在做什么与来宾执行是什么联系起来。
补充调试提示:如果您可以在错误被触发之前采取 'snapshot',那么您的重现案例会更短,即“从快照加载并触发错误”而不是“启动整个客户机 OS 和用户空间然后触发错误”。 this blog post.
中有更多详细信息补充调试技巧 2:如果您采用“使用主机 gdb 调试 QEMU”方法,则可以使用 reverse-debugger rr,这对于 memory-corruption 错误非常方便,因为您可以说“现在向后执行到最后触及此内存的任何内容”。 this post 中的更多信息。