Make logging work for MI.
authorStan Shebs <shebs@codesourcery.com>
Thu, 28 Jun 2012 22:11:23 +0000 (22:11 +0000)
committerStan Shebs <shebs@codesourcery.com>
Thu, 28 Jun 2012 22:11:23 +0000 (22:11 +0000)
* NEWS: Mention it.
* interps.h (interp_set_logging_ftype): New typedef.
(struct interp_procs): New field set_logging_proc.
(current_interp_set_logging): Declare.
* interps.c (current_interp_set_logging): New function.
* cli/cli-logging.c: Include interps.h.
(set_logging_redirect): Call current_interp_set_logging.
(pop_output_files): Ditto.
(handle_redirections): Ditto, plus skip ui-out redirect if MI.
* mi/mi-console.h (mi_console_set_raw): Declare.
* mi/mi-console.c (mi_console_set_raw): New function.
* mi/mi-interp.c (saved_raw_stdout): New global.
(mi_set_logging): New function.
(_initialize_mi_interp): Add it to interp procs.

* gdb.mi/mi-logging.exp: New file.

gdb/ChangeLog
gdb/NEWS
gdb/cli/cli-logging.c
gdb/interps.c
gdb/interps.h
gdb/mi/mi-console.c
gdb/mi/mi-console.h
gdb/mi/mi-interp.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.mi/mi-logging.exp [new file with mode: 0644]

index e17c44e..ad9b710 100644 (file)
@@ -1,3 +1,21 @@
+2012-06-28  Stan Shebs  <stan@codesourcery.com>
+
+       Make logging work for MI.
+       * NEWS: Mention it.
+       * interps.h (interp_set_logging_ftype): New typedef.
+       (struct interp_procs): New field set_logging_proc.
+       (current_interp_set_logging): Declare.
+       * interps.c (current_interp_set_logging): New function.
+       * cli/cli-logging.c: Include interps.h.
+       (set_logging_redirect): Call current_interp_set_logging.
+       (pop_output_files): Ditto.
+       (handle_redirections): Ditto, plus skip ui-out redirect if MI.
+       * mi/mi-console.h (mi_console_set_raw): Declare.
+       * mi/mi-console.c (mi_console_set_raw): New function.
+       * mi/mi-interp.c (saved_raw_stdout): New global.
+       (mi_set_logging): New function.
+       (_initialize_mi_interp): Add it to interp procs.
+
 2012-06-28  Doug Evans  <dje@google.com>
 
        * symtab.c (lookup_symbol_aux_objfile): Use
index b1f6775..5cfb48a 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
 
   ** New command -info-os is the MI equivalent of "info os".
 
+  ** Output logs ("set logging" and related) now include MI output.
+
 * New commands
 
   ** "catch load" and "catch unload" can be used to stop when a shared
index 0c5bda8..6ff064a 100644 (file)
@@ -20,6 +20,7 @@
 #include "defs.h"
 #include "gdbcmd.h"
 #include "ui-out.h"
+#include "interps.h"
 #include "gdb_assert.h"
 
 #include "gdb_string.h"
@@ -115,11 +116,17 @@ set_logging_redirect (char *args, int from_tty, struct cmd_list_element *c)
                            logging_filename);
     }
 
-  gdb_stdout = output;
-  gdb_stderr = output;
-  gdb_stdlog = output;
-  gdb_stdtarg = output;
-  gdb_stdtargerr = output;
+  /* Give the current interpreter a chance to do anything special that
+     it might need for logging, such as updating other channels.  */
+  if (current_interp_set_logging (1, output, NULL) == 0)
+    {
+      gdb_stdout = output;
+      gdb_stdlog = output;
+      gdb_stderr = output;
+      gdb_stdtarg = output;
+      gdb_stdtargerr = output;
+    }
+
   logging_no_redirect_file = new_logging_no_redirect_file;
 
   /* There is a former output pushed on the ui_out_redirect stack.  We
@@ -147,19 +154,25 @@ show_logging_redirect (struct ui_file *file, int from_tty,
 static void
 pop_output_files (void)
 {
-  /* Only delete one of the files -- they are all set to the same
-     value.  */
-  ui_file_delete (gdb_stdout);
   if (logging_no_redirect_file)
     {
       ui_file_delete (logging_no_redirect_file);
       logging_no_redirect_file = NULL;
     }
