public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [pushed] PR tui/16138, PR tui/17519, and misc failures to initialize the terminal
@ 2014-10-29 14:36 Pedro Alves
  2014-11-03 21:37 ` Simon Marchi
  0 siblings, 1 reply; 3+ messages in thread
From: Pedro Alves @ 2014-10-29 14:36 UTC (permalink / raw)
  To: gdb-patches

PR tui/16138 is about failure to initialize curses resulting in GDB
exiting instead of throwing an error.  E.g.:

 $ TERM=foo gdb
 (gdb) layout asm
 Error opening terminal: foo.
 $

The problem is that we're calling initscr to initialize the screen.
As mentioned in
http://pubs.opengroup.org/onlinepubs/7908799/xcurses/initscr.html:

 If errors occur, initscr() writes an appropriate error message to
 standard error and exits.
                    ^^^^^

Instead, we should use newterm:

 "A program that needs an indication of error conditions, so it can
 continue to run in a line-oriented mode if the terminal cannot support
 a screen-oriented program, would also use this function."

After the patch:

 $ TERM=foo gdb -q -nx
 (gdb) layout asm
 Cannot enable the TUI: error opening terminal [TERM=foo]
 (gdb)

And then PR tui/17519 is about GDB not validating whether the terminal
has the necessary capabilities when enabling the TUI.  If one tries to
enable the TUI with TERM=dumb (and e.g., from a shell within emacs),
GDB ends up with a clear screen, the cursor is placed at the
bottom/right corner of the screen, there's no prompt, typing shows no
echo, and there's no indication of what's going on.  c-x,a gets you
out of the TUI, but it's completely non-obvious.

After the patch, we get:

 $ TERM=dumb gdb -q -nx
 (gdb) layout asm
 Cannot enable the TUI: terminal doesn't support cursor addressing [TERM=dumb]
 (gdb)

While at it, I've moved all the tui_allowed_p validation to
tui_enable, and expanded the error messages.  Previously we'd get:

 $ gdb -q -nx -i=mi
 (gdb)
 layout asm
 &"layout asm\n"
 &"TUI mode not allowed\n"
 ^error,msg="TUI mode not allowed"

and:

 $ gdb -q -nx -ex "layout asm" > foo
 TUI mode not allowed

While now we get:

 $ gdb -q -nx -i=mi
 (gdb)
 layout asm
 &"layout asm\n"
 &"Cannot enable the TUI when the interpreter is 'mi'\n"
 ^error,msg="Cannot enable the TUI when the interpreter is 'mi'"
 (gdb)

and:

 $ gdb -q -nx -ex "layout asm" > foo
 Cannot enable the TUI when output is not a terminal

Tested on x86_64 Fedora 20.

gdb/
2014-10-29  Pedro Alves  <palves@redhat.com>

	PR tui/16138
	PR tui/17519
	* tui/tui-interp.c (tui_is_toplevel): Delete global.
	(tui_allowed_p): Delete function.
	* tui/tui.c: Include "interps.h".
	(tui_enable): Don't use tui_allowed_p.  Error out here with
	detailed error messages if the TUI is the top level interpreter,
	or if output is not a terminal.  Use newterm instead of initscr,
	and error out if initializing the terminal fails.  Also error out if
	the terminal doesn't support cursor addressing.
	* tui/tui.h (tui_allowed_p): Delete declaration.
---
 gdb/tui/tui-interp.c | 17 -----------------
 gdb/tui/tui.c        | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----
 gdb/tui/tui.h        |  4 ----
 3 files changed, 49 insertions(+), 25 deletions(-)

diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
index 24cb5a3..b377db2 100644
--- a/gdb/tui/tui-interp.c
+++ b/gdb/tui/tui-interp.c
@@ -51,9 +51,6 @@ tui_exit (void)
   tui_disable ();
 }
 
-/* True if TUI is the top-level interpreter.  */
-static int tui_is_toplevel = 0;
-
 /* Observers for several run control events.  If the interpreter is
    quiet (i.e., another interpreter is being run with
    interpreter-exec), print nothing.  */
@@ -126,8 +123,6 @@ tui_on_command_error (void)
 static void *
 tui_init (struct interp *self, int top_level)
 {
-  tui_is_toplevel = top_level;
-
   /* Install exit handler to leave the screen in a good shape.  */
   atexit (tui_exit);
 
@@ -150,18 +145,6 @@ tui_init (struct interp *self, int top_level)
   return NULL;
 }
 
-/* True if enabling the TUI is allowed.  Example, if the top level
-   interpreter is MI, enabling curses will certainly lose.  */
-
-int
-tui_allowed_p (void)
-{
-  /* Only if TUI is the top level interpreter.  Also don't try to
-     setup curses (and print funny control characters) if we're not
-     outputting to a terminal.  */
-  return tui_is_toplevel && ui_file_isatty (gdb_stdout);
-}
-
 static int
 tui_resume (void *data)
 {
diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c
index a02c855..ca66ccd 100644
--- a/gdb/tui/tui.c
+++ b/gdb/tui/tui.c
@@ -48,6 +48,7 @@
 #include <setjmp.h>
 
 #include "gdb_curses.h"
+#include "interps.h"
 
 /* This redefines CTRL if it is not already defined, so it must come
    after terminal state releated include files like <term.h> and
@@ -361,6 +362,20 @@ tui_initialize_readline (void)
   rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap);
 }
 
+/* Return the TERM variable from the environment, or "<unset>"
+   if not set.  */
+
+static const char *
+gdb_getenv_term (void)
+{
+  const char *term;
+
+  term = getenv ("TERM");
+  if (term != NULL)
+    return term;
+  return "<unset>";
+}
+
 /* Enter in the tui mode (curses).
    When in normal mode, it installs the tui hooks in gdb, redirects
    the gdb output, configures the readline to work in tui mode.
@@ -368,8 +383,7 @@ tui_initialize_readline (void)
 void
 tui_enable (void)
 {
-  if (!tui_allowed_p ())
-    error (_("TUI mode not allowed"));
+  struct interp *interp;
 
   if (tui_active)
     return;
@@ -380,9 +394,40 @@ tui_enable (void)
   if (tui_finish_init)
     {
       WINDOW *w;
+      SCREEN *s;
+      const char *cap;
+      const char *interp;
+
+      /* If the top level interpreter is not the console/tui (e.g.,
+	 MI), enabling curses will certainly lose.  */
+      interp = interp_name (top_level_interpreter ());
+      if (strcmp (interp, INTERP_TUI) != 0)
+	error (_("Cannot enable the TUI when the interpreter is '%s'"), interp);
+
+      /* Don't try to setup curses (and print funny control
+	 characters) if we're not outputting to a terminal.  */
+      if (!ui_file_isatty (gdb_stdout))
+	error (_("Cannot enable the TUI when output is not a terminal"));
+
+      s = newterm (NULL, NULL, NULL);
+      if (s == NULL)
+	{
+	  error (_("Cannot enable the TUI: error opening terminal [TERM=%s]"),
+		 gdb_getenv_term ());
+	}
+      w = stdscr;
+
+      /* Check required terminal capabilities.  */
+      cap = tigetstr ("cup");
+      if (cap == NULL || cap == (char *) -1 || *cap == '\0')
+	{
+	  endwin ();
+	  delscreen (s);
+	  error (_("Cannot enable the TUI: "
+		   "terminal doesn't support cursor addressing [TERM=%s]"),
+		 gdb_getenv_term ());
+	}
 
-      w = initscr ();
-  
       cbreak ();
       noecho ();
       /* timeout (1); */
diff --git a/gdb/tui/tui.h b/gdb/tui/tui.h
index 9afadb2..b1dddc0 100644
--- a/gdb/tui/tui.h
+++ b/gdb/tui/tui.h
@@ -64,10 +64,6 @@ extern int tui_get_command_dimension (unsigned int *width,
    key shortcut.  */
 extern void tui_initialize_readline (void);
 
-/* True if enabling the TUI is allowed.  Example, if the top level
-   interpreter is MI, enabling curses will certainly lose.  */
-extern int tui_allowed_p (void);
-
 /* Enter in the tui mode (curses).  */
 extern void tui_enable (void);
 
-- 
1.9.3

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [pushed] PR tui/16138, PR tui/17519, and misc failures to initialize the terminal
  2014-10-29 14:36 [pushed] PR tui/16138, PR tui/17519, and misc failures to initialize the terminal Pedro Alves
@ 2014-11-03 21:37 ` Simon Marchi
  2014-11-03 21:44   ` Pedro Alves
  0 siblings, 1 reply; 3+ messages in thread
From: Simon Marchi @ 2014-11-03 21:37 UTC (permalink / raw)
  To: gdb-patches; +Cc: Pedro Alves

Hi Pedro,

(see below)

On 2014-10-29 10:36 AM, Pedro Alves wrote:
> PR tui/16138 is about failure to initialize curses resulting in GDB
> exiting instead of throwing an error.  E.g.:
> 
>  $ TERM=foo gdb
>  (gdb) layout asm
>  Error opening terminal: foo.
>  $
> 
> The problem is that we're calling initscr to initialize the screen.
> As mentioned in
> http://pubs.opengroup.org/onlinepubs/7908799/xcurses/initscr.html:
> 
>  If errors occur, initscr() writes an appropriate error message to
>  standard error and exits.
>                     ^^^^^
> 
> Instead, we should use newterm:
> 
>  "A program that needs an indication of error conditions, so it can
>  continue to run in a line-oriented mode if the terminal cannot support
>  a screen-oriented program, would also use this function."
> 
> After the patch:
> 
>  $ TERM=foo gdb -q -nx
>  (gdb) layout asm
>  Cannot enable the TUI: error opening terminal [TERM=foo]
>  (gdb)
> 
> And then PR tui/17519 is about GDB not validating whether the terminal
> has the necessary capabilities when enabling the TUI.  If one tries to
> enable the TUI with TERM=dumb (and e.g., from a shell within emacs),
> GDB ends up with a clear screen, the cursor is placed at the
> bottom/right corner of the screen, there's no prompt, typing shows no
> echo, and there's no indication of what's going on.  c-x,a gets you
> out of the TUI, but it's completely non-obvious.
> 
> After the patch, we get:
> 
>  $ TERM=dumb gdb -q -nx
>  (gdb) layout asm
>  Cannot enable the TUI: terminal doesn't support cursor addressing [TERM=dumb]
>  (gdb)
> 
> While at it, I've moved all the tui_allowed_p validation to
> tui_enable, and expanded the error messages.  Previously we'd get:
> 
>  $ gdb -q -nx -i=mi
>  (gdb)
>  layout asm
>  &"layout asm\n"
>  &"TUI mode not allowed\n"
>  ^error,msg="TUI mode not allowed"
> 
> and:
> 
>  $ gdb -q -nx -ex "layout asm" > foo
>  TUI mode not allowed
> 
> While now we get:
> 
>  $ gdb -q -nx -i=mi
>  (gdb)
>  layout asm
>  &"layout asm\n"
>  &"Cannot enable the TUI when the interpreter is 'mi'\n"
>  ^error,msg="Cannot enable the TUI when the interpreter is 'mi'"
>  (gdb)
> 
> and:
> 
>  $ gdb -q -nx -ex "layout asm" > foo
>  Cannot enable the TUI when output is not a terminal
> 
> Tested on x86_64 Fedora 20.
> 
> gdb/
> 2014-10-29  Pedro Alves  <palves@redhat.com>
> 
> 	PR tui/16138
> 	PR tui/17519
> 	* tui/tui-interp.c (tui_is_toplevel): Delete global.
> 	(tui_allowed_p): Delete function.
> 	* tui/tui.c: Include "interps.h".
> 	(tui_enable): Don't use tui_allowed_p.  Error out here with
> 	detailed error messages if the TUI is the top level interpreter,
> 	or if output is not a terminal.  Use newterm instead of initscr,
> 	and error out if initializing the terminal fails.  Also error out if
> 	the terminal doesn't support cursor addressing.
> 	* tui/tui.h (tui_allowed_p): Delete declaration.
> ---
>  gdb/tui/tui-interp.c | 17 -----------------
>  gdb/tui/tui.c        | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----
>  gdb/tui/tui.h        |  4 ----
>  3 files changed, 49 insertions(+), 25 deletions(-)
> 
> diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
> index 24cb5a3..b377db2 100644
> --- a/gdb/tui/tui-interp.c
> +++ b/gdb/tui/tui-interp.c
> @@ -51,9 +51,6 @@ tui_exit (void)
>    tui_disable ();
>  }
>  
> -/* True if TUI is the top-level interpreter.  */
> -static int tui_is_toplevel = 0;
> -
>  /* Observers for several run control events.  If the interpreter is
>     quiet (i.e., another interpreter is being run with
>     interpreter-exec), print nothing.  */
> @@ -126,8 +123,6 @@ tui_on_command_error (void)
>  static void *
>  tui_init (struct interp *self, int top_level)
>  {
> -  tui_is_toplevel = top_level;
> -
>    /* Install exit handler to leave the screen in a good shape.  */
>    atexit (tui_exit);
>  
> @@ -150,18 +145,6 @@ tui_init (struct interp *self, int top_level)
>    return NULL;
>  }
>  
> -/* True if enabling the TUI is allowed.  Example, if the top level
> -   interpreter is MI, enabling curses will certainly lose.  */
> -
> -int
> -tui_allowed_p (void)
> -{
> -  /* Only if TUI is the top level interpreter.  Also don't try to
> -     setup curses (and print funny control characters) if we're not
> -     outputting to a terminal.  */
> -  return tui_is_toplevel && ui_file_isatty (gdb_stdout);
> -}
> -
>  static int
>  tui_resume (void *data)
>  {
> diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c
> index a02c855..ca66ccd 100644
> --- a/gdb/tui/tui.c
> +++ b/gdb/tui/tui.c
> @@ -48,6 +48,7 @@
>  #include <setjmp.h>
>  
>  #include "gdb_curses.h"
> +#include "interps.h"
>  
>  /* This redefines CTRL if it is not already defined, so it must come
>     after terminal state releated include files like <term.h> and
> @@ -361,6 +362,20 @@ tui_initialize_readline (void)
>    rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap);
>  }
>  
> +/* Return the TERM variable from the environment, or "<unset>"
> +   if not set.  */
> +
> +static const char *
> +gdb_getenv_term (void)
> +{
> +  const char *term;
> +
> +  term = getenv ("TERM");
> +  if (term != NULL)
> +    return term;
> +  return "<unset>";
> +}
> +
>  /* Enter in the tui mode (curses).
>     When in normal mode, it installs the tui hooks in gdb, redirects
>     the gdb output, configures the readline to work in tui mode.
> @@ -368,8 +383,7 @@ tui_initialize_readline (void)
>  void
>  tui_enable (void)
>  {
> -  if (!tui_allowed_p ())
> -    error (_("TUI mode not allowed"));
> +  struct interp *interp;
>  
>    if (tui_active)
>      return;
> @@ -380,9 +394,40 @@ tui_enable (void)
>    if (tui_finish_init)
>      {
>        WINDOW *w;
> +      SCREEN *s;
> +      const char *cap;
> +      const char *interp;
> +
> +      /* If the top level interpreter is not the console/tui (e.g.,
> +	 MI), enabling curses will certainly lose.  */
> +      interp = interp_name (top_level_interpreter ());
> +      if (strcmp (interp, INTERP_TUI) != 0)
> +	error (_("Cannot enable the TUI when the interpreter is '%s'"), interp);
> +
> +      /* Don't try to setup curses (and print funny control
> +	 characters) if we're not outputting to a terminal.  */
> +      if (!ui_file_isatty (gdb_stdout))
> +	error (_("Cannot enable the TUI when output is not a terminal"));
> +
> +      s = newterm (NULL, NULL, NULL);

I just noticed this causes a segfault on SLED11, which is somewhat old. "zypper info libncurses5" gives version "5.6-90.55".

Old ncurses' newterm doesn't like it when we pass NULL for ofp and ifp (the last two parameters). Passing NULL to name (the first parameter) is ok though.

I just checked the difference in the source, and new ncurses' newterm has this:

FILE *_ofp = ofp ? ofp : stdout;
FILE *_ifp = ifp ? ifp : stdin;

while the old one does not, which explains the difference in behavior. I tried to change the line for:

s = newterm (NULL, stdout, stdin);

and everything seems to work as usual.

Do you want me to submit a patch, or are you going to take care of it yourself?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [pushed] PR tui/16138, PR tui/17519, and misc failures to initialize the terminal
  2014-11-03 21:37 ` Simon Marchi
