This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB 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]

[RFA] Implement MI notification for new threads.


This patch implements reporting about new threads using MI
'notify-async-output' channel, like this:


	~"143\t  if (pthread_create...
	^done
	(gdb)
	n
	&"n\n"
	~"[New Thread 0xb7e36b90 (LWP 23024)]\n"
	=thread-created,id=2
	~"148\t  if (verbose) printf (\"Made thread %d\\n\", tid1);\n"
	^done


There are two technical details worth explaining.

First, we want those notification to be always printed, if GDB is started
in MI mode. If we fail to do that, then MI frontend might miss a notification
caused by CLI command. Therefore, it is best to register notification hooks
only when creating the top-level interpreter, and don't touch the hook if
we temporary switch interpreter. In order to make it possible, I've added
a 'top_level' flag to interpreter's initialization function.

Also, I'm using observers to report new thread event. Unfortunately, observers
does not allow to specify 'closure' that will be passed back to the callback, and
therefore, callback for the new thread observer does not have an easy way to get
MI interpreter data. I've introduced new function to get top-level's interpreter
data to fix this. 

OK?

- Volodya

	Implement MI notification for new threads.
	* doc/observer.texi (new_thread): Document.
	* observer.sh: Forward declare struct thread_info.
	* thread.c (add_thread): Notify observer.

	* interps.h (interp_init_ftype): New parameter
	top_level.
	(interp_set): Likewise.
	(top_level_interpreter_data): Declare.
	* interps.c (interp_set): New parameter top_level.
	Pass it to interpreter's init function.  Remember
	top level interpreter.
	(interpreter_exec_cmd): Adjust.
	(top_level_interpreter_data): New.
	* main.c (captured_main): Pass 1 for top_level
	parameter of interp_set.
        * cli/cli-interp.c (cli_interpreter_init): New
	parameter top_level.
	* tui/tui-interp.c (tui_init): New parameter top_level.

	* mi/mi-interp.c (mi_new_thread): New.
	(mi_interpreter_init): If top level, register
	observer for new threads.

	* Makefile.in (mi-interp.o, thread.o): Update dependencies.
---
 gdb/Makefile.in       |    4 ++--
 gdb/cli/cli-interp.c  |    2 +-
 gdb/doc/observer.texi |    4 ++++
 gdb/interps.c         |   39 ++++++++++++++++++++++++++++++---------
 gdb/interps.h         |    6 ++++--
 gdb/main.c            |    2 +-
 gdb/mi/mi-interp.c    |   18 +++++++++++++++++-
 gdb/observer.sh       |    1 +
 gdb/thread.c          |    3 +++
 gdb/tui/tui-interp.c  |    2 +-
 10 files changed, 64 insertions(+), 17 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index c48742e..9060e55 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2890,7 +2890,7 @@ target-memory.o: target-memory.c $(defs_h) $(vec_h) $(target_h) \
 thread.o: thread.c $(defs_h) $(symtab_h) $(frame_h) $(inferior_h) \
 	$(environ_h) $(value_h) $(target_h) $(gdbthread_h) $(exceptions_h) \
 	$(command_h) $(gdbcmd_h) $(regcache_h) $(gdb_h) $(gdb_string_h) \
-	$(ui_out_h)
+	$(ui_out_h) $(observer_h)
 top.o: top.c $(defs_h) $(gdbcmd_h) $(call_cmds_h) $(cli_cmds_h) \
 	$(cli_script_h) $(cli_setshow_h) $(cli_decode_h) $(symtab_h) \
 	$(inferior_h) $(exceptions_h) $(target_h) $(breakpoint_h) \
@@ -3235,7 +3235,7 @@ mi-getopt.o: $(srcdir)/mi/mi-getopt.c $(defs_h) $(mi_getopt_h) \
 mi-interp.o: $(srcdir)/mi/mi-interp.c $(defs_h) $(gdb_string_h) $(interps_h) \
 	$(event_top_h) $(event_loop_h) $(inferior_h) $(ui_out_h) $(top_h) \
 	$(exceptions_h) $(mi_main_h) $(mi_cmds_h) $(mi_out_h) \
-	$(mi_console_h)
+	$(mi_console_h) $(observer_h) $(gdbthread_h)
 	$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/mi/mi-interp.c
 mi-main.o: $(srcdir)/mi/mi-main.c $(defs_h) $(target_h) $(inferior_h) \
 	$(gdb_string_h) $(exceptions_h) $(top_h) $(gdbthread_h) $(mi_cmds_h) \
diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c
index 2761b1c..34a48cb 100644
--- a/gdb/cli/cli-interp.c
+++ b/gdb/cli/cli-interp.c
@@ -43,7 +43,7 @@ struct captured_execute_command_args
 /* These implement the cli out interpreter: */
 
 static void *
-cli_interpreter_init (void)
+cli_interpreter_init (int top_level)
 {
   return NULL;
 }
diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi
index e4c99d8..2420fd9 100644
--- a/gdb/doc/observer.texi
+++ b/gdb/doc/observer.texi
@@ -129,3 +129,7 @@ Called with @var{objfile} equal to @code{NULL} to indicate
 previously loaded symbol table data has now been invalidated.
 @end deftypefun
 
+@deftypefun void new_thread (struct thread_info *@var{t})
+The thread specified by @var{objfile} has been created.
+@end deftypefun
+
diff --git a/gdb/interps.c b/gdb/interps.c
index c855374..a226ae8 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -81,6 +81,7 @@ void _initialize_interpreter (void);
 
 static struct interp *interp_list = NULL;
 static struct interp *current_interpreter = NULL;
+static struct interp *top_level_interpreter = NULL;
 
 static int interpreter_initialized = 0;
 
@@ -124,16 +125,27 @@ interp_add (struct interp *interp)
    init proc is successful, return 1, if it fails, set the old
    interpreter back in place and return 0.  If we can't restore the
    old interpreter, then raise an internal error, since we are in
-   pretty bad shape at this point. */
+   pretty bad shape at this point. 
+
+   The TOP_LEVEL parameter tells if this new interpreter is
+   the top-level one.  The top-level is what is requested
+   on the command line, and is responsible for reporting general
+   notification about target state changes.  For example, if
+   MI is the top-level interpreter, then it will always report
+   events such a target stops and new thread creation, even if they
+   are caused by CLI commands.  */
 int
-interp_set (struct interp *interp)
+interp_set (struct interp *interp, int top_level)
 {
   struct interp *old_interp = current_interpreter;
   int first_time = 0;
-
-
   char buffer[64];
 
+  /* If we already have an interpreter, then trying to
+     set top level interpreter is kinda pointless.  */
+  gdb_assert (!top_level || !current_interpreter);
+  gdb_assert (!top_level || !top_level_interpreter);
+
   if (current_interpreter != NULL)
     {
       do_all_continuations ();
@@ -152,6 +164,8 @@ interp_set (struct interp *interp)
     }
 
   current_interpreter = interp;
+  if (top_level)
+    top_level_interpreter = interp;
 
   /* We use interpreter_p for the "set interpreter" variable, so we need
      to make sure we have a malloc'ed copy for the set command to free. */
@@ -171,7 +185,7 @@ interp_set (struct interp *interp)
     {
       if (interp->procs->init_proc != NULL)
 	{
-	  interp->data = interp->procs->init_proc ();
+	  interp->data = interp->procs->init_proc (top_level);
 	}
       interp->inited = 1;
     }
@@ -182,7 +196,7 @@ interp_set (struct interp *interp)
   if (interp->procs->resume_proc != NULL
       && (!interp->procs->resume_proc (interp->data)))
     {
-      if (old_interp == NULL || !interp_set (old_interp))
+      if (old_interp == NULL || !interp_set (old_interp, 0))
 	internal_error (__FILE__, __LINE__,
 			_("Failed to initialize new interp \"%s\" %s"),
 			interp->name, "and could not restore old interp!\n");
@@ -390,7 +404,7 @@ interpreter_exec_cmd (char *args, int from_tty)
   old_quiet = interp_set_quiet (old_interp, 1);
   use_quiet = interp_set_quiet (interp_to_use, 1);
 
-  if (!interp_set (interp_to_use))
+  if (!interp_set (interp_to_use, 0))
     error (_("Could not switch to interpreter \"%s\"."), prules[0]);
 
   for (i = 1; i < nrules; i++)
@@ -398,14 +412,14 @@ interpreter_exec_cmd (char *args, int from_tty)
       struct gdb_exception e = interp_exec (interp_to_use, prules[i]);
       if (e.reason < 0)
 	{
-	  interp_set (old_interp);
+	  interp_set (old_interp, 0);
 	  interp_set_quiet (interp_to_use, use_quiet);
 	  interp_set_quiet (old_interp, old_quiet);
 	  error (_("error in command: \"%s\"."), prules[i]);
 	}
     }
 
-  interp_set (old_interp);
+  interp_set (old_interp, 0);
   interp_set_quiet (interp_to_use, use_quiet);
   interp_set_quiet (old_interp, old_quiet);
 }
@@ -462,6 +476,13 @@ interpreter_completer (char *text, char *word)
   return matches;
 }
 
+extern void *top_level_interpreter_data ()
+{
+  if (top_level_interpreter)
+    return top_level_interpreter->data;
+  return NULL;
+}
+
 /* This just adds the "interpreter-exec" command.  */
 void
 _initialize_interpreter (void)
