在MinGW中的GDB上,我该如何让Ctrl-C停止程序?

16

我在Windows上,运行在MinGW下编译的可执行文件上的GDB。该程序有一个无限循环。我想通过按下Ctrl + C来找到它。但是这样做会导致程序和GDB同时退出。所有有关此主题的帮助似乎都默认我在Linux上。

8个回答

10

这是因为 GDB 不能正确处理 GUI(非控制台)程序的 Ctrl + C 事件。

你可以在 Workaround for GDB Ctrl-C Interrupt 中找到解决方法。


更新: 鉴于 mingw.org 域名已过期,以下代码通过 Web Archive 恢复:

如果您正在尝试使用 Ctrl-C 打断被 GDB 调试的程序并失败,则此小程序将允许您从另一个会话发出中断。只需运行它并传递正在调试的进程的 Windows pid,GDB 将重新获得控制。[要编译,请使用 gcc -o debugbreak -mno-cygwin -mthreads debugbreak.c ]

/* BEGIN debugbreak.c */

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#if _WIN32_WINNT < 0x0501
#error Must target Windows NT 5.0.1 or later for DebugBreakProcess
#endif

#include <Windows.h>
#include <stddef.h>
#include <stdlib.h>

/* Compile with this line:

    gcc -o debugbreak -mno-cygwin -mthreads debugbreak.c

*/

static char errbuffer[256];

static const char *geterrstr(DWORD errcode)
{
    size_t skip = 0;
    DWORD chars;
    chars = FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, errcode, 0, errbuffer, sizeof(errbuffer)-1, 0);
    errbuffer[sizeof(errbuffer)-1] = 0;
    if (chars) {
        while (errbuffer[chars-1] == '\r' || errbuffer[chars-1] == '\n') {
            errbuffer[--chars] = 0;
        }
    }
    if (chars && errbuffer[chars-1] == '.') errbuffer[--chars] = 0;
    if (chars >= 2 && errbuffer[0] == '%' && errbuffer[1] >= '0'
        && errbuffer[1] <= '9')
    {
        skip = 2;
        while (chars > skip && errbuffer[skip] == ' ') ++skip;
        if (chars >= skip+2 && errbuffer[skip] == 'i'
            && errbuffer[skip+1] == 's')
        {
            skip += 2;
            while (chars > skip && errbuffer[skip] == ' ') ++skip;
        }
    }
    if (chars > skip && errbuffer[skip] >= 'A' && errbuffer[skip] <= 'Z') {
        errbuffer[skip] += 'a' - 'A';
    }
    return errbuffer+skip;
}

int main(int argc, char *argv[])
{
    HANDLE proc;
    unsigned proc_id = 0;
    BOOL break_result;

    if (argc != 2) {
        printf("Usage: debugbreak process_id_number\n");
        return 1;
    }
    proc_id = (unsigned) strtol(argv[1], NULL, 0);
    if (proc_id == 0) {
        printf("Invalid process id %u\n", proc_id);
        return 1;
    }
    proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)proc_id);
    if (proc == NULL) {
        DWORD lastError = GetLastError();
        printf("Failed to open process %u\n", proc_id);
        printf("Error code is %lu (%s)\n", (unsigned long)lastError,
            geterrstr(lastError));
        return 1;
    }
    break_result = DebugBreakProcess(proc);
    if (!break_result) {
        DWORD lastError = GetLastError();
        printf("Failed to debug break process %u\n", proc_id);
        printf("Error code is %lu (%s)\n", (unsigned long)lastError,
            geterrstr(lastError));
        CloseHandle(proc);
        return 1;
    }
    printf("DebugBreak sent successfully to process id %u\n", proc_id);
    CloseHandle(proc);
    return 0;
}

/* END debugbreak.c */

感谢 Kyle McKay 在cygwin邮件列表上发布的代码http://cygwin.com/ml/cygwin/2006-06/msg00321.html


