GDB Ctrl-C Interrupt Fails WORKAROUND

Kyle McKay
Thu Jun 15 19:28:00 GMT 2006

On 15 Jun 2006 13:57:14 -0400, Christopher Faylor wrote:
> On Cygwin, gdb is the debugger for programs produced by gcc.  You are
> not going to be able to read many (any?) symbols for programs produced
> by other compilers so there really isn't much of a reason to use gdb
> to debug non-gcc-produced programs.

Apparently you did not see my previous comments about this:

> On 15 Jun 2006 10:38:57 -0700, Kyle McKay wrote:
>> Now if you have such a program compiled with the m$ compiler for  
>> which you do not have the source and such a program is loading a  
>> DLL plugin built with cygwin that you're trying to debug then CTRL- 
>> C WILL NEVER WORK no matter how many console windows you get out.

DLLs built with gcc cannot be interrupted with CTRL-C when they are  
loaded by programs built with other compilers.

> In any event, at least we're getting details now beyond the "CTRL-C
> doesn't work with gdb".

Or these comments about using a console window:

> On 15 Jun 2006 10:38:57 -0700, Kyle McKay wrote:
>> It is also a non-solution for ssh users and remote X users.

CTRL-C also appears to be used by the X windows ddd break button.   
Doesn't work there either.

However, in all these cases the debugbreak (debugbreak.c) utility can  
provide a usable, if awkward, workaround.

For example, build the loopdll.dll and runloop.exe from the following  
sources using the gcc and m$ compilers as indicated:

/* BEGIN loopdll.c */
/* Compile with cygwin gcc -o loopdll.dll -g -mno-cygwin -mthreads - 
mdll loopdll.c */
#include <Windows.h>
extern __declspec(dllexport) void Looper(void);
__declspec(dllexport) void Looper(void)
     for (;;)
         /* do nothing except eat CPU */
/* END loopdll.c */

/* BEGIN runloop.c */
#include <Windows.h>
#include <stddef.h>
/* Compile with m$ cl -o runloop.exe runloop.c */
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
     FARPROC loopfunc;
     HMODULE lib = LoadLibrary("loopdll.dll");
     if (lib != NULL) {
         loopfunc = GetProcAddress(lib, "Looper");
         if (loopfunc != NULL) {
     return 0;
/* END runloop.c */

Then type gdb runloop.exe (making sure loopdll.dll is located in the  
current directory along with runloop.exe) followed by run.  Do this  
from a CONSOLE window without tty set.  Now try CTRL-C.  Doesn't  
work.  Build the debugbreak utility and then type this at a cygwin  
bash prompt while gdb is stuck:

./debugbreak `ps -W | grep runloop | awk '{print $1}'`

gdb will regain control.  Type "list Looper" or "break Looper" and  
you will see that indeed you can use gdb to debug with symbols in  
this case, set breakpoints, etc.  However CTRL-C is useless for  
interrupting the running program.

This is not a contrived example.  It mirrors the situation found when  
attempting to use the cygwin environment to build DLL plugins (and  
debug them) for commercial windows applications for which you do not  
have the source and which were not built using cygwin.  (Note that  
this example can also be used to demonstrate that having "set stop-on- 
solib-events 1" active in gdb before typing run does NOT cause gdb to  
regain control when loopdll.dll is first loaded.)


P.S. (Here is debugbreak.c again to build debugbreak for use with  
this example:)

/* BEGIN debugbreak.c */
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501

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

#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(
         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,
         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,
         return 1;
     printf("DebugBreak sent successfully to process id %u\n", proc_id);
     return 0;
/* END debugbreak.c */

Unsubscribe info:
Problem reports:

More information about the Cygwin mailing list