Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Stan Shebs <stanshebs@earthlink.net>
To: gdb-patches@sourceware.org
Subject: [PATCH] Logging for MI
Date: Mon, 25 Jun 2012 07:09:00 -0000	[thread overview]
Message-ID: <4FE80E5E.903@earthlink.net> (raw)

[-- Attachment #1: Type: text/plain, Size: 1533 bytes --]

This patch extends GDB's logging support to the MI.  It's a little messy 
because the MI creates additional ui-file objects (the "consoles") that 
need to have their underlying ui-files changed to either a tee or the 
open log file, depending on redirection mode.  So I added a new 
interpreter proc that hooks into the logging enable/disable, and an 
MI-specific function to change the console's "raw" ui-file.

Although the patch seems straightforward, it entailed quite a bit of 
trial-and-error, and I've probably missed a few cases in the tangled 
tubing that is GDB's ui-file system; comments welcome.  I plan to commit 
this in a few days if no flaws come to light.

Stan
stan@codesourcery.com

2012-06-24  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.

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


[-- Attachment #2: milog-patch-1 --]
[-- Type: text/plain, Size: 13891 bytes --]

? testsuite/gdb.mi/crud
? testsuite/gdb.mi/foo
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.528
diff -u -p -r1.528 NEWS
--- NEWS	23 Jun 2012 22:23:46 -0000	1.528
+++ NEWS	25 Jun 2012 06:42:06 -0000
@@ -127,6 +127,8 @@
 
   ** 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: interps.c
===================================================================
RCS file: /cvs/src/src/gdb/interps.c,v
retrieving revision 1.46
diff -u -p -r1.46 interps.c
--- interps.c	13 Jun 2012 15:47:14 -0000	1.46
+++ interps.c	25 Jun 2012 06:42:06 -0000
@@ -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: interps.h
===================================================================
RCS file: /cvs/src/src/gdb/interps.h,v
retrieving revision 1.23
diff -u -p -r1.23 interps.h
--- interps.h	8 May 2012 18:49:41 -0000	1.23
+++ interps.h	25 Jun 2012 06:42:06 -0000
@@ -45,6 +45,10 @@ typedef struct gdb_exception (interp_exe
 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 (c
 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: cli/cli-logging.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-logging.c,v
retrieving revision 1.31
diff -u -p -r1.31 cli-logging.c
--- cli/cli-logging.c	4 Jan 2012 08:17:17 -0000	1.31
+++ cli/cli-logging.c	25 Jun 2012 06:42:06 -0000
@@ -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 fr
 			    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 *f
 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: mi/mi-console.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-console.c,v
retrieving revision 1.21
diff -u -p -r1.21 mi-console.c
--- mi/mi-console.c	6 Mar 2012 22:48:53 -0000	1.21
+++ mi/mi-console.c	25 Jun 2012 06:42:06 -0000
@@ -135,4 +135,21 @@ mi_console_file_flush (struct ui_file *f
 
   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: mi/mi-console.h
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-console.h,v
retrieving revision 1.13
diff -u -p -r1.13 mi-console.h
--- mi/mi-console.h	4 Jan 2012 08:17:24 -0000	1.13
+++ mi/mi-console.h	25 Jun 2012 06:42:06 -0000
@@ -24,4 +24,7 @@ extern struct ui_file *mi_console_file_n
 					    const char *prefix,
 					    char quote);
 
+extern void mi_console_set_raw (struct ui_file *console,
+				struct ui_file *raw);
+
 #endif
Index: mi/mi-interp.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-interp.c,v
retrieving revision 1.74
diff -u -p -r1.74 mi-interp.c
--- mi/mi-interp.c	6 Jun 2012 06:04:46 -0000	1.74
+++ mi/mi-interp.c	25 Jun 2012 06:42:06 -0000
@@ -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: testsuite/gdb.mi/mi-logging.exp
===================================================================
RCS file: testsuite/gdb.mi/mi-logging.exp
diff -N testsuite/gdb.mi/mi-logging.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.mi/mi-logging.exp	25 Jun 2012 06:42:06 -0000
@@ -0,0 +1,93 @@
+# 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
+
+pass "Log file has '$logcontent'"
+
+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
+
+pass "Log file has '$logcontent'"
+
+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
+return 0

             reply	other threads:[~2012-06-25  7:09 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-25  7:09 Stan Shebs [this message]
2012-06-26 16:18 ` Eli Zaretskii
2012-06-27 19:40 ` Tom Tromey
2012-06-27 22:24   ` Stan Shebs
2012-06-28  3:14 ` Yao Qi
2012-06-28 22:15   ` Stan Shebs

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4FE80E5E.903@earthlink.net \
    --to=stanshebs@earthlink.net \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox