在恰当的时间点将调试器附加到VC ++中的过程的最佳方法是什么?

时间:2009-03-19 18:31:42

标签: c++ debugging visual-c++ debugbreak

调试时,有时需要附加已经运行的进程,而不是仅在调试器中启动应用程序。

我自己常常输入Sleep()或MessageBox调用,以便更容易连接调试器。我担心其中一些可能最终会被提交给源代码控制。

在延迟足够时间的同时避免这种情况的最佳方法是什么,以便将调试器连接到正在运行的进程?

使用#ifdef _DEBUG保护睡眠或消息框是一种方式,但我想知道是否有更好的方法。

有了睡眠,您也会遇到可能无法及时附着的问题。使用MessageBox,您可能会遇到远程调试或调试没有可见GUI的进程的问题(例如在Vista上作为服务运行)

8 个答案:

答案 0 :(得分:11)

答案 1 :(得分:9)

要在特定点附加调试器,您有以下几种选择:

最简单的只是调用DebugBreak,它几​​乎等同于__asm int 3,但也适用于其他架构(如果我没记错的话,MSVC for x64不允许内联汇编)。这将打开即时调试器窗口,您将能够从已注册的调试器(即Visual Studio)中进行选择以附加到该过程。

或者,您可以调用Sleep,让您有机会附加调试器。您应该在此周围使用#ifdef _DEBUG,以确保您实际上并未附带此代码。

一个问题:为什么不能从IDE运行代码?它是服务还是IIS加载的DLL或类似的?

在这种情况下,您可以查看ImageFileExecutionOptions注册表项,该注册表项允许您在进程启动时附加调试器。

如果您使用cdb,则可以将其配置为WinDbg实例的服务器或客户端,并以此方式进行调试。我以前使用WinDbg作为内核调试器,并使用ImageFileExecutionOptions用命名进程启动ntsd -d来完成此操作。这会导致WinDbg进入用户模式。这有时是一种有用的技术。

答案 2 :(得分:7)

另一种变体,我有时使用

while( !::IsDebuggerPresent() )
    ::Sleep( 100 ); // to avoid 100% CPU load

它应该静静等待,直到您将调试器附加到进程。

答案 3 :(得分:6)

弗雷迪和雷奥有正确的解决方案。但我想补充一个不使用MessageBox的原因。

仅显示MessageBox会部分停止您的应用程序。因为您正在显示UI,所以程序中至少有一个线程仍在运行消息泵。因此,如果您的代码执行以下任何操作。

  1. 通过Windows消息进行通信
  2. 有非平凡的用户界面
  3. 是多线程的
  4. 您实际上将在一个状态下请求调试器,但在完全不同的状态下附加到您的程序。这可能会导致令人困惑的情况和错误。

    我们最近对我们的代码库进行了更改,以便永远不会显示MessageBox,以便为此提供便利。它会为非平凡的应用程序产生非常糟糕的行为。

答案 4 :(得分:2)

必须附加'恰到好处'是一种痛苦...一种选择是将明确的DebugBreak()语句添加到代码中以强制解决问题,并用#ifdef _DEBUG保护它们会很好理念。我们使用可以调用DebugBreak()的ASSERT宏,因此您只需编写ASSERT(false)

另一个需要考虑的选择是使用“图像文件执行选项”自动启动调试器。请参阅此blogMSDN文档。

答案 5 :(得分:1)

向上看:

DebugBreak,__ decugug和朋友

static void timeToChase(){__ asm {int 3; }; }

答案 6 :(得分:1)

__asm int 3 

这个硬断点将打开调试对话框,它将允许您附加到进程。在#ifdef _DEBUG中包装,你只能在调试版本中点击它。

答案 7 :(得分:1)

如果__debugbreak()或DebugBreak()适合您,那么我认为这可能是最好的方法。

但是,由于某些未知原因,我遇到了__debugbreak()只是立即终止程序而不是等待调试器连接的情况。我尝试了各种方法来解决此问题(注册表黑客等),但似乎没有任何效果。 (在这种情况下,我尝试调试的过程可能不是直接从命令行启动,而是由Java程序启动的。)

无论如何,在那种情况下,我使用了这种方法,效果似乎很好:

void waitForDebuggerToConnect() {
    #ifdef _DEBUG
    static volatile bool spin = true;
    while (spin)
    {}
    #endif
}

调用此函数后,我的程序将无限期地挂在“ while”循环中。然后,我可以调用VC ++调试器并附加到该进程。然后,我可以使用“全部中断”来停止所有线程。然后,我可以找到卡住的线程的调用堆栈,并可以使用调试器检查顶部框架,并可以检查“ spin”的值,然后将其设置为“ false”。此时,我可以退出此功能和/或设置断点,然后继续运行部分或全部线程。