-  gdb_stdout = saved_output.out;
-  gdb_stderr = saved_output.err;
-  gdb_stdlog = saved_output.log;
-  gdb_stdtarg = saved_output.targ;
-  gdb_stdtargerr = saved_output.targ;
+
+  if (current_interp_set_logging (0, NULL, NULL) == 0)
+    {
+      /* Only delete one of the files -- they are all set to the same
+        value.  */
+      ui_file_delete (gdb_stdout);
+
+      gdb_stdout = saved_output.out;
+      gdb_stderr = saved_output.err;
+      gdb_stdlog = saved_output.log;
+      gdb_stdtarg = saved_output.targ;
+      gdb_stdtargerr = saved_output.targ;
+    }
+
   saved_output.out = NULL;
   saved_output.err = NULL;
   saved_output.log = NULL;
@@ -175,6 +188,7 @@ handle_redirections (int from_tty)
 {
   struct cleanup *cleanups;
   struct ui_file *output;
+  struct ui_file *no_redirect_file = NULL;
 
   if (saved_filename != NULL)
     {
@@ -191,7 +205,7 @@ handle_redirections (int from_tty)
   /* Redirects everything to gdb_stdout while this is running.  */
   if (!logging_redirect)
     {
-      struct ui_file *no_redirect_file = output;
+      no_redirect_file = output;
 
       output = tee_file_new (gdb_stdout, 0, no_redirect_file, 0);
       if (output == NULL)
@@ -220,14 +234,22 @@ handle_redirections (int from_tty)
   saved_output.targ = gdb_stdtarg;
   saved_output.targerr = gdb_stdtargerr;
 
-  gdb_stdout = output;
-  gdb_stderr = output;
-  gdb_stdlog = output;
-  gdb_stdtarg = output;
-  gdb_stdtargerr = output;
+  /* Let the interpreter do anything it needs.  */
+  if (current_interp_set_logging (1, output, no_redirect_file) == 0)
+    {
+      gdb_stdout = output;
+      gdb_stdlog = output;
+      gdb_stderr = output;
+      gdb_stdtarg = output;
+      gdb_stdtargerr = output;
+    }
 
-  if (ui_out_redirect (current_uiout, output) < 0)
-    warning (_("Current output protocol does not support redirection"));
+  /* Don't do the redirect for MI, it confuses MI's ui-out scheme.  */
+  if (!ui_out_is_mi_like_p (current_uiout))
+    {
+      if (ui_out_redirect (current_uiout, output) < 0)
+       warning (_("Current output protocol does not support redirection"));
+    }
 }
 
 static void
index 9b24c59..698e26e 100644 (file)
@@ -251,6 +251,19 @@ interp_ui_out (struct interp *interp)
   return current_interpreter->procs->ui_out_proc (current_interpreter);
 }
 
+int
+current_interp_set_logging (int start_log, struct ui_file *out,
+                           struct ui_file *logfile)
+{
+  if (current_interpreter == NULL
+      || current_interpreter->procs->set_logging_proc == NULL)
+    return 0;
+
+  return current_interpreter->procs->set_logging_proc (current_interpreter,
+                                                      start_log, out,
+                                                      logfile);
+}
+
 /* Temporarily overrides the current interpreter.  */
 struct interp *
 interp_set_temp (const char *name)
index bbf0838..2102eca 100644 (file)
@@ -45,6 +45,10 @@ typedef struct gdb_exception (interp_exec_ftype) (void *data,
 typedef void (interp_command_loop_ftype) (void *data);
 typedef struct ui_out *(interp_ui_out_ftype) (struct interp *self);
 
+typedef int (interp_set_logging_ftype) (struct interp *self, int start_log,
+                                       struct ui_file *out,
+                                       struct ui_file *logfile);
+
 struct interp_procs
 {
   interp_init_ftype *init_proc;
@@ -59,6 +63,11 @@ struct interp_procs
      formatter.  */
   interp_ui_out_ftype *ui_out_proc;
 
+  /* Provides a hook for interpreters to do any additional
+     setup/cleanup that they might need when logging is enabled or
+     disabled.  */
+  interp_set_logging_ftype *set_logging_proc;
+
   interp_command_loop_ftype *command_loop_proc;
 };
 
@@ -74,6 +83,17 @@ extern struct interp *interp_set_temp (const char *name);
 extern int current_interp_named_p (const char *name);
 extern int current_interp_display_prompt_p (void);
 extern void current_interp_command_loop (void);
+
+/* Call this function to give the current interpreter an opportunity
+   to do any special handling of streams when logging is enabled or
+   disabled.  START_LOG is 1 when logging is starting, 0 when it ends,
+   and OUT is the stream for the log file; it will be NULL when
+   logging is ending.  LOGFILE is non-NULL if the output streams
+   are to be tees, with the log file as one of the outputs.  */
+
+extern int current_interp_set_logging (int start_log, struct ui_file *out,
+                                      struct ui_file *logfile);
+
 /* Returns opaque data associated with the top-level interpreter.  */
 extern void *top_level_interpreter_data (void);
 extern struct interp *top_level_interpreter (void);
index 102b8be..b04e65c 100644 (file)
@@ -135,4 +135,21 @@ mi_console_file_flush (struct ui_file *file)
 
   ui_file_put (mi_console->buffer, mi_console_raw_packet, mi_console);
   ui_file_rewind (mi_console->buffer);
+
+}
+
+/* Change the underlying stream of the console directly; this is
+   useful as a minimum-impact way to reflect external changes like
+   logging enable/disable.  */
+
+void
+mi_console_set_raw (struct ui_file *file, struct ui_file *raw)
+{
+  struct mi_console_file *mi_console = ui_file_data (file);
+
+  if (mi_console->magic != &mi_console_file_magic)
+    internal_error (__FILE__, __LINE__,
+                   _("mi_console_file_set_raw: bad magic number"));
+
+  mi_console->raw = raw;
 }
index 9727eb6..7e9009f 100644 (file)
@@ -24,4 +24,7 @@ extern struct ui_file *mi_console_file_new (struct ui_file *raw,
                                            const char *prefix,
                                            char quote);
 
+extern void mi_console_set_raw (struct ui_file *console,
+                               struct ui_file *raw);
+
 #endif
index ac0af70..b487136 100644 (file)
@@ -755,6 +755,54 @@ mi_ui_out (struct interp *interp)
   return mi->uiout;
 }
 