diff --git a/gdb/interps.h b/gdb/interps.h
index e1286be..e11f914 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -35,7 +35,7 @@ extern struct gdb_exception interp_exec (struct interp *interp,
 					 const char *command);
 extern int interp_quiet_p (struct interp *interp);
 
-typedef void *(interp_init_ftype) (void);
+typedef void *(interp_init_ftype) (int top_level);
 typedef int (interp_resume_ftype) (void *data);
 typedef int (interp_suspend_ftype) (void *data);
 typedef int (interp_prompt_p_ftype) (void *data);
@@ -57,13 +57,15 @@ extern struct interp *interp_new (const char *name, void *data,
 				  struct ui_out *uiout,
 				  const struct interp_procs *procs);
 extern void interp_add (struct interp *interp);
-extern int interp_set (struct interp *interp);
+extern int interp_set (struct interp *interp, int top_level);
 extern struct interp *interp_lookup (const char *name);
 extern struct ui_out *interp_ui_out (struct interp *interp);
 
 extern int current_interp_named_p (const char *name);
 extern int current_interp_display_prompt_p (void);
 extern void current_interp_command_loop (void);
+/* Returns opaque data associated with the top-level interpreter.  */
+extern void *top_level_interpreter_data ();
 
 extern void clear_interpreter_hooks (void);
 
diff --git a/gdb/main.c b/gdb/main.c
index eb7ad5a..df3e48a 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -649,7 +649,7 @@ Excess command line arguments ignored. (%s%s)\n"),
     if (interp == NULL)
       error (_("Interpreter `%s' unrecognized"), interpreter_p);
     /* Install it.  */
-    if (!interp_set (interp))
+    if (!interp_set (interp, 1))
       {
         fprintf_unfiltered (gdb_stderr,
 			    "Interpreter `%s' failed to initialize.\n",
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 4e976d8..05a64d8 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -31,6 +31,8 @@
 #include "mi-cmds.h"
 #include "mi-out.h"
 #include "mi-console.h"
+#include "observer.h"
+#include "gdbthread.h"
 
 struct mi_interp
 {
@@ -64,8 +66,10 @@ static void mi1_command_loop (void);
 static void mi_insert_notify_hooks (void);
 static void mi_remove_notify_hooks (void);
 
+static void mi_new_thread (struct thread_info *t);
+
 static void *
-mi_interpreter_init (void)
+mi_interpreter_init (int top_level)
 {
   struct mi_interp *mi = XMALLOC (struct mi_interp);
 
@@ -83,6 +87,9 @@ mi_interpreter_init (void)
   mi->targ = mi_console_file_new (raw_stdout, "@", '"');
   mi->event_channel = mi_console_file_new (raw_stdout, "=", 0);
 
+  if (top_level)
+    observer_attach_new_thread (mi_new_thread);
+
   return mi;
 }
 
@@ -304,6 +311,15 @@ mi_command_loop (int mi_version)
   start_event_loop ();
 }
 
+static void
+mi_new_thread (struct thread_info *t)
+{
+  struct mi_interp *mi = top_level_interpreter_data ();
+
+  fprintf_unfiltered (mi->event_channel, "thread-created,id=%d", t->num);
+  gdb_flush (mi->event_channel);
+}
+
 extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */
 
 void
diff --git a/gdb/observer.sh b/gdb/observer.sh
index 96700b0..2b2eb15 100755
--- a/gdb/observer.sh
+++ b/gdb/observer.sh
@@ -62,6 +62,7 @@ struct observer;
 struct bpstats;
 struct so_list;
 struct objfile;
+struct thread_info;
 EOF
         ;;
 esac
diff --git a/gdb/thread.c b/gdb/thread.c
index 1f4b099..6e3af26 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -39,6 +39,7 @@
 #include <sys/types.h>
 #include <signal.h>
 #include "ui-out.h"
+#include "observer.h"
 
 /* Definition of struct thread_info exported to gdbthread.h */
 
@@ -137,6 +138,8 @@ add_thread (ptid_t ptid)
 
   if (print_thread_events)
     printf_filtered (_("[New %s]\n"), target_pid_to_str (ptid));
+
+  observer_notify_new_thread (result);
   
   return result;
 }
diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
index 86a288e..e9a516a 100644
--- a/gdb/tui/tui-interp.c
+++ b/gdb/tui/tui-interp.c
@@ -48,7 +48,7 @@ tui_exit (void)
 /* These implement the TUI interpreter.  */
 
 static void *
-tui_init (void)
+tui_init (int top_level)
 {
   /* Install exit handler to leave the screen in a good shape.  */
   atexit (tui_exit);
-- 
1.5.3.5


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