This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: gdb pty problem [Was: emacs gud-interface is not updated after gdb command execution (maybe because of incomplete output from gdb)]


Hi Corinna,

I had looked into this problem, and found the cause.

'man termios' says:
"A read(2) returns at most one line of input" in canonical mode.

On cygwin 2.5.1, read(2) returns all data in buffer if the buffer
size specified is large enough. This behaviour is correct in
noncanonical mode, but is not correct in canonical mode.

So, I would like to propose a following patch.

diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 6c00d61..733644f 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -756,10 +756,6 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 	  goto out;
 	}
 
-      /* On first peek determine no. of bytes to flush. */
-      if (!ptr && len == UINT_MAX)
-	len = (size_t) bytes_in_pipe;
-
       if (ptr && !bytes_in_pipe && !vmin && !time_to_wait)
 	{
 	  ReleaseMutex (input_mutex);
@@ -767,7 +763,9 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 	  return;
 	}
 
-      readlen = MIN (bytes_in_pipe, MIN (len, sizeof (buf)));
+      readlen = bytes_in_pipe ? MIN (len, sizeof (buf)) : 0;
+      if (get_ttyp ()->ti.c_lflag & ICANON && ptr)
+	readlen = MIN (bytes_in_pipe, readlen);
 
 #if 0
       /* Why on earth is the read length reduced to vmin, even if more bytes
@@ -804,7 +802,8 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 		}
 	      if (n)
 		{
-		  len -= n;
+		  if (!(!ptr && len == UINT_MAX)) /* not tcflush() */
+		    len -= n;
 		  totalread += n;
 		  if (ptr)
 		    {

While checking this problem, I found a bug of tcflush().
tcflush() flushes only partial data in the buffer.

The patch above also fixes this bug.

A test case attached (pty_readlen.c) is for confirming
the behaviour of read() and tcflush(). 

Test results are as follows.

debian GNU/Linux:
----------
01234
----------
56789
ABCDE
----------
----------

cygwin 2.5.1:
----------
01234
56789
----------
ABCDE
----------
KLMNO
----------

cygwin 2.5.2 (git head):
----------
01234
----------
56789
----------
FGHIJ
----------

cygwin with a patch I propose:
----------
01234
----------
56789
ABCDE
----------
----------

Of course, Ken's gdbstc works fine without sleep with this patch,
as well as original emacs case.



On Wed, 1 Jun 2016 16:18:20 +0200
Corinna Vinschen <corinna-cygwin@cygwin.com> wrote:

> Hi Takashi,
> 
> 
> can you please have a look?
> 
> 
> Thanks,
> Corinna
> 
> 
> On Jun  1 08:51, Ken Brown wrote:
> > On 5/31/2016 5:41 AM, Corinna Vinschen wrote:
> > > Any chance you could bisect Cygwin to help finding the culprit?
> > 
> > The culprit is
> > 
> > commit 252a07b0ad3353abcd0fcd9b1b65ff977acd679e
> > Author: Takashi Yano <takashi.yano@nifty.ne.jp>
> > Date:   Fri Apr 3 13:07:35 2015 +0900
> > 
> >    Cygwin hangs up if several keys are typed during outputting a lot of texts.
> > 
> >        * fhandler_tty.cc (fhandler_pty_slave::read): Change calculation of
> >        "readlen" not to use "bytes_in_pipe" value directly.
> > 
> > 
> > Reverting that commit fixes the problem.  To test, compile and run the attached file.
> > 
> > $ gcc gdbstc.cc
> > 
> > $ ./a
> > 1-inferior-tty-set /dev/pty3
> > 2-gdb-set height 0
> > 3-gdb-set non-stop 1
> > 4-file-list-exec-source-files
> > 5-file-list-exec-source-file
> > 6-gdb-show prompt
> > 7-stack-info-frame
> > 8-thread-info
> > 9-break-list
> > q
> > *** using gdb
> > =thread-group-added,id="i1"
> > ~"GNU gdb (GDB) (Cygwin 7.10.1-1) 7.10.1\n"
> > ~"Copyright (C) 2015 Free Software Foundation, Inc.\n"
> > ~"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\nand \"show warranty\" for details.\n"
> > ~"This GDB was configured as \"i686-pc-cygwin\".\nType \"show configuration\" for configuration details."
> > ~"\nFor bug reporting instructions, please see:\n"
> > ~"<http://www.gnu.org/software/gdb/bugs/>.\n"
> > ~"Find the GDB manual and other documentation resources online at:\n<http://www.gnu.org/software/gdb/documentation/>.\n"
> > ~"For help, type \"help\".\n"
> > ~"Type \"apropos word\" to search for commands related to \"word\".\n"
> > =cmd-param-changed,param="auto-load safe-path",value="/"
> > (gdb)
> > ...
> > 
> > In bad cases (bug present), the program hangs and doesn't complete until the gdb process is killed from a different terminal.  In good cases it runs to completion.
> > 
> > Ken
> 
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <unistd.h>
> > #include <pty.h>
> > #include <string.h>
> > #include <sys/wait.h>
> > 
> > void get_output (int fd);
> > 
> > int
> > main (int argc, const char **argv) 
> > {
> >   int master;
> >   pid_t pid;
> > 
> >   if ((pid = forkpty (&master, NULL, NULL, NULL)) < 0)
> >     {
> >       perror ("forkpty");
> >       exit (1);
> >     }
> >   /* child */
> >   if (pid == 0) 
> >     {
> >       const char *av[100];
> >       // putenv ("HOME=/tmp");
> >       int i = 0;
> > #ifdef STRACE_GDB
> >       av[i++] = "strace";
> >       av[i++] = "-o";
> >       av[i++] = "/tmp/strace.out";
> > #ifdef __CYGWIN__
> >       av[i++] = "--mask=all+paranoid";
> > #endif
> > #endif
> >       av[i++] = argv[1] ?: "gdb";
> >       fprintf (stderr, "*** using %s\n", av[0]);
> >       av[i++] = "-i=mi";
> >       av[i] = NULL;
> >       execvp (av[0], (char * const *) av);
> >       /* shouldn't get here */
> >       exit (1);
> >     }
> >   /* parent */
> >   const char *input[20];
> > 
> >   int i = 0;
> >   input[i++] = "1-inferior-tty-set /dev/pty3\n";
> >   input[i++] = "2-gdb-set height 0\n";
> >   input[i++] = "3-gdb-set non-stop 1\n";
> >   input[i++] = "4-file-list-exec-source-files\n";
> >   input[i++] = "5-file-list-exec-source-file\n";
> >   input[i++] = "6-gdb-show prompt\n";
> >   input[i++] = "7-stack-info-frame\n";
> >   input[i++] = "8-thread-info\n";
> >   input[i++] = "9-break-list\n";
> >   input[i++] = "q\n";
> >   input[i] = NULL;
> > 
> >   for (int i = 0; input[i]; ++i)
> >     {
> >       write (master, input[i], strlen (input[i]));
> >       // sleep (1);
> >     }
> >   get_output (master);
> >   wait (NULL);
> > }
> > 
> > void
> > get_output (int fd)
> > {
> >   char buf[4096];
> > 
> >   while (1)
> >     {
> >       int nread = read (fd, buf, sizeof (buf));
> >       if (nread > 0)
> > 	write (STDOUT_FILENO, buf, nread);
> >       else
> > 	{
> > 	  printf ("No more output.  nread %d\n", nread);
> > 	  break;
> > 	}
> >     }
> > }
> > 
> > 
> 
> > --
> > Problem reports:       http://cygwin.com/problems.html
> > FAQ:                   http://cygwin.com/faq/
> > Documentation:         http://cygwin.com/docs.html
> > Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
> 
> 
> -- 
> Corinna Vinschen                  Please, send mails regarding Cygwin to
> Cygwin Maintainer                 cygwin AT cygwin DOT com
> Red Hat


-- 
Takashi Yano <takashi.yano@nifty.ne.jp>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <pty.h>

int main()
{
	int master, slave;
	struct termios tt;
	int val;
	char buf[1024];
	int len;

	if (openpty(&master, &slave, NULL, NULL, NULL) < 0) {
		perror("openpty()");
		exit(EXIT_FAILURE);
	}

	write(STDOUT_FILENO, "----------\n", 11);

	/* Write two messages */
	write(master, "01234\n", 6);
	write(master, "56789\n", 6);

	/* Read (Canonical Mode) */
	len = read(slave, buf, sizeof(buf));
	if (len>0) write(STDOUT_FILENO, buf, len);

	/* Rrset Canonical Mode */
	tcgetattr(slave, &tt);
	tt.c_lflag &= ~ICANON;
	tcsetattr(slave, TCSANOW, &tt);

	write(STDOUT_FILENO, "----------\n", 11);

	write(master, "ABCDE\n", 6);

	/* Read (Non-Canonical Mode) */
	len = read(slave, buf, sizeof(buf));
	if (len>0) write(STDOUT_FILENO, buf, len);

	write(STDOUT_FILENO, "----------\n", 11);

	/* Write two messages */
	write(master, "FGHIJ\n", 6);
	write(master, "KLMNO\n", 6);

	/* Set Canonical Mode */
	tcgetattr(slave, &tt);
	tt.c_lflag |= ICANON;
	tcsetattr(slave, TCSANOW, &tt);

	/* Flush buffer */
	tcflush(slave, TCIFLUSH);
	val = 1;
	ioctl(slave, FIONBIO, &val);

	/* Read */
	len = read(slave, buf, sizeof(buf));
	if (len>0) write(STDOUT_FILENO, buf, len);

	write(STDOUT_FILENO, "----------\n", 11);

	close(slave);
	close(master);

	return EXIT_SUCCESS;
}

--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]