@ 2014-11-03 21:44   ` Pedro Alves
  0 siblings, 0 replies; 3+ messages in thread
From: Pedro Alves @ 2014-11-03 21:44 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

Hi Simon,

On 11/03/2014 09:37 PM, Simon Marchi wrote:

> I just noticed this causes a segfault on SLED11, which is somewhat old. "zypper info libncurses5" gives version "5.6-90.55".
> 
> Old ncurses' newterm doesn't like it when we pass NULL for ofp and ifp (the last two parameters). Passing NULL to name (the first parameter) is ok though.
> 
> I just checked the difference in the source, and new ncurses' newterm has this:
> 
> FILE *_ofp = ofp ? ofp : stdout;
> FILE *_ifp = ifp ? ifp : stdin;
> 
> while the old one does not, which explains the difference in behavior. I tried to change the line for:
> 
> s = newterm (NULL, stdout, stdin);
> 
> and everything seems to work as usual.
> 
> Do you want me to submit a patch, or are you going to take care of it yourself?

Sounds like an obvious fix then.  If you could submit/push a patch, I'd
appreciate it.  Just be sure to include the info above in the commit
log.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2014-11-03 21:44 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-29 14:36 [pushed] PR tui/16138, PR tui/17519, and misc failures to initialize the terminal Pedro Alves
2014-11-03 21:37 ` Simon Marchi
2014-11-03 21:44   ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).