1
很遗憾,你只能使用解决方法来调试Windows程序。至于我,我正在调试一个Android程序 :( - Qwertie
1
切换到适当的控制台,例如cmd.exe的控制台确实有所帮助:我按下Ctrl+C,GDB的行为就像在Linux上一样。因此,这里链接的解决方法并不是那么有用。 - Ruslan
在我的情况下,使用mingw gdb在Windows 10下测试程序,从powershell切换到cmd.exe并没有帮助。而所提出的解决方法却有效。 - borizzzzz

5

您使用的是哪种“shell”?如果您使用的是MSYS“rxvt” shell,则行为与您描述的基本相同。只有在从普通的Windows命令提示符中运行时,Ctrl-C才有效。


这是有用的信息。我正在Windows命令提示符下,它似乎不起作用,但我会再试一次。 - Mike Dunlavey
你也可以尝试使用“Console2”。它的行为类似于Windows命令提示符,但外观更加美观。我经常使用它来调试gdb,从未遇到任何问题。https://sourceforge.net/projects/console/ - Matthew Talbert

4

我也遇到过同样的问题。解决方法是使用cmd.exe和gdb,并在gdb中设置如下选项。

set new-console on

现在我可以使用Ctrl + c来中断GUI和控制台程序。

2
我刚刚遇到了同样的问题。
维基上的解决方法是使用调试进程的pid运行debugbreak,但是ps只显示gdb的pid,可能还有其他获取pid的方法。
但是有一个更简单的解决方法。只需正常启动程序(不在gdb中),从ps中检查pid并将其作为第二个参数启动gdb。 当gdb附加到进程时,进程会停止,然后我可以打印回溯信息。

1
谢谢。一段时间前我遇到了这个问题:按下^C,调试器停止了,但它停在了一个不同于主程序的线程上。如果我运行info threads命令,它会告诉我主程序的线程号。然后我切换到那个线程,就可以使用bt命令来显示堆栈信息。我想对真正了解GDB的人来说,这是非常显而易见的。 - Mike Dunlavey

1
这里有一个每次都有效的解决方案:
当GDB启动时,请使用此正则表达式来捕获子进程ID:
"\[New Thread (\d+)\."

然后使用:
hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, PID);
DebugBreakProcess(hProcess);
CloseHandle(hProcess);

请查看以下GDB初始化脚本,这是在Windows 7及更高版本上使用MinGW所必需的:
# =====================================
#  GDB preload initialization commands
# =====================================

# Set Unicode Charset
#set target-charset UCS-2
#set host-charset UCS-2
#set charset UCS-2
#set target-wide-charset UCS-2

# Set UTF-8 Charset
set target-charset UTF-8
set host-charset UTF-8
set charset UTF-8
set target-wide-charset UTF-8

# Request async target mode
set target-async 1

# Do not complain about pending breakpoints
set breakpoint pending on

# Enable All-Stop for all Threads
set non-stop off

# Do not ask for confirmations
set confirm off

# Do not create new console for output/logging
set new-console off

# Turn-off paggination to allow integration with IDE
set pagination off

# Call Stack files (and anywhere else) should be absolute path
set filename-display absolute

# Enable Pretty Print in GDB Panel
set print pretty on

# Enable notification of completion for asynchronous execution commands.
set exec-done-display on

# Show Addresses in objects, required for integration with IDE
set print address on

# Enable Pretty Print for Arrays
set print array on

# Flatten objects, required for integration with IDE
set print object off

# Include static members, required for integration with IDE
set print static-members on

# Show demangled vtable, required for integration with IDE
set print vtbl off
set print demangle on
set demangle-style gnu-v3

# Print full eight-bit characters, required for integration with IDE
set print sevenbit-strings off

# Set path for obj files
path $(TARGET_ROOT)/obj

# Load gdb scripts for STL (string, vector, map, etc.)
source $(PATH_SDK_DEBUGGER)/stl-views-1.0.3.gdb

# List of source code files
dir $(PATH_SDK_COMMON)
dir $(PATH_SDK_FRAMEWORKS)
dir $(PATH_SDK_INCLUDES)
dir $(PROJECT_PATHS.NATIVE_COMMON)

# Load the binary
file $(TARGET_OUTPUT)

1

正如Matthew Talbert提出的那样,当使用使用本地MinGW工具链构建的GDB在MSYS/Cygwin中使用时会出现这种情况。使用winpty启动GDB非常有效,因为它是专门为此设计的工具。它也适用于cdb.exe


1
如果您从https://www.msys2.org/安装最新的MinGW-x64,那么Ctrl-C就可以正常使用。
c:\test>gdb a.exe
GNU gdb (GDB) 7.11.1
. . .
Reading symbols from a.exe...done.
(gdb) r
Starting program: c:\test\a.exe
<Ctrl>-<C>
Thread 5 received signal SIGINT, Interrupt.
[Switching to Thread 17312.0x5614]
0x00007ff97e75d7e3 in TlsGetValue () from C:\WINDOWS\System32\KernelBase.dll
(gdb)

如果您使用的是Cygwin,您需要告诉gdb处理SIGINT:
(gdb) handle SIGINT
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal        Stop      Print   Pass to program Description
SIGINT        Yes       Yes     No              Interrupt
(gdb)

0

要找到无限循环,您可以尝试逐步执行,直到您到达一个无限重复的序列。

我不确定,但我认为Ctrl-C只应该停止执行,而不是gdb本身...

我认为有一个“handle”命令,您可以使用它来控制如何处理中断信号。


我知道。我希望不必这么做。 - Mike Dunlavey
你说的两点都是对的,但在Windows DOS窗口似乎不起作用。 - Mike Dunlavey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接