Bug 86904 - "Pause" does not work properly on Windows
"Pause" does not work properly on Windows
Status: RESOLVED FIXED
Product: cnd
Classification: Unclassified
Component: Debugger
5.x
All All
: P1 (vote)
: 6.x
Assigned To: _ gordonp
issues@cnd
gdb
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2006-10-11 06:27 UTC by Nikolay Molchanov
Modified: 2009-06-25 10:59 UTC (History)
0 users

See Also:
Issue Type: DEFECT
:


Attachments
Project Args with infinite loop ("Pause" pressed) (850.55 KB, application/octet-stream)
2006-11-10 19:41 UTC, Nikolay Molchanov
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Nikolay Molchanov 2006-10-11 06:27:12 UTC
Button "Pause" is supposed to interrupt program, so that it is possible to 
find out where it runs, and continue its execution using "Step" or "Continue"
buttons. The implementation is based on signal SIGINT, which is sent to the
program, and gdb reports where teh program is stopped. It works just fine on 
Solaris and Linux with all supported gdb versions, but does not work properly
on Windows - it interrupts program, but this interruption causes SIGSEGV, which
means "Segmentation Fault" in program, so there is no information where the
program was stopped (at what line), and there is no way to continue it.

Here is a log file, that shows what happens in program when "Pause" is pressed:

Start debugger gdb
gdb_cmd=gdb --nw --interpreter=mi
~"GNU gdb 6.5.50.20060706-cvs (cygwin-special)\n"
~"Copyright (C) 2006 Free Software Foundation, Inc.\n"
set new-console
~"GDB is free software, covered by the GNU General Public License, and you are\n"
~"welcome to change it and/or distribute copies of it under certain conditions.\n"
~"Type \"show copying\" to see the conditions.\n"
~"There is absolutely no warranty for GDB.  Type \"show warranty\" for details.\n"
~"This GDB was configured as \"i686-pc-cygwin\"."
~"\n"
(gdb) 
&"set new-console\n"
^done
(gdb) 
212-file-exec-and-symbols
C:/tmp/nikm/samples/LongLoop1/dist/Debug/GNU-Windows/longloop1
212^done
(gdb) 
501-break-insert C:/tmp/nikm/samples/HelloApp1/helloapp/main.cc:28
502-break-insert hello2
&"No source file named C:/tmp/nikm/samples/HelloApp1/helloapp/main.cc.\n"
501^done
503-break-insert main.cc:39
(gdb) 
&"Function \"hello2\" not defined.\n"
502^done
(gdb) 
503^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x00401270",func="main",file="main.cc",fullname="/cygdrive/c/tmp/nikm/samples/LongLoop1/main.cc",line="39",times="0"}
(gdb) 
-exec-run 
^running
(gdb) 
*stopped,reason="breakpoint-hit",bkptno="1",thread-id="1",frame={addr="0x00401270",func="main",args=[{name="argc",value="1"},{name="argv",value="0x6c1b58"}],file="main.cc",fullname="/cygdrive/c/tmp/nikm/samples/LongLoop1/main.cc",line="39"}
(gdb) 
shell ps
&"shell ps\n"
-stack-list-locals --all-values
      PID    PPID    PGID     WINPID  TTY  UID    STIME COMMAND
     2324       1    2324       2324  con 1005 22:17:29 /usr/bin/gdb
-stack-list-locals --simple-values
     3736       1    3736       3736  con 1005 22:17:30
/cygdrive/c/tmp/nikm/samples/LongLoop1/dist/Debug/GNU-Windows/longloop1
     2080    2324    2324       2544  con 1005 22:17:30 /usr/bin/ps
