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/RFC] Implement =thread-selected notification.


I've previously posted a spec of how thread management should work
between MI frontend and GDB. Parts of that spec -- namely --thread
and --frame options were implemented. This patch adds =thread-selected
notification, so that I go ahead and convert that spec into manual
section.

I went with Dan's suggestion of reporting the change if we end up
with different thread after MI command is done -- which means we don't
have to carefully suppress notifications due to "internal" thread switches.

Only doc changes need formal approval; feedback on other bits is appreciated.

- Volodya

	[doc/ChangeLog]
	* gdb.texinfo (GDB/MI Async Records): Document
	=thread-selected.

	[ChangeLog]
	* interps.c (top_level_interpreter_name): New.
	* interps.h (top_level_interpreter_name): Declare.
	* mi/mi-common.h (struct mi_interp): New, moved from ...
	* mi/mi-interp.c: ...here.
	* mi/mi-main.c (mi_execute_command): If the thread changed
	as result of command, report that.

	[testsuite/ChangeLog]
	* gdb.mi/mi-pthreads.exp (check_mi_thread_command_set): Make sure
	"thread N" results in =thread-selected.
	* lib/mi-support (mi_run_cmd, mi_expect_stop)
	(mi_send_resuming_command_raw, mi_get_stop_line): Be prepared for
	=thread-selected.
---
 gdb/doc/gdb.texinfo                  |   13 +++++++++++++
 gdb/interps.c                        |    7 +++++++
 gdb/interps.h                        |    1 +
 gdb/mi/mi-common.h                   |   15 +++++++++++++++
 gdb/mi/mi-interp.c                   |   16 +---------------
 gdb/mi/mi-main.c                     |   30 ++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.mi/mi-pthreads.exp |    6 ++++++
 gdb/testsuite/lib/mi-support.exp     |   10 +++++-----
 8 files changed, 78 insertions(+), 20 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 72455f3..e8238d8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18821,6 +18821,19 @@ A signal was received by the inferior.
 @itemx =thread-exited,id="@var{id}"
 A thread either was created, or has exited.  The @var{id} field
 contains the @value{GDBN} identifier of the thread.
+
+@item =thread-selected,id="@var{id}"
+Informs that the selected thread was changed as result of the last
+command.  This notification is not emitted as result of -thread-select
+command but is emitted whenever an MI command that is not documented
+to change the selected thread actually changes it. In particular,
+invoking, directly or indirectly (via user-defined command), the CLI
+``thread'' command, will generate this notification.
+
+It is suggested that the frontend, in response to this notification, 
+hightlights the selected thread and makes any further operations
+apply to this thread.  
+
 @end table
 
 
diff --git a/gdb/interps.c b/gdb/interps.c
index 3bb7811..dcd430c 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -484,6 +484,13 @@ top_level_interpreter_data (void)
   return top_level_interpreter_ptr->data;  
 }
 
+const char *
+top_level_interpreter_name (void)
+{
+  gdb_assert (top_level_interpreter_ptr);
+  return top_level_interpreter_ptr->name;  
+}
+
 /* This just adds the "interpreter-exec" command.  */
 void
 _initialize_interpreter (void)
diff --git a/gdb/interps.h b/gdb/interps.h
index 353ed0a..e6aa57a 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -67,6 +67,7 @@ extern void current_interp_command_loop (void);
 /* Returns opaque data associated with the top-level interpreter.  */
 extern void *top_level_interpreter_data (void);
 extern struct interp *top_level_interpreter (void);
+extern const char *top_level_interpreter_name (void);
 
 extern void clear_interpreter_hooks (void);
 
diff --git a/gdb/mi/mi-common.h b/gdb/mi/mi-common.h
index e47afd1..8778e74 100644
--- a/gdb/mi/mi-common.h
+++ b/gdb/mi/mi-common.h
@@ -41,4 +41,19 @@ enum async_reply_reason
 
 const char *async_reason_lookup (enum async_reply_reason reason);
 
+struct mi_interp
+{
+  /* MI's output channels */
+  struct ui_file *out;
+  struct ui_file *err;
+  struct ui_file *log;
+  struct ui_file *targ;
+  struct ui_file *event_channel;
+
+  /* This is the interpreter for the mi... */
+  struct interp *mi2_interp;
+  struct interp *mi1_interp;
+  struct interp *mi_interp;
+};
+
 #endif
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 361cf7f..e7b9244 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -31,24 +31,10 @@
 #include "mi-cmds.h"
 #include "mi-out.h"
 #include "mi-console.h"
+#include "mi-common.h"
 #include "observer.h"
 #include "gdbthread.h"
 
-struct mi_interp
-{
-  /* MI's output channels */
-  struct ui_file *out;
-  struct ui_file *err;
-  struct ui_file *log;
-  struct ui_file *targ;
-  struct ui_file *event_channel;
-
-  /* This is the interpreter for the mi... */
-  struct interp *mi2_interp;
-  struct interp *mi1_interp;
-  struct interp *mi_interp;
-};
-
 /* These are the interpreter setup, etc. functions for the MI interpreter */
 static void mi_execute_command_wrapper (char *cmd);
 static void mi_command_loop (int mi_version);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 733fc47..a77d811 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -44,6 +44,7 @@
 #include "gdb.h"
 #include "frame.h"
 #include "mi-main.h"
+#include "mi-common.h"
 #include "language.h"
 
 #include <ctype.h>
