当我们使用 CTRL + Break 中断时如何在 WinDbg 中查看真实的调用堆栈

How to see real call stack in WinDbg when we break with CTRL + Break

当我们有一个常规断点时,调试器会在那里中断,我们会看到调用堆栈。但是如果我们使用 CTRL + Break 中断执行呢?我希望当前的 运行 线程会中断它所在的位置,但我通常看到的是以下内容:

 # ChildEBP RetAddr  
00 02b7fdc4 7762f216 ntdll!DbgBreakPoint
01 02b7fdf4 76c0336a ntdll!DbgUiRemoteBreakin+0x3c
02 02b7fe00 775c9902 kernel32!BaseThreadInitThunk+0xe
03 02b7fe40 775c98d5 ntdll!__RtlUserThreadStart+0x70
04 02b7fe58 00000000 ntdll!_RtlUserThreadStart+0x1b
*** WARNING: Unable to verify checksum for ThreadDemo.exe

但是我怎样才能得到被中断的当前线程的实际代码呢?

我是 运行 一个长循环,如下所示,需要一些时间,我希望它能在那里中断。

void Bank::Deposit(void* param)
{
    Bank * bank = (Bank *)param;

    char *who = (char*) bank->bankName;

    int i;
    printf("%s: begin deposite\n", who);
    for (i = 0; i < 1000000000; i++) {
        bank->balance = bank->balance + 1;
    }
    printf("%s: done\n", who);

    return;
}

在这种情况下,当它中断时,另一个线程确实中断了这个函数,但这是一个简单的演示。当我有很多线程时,我如何知道在真实的 windows 应用程序中应用程序真正停止的位置(以及它在做什么)?

按 ctrl-c 时没有 'current thread that is running'。零个、一个或多个线程 可能 是 运行,因此调试器甚至不会尝试找出 'which' 一个要中断的线程 - 它会自行中断线。仅仅因为您有一个当时可能是 运行 的线程(但不一定如此)并不会改变这一点。

但你可以得到你想要的。当您按下 ctrl-c 时,它会中断调试器线程并停止所有其他线程。您可以通过键入以下内容列出所有线程及其调用堆栈:

~*k

您还可以通过键入以下内容切换到另一个线程:

~Ns

其中 N 是要切换到的线程的 ID。

一旦您将 'current thread' 设置为您想要的,您可以键入 k 以列出其调用堆栈和其他线程特定命令。

编辑:

有关 windbg 线程特定命令的完整列表,请参阅 this

当您按下 Ctrl+CCtrl+Break ,该键盘事件由调试器处理。这意味着当时至少有一个调试器线程 运行ning 而不是你的。弄清楚哪个线程之前 运行ning 不是那么容易。

调试器将使用调用堆栈上的 int 3 指令将新线程注入您的应用程序。此线程不会立即 运行ning,它首先必须由操作系统调度到 运行。这需要上下文切换。

当达到 int 3 时,进程中的所有线程都会暂停,并且调试器会收到有关异常的通知。因此,在您按下一个键和线程实际停止之间,经过了大约 15 毫秒到大约 30 毫秒的时间。

不过,这没什么好担心的。鉴于人类的平均反应时间为 25 毫秒,数百万条 CPU 指令发生在使您想停止线程的观察结果与实际停止之间。

正如 Sean Cline 在评论和 Mike Vine 的回答中提到的那样,其他线程仍然存在。您可以使用 ~ 列出它们,使用 ~*k 显示它们的调用堆栈或使用 ~[= 切换到特定线程15=].