+/* Save the original value of raw_stdout here when logging, so we can
+   restore correctly when done.  */
+
+static struct ui_file *saved_raw_stdout;
+
+/* Do MI-specific logging actions; save raw_stdout, and change all
+   the consoles to use the supplied ui-file(s).  */
+
+static int
+mi_set_logging (struct interp *interp, int start_log,
+               struct ui_file *out, struct ui_file *logfile)
+{
+  struct mi_interp *mi = interp_data (interp);
+
+  if (!mi)
+    return 0;
+
+  if (start_log)
+    {
+      /* The tee created already is based on gdb_stdout, which for MI
+        is a console and so we end up in an infinite loop of console
+        writing to ui_file writing to console etc.  So discard the
+        existing tee (it hasn't been used yet, and MI won't ever use
+        it), and create one based on raw_stdout instead.  */
+      if (logfile)
+       {
+         ui_file_delete (out);
+         out = tee_file_new (raw_stdout, 0, logfile, 0);
+       }
+
+      saved_raw_stdout = raw_stdout;
+      raw_stdout = out;
+    }
+  else
+    {
+      raw_stdout = saved_raw_stdout;
+      saved_raw_stdout = NULL;
+    }
+  
+  mi_console_set_raw (mi->out, raw_stdout);
+  mi_console_set_raw (mi->err, raw_stdout);
+  mi_console_set_raw (mi->log, raw_stdout);
+  mi_console_set_raw (mi->targ, raw_stdout);
+  mi_console_set_raw (mi->event_channel, raw_stdout);
+
+  return 1;
+}
+
 extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */
 
 void
@@ -767,7 +815,8 @@ _initialize_mi_interp (void)
       mi_interpreter_suspend,  /* suspend_proc */
       mi_interpreter_exec,     /* exec_proc */
       mi_interpreter_prompt_p, /* prompt_proc_p */
-      mi_ui_out                /* ui_out_proc */
+      mi_ui_out,               /* ui_out_proc */
+      mi_set_logging           /* set_logging_proc */
     };
 
   /* The various interpreter levels.  */
index 0e857df..44c72d8 100644 (file)
@@ -1,3 +1,7 @@
+2012-06-28  Stan Shebs  <stan@codesourcery.com>
+
+       * gdb.mi/mi-logging.exp: New file.
+
 2012-06-28  Jan Kratochvil  <jan.kratochvil@redhat.com>
            Pedro Alves  <palves@redhat.com>
 
diff --git a/gdb/testsuite/gdb.mi/mi-logging.exp b/gdb/testsuite/gdb.mi/mi-logging.exp
new file mode 100644 (file)
index 0000000..d62bfe6
--- /dev/null
@@ -0,0 +1,90 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if [mi_gdb_start] {
+    continue
+}
+
+set testfile basics
+set srcfile "$testfile.c"
+set executable ${testfile}
+set binfile $objdir/$subdir/$testfile
+set opts {debug}
+
+if [build_executable $testfile.exp $executable $srcfile $opts] {
+    untested mi-logging.exp
+    return -1;
+}
+
+if {[mi_run_to_main] < 0} {
+    return -1
+}
+
+set milogfile "milog.txt"
+
+mi_gdb_test "-gdb-set logging file $milogfile" ".*"
+
+mi_gdb_test "-gdb-set logging overwrite on" ".*"
+
+mi_gdb_test "-gdb-set logging on" ".*" "logging on"
+
+mi_step "logged step"
+
+mi_next "logged next"
+
+mi_gdb_test "-gdb-set logging off" ".*" "logging off"
+
+set chan [open $milogfile]
+set logcontent [read $chan]
+close $chan
+
+set mi_log_prompt "\[(\]gdb\[)\] \[\r\n\]+"
+
+if [regexp "\\^done\[\r\n\]+$mi_log_prompt\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" $logcontent] {
+    pass "Log file contents"
+} else {
+    fail "Log file contents"
+}
+
+# Now try the redirect, which writes into the file only.
+
+mi_gdb_test "-gdb-set logging redirect on" ".*" "redirect logging on"
+
+# Since all output will be going into the file, just keep sending commands
+# and don't expect anything to appear until logging is turned off.
+
+send_gdb "1001-gdb-set logging on\n"
+send_gdb "1002-exec-step\n"
+send_gdb "1003-exec-next\n"
+
+mi_gdb_test "1004-gdb-set logging off" ".*" "redirect logging off"
+
+set chan [open $milogfile]
+set logcontent [read $chan]
+close $chan
+
+if [regexp "1001\\^done\[\r\n\]+$mi_log_prompt.*1002\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt.*1003\\^running\[\r\n\]+\\*running,thread-id=\"all\"\[\r\n\]+$mi_log_prompt\\*stopped,reason=\"end-stepping-range\",.*\[\r\n\]+$mi_log_prompt" $logcontent] {
+    pass "Redirect log file contents"
+} else {
+    fail "Redirect log file contents"
+}
+
+mi_gdb_exit
+
+remote_file host delete $milogfile