@@ -1031,6 +1032,9 @@ mi_execute_command (char *cmd, int from_tty)
   if (command != NULL)
     {
       struct gdb_exception result;
+      ptid_t previous_ptid = inferior_ptid;
+      int had_execution = target_has_execution;
+      const char *top_name;
 
       if (do_timings)
 	{
@@ -1055,6 +1059,32 @@ mi_execute_command (char *cmd, int from_tty)
 	  mi_out_rewind (uiout);
 	}
 
+      /* The notifications are only output when the top-level
+	 interpreter (specified on the command line) is MI.  */      
+      top_name = top_level_interpreter_name ();
+      if (had_execution && target_has_execution 
+	  && strstr (top_name, "mi") == top_name)
+	{
+	  struct mi_interp *mi = top_level_interpreter_data ();
+	  if (!ptid_equal (previous_ptid, null_ptid)
+	      && !ptid_equal (inferior_ptid, previous_ptid))
+	    {
+	      /* If there are no threads in thread table, we cannot report
+		 anything.  */
+	      if (strcmp (command->command, "thread-select") != 0
+		  && thread_count () != 0)
+		{
+		  struct thread_info *ti = find_thread_pid (inferior_ptid);
+		  gdb_assert (ti);	      
+		  target_terminal_ours ();
+		  fprintf_unfiltered (mi->event_channel, 
+				      "thread-selected,id=\"%d\"",
+				      ti->num);
+		  gdb_flush (mi->event_channel);
+		}
+	    }
+	}
+
       mi_parse_free (command);
     }
 
diff --git a/gdb/testsuite/gdb.mi/mi-pthreads.exp b/gdb/testsuite/gdb.mi/mi-pthreads.exp
index dbb3ece..0ab5715 100644
--- a/gdb/testsuite/gdb.mi/mi-pthreads.exp
+++ b/gdb/testsuite/gdb.mi/mi-pthreads.exp
@@ -55,6 +55,12 @@ proc check_mi_thread_command_set {} {
       "\\^done,new-thread-id=\"$thread\",frame={.*}(,line=\"(-)?\[0-9\]+\",file=\".*\")?" \
       "check_mi_thread_command_set: -thread-select $thread"
   }
+
+  foreach thread $thread_list {
+      mi_gdb_test "-interpreter-exec console \"thread $thread\"" \
+          ".*\\^done\r\n=thread-selected,id=\"$thread\"" \
+          "check =thread-selected: thread $thread"
+  }
 }
 
 #
diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
index 762e4e2..30961b2 100644
--- a/gdb/testsuite/lib/mi-support.exp
+++ b/gdb/testsuite/lib/mi-support.exp
@@ -816,7 +816,7 @@ proc mi_run_cmd {args} {
 
     send_gdb "220-exec-run $args\n"
     gdb_expect {
-	-re "220\\^running\r\n(\\*running,thread-id=\"\[^\"\]+\"\r\n|=thread-created,id=\"1\"\r\n)*${mi_gdb_prompt}" {
+	-re "220\\^running\r\n(\\*running,thread-id=\"\[^\"\]+\"\r\n|=thread-created,id=\"1\"\r\n)*(=thread-selected,id=\".*\"\r\n)?${mi_gdb_prompt}" {
 	}
 	timeout {
 	    perror "Unable to start target"
@@ -1009,9 +1009,9 @@ proc mi_expect_stop { reason func args file line extra test } {
 
     set a $after_reason
 
-    verbose -log "mi_expect_stop: expecting: \\*stopped,${r}${a}${bn}thread-id=\"$decimal\",stopped-threads=.*,frame=\{addr=\"$hex\",func=\"$func\",args=$args,file=\".*$file\",fullname=\"${fullname_syntax}$file\",line=\"$line\"\}$after_stopped\r\n$prompt_re$"
+    verbose -log "mi_expect_stop: expecting: \\*stopped,${r}${a}${bn}thread-id=\"$decimal\",stopped-threads=.*,frame=\{addr=\"$hex\",func=\"$func\",args=$args,file=\".*$file\",fullname=\"${fullname_syntax}$file\",line=\"$line\"\}$after_stopped\r\n(=thread-selected,id=\".*\"\r\n)?$prompt_re$"
     gdb_expect {
-	-re "\\*stopped,${r}${a}${bn}thread-id=\"$decimal\",stopped-threads=.*,frame=\{addr=\"$hex\",func=\"$func\",args=$args,file=\".*$file\",fullname=\"${fullname_syntax}$file\",line=\"($line)\"\}$after_stopped\r\n$prompt_re$" {
+	-re "\\*stopped,${r}${a}${bn}thread-id=\"$decimal\",stopped-threads=.*,frame=\{addr=\"$hex\",func=\"$func\",args=$args,file=\".*$file\",fullname=\"${fullname_syntax}$file\",line=\"($line)\"\}$after_stopped\r\n(=thread-selected,id=\".*\"\r\n)?$prompt_re$" {
 	    pass "$test"
             return $expect_out(2,string)
 	}
@@ -1390,7 +1390,7 @@ proc mi_send_resuming_command_raw {command test} {
 
     send_gdb "$command\n"
     gdb_expect {
-        -re "\\^running\r\n\\*running,thread-id=\"\[^\"\]+\"\r\n${mi_gdb_prompt}" {
+        -re "\\^running\r\n\\*running,thread-id=\"\[^\"\]+\"\r\n(=thread-selected,id=\".*\"\r\n)?${mi_gdb_prompt}" {
             # Note that lack of 'pass' call here -- this works around limitation
             # in DejaGNU xfail mechanism. mi-until.exp has this:
             #
@@ -1446,7 +1446,7 @@ proc mi_get_stop_line {test} {
   }
 
   gdb_expect {
-      -re ".*line=\"(.*)\".*\r\n$prompt_re$" {
+      -re ".*line=\"(\[0-9\]+)\"\[^\r\]*\r\n(=thread-selected,id=\".*\"\r\n)?$prompt_re$" {
           return $expect_out(1,string)
       }
       -re ".*$mi_gdb_prompt$" {
-- 
1.5.3.5


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