Have 'thread|frame apply' style their output.
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sat, 9 Mar 2019 22:55:44 +0000 (23:55 +0100)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sat, 27 Apr 2019 12:25:28 +0000 (14:25 +0200)
'thread|frame apply CMD' launches CMD so that CMD output goes to a string_file.
This patch ensures that string_file for such CMD output contains
style escape sequences that 'thread|frame apply' will later on
output on the real terminal, so as to have CMD output properly styled.

The idea is to have the class ui_file having overridable methods
to indicate that the output to this ui_file should be done using
'terminal' behaviour such as styling.
Then these methods are overriden in string_file so that a specially
constructed string_file will get output with style escape sequences.

After this patch, the output of CMD by thread|frame apply CMD is styled
similarly as when CMD is launched directly.
Note that string_file (term_out true) could also support wrapping,
but this is not done (yet?).

Tested on debian/amd64.

gdb/ChangeLog
2019-04-27  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

Support style in 'frame|thread apply'

* gdbcmd.h (execute_command_to_string): New term_out parameter.
* record.c (record_start, record_stop): Update callers of
execute_command_to_string with false.
* ui-file.h (class ui_file): New term_out and can_emit_style_escape
methods.
(class string_file): New constructor with term_out parameter.
Override methods term_out and can_emit_style_escape.  New member
term_out.
(class stdio_file): Override can_emit_style_escape.
(class tee_file): Override term_out and can_emit_style_escape.
* utils.h (can_emit_style_escape): Remove.
* utils.c (can_emit_style_escape): Likewise.
Update all callers of can_emit_style_escape (SOMESTREAM) to
SOMESTREAM->can_emit_style_escape.
* source-cache.c (source_cache::get_source_lines): Likewise.
* stack.c (frame_apply_command_count): Call execute_command_to_string
passing the term_out characteristic of the current gdb_stdout.
* thread.c (thr_try_catch_cmd): Likewise.
* top.c (execute_command_to_string): pass term_out parameter
to construct the string_file for the command output.
* ui-file.c (term_cli_styling): New function (most code moved
from utils.c can_emit_style_escape).
(string_file::string_file, string_file::can_emit_style_escape,
stdio_file::can_emit_style_escape, tee_file::term_out,
tee_file::can_emit_style_escape): New functions.

gdb/gdbcmd.h
gdb/record.c
gdb/source-cache.c
gdb/stack.c
gdb/thread.c
gdb/top.c
gdb/ui-file.c
gdb/ui-file.h
gdb/utils.c
gdb/utils.h

index 4614ec7..5d0e697 100644 (file)
@@ -133,7 +133,12 @@ extern struct cmd_list_element *showchecklist;
 extern struct cmd_list_element *save_cmdlist;
 
 extern void execute_command (const char *, int);
-extern std::string execute_command_to_string (const char *p, int from_tty);
+
+/* Execute command P and returns its output.  If TERM_OUT,
+   the output is built using terminal output behaviour such
+   as cli_styling.  */
+extern std::string execute_command_to_string (const char *p, int from_tty,
+                                             bool term_out);
 
 extern void print_command_line (struct command_line *, unsigned int,
                                struct ui_file *);
index 9c70364..828c199 100644 (file)
@@ -99,25 +99,25 @@ record_start (const char *method, const char *format, int from_tty)
   if (method == NULL)
     {
       if (format == NULL)
-       execute_command_to_string ("record", from_tty);
+       execute_command_to_string ("record", from_tty, false);
       else
        error (_("Invalid format."));
     }
   else if (strcmp (method, "full") == 0)
     {
       if (format == NULL)
-       execute_command_to_string ("record full", from_tty);
+       execute_command_to_string ("record full", from_tty, false);
       else
        error (_("Invalid format."));
     }
   else if (strcmp (method, "btrace") == 0)
     {
       if (format == NULL)
-       execute_command_to_string ("record btrace", from_tty);
+       execute_command_to_string ("record btrace", from_tty, false);
       else if (strcmp (format, "bts") == 0)
-       execute_command_to_string ("record btrace bts", from_tty);
+       execute_command_to_string ("record btrace bts", from_tty, false);
       else if (strcmp (format, "pt") == 0)
-       execute_command_to_string ("record btrace pt", from_tty);
+       execute_command_to_string ("record btrace pt", from_tty, false);
       else
        error (_("Invalid format."));
     }
@@ -130,7 +130,7 @@ record_start (const char *method, const char *format, int from_tty)
 void
 record_stop (int from_tty)
 {
-  execute_command_to_string ("record stop", from_tty);
+  execute_command_to_string ("record stop", from_tty, false);
 }
 
 /* See record.h.  */
index 5eae020..d2fb4b8 100644 (file)
@@ -181,7 +181,7 @@ source_cache::get_source_lines (struct symtab *s, int first_line,
     return false;
 
 #ifdef HAVE_SOURCE_HIGHLIGHT
-  if (source_styling && can_emit_style_escape (gdb_stdout))
+  if (source_styling && gdb_stdout->can_emit_style_escape ())
     {
       const char *fullname = symtab_to_fullname (s);
 
index f7fd943..e5de109 100644 (file)
@@ -2693,7 +2693,8 @@ frame_apply_command_count (const char *which_command,
               set to the selected frame.  */
            scoped_restore_current_thread restore_fi_current_frame;
 
-           cmd_result = execute_command_to_string (cmd, from_tty);
+           cmd_result = execute_command_to_string
+             (cmd, from_tty, gdb_stdout->term_out ());
          }
          fi = get_selected_frame (_("frame apply "
                                     "unable to get selected frame."));
index dbcf8be..b6c9b95 100644 (file)
@@ -1461,7 +1461,8 @@ thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty,
   switch_to_thread (thr);
   try
     {
-      std::string cmd_result = execute_command_to_string (cmd, from_tty);
+      std::string cmd_result = execute_command_to_string
+       (cmd, from_tty, gdb_stdout->term_out ());
       if (!flags.silent || cmd_result.length () > 0)
        {
          if (!flags.quiet)
index 9a1c258..bacd684 100644 (file)
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -657,7 +657,8 @@ execute_command (const char *p, int from_tty)
    temporarily set to true.  */
 
 std::string
-execute_command_to_string (const char *p, int from_tty)
+execute_command_to_string (const char *p, int from_tty,
+                          bool term_out)
 {
   /* GDB_STDOUT should be better already restored during these
      restoration callbacks.  */
@@ -665,7 +666,7 @@ execute_command_to_string (const char *p, int from_tty)
 
   scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
 
-  string_file str_file;
+  string_file str_file (term_out);
 
   {
     current_uiout->redirect (&str_file);
index 77f6b31..4139b5d 100644 (file)
@@ -101,6 +101,33 @@ ui_file_isatty (struct ui_file *file)
   return file->isatty ();
 }
 
+/* true if the gdb terminal supports styling, and styling is enabled.  */
+
+static bool
+term_cli_styling ()
+{
+  extern int cli_styling;
+
+  if (!cli_styling)
+    return false;
+
+  const char *term = getenv ("TERM");
+  /* Windows doesn't by default define $TERM, but can support styles
+     regardless.  */
+#ifndef _WIN32
+  if (term == nullptr || !strcmp (term, "dumb"))
+    return false;
+#else
+  /* But if they do define $TERM, let us behave the same as on Posix
+     platforms, for the benefit of programs which invoke GDB as their
+     back-end.  */
+  if (term && !strcmp (term, "dumb"))
+    return false;
+#endif
+  return true;
+}
+
+
 void
 ui_file_write (struct ui_file *file,
                const char *buf,
@@ -140,6 +167,22 @@ string_file::write (const char *buf, long length_buf)
   m_string.append (buf, length_buf);
 }
 
+/* See ui-file.h.  */
+
+bool
+string_file::term_out ()
+{
+  return m_term_out;
+}
+
+/* See ui-file.h.  */
+
+bool
+string_file::can_emit_style_escape ()
+{
+  return m_term_out && term_cli_styling ();
+}
+
 \f
 
 stdio_file::stdio_file (FILE *file, bool close_p)
@@ -255,6 +298,16 @@ stdio_file::isatty ()
   return ::isatty (m_fd);
 }
 
+/* See ui-file.h.  */
+
+bool
+stdio_file::can_emit_style_escape ()
+{
+  return (this == gdb_stdout
+         && this->isatty ()
+         && term_cli_styling ());
+}
+
 \f
 
 /* This is the implementation of ui_file method 'write' for stderr.
@@ -332,3 +385,21 @@ tee_file::isatty ()
 {
   return m_one->isatty ();
 }
+
+/* See ui-file.h.  */
+
+bool
+tee_file::term_out ()
+{
+  return m_one->term_out ();
+}
+
+/* See ui-file.h.  */
+
+bool
+tee_file::can_emit_style_escape ()
+{
+  return (this == gdb_stdout
+         && m_one->term_out ()
+         && term_cli_styling ());
+}
index 6e6ca1c..56f0c0f 100644 (file)
@@ -70,6 +70,17 @@ public:
   virtual bool isatty ()
   { return false; }
 
+  /* true indicates terminal output behaviour such as cli_styling.
+     This default implementation indicates to do terminal output
+     behaviour if the UI_FILE is a tty.  A derived class can override
+     TERM_OUT to have cli_styling behaviour without being a tty.  */
+  virtual bool term_out ()
+  { return isatty (); }
+
+  /* true if ANSI escapes can be used on STREAM.  */
+  virtual bool can_emit_style_escape ()
+  { return false; }
+
   virtual void flush ()
   {}
 };
@@ -109,7 +120,13 @@ extern int gdb_console_fputs (const char *, FILE *);
 class string_file : public ui_file
 {
 public:
-  string_file () {}
+  /* Construct a string_file to collect 'raw' output, i.e. without
+     'terminal' behaviour such as cli_styling.  */
+  string_file () : m_term_out (false) {};
+  /* If TERM_OUT, construct a string_file with terminal output behaviour
+     such as cli_styling)
+     else collect 'raw' output like the previous constructor.  */
+  explicit string_file (bool term_out) : m_term_out (term_out) {};
   ~string_file () override;
 
   /* Override ui_file methods.  */
@@ -119,6 +136,9 @@ public:
   long read (char *buf, long length_buf) override
   { gdb_assert_not_reached ("a string_file is not readable"); }
 
+  bool term_out () override;
+  bool can_emit_style_escape () override;
+
   /* string_file-specific public API.  */
 
   /* Accesses the std::string containing the entire output collected
@@ -145,6 +165,8 @@ public:
 private:
   /* The internal buffer.  */
   std::string m_string;
+
+  bool m_term_out;
 };
 
 /* A ui_file implementation that maps directly onto <stdio.h>'s FILE.
@@ -183,6 +205,8 @@ public:
 
   bool isatty () override;
 
+  bool can_emit_style_escape () override;
+
 private:
   /* Sets the internal stream to FILE, and saves the FILE's file
      descriptor in M_FD.  */
@@ -255,6 +279,8 @@ public:
   void puts (const char *) override;
 
   bool isatty () override;
+  bool term_out () override;
+  bool can_emit_style_escape () override;
   void flush () override;
 
 private:
index fa7a267..fd4427d 100644 (file)
@@ -1435,38 +1435,13 @@ emit_style_escape (const ui_file_style &style,
     fputs_unfiltered (style.to_ansi ().c_str (), stream);
 }
 
-/* See utils.h.  */
-
-bool
-can_emit_style_escape (struct ui_file *stream)
-{
-  if (stream != gdb_stdout
-      || !cli_styling
-      || !ui_file_isatty (stream))
-    return false;
-  const char *term = getenv ("TERM");
-  /* Windows doesn't by default define $TERM, but can support styles
-     regardless.  */
-#ifndef _WIN32
-  if (term == nullptr || !strcmp (term, "dumb"))
-    return false;
-#else
-  /* But if they do define $TERM, let us behave the same as on Posix
-     platforms, for the benefit of programs which invoke GDB as their
-     back-end.  */
-  if (term && !strcmp (term, "dumb"))
-    return false;
-#endif
-  return true;
-}
-
 /* Set the current output style.  This will affect future uses of the
    _filtered output functions.  */
 
 static void
 set_output_style (struct ui_file *stream, const ui_file_style &style)
 {
-  if (!can_emit_style_escape (stream))
+  if (!stream->can_emit_style_escape ())
     return;
 
   /* Note that we don't pass STREAM here, because we want to emit to
@@ -1479,7 +1454,7 @@ set_output_style (struct ui_file *stream, const ui_file_style &style)
 void
 reset_terminal_style (struct ui_file *stream)
 {
-  if (can_emit_style_escape (stream))
+  if (stream->can_emit_style_escape ())
     {
       /* Force the setting, regardless of what we think the setting
         might already be.  */
@@ -1504,7 +1479,7 @@ prompt_for_continue (void)
   bool disable_pagination = pagination_disabled_for_command;
 
   /* Clear the current styling.  */
-  if (can_emit_style_escape (gdb_stdout))
+  if (gdb_stdout->can_emit_style_escape ())
     emit_style_escape (ui_file_style (), gdb_stdout);
 
   if (annotation_level > 1)
@@ -1801,7 +1776,7 @@ fputs_maybe_filtered (const char *linebuffer, struct ui_file *stream,
              lines_printed++;
              if (wrap_column)
                {
-                 if (can_emit_style_escape (stream))
+                 if (stream->can_emit_style_escape ())
                    emit_style_escape (ui_file_style (), stream);
                  /* If we aren't actually wrapping, don't output
                     newline -- if chars_per_line is right, we
@@ -1823,7 +1798,7 @@ fputs_maybe_filtered (const char *linebuffer, struct ui_file *stream,
              if (wrap_column)
                {
                  fputs_unfiltered (wrap_indent, stream);
-                 if (can_emit_style_escape (stream))
+                 if (stream->can_emit_style_escape ())
                    emit_style_escape (wrap_style, stream);
                  /* FIXME, this strlen is what prevents wrap_indent from
                     containing tabs.  However, if we recurse to print it
index f0cb48e..76c1004 100644 (file)
@@ -440,10 +440,6 @@ extern void fputs_styled (const char *linebuffer,
 
 extern void reset_terminal_style (struct ui_file *stream);
 
-/* Return true if ANSI escapes can be used on STREAM.  */
-
-extern bool can_emit_style_escape (struct ui_file *stream);
-
 /* Display the host ADDR on STREAM formatted as ``0x%x''.  */
 extern void gdb_print_host_address_1 (const void *addr, struct ui_file *stream);