-stack-list-frames
^done
(gdb) 
^done,locals=[{name="cp",value="0x6c1b88
\"/cygdrive/c/tmp/nikm/samples/LongLoop1/dist/Debug/GNU-Windows/longloop1\""},{name="car",value="\"qwerty\\000\\000\\000\\000\\000\""},{name="iar",value="{1,
2, 3}"},{name="lar2",value="{{1, 2, 3}, {4, 5, 6}, {7, 8,
9}}"},{name="shar4",value="{{{1, 2}, {3, 4}}, {{5, 6}, {7,
8}}}"},{name="i",value="1627534502"},{name="a",value="{fa = -1, fb =
25241}"},{name="b",value="{fa = 0, fb = 2089917168}"}]
(gdb) 
^done,locals=[{name="cp",type="char *",value="0x6c1b88
\"/cygdrive/c/tmp/nikm/samples/LongLoop1/dist/Debug/GNU-Windows/longloop1\""},{name="car",type="char
[12]"},{name="iar",type="int [3]"},{name="lar2",type="long int
[3][3]"},{name="shar4",type="short int
[2][2][2]"},{name="i",type="int",value="1627534502"},{name="a",type="s2"},{name="b",type="s2"}]
(gdb) 
^done,stack=[frame={level="0",addr="0x00401270",func="main",file="main.cc",fullname="/cygdrive/c/tmp/nikm/samples/LongLoop1/main.cc",line="39"}]
(gdb) 
-exec-continue 
^running
(gdb) 
~"\n"
~"[Switching to thread 3736.0xc58]\n"
*stopped,reason="signal-received",signal-name="SIGSEGV",signal-meaning="Segmentation
fault",thread-id="2",frame={addr="0x00002778",func="??",args=[]}
(gdb) 
-stack-list-locals --all-values
^done,locals=[]
(gdb) 
-stack-list-locals --simple-values
^done,locals=[]
(gdb) 
215-file-list-exec-source-file
215^done,line="39",file="main.cc",fullname="/cygdrive/c/tmp/nikm/samples/LongLoop1/main.cc"
(gdb) 
-stack-list-frames
^done,stack=[frame={level="0",addr="0x00002778",func="??"},frame={level="1",addr="0x00000000",func="??"}]
(gdb)
Comment 1 Nikolay Molchanov 2006-10-11 08:45:47 UTC
I found interesting information about Eclipse implementation:

http://sources.redhat.com/ml/gdb/2006-05/msg00274.html

    * From: Alain Magloire <alain at qnx dot com>
    * To: gdb at sources dot redhat dot com
    * Date: Thu, 18 May 2006 10:35:53 -0400
    * Subject: RE: invoking GDB from FE and signals

Surprisingly, the main reason why we use a pty in the Eclipse/CDT is to get
rid of the xxxx number of PRs about not seeing the output of printf() and
before you ask ... telling the user to fflush() and explaining the stdio
flushing policy for pipes/files vs. terminal ended up in hate mails.

We send the signal to the inferior ... the problem when running gdb is to
... get the inferior PID ... sigh. We have circumvent the problem is
commercial products but did not fine a generic way to get the pid.

------------------------------------------------------------------

It shows that Eclipse/CDT is using a similar way to interrupt inferior program
as we do (send signal), they also have a problem finding pid, and they solved 
the "fflush()" problem using pty (IZ 76173 No automatic flush of output before 
input), which is very close to what we use in gdb-lite.

BTW, I posted a question about our problem with SIGINT on Windows:

http://sources.redhat.com/ml/gdb/2006-10/msg00058.html

Hope to see some answer tomorrow.
Comment 2 Nikolay Molchanov 2006-10-17 18:59:36 UTC
I have received 2 answers from gdb developers. Here is my question and their 
answers.
----------------------------------------------------------------------------
http://sources.redhat.com/ml/gdb/2006-10/msg00058.html

Hello GDB developers and users!

I asked this question some time ago (in May 2006), and I implemented a solution
for Unix (it is based on signal), but I still cannot find a good solution for
Windows.
Here is my original question:

I'm looking for a solution how to stop the inferior program,
which is running under gdb. I can stop it using ^C, but I
need a solution, that will work for the following case
on Windows (Cygwin):

Java application
      |   ^
stdin |   | stdout
      v   |
GDB debugger (gdb --i mi --tty /dev/pts/2 ...)
      |  ^
      v  |
Inferior program  <--> External terminal (/dev/pts/28)


The action to stop the program is initiated by
Java application, which cannot send ^C to the
external terminal.


Nick Roberts suggested to use signals to implement "interrupt" action,
and it really works very well on Unix (Solaris and Linux), but unfortunately
it does not work properly on Windows. If I send SIGTSTP, gdb does not
show that the program is stopped (and it really does not stop), so it looks
like this signal is ignored. If I send SIGINT, the program is really interrupted,
but it seems to cause SIGSEGV (Segmentation Fault), and there is no way
to continue the debugging. Here is a scenario (I use Cygwin):

Window 1: start debugging, set breakpoint, continue
===================================
$ gdb --i mi
~"GNU gdb 6.5.50.20060706-cvs (cygwin-special)\n"
~"Copyright (C) 2006 Free Software Foundation, Inc.\n"
~"GDB is free software, covered by the GNU General Public License, and you are\n"
~"welcome to change it and/or distribute copies of it under certain conditions.\n"
~"Type \"show copying\" to see the conditions.\n"
~"There is absolutely no warranty for GDB.  Type \"show warranty\" for details.\n"
~"This GDB was configured as \"i686-pc-cygwin\"."
~"\n"
(gdb)
-file-exec-and-symbols
C:/tmp/nikm/samples/LongLoop1/dist/Debug/GNU-Windows/longloop1
^done
(gdb)
-break-insert main
^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x004011b
5",func="main",file="main.cc",fullname="/cygdrive/c/tmp/nikm/samples/LongLoop1/m
ain.cc",line="33",times="0"}
(gdb)
-exec-run
^running
(gdb)
*stopped,reason="breakpoint-hit",bkptno="1",thread-id="1",frame={addr="0x004011b
5",func="main",args=[{name="argc",value="1"},{name="argv",value="0x6c1f50"}],fil
e="main.cc",fullname="/cygdrive/c/tmp/nikm/samples/LongLoop1/main.cc",line="33"}
(gdb)
-exec-continue
^running
(gdb)


Window 2: send signal
===============
$ ps
     PID    PPID    PGID     WINPID  TTY  UID    STIME COMMAND
    1520       1    1520       1520  con 1005 23:22:26 /usr/bin/bash
     884       1     884        884  con 1005 23:23:47 /usr/bin/bash
    1832    1520    1832       3880  con 1005 23:24:17 /usr/bin/gdb
    4036       1    4036       4036  con 1005 23:27:14 /cygdrive/c/tmp/nikm/sam
ples/LongLoop1/dist/Debug/GNU-Windows/longloop1
    2380     884    2380       2336  con 1005 23:27:16 /usr/bin/ps

$ kill  -INT  4036


Window 1: caught signal
=================
~"\n"
~"[Switching to thread 4036.0x784]\n"
*stopped,reason="signal-received",signal-name="SIGSEGV",signal-meaning="Segmenta
tion fault",thread-id="2",frame={addr="0x000007d4",func="??",args=[]}
(gdb)

After that there is no way to continue:
=========================
-exec-continue
^running
(gdb)
*stopped,reason="signal-received",signal-name="SIGSEGV",signal-meaning="Segmenta
tion fault",thread-id="2",frame={addr="0x000007d4",func="??",args=[]}
(gdb)

Could you please tell me if there any other way to interrupt program on Windows,
so that gdb will show where it is stopped, and will allow to continue?

Thanks in advance,
Nikolay Molchanov

--------------------------------------------------------------------------
From: Daniel Jacobowitz 

On Tue, Oct 10, 2006 at 11:47:11PM -0700, Nikolay Molchanov wrote:
> > Nick Roberts suggested to use signals to implement "interrupt" action,
> > and it really works very well on Unix (Solaris and Linux), but unfortunately
> > it does not work properly on Windows. If I send SIGTSTP, gdb does not
> > show that the program is stopped (and it really does not stop), so it looks
> > like this signal is ignored. If I send SIGINT, the program is really 
> > interrupted,
> > but it seems to cause SIGSEGV (Segmentation Fault), and there is no way
> > to continue the debugging. Here is a scenario (I use Cygwin):

You should be using SIGINT for this, yes.  Windows SIGINT is funny, but
if you're dealing with Cygwin, it should be OK - in theory.  It doesn't
work for me either; I get the program exiting with code 01000, instead
of a segfault, but the result is equally useless.

Maybe this is a bug in Cygwin?

Eventually, you'll be able to do this properly in GDB.  Nick has been
working hard on asynchronous operation, and once that's ready, we can
implement the -exec-interrupt operation using Windows API calls.  In
the mean time, I have no idea.

-- Daniel Jacobowitz 
CodeSourcery 
---------------------------------------------------------------------
From: Eli Zaretskii 

> Date: Wed, 11 Oct 2006 09:49:16 -0400
> > From: Daniel Jacobowitz <drow@false.org>
> > Cc: gdb@sourceware.org
> > 
> > Eventually, you'll be able to do this properly in GDB.  Nick has been
> > working hard on asynchronous operation, and once that's ready, we can
> > implement the -exec-interrupt operation using Windows API calls.

Yes, I think the best idea is to have a Windows-specific
implementation (alongside with a Posix one).  Signals is one of the
few areas where Windows is so different from Posix that no matter how
hard you try to disguise it, that difference will eventually stick
out.

-------------------------------------------------------------------------

Here is a result of my investigation and a potential solution we can try to 
implement (for Windows only).

I found out that if I add a signal handler to a program (which has a long loop),
start debugger, load program, set breakpoint at my signal handler, run program,
and send the expected signal (e.g. SIGTSTP, or SIGINT, or SIGALRM) - debugger
shows that program is stooped at the breakpoint in signal handler. After that 
I can do several single steps (usually 5), and it shows the line in source 
code where the loop is (where the execution was interrupted).
If I do "continue", the execution goes on. 
I think this is very important "discovery", and we can use this approach to
implement a solution for this problem. What we need is to add a specific 
signal handler to the binary. We can do it by adding a simple source file
to the project:
/* File: gdb_set_SIGTSTP_signal_handler.cc */
#include <signal.h>
void gdb_SIGTSTP_signal_handler(int signal) {return;}
void gdb_set_SIGTSTP_signal_handler() {
    signal(SIGTSTP, gdb_SIGTSTP_signal_handler);
}

When debugging is starting, we can set a breakpoint in gdb_SIGINT_signal_handler
function, and when this breakpoint happens, we can do several single steps to 
get to the line in project sources, and display this line.
If user pressed "Pause" button, IDE sends SIGTSTP signal to the program,
and gdb will stop execution at the breakpoint in gdb_SIGINT_signal_handler.

There is an alternative solution. We can try to add a SIGALRM signal handler 
to the program, which will generate SIGALRM every 5 seconds (or more often),
We will set a breakpoint at this signal handler, and every time it happens
we will check if user wants to interrupt the program. If yes, we will use the
algorithm described above to go to the proper source line and stop there.
I think something similar is used in Eclipse. I think so because there is a
long delay (about 5 seconds) between the moment "Pause" is pressed, and the 
moment program is stopped.

The first potential solution looks better to me, because it works faster.






Comment 3 Nikolay Molchanov 2006-10-24 19:45:26 UTC
I marked this issue FIXED, though there are still several minor problems.
First of all we have to document teh recommendation to customers to add 
the following code to their projects if they need multiple ability to
interrupt their programs (this is for Windows/Cygwin only):

#ifdef __CYGWIN__
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *             Windows Cygwin SIGTSTP Signal Handler             *
 * This code is not used by the program.  It is used by debugger *
 * to implement execution interruption in Cygwin environment.    *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <signal.h>
void gdb_set_SIGTSTP_signal_handler();
void gdb_SIGTSTP_signal_handler(int signal) {
    gdb_set_SIGTSTP_signal_handler();
}
void gdb_set_SIGTSTP_signal_handler() {
    signal(SIGTSTP, gdb_SIGTSTP_signal_handler);
}
#endif /* __CYGWIN__ */


I added a long loop to the sample project MP, so QA can use it to verify the 
fix. As soon as the fix is verified, I'll remove this loop.

Comment 4 Jesse Grodnik 2006-10-25 17:30:08 UTC
Unfortunately it's unrealistic to expect users to add code to their existing
code so as to make "pause" work. Perhaps the gdb interface can implicitly add
the necessary code. In any case some other solution is needed.

The defect is reopened with priority P1.
Comment 5 Nikolay Molchanov 2006-10-30 03:49:24 UTC
Unfortunately there was a misunderstanding how the fix works. The recommendation
to customers to add the signal handler code *is only for unmanaged projects*. 
All managed projects are under our control, so we do not need to ask users to 
add this code to their projects (we will add them in build process, IZ 88196).
In unmanaged projects "Pause" also works, but there is a problem with second
interruption (it is ignored). So, after the implemented fix, the remaining 
issue is pretty minor (see IZ 88195).
Comment 6 _ gordonp 2006-10-30 20:07:41 UTC
Opening and reassigning.
Comment 7 _ gordonp 2006-11-09 02:26:34 UTC
Added signal handler which is LD_PRELOADED from cnd.dll (windows only).

This solution is limited in that it won't work for attach. However, we
don't support attach in gdb-lite 1.0, so I'll worry about attach later...
Comment 8 Nikolay Molchanov 2006-11-10 19:41:39 UTC
Created attachment 35975 [details]
Project Args with infinite loop ("Pause" pressed)
Comment 9 Nikolay Molchanov 2006-11-10 19:44:54 UTC
Unfortunately the fix does not work in some cases.
Create a sample project Args, and add one line to the source (an infinite loop):

   for (i=1; i>0; i);

Now if you start debugging, the program will run this infinite loop. If you
press "Pause" it will not stop. If you press "Pause" again,
it will stop in function _CndSigHandler, which is not user's function, and there
is no indication in source file where it is stopped.

See the attached screen shot, which shows how it looks, when Pause is pressed.
The problem is that it stops the execution, but does not show the source line.
Call stack shows that it is stopped in _CndSigHandler, which is not user's
function, and is not a part of Cygwin, so user has no clue what is going on.

The source code of Args project with added line is as follows:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char**argv) {
   int i;
      // Prints arguments
   printf("Arguments:\n");
   for (i = 0; i < argc; i++) {
       printf("%i: %s\n", i, argv[i]);
   }
   for (i=1; i>0; i);
   return 0;
} 


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo