【问题标题】:Implementing breakpoints that resume safely in multithreaded code在多线程代码中实现安全恢复的断点
【发布时间】:2018-06-30 14:25:45
【问题描述】:

我正在编写一个调试器,目前正在尝试使断点在多个线程同时命中它们时可靠地工作。据我所知,大多数调试器通过将指令的第一个字节替换为 0xCC 来实现断点,这也是我目前的做法。但是,我看不到任何恢复原始字节的方法,同时仍然能够停止即将到达该断点的其他线程,而不会停止所有正在运行的线程。有没有人有任何关于通常如何实现的信息?停止所有线程真的是唯一的解决方案吗?

【问题讨论】:

  • 据我所知,调试器会停止所有线程。
  • 通常有有限数量的“硬件断点”不受相同限制,但不足以将它们用于所有个断点

标签: c++ debugging x86-64 breakpoints machine-code


【解决方案1】:

在所有线程停止的情况下,您恢复该字节,仅在一个线程执行一条指令,重新创建断点,然后恢复所有线程的执行。如果您使用有限的硬件调试寄存器之一,您可以使用 RF 暂时忽略一条指令的断点(见下文)。

在调试过程中只停止一个线程,而其他线程继续运行,只是自找麻烦。考虑一下,当您一开始就停止时,您将如何处理击中相同或不同的断点?或者如果发生异常?

在 Intel CPU 上,可以在 EFLAGS 寄存器中设置一个标志(恢复标志,第 16 位)。设置后,将允许在不触发断点的情况下执行第一条指令,并且在使用硬件断点(而不是断点指令)时有效。

第 3 卷的第 17 章(系统编程指南,适用于 download from Intel)包含有关英特尔 IA-32 CPU 调试功能的大量详细信息。

【讨论】:

  • 嗯,这正是我的问题。我知道暂时暂停所有线程是解决这个问题的常用方法。我在问是否有任何方法可以避免这样做。
  • @Kalinovcic - 认为别无他法。您在 dbg_continue 之前挂起所有线程,并在线程上下文中设置跟踪标志,它会命中断点。单步异常后 - 您恢复 bp 并恢复另一个线程
  • @1201ProgramAlarm - 谢谢!我认为您的编辑回答了我的问题,我现在可能知道一种方法。
  • 或禁用硬件断点 - 对于硬件断点(使用DRx 设置),我们不需要挂起另一个线程,因为它是每个线程的。仅在int 3内存补丁的情况下才需要暂停
  • @RbMm 我已经改写了一些
【解决方案2】:

我知道暂时暂停所有线程是解决该问题的常用方法。我在问是否有任何方法可以避免这样做。

第一个遇到int3 软件断点的线程是您想要停止的线程。

如果其他线程在您将其修补回正确内容之前遇到了它,请在删除软件断点后恢复这些线程。 (x86 具有连贯的指令缓存,因此您可以安全地修改单个代码字节,而无需其他内核运行栅栏/isync 指令以将其指令缓存与数据缓存重新同步。这是一个其他 ISA 上更难的问题。)

其他线程可以看到一个小中断。


当然,如果用户在临界区(持有锁)内放置断点,或单步进入临界区,其他线程将阻塞该部分。这对于不是lock-free (in the computer science sense) 的无锁代码也是可能的。


在其他线程运行时检查和修改内存具有潜在的风险。在您尝试读取或修改内存之前,另一个线程可能会取消映射内存。不过,只要您的调试器本身不崩溃,就取决于用户他们想要制造多少混乱。

【讨论】:

  • 真正的问题非常强烈依赖于操作系统。在 Windows 上说 - 如果发生异常 - 系统挂起进程中的所有线程,然后从线程发送通知到调试器(这是异常)。并等待调试器响应(因此 cpu 开始执行另一个软件线程)。这里没有任何指令缓存问题,断点(在关键部分内与否 - 所有其他线程无论如何都将被挂起)。调试器通常会再次挂起所有其他线程并在当前线程中设置跟踪标志 (0x100)。恢复原始字节并继续。
  • 单步异常后 - 恢复断点并恢复线程
猜你喜欢
  • 2011-01-02
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 2014-05-21
  • 2013-02-20
  • 1970-01-01
  • 2019-04-05
  • 1970-01-01
相关资源
最近更新 更多