【问题标题】:Break on thread creation in Visual Studio debugger在 Visual Studio 调试器中中断线程创建
【发布时间】:2010-09-29 10:46:55
【问题描述】:

我可以将 Visual Studio 调试器设置为在创建线程时中断吗?

(请注意,这与在另一个问题中要求的上下文切换中断不同:Can I set a breakpoint in Visual Studio (c++) to break on a thread context switch?

【问题讨论】:

  • 你有想过这个吗?
  • @Derek Tomes:我没有。这些天我没有使用 Windows 或 Visual Studio,所以我不再自己寻找解决方案。

标签: visual-studio multithreading debugging


【解决方案1】:

正如@George 所解释的,您可以使用两个函数。如果您想了解是什么创建了新线程,您应该中断创建 WINAPI 函数调用的线程,例如 CreateThread_beginthread(ex)。如果你想闯入新创建线程的开头(在该线程本身的调用堆栈上),你应该闯入RtlUserThreadStart

但是,要突破其中任何一个,有两个先决条件:

1.需要启用从 Microsoft 服务器下载符号

为了使断点工作,调试器需要知道本机 dll 的一些基本符号信息。确保已启用从 MS 服务器加载符号(工具 -> 选项 -> 调试 -> 符号)

2。您需要目标函数的实际 stdcall 签名

由于某种原因,即使启用了导出加载,VS 也无法找到我需要中断的功能。所以我去检查实际签名是什么。这可以通过使用dumpbin cmd line tool of VS 来完成,或者通过对代码运行一个简短的测试来完成:

void* ptr = CreateThread;

在调试器中运行此代码会显示CreateThread 的地址和签名,例如对我来说是_CreateThreadStub@24(Win7 32 位应用程序)。对于RtlUserThreadStart,我是___RtlUserThreadStart@8

3.设置断点

现在,只需选择 Debug -> New Breakpoint -> Break at Function 即可。对于函数名称,您可以输入 _CreateThreadStub@24___RtlUserThreadStart@8,其余的保留。

然后在创建新线程时触发。

观察

_CreateThreadStub@24 似乎涵盖了 CreateThread、_beginthread 和 _beginthreadex 函数。

【讨论】:

    【解决方案2】:

    我找到了一种方法。在谷歌搜索时,我看到了相反的问题: Is it possible to break on thread exit with specific error code?

    我在函数RtlExitUserThread 处设置了一个断点,之后我意识到所有线程都是使用函数RtlUserThreadStart 创建的,所以在该函数处设置了另一个断点,仅此而已:)

    【讨论】:

    • 您介意详细说明您是如何确切“在函数 RtlUserThreadStart 处设置断点”的吗?即使使用其他帖子中解释的技术,我似乎也无法在 Windows 内部 dll 函数上放置任何断点。
    • 在 Visual Studio 中,在断点窗口(调试/Windows/断点或 Alt+F9)中,您可以为函数符号设置断点。在 Breakpoints 窗口中单击“New”/“Functions Breakpoint...”或 Ctrl+B,然后在对话框中输入函数名称,在本例中为 RtlUserThreadStart。对我来说,即使没有加载 microsoft ntdll.pdb 符号,它也能正常工作。
    • 好吧,这很奇怪。我启用了导出和符号,并且从未在RtlUserThreadStart 上设置断点...但是我能够找到调试器成功中断的 stdcall 签名。还要感谢 Visual Studio 在由于某种原因创建新断点失败时没有给出任何错误消息。
    【解决方案3】:

    当您说要中断线程创建时,您可能意味着至少有两件事:

    1. 在创建新线程之前中断创建线程
    2. 在调用用户提供的函数之前中断 NEW 线程。

    对于第一个选项,您需要中断 CreateThread、_beginthread 或 _beginthreadex。

    对于第二个选项,您需要中断 RtlUserThreadStart 或 BaseThreadInitThunk,以便在调用用户代码之前及早捕获新线程。

    不幸的是,如果您使用我上面列出的函数名称创建断点,Visual Studio 不会中断这些函数,至少默认情况下不会。问题是,对于本机调试,默认情况下它不会加载 DLL 导出,没有它,调试器不知道在哪里可以找到我上面提供的名称。

    在开始调试之前,在 Visual Studio 中转到工具、选项、调试,然后在左窗格中展开调试选项树。然后单击“native”并选中“Load DLL Exports”。然后您可以再次开始调试您的可执行文件。

    之后,您应该能够通过输入名称在我提到的任何函数上创建断点。您可能需要也可能不需要通过创建名称如下的断点来指定函数所在的 DLL:

    {,,kernel32.dll}CreateThread

    {,,ntdll.dll}RtlUserThreadStart

    我从这里开始得到这些信息:

    https://blogs.msdn.microsoft.com/reiley/2011/07/26/debugging-tips-for-multi-threaded-application/

    并自己做一些实验。我知道这是对一个老问题的回答,但我希望这可以减轻其他人在尝试调试线程启动时立即发生的崩溃时所遇到的一些痛苦。

    【讨论】:

    • 关于这个主题的第一个有用的答案......我滚动的时间太长了。我对您的解释仍然存在的唯一问题是通过 {,,dll}FuncName 约定设置的断点似乎仍然没有触发。我不得不做一些 hacky 指针复制来获得这些函数的 stdcall 签名,然后似乎触发了这些签名。但是,不确定这是否只是我的问题。我会再写一个答案,以防有人还在阅读。
    • 这非常有用,谢谢。但是,您确定必须重新启动 VisualStudio 并加载导出吗?我只是有一个进程在工作中停止,并在Watch 面板中编写 CreateThread 或 _beginthreadex,我可以看到它们的地址就好了。实际上,导致它们的导入 thunk 的地址。另一方面,如果我将{,,kernel32.dll}CreateThread 粘贴到Watch 中,我会得到kernel32.dll!0x00007ffe8659b540 (load symbols for additional information)。我能够在'break at function'中使用这个kernel32.dll!0x00007ffe8659b540,它很好地抓住了它。
    【解决方案4】:

    如果您知道线程是由托管 Thread 类创建的,那么一个简单的解决方案是在其条目上放置一个断点(通过转到 Breakpoints 窗口,单击 New->Break at function 并标记 Thread.Start )。

    否则,正如 Hans 所说,您将不得不使用更复杂的解决方案,如 mdbg,或使用 CLR 分析器的开源实现(如 SAE)并在 ICorProfilerCallback 中放置“asm int 3”指令: :ThreadCreated 方法。

    另外,如果你有 VS2010 Ultimate,你可以在 Intellitrace 事件中查找 Thread Created 事件,并可能从那里得到你需要的信息(虽然这不会在创建线程时中断,而是给你一些回想起来,有关堆栈跟踪/变量值在创建时的信息)。

    【讨论】:

    • 一个相关提示是进入断点工具窗口 (Ctrl+Alt+B),点击添加断点并输入 Foo.Foo(其中 Foo 是类的名称,如 Thread 或 BackgroundWorker),它会在 Foo 的所有构造函数上放置断点。
    • 我的应用程序一启动就为Thread.Start 添加了一个断点,VS 捕获了它。然后,我无法恢复,每次点击VS2010,它都会告诉我它正在等待一个进程。重点是,在您的应用程序启动之前不要添加断点。
    【解决方案5】:

    是的,调试器会收到通知。不,用户界面不允许您告诉它中断该通知。如果这是用于托管代码,那么您可以使用 MDbg 示例并对其进行修改。

    【讨论】:

      【解决方案6】:

      您可以在第一次调用CreateThread 时设置断点(也许您为此插入了一个假调用)。 点击后,从 Source Code View 更改为 Disassemly View 并进入通话。浏览 ImportLib-Code 并设置断点。不完美,但它有效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-28
        • 2017-05-23
        • 1970-01-01
        • 1970-01-01
        • 2014-05-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多