Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH 0/3] Basic ITSET
@ 2012-05-31 13:15 Yao Qi
  2012-05-31 13:15 ` [PATCH 2/3] Test case: gdb.base/itset.exp and gdb.multi/itset.exp Yao Qi
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Yao Qi @ 2012-05-31 13:15 UTC (permalink / raw)
  To: gdb-patches

Hi,
This patch introduces the basic functionality of ITSET, including
define/undefine itset and check itset, etc.  This piece of work
is quite isolated from the rest of GDB.  With this patch, user can
define itset in GDB, and check various inferior/thread/core are
contained in itset or not.

I don't think this bit should go to 7.5.  Is it OK for mainline
after branch 7.5 is created?

Note that even ITSET is not associated with other execution commands,
with this patch, users can start to play with ITSET and understand
its syntax.  When we add command 'itfoucs', add itset into breakpoint,
and execution commands, it will be easier for users to understand
and accept this concept.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 3/3] Doc
  2012-05-31 13:15 [PATCH 0/3] Basic ITSET Yao Qi
  2012-05-31 13:15 ` [PATCH 2/3] Test case: gdb.base/itset.exp and gdb.multi/itset.exp Yao Qi
@ 2012-05-31 13:15 ` Yao Qi
  2012-05-31 13:15 ` [PATCH 1/3] define and check itset Yao Qi
  2 siblings, 0 replies; 8+ messages in thread
From: Yao Qi @ 2012-05-31 13:15 UTC (permalink / raw)
  To: gdb-patches

This patch is about the commands of itsets, and syntax of itset.

gdb/doc:

2012-05-03  Yao Qi  <yao@codesourcery.com>

	* gdb.texinfo (ITSET): New node.
	(Maintenance Commands): Add for `maint info itsets'.
	(ITSET Syntax): New node.
---
 gdb/doc/gdb.texinfo |  137 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 137 insertions(+), 0 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 014cfd8..582dec7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -178,6 +178,7 @@ software in general.  We will miss him.
                                  the operating system
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
+* ITSET Syntax::                Syntax of ITSET specification
 * Copying::			GNU General Public License says
                                 how you can copy and share GDB
 * GNU Free Documentation License::  The license for this documentation
@@ -3261,6 +3262,7 @@ running or not, what process it is, and why it stopped.
 @menu
 * Breakpoints::                 Breakpoints, watchpoints, and catchpoints
 * Continuing and Stepping::     Resuming execution
+* ITSET::                       ITSET
 * Skipping Over Functions and Files::
                                 Skipping over functions and files
 * Signals::                     Signals
@@ -5129,6 +5131,65 @@ proceed until the function returns.
 An argument is a repeat count, as in @code{next}.
 @end table
 
+@node ITSET
+@section ITSET
+@cindex itset
+If a program with dozens, hundreds, or even thousands, of threads,
+@value{GDBN} needs a concept for users to refer to a certain processes,
+threads, and cores in a consistent way using a flexible syntax.  This
+is called inferior/process/thread/core set construct, or inferior/thread
+set (I/T set or just itset) for short.  For the details of the
+specification of ITSET syntax, @pxref{ITSET Syntax}.
+
+@subsection itset commands
+
+@table @code
+@kindex defset
+@item defset @var{name} @var{spec}
+Define a new named set.
+
+@kindex undefset @var{name}
+@item undefset @var{name}
+Undefine an existing named set.
+
+@kindex undefset -all
+@item undefset -all
+Undefine all existing named set.
+
+@kindex whichsets @r{[}@var{spec}@r{]}
+@item whichsets @r{[}@var{spec}@r{]}
+List all sets to which threads in the set @var{spec} belong to.  If
+@var{spec} is not set, list all sets to all threads belong to.
+
+@smallexample
+(@value{GDBP}) whichsets t1
+i1.t1 (Thread 0x401ce6c0 (LWP 12089)) is in: stopped, all, curinf, itset-t1, itset-t1-t2
+@end smallexample
+
+@kindex viewset @r{[}@var{spec}@r{]}
+@item viewset @r{[}@var{spec}@r{]}
+List the menbers of the set @var{spec}.  If @var{secp} is not set, it
+lists the members of all named sets.
+@smallexample
+(@value{GDBP}) viewset t1
+t1 contains:
+  inferiors: 1
+  threads: 1
+  cores: 3
+@end smallexample
+
+@kindex info itsets @r{[}@var{range}@r{]}
+@item info itsets @r{[}@var{range}@r{]}
+Display the list of defined named itsets.  The @var{range} can not only be
+specific numbers, such as 1, 3, but also can be range, like 4-8.
+@smallexample
+(@value{GDBP}) info itsets 1 3 4-8
+@end smallexample
+
+@item @ref{maint info itsets}
+
+@end table
+
 @node Skipping Over Functions and Files
 @section Skipping Over Functions and Files
 @cindex skipping over functions and files
@@ -34283,6 +34344,21 @@ Shared library events.
 
 @end table
 
+@kindex maint info itsets
+@item @anchor{maint info itsets}maint info sets
+Diplay the list of all defined named itsets, ubser-defined and built-in.
+@smallexample
+@value{GDBP} maint info itsets
+Num     Name           What
+-1      running        running
+-2      stopped        stopped
+-3      all            all
+-4      empty          
+-5      curinf         curinf
+1       itset-c1       c1
+2       itset-c2       c2
+@end smallexample
+
 @kindex set displaced-stepping
 @kindex show displaced-stepping
 @cindex displaced stepping support
@@ -40225,6 +40301,67 @@ symbol.
 A string in the constant pool is zero-terminated.
 @end enumerate
 
+@node ITSET Syntax
+@appendix ITSET Syntax
+@cindex itset syntax
+This section documents the syntax of ITSET, which is used to refer to
+a set of inferiors, processes, threads and cores.  This allows
+@value{GDBN} command to be applied to a set or collection of inferiors,
+processes, and threads, instead of just one thread/process or all at
+a time.
+
+@subsection Syntax
+Naturally, ITSET is composed by several @dfn{ITSET elements} and
+@dfn{ITSET operators} among them.  ITSET elements are composed by
+@dfn{prefix letter}s and range or number.  The table below is
+about the supported prefix letters,
+
+@multitable @columnfractions .6 .6
+@item @strong{Prefix Letter}
+@tab @strong{Description}
+@item i @tab inferior
+@item t @tab thread
+@item a @tab Ada task
+@item c @tab core
+@end multitable
+
+Since ITSET is similar to general set, the ITSET operators are
+similar to general set operators too, such as intersection
+and union.  The table below is about the supported operators,
+
+@multitable @columnfractions .6 .6
+@item @strong{Operator}
+@tab @strong{Description}
+@item . @tab intersection operator
+@item , @tab union operator
+@item ~ @tab negating operator
+@item ! @tab specifies static itset
+@item $ @tab represents current itset
+@item () @tab defeat precedence
+@end multitable
+
+The syntax follow this grammar:
+@smallexample
+  ELEM = RANGE | NEG | PARENS_SET | '$' | NAME
+  NEG = '~' ELEM
+  PARENS_SET = '(' ITSET_ONE ')'
+  INTERS = ELEM ('.' ELEM)*
+  ITSET_ONE = INTERS (',' INTERS)*
+  ITSET = | ('!' ITSET_ONE) | ITSET_ONE
+@end smallexample
+
+@subsection Examples
+
+@multitable @columnfractions .6 .6
+@item @strong{ITSET} @tab @strong{Description}
+@item @var{t1-3} @tab Thread 1, 2, and 3
+@item @var{i1.t2} @tab Thread 2 in inferior 1
+@item @var{i1.t1-100.c1-2} @tab Threads 1 to 100 in inferior 1 running on core 1 to 2.
+@item @var{workers.~c1-3}
+@tab All in the user-defined set ``workers'', except those on core 1 to core 3.
+@item @var{running.i1} @tab All running threads in inferior 1
+@end multitable
+
 @include gpl.texi
 
 @node GNU Free Documentation License
-- 
1.7.0.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/3] Test case: gdb.base/itset.exp and gdb.multi/itset.exp
  2012-05-31 13:15 [PATCH 0/3] Basic ITSET Yao Qi
@ 2012-05-31 13:15 ` Yao Qi
  2012-05-31 13:15 ` [PATCH 3/3] Doc Yao Qi
  2012-05-31 13:15 ` [PATCH 1/3] define and check itset Yao Qi
  2 siblings, 0 replies; 8+ messages in thread
From: Yao Qi @ 2012-05-31 13:15 UTC (permalink / raw)
  To: gdb-patches

These tests are about ITSET.  They cover most of ITSET syntax and error
checking.

These tests are run with native gdb and gdbserver/
native-extended-gdbserver.exp respectively.  No fails.

gdb/testsuite:

	* gdb.base/itset.c: New.
	* gdb.base/itset.exp: New.
	* gdb.multi/itset.c: New.
	* gdb.multi/itset.exp: New.
---
 gdb/testsuite/gdb.base/itset.c    |   63 +++++++
 gdb/testsuite/gdb.base/itset.exp  |  325 +++++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.multi/itset.c   |   57 +++++++
 gdb/testsuite/gdb.multi/itset.exp |   93 +++++++++++
 4 files changed, 538 insertions(+), 0 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/itset.c
 create mode 100644 gdb/testsuite/gdb.base/itset.exp
 create mode 100644 gdb/testsuite/gdb.multi/itset.c
 create mode 100644 gdb/testsuite/gdb.multi/itset.exp

diff --git a/gdb/testsuite/gdb.base/itset.c b/gdb/testsuite/gdb.base/itset.c
new file mode 100644
index 0000000..2e4ba9e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/itset.c
@@ -0,0 +1,63 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+static void
+marker1 (void)
+{}
+
+static void
+end (void)
+{}
+
+#ifdef THREAD
+#include <pthread.h>
+#include <unistd.h>
+
+static void *
+thread_function (void *arg)
+{
+  int *flag = (int *) arg;
+
+  sleep (10);
+}
+#endif
+
+int
+main (int argc, char *argv[], char *envp[])
+{
+#ifdef THREAD
+  pthread_t threads[2];
+  int i;
+
+  for (i = 0; i < 2; i++)
+    pthread_create (&threads[i], NULL, thread_function, (void *) (&i));
+
+  /* Sleep for a while to make sure child threads are in sleep.  */
+  sleep (3);
+#endif
+
+  marker1 ();
+
+#ifdef THREADS
+  for (i = 0; i < 2; i++)
+    pthread_join (threads[i], NULL);
+#endif
+
+  end ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/itset.exp b/gdb/testsuite/gdb.base/itset.exp
new file mode 100644
index 0000000..3546a79
--- /dev/null
+++ b/gdb/testsuite/gdb.base/itset.exp
@@ -0,0 +1,325 @@
+# 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/>.
+
+set testfile "itset"
+set srcfile ${testfile}.c
+set expfile $testfile.exp
+set binfile $objdir/$subdir/$testfile
+set executable $testfile
+
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list nowarnings debug]] != "" } { 
+     untested itset.exp
+     return -1
+}
+
+# Test the behaviour of ITSET without any running process.
+
+proc test_itset_without_running_process { } \
+{ with_test_prefix "wo run" \
+{
+    global srcdir
+    global subdir
+    global binfile
+
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    # Test `maint info itsets'
+    gdb_test_sequence "maint info itsets" "maint info itsets 1" {
+	"\[\r\n\]Num\[ \]+Name\[ \]+What"
+	"\[\r\n\]\-1\[ \]+all\[ \]+all"
+	"\[\r\n\]\-2\[ \]+empty\[ \]+"
+	"\[\r\n\]\-3\[ \]+running\[ \]+running"
+	"\[\r\n\]\-4\[ \]+stopped\[ \]+stopped"
+	"\[\r\n\]\-5\[ \]+curinf\[ \]+curinf"
+    }
+
+    # Test `info itsets'
+    with_test_prefix "info itsets" {
+	gdb_test "info itsets" "No named itsets\." "no itsets no arg"
+	gdb_test "info itsets 1" "No named itset found with number 1\." \
+	    "no itsets with number"
+    }
+
+    # Test defset and undefset
+    gdb_test_no_output "defset itset-c1 c1"
+    gdb_test_no_output "defset itset-c2 c2"
+
+    with_test_prefix "viewset" {
+	if [ gdb_test_sequence "viewset" "no arg" {
+	    "\[\n\r\]all contains:"
+	    "\[\n\r\]  inferiors: 1"
+	    "\[\n\r\]empty contains:"
+	    "\[\n\r\]running contains:"
+	    "\[\n\r\]stopped contains:"
+	    "\[\n\r\]curinf contains:"
+	    "\[\n\r\]  inferiors: 1"
+	    "\[\n\r\]itset-c1 contains:"
+	    "\[\n\r\]itset-c2 contains:"
+	} ] {
+	    fail "no arg"
+	}
+
+	gdb_test "viewset c1" "c1 contains:" "arg c1"
+	gdb_test_sequence "viewset i1" "arg i1" {
+	    "\[\n\r\]i1 contains:"
+	    "\[\n\r\]  inferiors: 1"
+	}
+    }
+
+    gdb_test "whichsets itset-c1" ".*"
+
+    with_test_prefix "info itsets" {
+	gdb_test_sequence "info itsets" "itset-c1" {
+	    "\[\n\r\]1\[ \]+itset-c1\[ \]+c1"
+	    "\[\n\r\]2\[ \]+itset-c2\[ \]+c2"
+	}
+    }
+
+    with_test_prefix "maint info itsets" {
+	if [ gdb_test_sequence "maint info itsets" "2" {
+	    "\[\n\r\]\Num\[ \]+Name\[ \]+What"
+	    "\[\n\r\]\-1\[ \]+all\[ \]+all"
+	    "\[\n\r\]\-2\[ \]+empty\[ \]+"
+	    "\[\n\r\]\-3\[ \]+running\[ \]+running"
+	    "\[\n\r\]\-4\[ \]+stopped\[ \]+stopped"
+	    "\[\n\r\]\-5\[ \]+curinf\[ \]+curinf"
+	    "\[\n\r\]1\[ \]+itset-c1\[ \]+c1"
+	    "\[\n\r\]2\[ \]+itset-c2\[ \]+c2"
+	} ] {
+	    fail "2"
+	}
+    }
+
+    # Test undefset
+    gdb_test_no_output "undefset itset-c1"
+    gdb_test_no_output "undefset -all"
+}}
+
+# Test various error messages from ITSET related commands.
+
+proc test_itset_check_error { } \
+{ with_test_prefix "error message" \
+{
+    global srcdir
+    global subdir
+    global binfile
+
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    with_test_prefix "defset" {
+	gdb_test "defset" "Argument required \\(no args\\)\." \
+	    "without args"
+
+	gdb_test_no_output "defset itset-c1 c1"
+	gdb_test "defset itset-c1 c2" "itset itset-c1 already exists" \
+	    "already exists"
+
+	# Test "static itset is empty".
+	gdb_test "defset itset2 !i8" "warning: static itset is empty" \
+	    "empty static itset"
+
+	gdb_test_no_output "undefset -all"
+    }
+
+    with_test_prefix "undefset" {
+	gdb_test "undefset" "Argument required \\(no args\\)\." \
+	    "without args"
+	gdb_test "undefset itset-c1" "itset itset-c1 does not exist" \
+	    "does not exist"
+
+	gdb_test_no_output "defset itset-c1 c1"
+	gdb_test_no_output "undefset -all"
+    }
+
+    gdb_test "info itsets foo" "No named itset found with number foo\."
+}}
+
+proc test_itset_check_syntax { } \
+{ with_test_prefix "syntax" \
+{
+    global srcdir
+    global subdir
+    global binfile
+
+    gdb_exit
+    gdb_start
+    gdb_reinitialize_dir $srcdir/$subdir
+    gdb_load ${binfile}
+
+    gdb_test "defset itset-c1 c2-a" "Expected digit in I/T set, at `a'"
+
+    gdb_test "defset itset1 c1,other" "Unknown named I/T set: `other\'"
+
+    # Test parse_parens_set.
+    gdb_test "defset itset1 c1.(t1,other" "Unknown named I/T set: `other\'" \
+	"parens set: parse one"
+    gdb_test "defset itset1 c1.(t1,t2w" "Invalid I/T syntax at `w\'" \
+	"parens set: missing )"
+
+    # Test exec ITSET.
+    with_test_prefix "exec" {
+	gdb_test "defset itset-exec2 exec./gdb" \
+	    "\'\\(\' expected in I/T set after `exec\'" "missing ("
+	gdb_test "defset itset-exec2 exec\(./gdb" \
+	    "No closing \'\\)\' in I/T set for `exec\'" "missing )"
+	gdb_test "defset itset-exec2 exec\(\)" \
+	    "Empty argument to `exec\' in I/T set" "empty argument"
+    }
+}}
+
+test_itset_without_running_process
+
+test_itset_check_error
+
+test_itset_check_syntax
+
+# Test the behaviour of ITSET in single-thread.
+
+proc test_itset_with_running_single_thread { } \
+{ with_test_prefix "w run st" \
+{
+    global executable
+    global binfile
+
+    # Start with a fresh gdb.
+    clean_restart ${executable}
+    if ![runto_main] {
+	fail "Can't run to main"
+	return -1
+    }
+
+    gdb_test_no_output "defset itset-t1 t1"
+    gdb_test_no_output "defset itset-t2 t2"
+    gdb_test_no_output "defset itset-t1-t2 t1-2"
+
+    gdb_test "whichsets t1" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, itset-t1, itset-t1-t2"
+    # whichsets for current itset.
+    gdb_test "whichsets" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, itset-t1, itset-t1-t2"
+
+    gdb_test_no_output "defset itset-exec1 exec\(${binfile}\)" "defset exec"
+    gdb_test "whichsets i1" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, itset-t1, itset-t1-t2, itset-exec1" \
+	"wichsets exec"
+
+    # Test viewset
+    set test "viewset t1"
+    if [ gdb_test_sequence "viewset t1" $test {
+	"\[\n\r\]t1 contains:"
+	"\[\n\r\]  inferiors: 1"
+	"\[\n\r\]  threads: 1"
+	"\[\n\r\]  cores: \[0-9\]+"
+    } ] {
+	fail $test
+    }
+}}
+
+test_itset_with_running_single_thread
+
+set testfile "itset-mt"
+set binfile $objdir/$subdir/$testfile
+set executable $testfile
+lappend compile_flags "additional_flags=-DTHREAD"
+
+# Compile a multi-threaded version of test case.
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+	   executable [list nowarnings debug $compile_flags]] != "" } {
+     untested itset.exp
+     return -1
+}
+
+# Test the behaviour of static itset in mulit-thread.
+
+proc test_static_itset_with_multi_thread { } \
+{ with_test_prefix "static w run mt" \
+{
+    global executable
+    global srcfile
+
+    # Start with a fresh gdb.
+    clean_restart ${executable}
+    if ![runto_main] {
+	fail "Can't run to main"
+	return -1
+    }
+
+    # So far, there is only one thread t1.
+    gdb_test_no_output "defset s1 !i1.t1-2,i2.t3-4"
+    gdb_test "whichsets t1" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, s1" \
+	"1 thread: s1 contains t1"
+
+    gdb_test "break marker1" "Breakpoint \[0-9\] at .*${srcfile}.*"
+    gdb_test "continue" ".*Breakpoint \[0-9\], marker1.*" \
+	"continue to marker1"
+
+    # Update GDB's knowledge on existing threads.
+    gdb_test "info threads" ".*"
+
+   # Now, there are three threads.
+    gdb_test "whichsets t1" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, s1" \
+	"3 threads: s1 contains t1"
+    gdb_test "whichsets t2" \
+	"i1\.t2 \\(Thread .*\\) is in: all, stopped, curinf" \
+	"3 threads: s1 doesn't contain t2"
+    gdb_test "whichsets t3" \
+	"i1\.t3 \\(Thread .*\\) is in: all, stopped, curinf" \
+	"3 threads: s1 doesn't contain t3"
+
+    gdb_test_no_output "defset s2 !i1.t1-2,i2.t3-4"
+    gdb_test "whichsets t3" \
+	"i1\.t3 \\(Thread .*\\) is in: all, stopped, curinf" \
+	"3 threads: s2 doesn't contain t3"
+
+    gdb_test_sequence "whichsets" "whichsets no arg" {
+	"i1\.t3 \\(Thread .*\\) is in: all, stopped, curinf\[\n\r\]"
+	"i1\.t2 \\(Thread .*\\) is in: all, stopped, curinf, s2\[\n\r\]"
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, s1, s2\[\n\r\]"
+    }
+
+    # Assume that three threads are running on one of core from core 0 to
+    # core 1000.
+    gdb_test_no_output "defset s3 !t1,c0-1000"
+    gdb_test "whichsets t1" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, s1, s2, s3" \
+	"3 threads: s3 contains t1"
+    gdb_test "whichsets t2" \
+	"i1\.t2 \\(Thread .*\\) is in: all, stopped, curinf, s2, s3" \
+	"3 threads: s3 contains t2"
+    gdb_test "whichsets t3" \
+	"i1\.t3 \\(Thread .*\\) is in: all, stopped, curinf, s3" \
+	"3 threads: s3 contains t3"
+
+    # All three threads are stopped.
+    gdb_test_no_output "defset s4 !t1,stopped"
+    gdb_test "whichsets t1" \
+	"i1\.t1 \\(Thread .*\\) is in: all, stopped, curinf, s1, s2, s3, s4" \
+	"3 threads: s4 contains t1"
+    gdb_test "whichsets t2" \
+	"i1\.t2 \\(Thread .*\\) is in: all, stopped, curinf, s2, s3, s4" \
+	"3 threads: s4 contains t2"
+    gdb_test "whichsets t3" \
+	"i1\.t3 \\(Thread .*\\) is in: all, stopped, curinf, s3, s4" \
+	"3 threads: s4 contains t3"
+}}
+
+test_static_itset_with_multi_thread
diff --git a/gdb/testsuite/gdb.multi/itset.c b/gdb/testsuite/gdb.multi/itset.c
new file mode 100644
index 0000000..4ad59f5
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/itset.c
@@ -0,0 +1,57 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+static void
+marker1 (void)
+{}
+
+static void
+end (void)
+{}
+
+#include <pthread.h>
+#include <unistd.h>
+
+static void *
+thread_function (void *arg)
+{
+  int *flag = (int *) arg;
+
+  sleep (10);
+}
+
+int
+main (int argc, char *argv[], char *envp[])
+{
+  pthread_t threads[2];
+  int i;
+
+  for (i = 0; i < 2; i++)
+    pthread_create (&threads[i], NULL, thread_function, (void *) (&i));
+
+  /* Sleep for a while to make sure child threads are in sleep.  */
+  sleep (3);
+
+  marker1 ();
+
+  for (i = 0; i < 2; i++)
+    pthread_join (threads[i], NULL);
+
+  end ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.multi/itset.exp b/gdb/testsuite/gdb.multi/itset.exp
new file mode 100644
index 0000000..667a32e
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/itset.exp
@@ -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/>.
+
+if { [is_remote target] || ![isnative] } then {
+    continue
+}
+
+# Until "catch exec" is implemented on other targets...
+#
+if {![istarget "hppa*-hp-hpux*"] && ![istarget "*-linux*"]} then {
+    continue
+}
+
+set testfile "itset"
+set srcfile ${testfile}.c
+set expfile $testfile.exp
+set binfile $objdir/$subdir/$testfile
+set executable $testfile
+
+if  { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
+	   executable [list nowarnings debug]] != "" } {
+     untested itset.exp
+     return -1
+}
+
+# Test the behaviour of static itset in multi-inferior and mulit-thread.
+
+proc test_static_itset { } \
+{ with_test_prefix "static w run" \
+{
+    global executable
+    global binfile
+    global srcfile
+
+    # Start with a fresh gdb.
+    clean_restart ${executable}
+    if ![runto_main] {
+	fail "Can't run to main 1"
+	return -1
+    }
+
+    gdb_test "add-inferior -exec ${binfile}" \
+	"Added inferior 2.*" \
+	"add inferior 2 with -exec ${binfile}"
+
+    gdb_test "inferior 2"
+    if ![runto_main] {
+	fail "Can't run to main 2"
+	return -1
+    }
+
+    # So far, there are two inferiors i1 and i2.  Each of them has only
+    # one thread.
+    gdb_test_no_output "defset s1 !i1.t1,i2.t2-10"
+    gdb_test "whichsets t1" "i1\.t1 \\(Thread .*\\) is in: all, stopped, s1" \
+	"1 thread: s1 contains i1.t1"
+    gdb_test "whichsets t2" "i2\.t2 \\(Thread .*\\) is in: all, stopped, curinf, s1" \
+	"1 thread: s1 contains i2.t2"
+
+    # This breakpoint is set to both inferior 1 and inferior 2.
+    gdb_test "break marker1" \
+	"Breakpoint \[0-9\] at .* marker1.*"
+
+    gdb_test "inferior 1"
+    gdb_test "continue" ".*Breakpoint \[0-9\], marker1.*" \
+	"continue to marker1"
+
+    # Now, there are three threads in inferior 1, t1, t3, t4, and still
+    # only one thread t2 in inferior 2.
+    # Update GDB's knowledge on existing threads.
+    gdb_test "info threads" ".*"
+    gdb_test "whichsets t2" "i2\.t2 \\(Thread .*\\) is in: all, stopped, s1" \
+	"1 thread: s1 contains i1.t2"
+    gdb_test "whichsets t3" \
+	"i1\.t3 \\(Thread .*\\) is in: all, stopped, curinf" \
+	"1 thread: s1 contains i1.t3"
+    gdb_test "whichsets t4" \
+	"i1\.t4 \\(Thread .*\\) is in: all, stopped, curinf" \
+	"1 thread: s1 contains i1.t4"
+}}
+
+test_static_itset
-- 
1.7.0.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] define and check itset
  2012-05-31 13:15 [PATCH 0/3] Basic ITSET Yao Qi
  2012-05-31 13:15 ` [PATCH 2/3] Test case: gdb.base/itset.exp and gdb.multi/itset.exp Yao Qi
  2012-05-31 13:15 ` [PATCH 3/3] Doc Yao Qi
@ 2012-05-31 13:15 ` Yao Qi
  2012-05-31 13:34   ` Pedro Alves
  2 siblings, 1 reply; 8+ messages in thread
From: Yao Qi @ 2012-05-31 13:15 UTC (permalink / raw)
  To: gdb-patches

This patch is almost from Pedro's patch, with some minor fixes for the
new GDB code base and removal of command 'itfocus'.

  [RFC/WIP PATCH v2 06/14] Add base itsets support
  http://sourceware.org/ml/gdb-patches/2011-12/msg00566.html

command 'itfocus' will not only change 'current itset', but also associate
itset with other execution commands.  I decide to postpone this bit
to next patch series.

gdb:
2011-12-16  Pedro Alves  <pedro@codesourcery.com>
            Tom Tromey  <tromey@redhat.com>

        * Makefile.in (SFILES): Add itset.c.
        (COMMON_OBS): Add itset.o.
        * gdbthread.h (get_thread_inferior): Declare.
	(ALL_THREADS): New.
        * thread.c (get_thread_inferior): New.
	(thread_list): Make extern.
        * itset.h, itset.c: New.

2012-05-31  Yao Qi  <yao@codesourcery.com>

	* itset.c (make_cleanup_itset_elt_free): Add static.
	(parse_named_or_throw, iterate_over_itset_threads): Likewise.
	Add DEF_VEC_I (int).
	Declare _initialize_itset.
---
 gdb/Makefile.in |    4 +-
 gdb/gdbthread.h |    8 +
 gdb/itset.c     | 2380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/itset.h     |  122 +++
 gdb/thread.c    |   11 +-
 5 files changed, 2522 insertions(+), 3 deletions(-)
 create mode 100644 gdb/itset.c
 create mode 100644 gdb/itset.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bf6b0da..9b4140a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -738,7 +738,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	user-regs.c \
 	valarith.c valops.c valprint.c value.c varobj.c common/vec.c \
 	xml-tdesc.c xml-support.c \
-	inferior.c gdb_usleep.c \
+	inferior.c itset.c gdb_usleep.c \
 	record.c gcore.c \
 	jit.c \
 	xml-syscall.c \
@@ -914,7 +914,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	prologue-value.o memory-map.o memrange.o \
 	xml-support.o xml-syscall.o xml-utils.o \
 	target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
-	inferior.o osdata.o gdb_usleep.o record.o gcore.o \
+	inferior.o itset.o osdata.o gdb_usleep.o record.o gcore.o \
 	jit.o progspace.o skip.o probe.o \
 	common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o
 
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 2512357..a952315 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -38,6 +38,10 @@ enum thread_state
   THREAD_EXITED,
 };
 
+#define ALL_THREADS(T) \
+  for ((T) = thread_list; (T); (T) = (T)->next)
+
+extern struct thread_info *thread_list;
 /* Inferior thread specific part of `struct infcall_control_state'.
 
    Inferior process counterpart is `struct inferior_control_state'.  */
@@ -389,4 +393,8 @@ extern struct thread_info* inferior_thread (void);
 
 extern void update_thread_list (void);
 
+/* Return a pointer to the inferior of thread THR.  */
+
+extern struct inferior *get_thread_inferior (struct thread_info *thr);
+
 #endif /* GDBTHREAD_H */
diff --git a/gdb/itset.c b/gdb/itset.c
new file mode 100644
index 0000000..5b3098e
--- /dev/null
+++ b/gdb/itset.c
@@ -0,0 +1,2380 @@
+/* itset.c - Inferior/Thread sets.
+   Copyright (C) 2010 - 2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "itset.h"
+#include "vec.h"
+#include "bfd.h"
+#include "inferior.h"
+#include "progspace.h"
+#include "gdb_string.h"
+#include "cli/cli-utils.h"
+#include "gdbthread.h"
+#include "command.h"
+#include <ctype.h>
+#include "gdbcmd.h"
+
+/* Rather than creating/destroying these dynamic itsets when
+   necessary, keep global copies around (itsets are refcounted).  */
+static struct itset *all_itset;
+static struct itset *empty_itset;
+static struct itset *running_itset;
+static struct itset *stopped_itset;
+static struct itset *curinf_itset;
+
+/* Forward declaration of the base class.  */
+
+struct itset_elt;
+
+/* An element of an I/T set is a class with some virtual methods,
+   defined here.  */
+
+struct itset_elt_vtable
+{
+  /* Destroy the contents of this element.  If the element does not
+     require any special cleanup, this can be NULL.  This should not
+     free the element itself; that is done by the caller.  */
+
+  void (*destroy) (struct itset_elt *);
+
+  /* Return true if the element contains the inferior.  The element
+     and the inferior are passed as arguments.  */
+
+  int (*contains_inferior) (struct itset_elt *elt, struct inferior *inf);
+
+  /* Return true if the element contains the thread.  The element and
+     the thread are passed as arguments.  */
+
+  int (*contains_thread) (struct itset_elt *elt, struct thread_info *thr);
+
+  /* Return true if the element is empty.  */
+
+  int (*is_empty) (struct itset_elt *elt);
+};
+
+/* The base class of all I/T set elements.  */
+
+struct itset_elt
+{
+  const struct itset_elt_vtable *vtable;
+};
+
+static void
+itset_elt_free (struct itset_elt *elt)
+{
+  if (elt->vtable->destroy != NULL)
+    elt->vtable->destroy (elt);
+  xfree (elt);
+}
+
+static void
+itset_elt_free_cleanup (void *arg)
+{
+  struct itset_elt *elt = arg;
+
+  itset_elt_free (elt);
+}
+
+static struct cleanup *
+make_cleanup_itset_elt_free (struct itset_elt *elt)
+{
+  return make_cleanup (itset_elt_free_cleanup, elt);
+}
+
+typedef struct itset_elt *itset_elt_ptr;
+DEF_VEC_P (itset_elt_ptr);
+
+struct itset
+{
+  /* The itset's name.  May be NULL.  */
+  char *name;
+
+  /* The original specification of the set.  */
+  char *spec;
+
+  /* The reference count.  */
+  int refc;
+
+  /* The elements making up the set.  */
+  VEC (itset_elt_ptr) *elements;
+};
+
+const char *
+itset_name (const struct itset *itset)
+{
+  return itset->name;
+}
+
+const char *
+itset_spec (const struct itset *itset)
+{
+  return itset->spec;
+}
+
+int
+itset_is_empty_set (struct itset *set)
+{
+  return VEC_empty (itset_elt_ptr, set->elements);
+}
+
+\f
+
+/* An element in the list of named itsets, which can be either
+   debugger built-in (all, stopped, running, etc.), or user
+   defined.  */
+
+struct named_itset
+{
+  /* Pointer to next in linked list.  */
+  struct named_itset *next;
+
+  /* Unique identifier.  Positive if user defined, negative if
+     internal and built-in.  */
+  int number;
+
+  /* The I/T set.  */
+  struct itset *set;
+};
+
+/* The head of the list of named I/T sets.  */
+
+static struct named_itset *named_itsets;
+
+/* Number of last named itset made.  */
+
+static int named_itset_count;
+
+/* Number of last internal named itset made.  */
+
+static int internal_named_itset_count;
+
+/* Traverse all named itsets.  */
+
+#define ALL_NAMED_ITSETS(E) \
+  for ((E) = named_itsets; (E); (E) = (E)->next)
+
+/* Add IT at the end of the named itset chain.  */
+
+static void
+add_to_named_itset_chain (struct named_itset *it)
+{
+  struct named_itset *it1;
+
+  /* Add this itset to the end of the chain so that a list of itsets
+     will come out in order of increasing numbers.  */
+
+  it1 = named_itsets;
+  if (it1 == 0)
+    named_itsets = it;
+  else
+    {
+      while (it1->next)
+	it1 = it1->next;
+      it1->next = it;
+    }
+}
+
+static struct named_itset *
+get_named_itset (char *name)
+{
+  struct named_itset *it;
+
+  for (it = named_itsets; it != NULL; it = it->next)
+    if (strcmp (it->set->name, name) == 0)
+      return it;
+  return NULL;
+}
+
+\f
+
+/* A helper function that returns true if all elements in the ELEMENTS
+   set are empty.  */
+
+static int
+set_is_empty (VEC (itset_elt_ptr) *elements)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    if (!elt->vtable->is_empty (elt))
+      return 0;
+
+  return 1;
+}
+
+/* A helper function that returns true if the inferior INF is
+   contained by the set ELEMENTS.  */
+
+static int
+set_contains_inferior (VEC (itset_elt_ptr) *elements, struct inferior *inf)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    {
+      if (elt->vtable->contains_inferior (elt, inf))
+	return 1;
+    }
+
+  return 0;
+}
+
+/* A helper function that returns true if the thread THR is contained
+   by the set ELEMENTS.  */
+
+static int
+set_contains_thread (VEC (itset_elt_ptr) *elements, struct thread_info *thr)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    {
+      if (elt->vtable->contains_thread (elt, thr))
+	return 1;
+    }
+
+  return 0;
+}
+
+/* A helper function to destroy all the elements in the set ELEMENTS.
+   This also destroys ELEMENTS itself.  */
+
+static void
+set_free (VEC (itset_elt_ptr) *elements)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    itset_elt_free (elt);
+
+  VEC_free (itset_elt_ptr, elements);
+}
+
+\f
+
+/* An I/T set element representing all inferiors using a certain
+   executable.  */
+
+struct itset_elt_exec
+{
+  struct itset_elt base;
+
+  /* The name of the executable.  */
+  char *exec_name;
+};
+
+/* Implementation of `destroy' method.  */
+
+static void
+exec_destroy (struct itset_elt *base)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+
+  xfree (exec->exec_name);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+exec_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+
+  /* FIXME: smarter compare.  */
+  return (inf->pspace->ebfd != NULL
+	  && strcmp (exec->exec_name,
+		     bfd_get_filename (inf->pspace->ebfd)) == 0);
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+exec_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+  struct inferior *inf = get_thread_inferior (thr);
+
+  /* FIXME: smarter compare.  */
+  return (inf->pspace->ebfd != NULL
+	  && strcmp (exec->exec_name,
+		     bfd_get_filename (inf->pspace->ebfd)) == 0);
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+exec_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+  struct inferior *inf;
+
+  ALL_INFERIORS (inf)
+    if (exec_contains_inferior (base, inf))
+      return 0;
+
+  return 1;
+}
+
+static const struct itset_elt_vtable exec_vtable =
+{
+  exec_destroy,
+  exec_contains_inferior,
+  exec_contains_thread,
+  exec_is_empty
+};
+
+/* Create a new `exec' I/T set element.  */
+
+static struct itset_elt *
+create_exec_itset (char *arg)
+{
+  struct itset_elt_exec *elt;
+
+  elt = XNEW (struct itset_elt_exec);
+  elt->base.vtable = &exec_vtable;
+  elt->exec_name = arg;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* The value representing any inferior or thread.  */
+
+#define WILDCARD -1
+
+/* An I/T set element representing a range of inferiors.  */
+
+struct itset_elt_inferior_range
+{
+  struct itset_elt base;
+
+  /* The first and last inferiors in this range.  If FIRST is
+     WILDCARD, then LAST is unused.  */
+  int inf_first, inf_last;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+inferior_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+
+  if (range->inf_first == WILDCARD
+      || (range->inf_first <= inf->num && inf->num <= range->inf_last))
+    return 1;
+
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+inferior_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+  struct inferior *inf;
+
+  if (range->inf_first == WILDCARD)
+    return 1;
+
+  inf = get_thread_inferior (thr);
+  return range->inf_first <= inf->num && inf->num <= range->inf_last;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+inferior_range_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (inferior_range_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (inferior_range_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable inferior_range_vtable =
+{
+  NULL,
+  inferior_range_contains_inferior,
+  inferior_range_contains_thread,
+  inferior_range_is_empty
+};
+
+/* Create a new `range' I/T set element.  */
+
+static struct itset_elt *
+create_inferior_range_itset (int inf_first, int inf_last)
+{
+  struct itset_elt_inferior_range *elt;
+
+  elt = XNEW (struct itset_elt_inferior_range);
+  elt->base.vtable = &inferior_range_vtable;
+  elt->inf_first = inf_first;
+  elt->inf_last = inf_last;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing a range of threads.  */
+
+struct itset_elt_thread_range
+{
+  struct itset_elt base;
+
+  /* The first and last threads in this range.  If FIRST is WILDCARD,
+     then LAST is unused.  */
+  int thr_first, thr_last;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+thread_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_thread_range *range
+    = (struct itset_elt_thread_range *) base;
+  struct thread_info *thr;
+  int pid;
+
+  /* If there are no threads in the inferior, INF can't be part of any
+     thread range.  */
+  if (inf->pid == 0)
+    return 0;
+
+  /* If range is a wildcard, this inferior is part of the range, given
+     that it must have at least one thread.  */
+  if (range->thr_first == WILDCARD)
+    return 1;
+
+  /* Walk threads of INF, check if the range contains any of those.
+     If so, then the range contains the inferior.  */
+  ALL_THREADS (thr)
+    if (ptid_get_pid (thr->ptid) == inf->pid)
+      {
+	if (range->thr_first <= thr->num && thr->num <= range->thr_last)
+	  return 1;
+      }
+
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+thread_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_thread_range *range
+    = (struct itset_elt_thread_range *) base;
+
+  if (range->thr_first == WILDCARD
+      || (range->thr_first <= thr->num && thr->num <= range->thr_last))
+    return 1;
+
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+thread_range_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (thread_range_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (thread_range_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable thread_range_vtable =
+{
+  NULL,
+  thread_range_contains_inferior,
+  thread_range_contains_thread,
+  thread_range_is_empty
+};
+
+/* Create a new `range' I/T set element.  */
+
+static struct itset_elt *
+create_thread_range_itset (int thr_first, int thr_last)
+{
+  struct itset_elt_thread_range *elt;
+
+  elt = XNEW (struct itset_elt_thread_range);
+  elt->base.vtable = &thread_range_vtable;
+  elt->thr_first = thr_first;
+  elt->thr_last = thr_last;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing a range of cores.  */
+
+struct itset_elt_core_range
+{
+  struct itset_elt base;
+
+  /* The first and last cores in this range.  If CORE_FIRST is
+     WILDCARD, then CORE_LAST is unused.  */
+  int core_first, core_last;
+};
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+core_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_core_range *core_range
+    = (struct itset_elt_core_range *) base;
+  int core;
+
+  if (core_range->core_first == WILDCARD)
+    return 1;
+
+  core = target_core_of_thread (thr->ptid);
+  if (core_range->core_first <= core && core <= core_range->core_last)
+    return 1;
+
+  return 0;
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+core_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_core_range *core_range
+    = (struct itset_elt_core_range *) base;
+  struct thread_info *thr;
+
+  /* True if we find a thread of this inferior that is running on our
+     core range.  */
+  ALL_THREADS (thr)
+    {
+      /* It's cheaper to check the core range first, because looking
+	 up the a thread's inferior is O(n).  */
+      if (core_range_contains_thread (base, thr))
+	{
+	  struct inferior *thr_inf;
+
+	  thr_inf = get_thread_inferior (thr);
+	  if (thr_inf == inf)
+	    return 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+core_range_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_core_range *core_range
+    = (struct itset_elt_core_range *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_THREADS (thr)
+    {
+      if (core_range_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable core_range_vtable =
+{
+  NULL,
+  core_range_contains_inferior,
+  core_range_contains_thread,
+  core_range_is_empty
+};
+
+/* Create a new `core_range' I/T set element.  */
+
+static struct itset_elt *
+create_core_range_itset (int core_first, int core_last)
+{
+  struct itset_elt_core_range *elt;
+
+  elt = XNEW (struct itset_elt_core_range);
+  elt->base.vtable = &core_range_vtable;
+  elt->core_first = core_first;
+  elt->core_last = core_last;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing an intersection of sets.  */
+
+struct itset_elt_intersect
+{
+  struct itset_elt base;
+
+  /* The elements that will be intersected.  */
+  VEC (itset_elt_ptr) *elements;
+};
+
+/* Implementation of `destroy' method.  */
+
+static void
+intersect_destroy (struct itset_elt *base)
+{
+  struct itset_elt_intersect *set = (struct itset_elt_intersect *) base;
+
+  VEC_free (itset_elt_ptr, set->elements);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+intersect_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+  struct itset_elt *elt;
+  int ix;
+
+  gdb_assert (!VEC_empty (itset_elt_ptr, intersect->elements));
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, intersect->elements, ix, elt); ++ix)
+    {
+      if (!elt->vtable->contains_inferior (elt, inf))
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+intersect_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+  struct itset_elt *elt;
+  int ix;
+
+  gdb_assert (!VEC_empty (itset_elt_ptr, intersect->elements));
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, intersect->elements, ix, elt); ++ix)
+    {
+      if (!elt->vtable->contains_thread (elt, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+intersect_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (intersect_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (intersect_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable intersect_vtable =
+{
+  intersect_destroy,
+  intersect_contains_inferior,
+  intersect_contains_thread,
+  intersect_is_empty
+};
+
+/* Create a new `intersect' I/T set element.  */
+
+static struct itset_elt_intersect *
+create_intersect_itset (void)
+{
+  struct itset_elt_intersect *elt;
+
+  elt = XNEW (struct itset_elt_intersect);
+  elt->base.vtable = &intersect_vtable;
+  elt->elements = NULL;
+
+  return elt;
+}
+
+\f
+
+/* An I/T set element representing all inferiors.  */
+
+struct itset_elt_all
+{
+  struct itset_elt base;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+all_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  return 1;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+all_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  return 1;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+all_is_empty (struct itset_elt *base)
+{
+  /* There's always at least one inferior.  */
+  return 0;
+}
+
+static const struct itset_elt_vtable all_vtable =
+{
+  NULL,
+  all_contains_inferior,
+  all_contains_thread,
+  all_is_empty
+};
+
+static struct itset_elt *
+create_all_itset (void)
+{
+  struct itset_elt_all *elt;
+
+  elt = XNEW (struct itset_elt_all);
+  elt->base.vtable = &all_vtable;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing no inferiors.  */
+
+struct itset_elt_empty
+{
+  struct itset_elt base;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+empty_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+empty_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+empty_is_empty (struct itset_elt *base)
+{
+  /* Always empty.  */
+  return 1;
+}
+
+static const struct itset_elt_vtable empty_vtable =
+{
+  NULL,
+  empty_contains_inferior,
+  empty_contains_thread,
+  empty_is_empty
+};
+
+static struct itset_elt *
+create_empty_itset (void)
+{
+  struct itset_elt_empty *elt;
+
+  elt = XNEW (struct itset_elt_empty);
+  elt->base.vtable = &empty_vtable;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing an itset.  */
+
+struct itset_elt_itset
+{
+  struct itset_elt base;
+
+  /* The I/T set this element wraps.  */
+  struct itset *set;
+};
+
+static void
+itset_elt_itset_destroy (struct itset_elt *base)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+
+  itset_free (iiset->set);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+itset_elt_itset_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+  return itset_contains_inferior (iiset->set, inf);
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+itset_elt_itset_contains_thread (struct itset_elt *base,
+				 struct thread_info *thr)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+  return itset_contains_thread (iiset->set, thr);
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+itset_elt_itset_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+  return itset_is_empty (iiset->set);
+}
+
+static const struct itset_elt_vtable itset_elt_itset_vtable =
+{
+  itset_elt_itset_destroy,
+  itset_elt_itset_contains_inferior,
+  itset_elt_itset_contains_thread,
+  itset_elt_itset_is_empty
+};
+
+static struct itset_elt_itset *
+create_itset_elt_itset (struct itset *set)
+{
+  struct itset_elt_itset *elt;
+
+  elt = XNEW (struct itset_elt_itset);
+  elt->base.vtable = &itset_elt_itset_vtable;
+  elt->set = itset_reference (set);
+
+  return elt;
+}
+
+void
+itset_add_set (struct itset *to, struct itset *addme)
+{
+  struct itset_elt *elt;
+
+  elt = (struct itset_elt *) create_itset_elt_itset (addme);
+  VEC_safe_push (itset_elt_ptr, to->elements, elt);
+}
+
+\f
+
+/* An I/T set element representing a negated set.  */
+
+struct itset_elt_negated
+{
+  struct itset_elt base;
+
+  /* The negated element.  */
+  struct itset_elt *negated;
+};
+
+static void
+itset_elt_negated_destroy (struct itset_elt *base)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+
+  if (elt->negated->vtable->destroy != NULL)
+    elt->negated->vtable->destroy (elt->negated);
+  xfree (elt->negated);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+itset_elt_negated_contains_inferior (struct itset_elt *base,
+				     struct inferior *inf)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+  return !elt->negated->vtable->contains_inferior (elt->negated, inf);
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+itset_elt_negated_contains_thread (struct itset_elt *base,
+				   struct thread_info *thr)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+  return !elt->negated->vtable->contains_thread (elt->negated, thr);
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+itset_elt_negated_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (itset_elt_negated_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (itset_elt_negated_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable itset_elt_negated_vtable =
+{
+  itset_elt_negated_destroy,
+  itset_elt_negated_contains_inferior,
+  itset_elt_negated_contains_thread,
+  itset_elt_negated_is_empty
+};
+
+static struct itset_elt_negated *
+create_itset_elt_negated (void)
+{
+  struct itset_elt_negated *elt;
+
+  elt = XNEW (struct itset_elt_negated);
+  elt->base.vtable = &itset_elt_negated_vtable;
+  elt->negated = NULL;
+
+  return elt;
+}
+
+\f
+
+/* An I/T set element representing all inferiors of a given state.  */
+
+struct itset_elt_state
+{
+  struct itset_elt base;
+
+  int state;
+};
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+state_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_state *state = (struct itset_elt_state *) base;
+
+  return thr->state == state->state;
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+state_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_state *state = (struct itset_elt_state *) base;
+  struct thread_info *thr;
+
+  /* True if we find a thread of this inferior that is in the state
+     we're interested in.  */
+  ALL_THREADS (thr)
+    {
+      /* It's cheaper to check the state first, because looking up the
+	 a thread's inferior is O(n).  */
+      if (state_contains_thread (base, thr))
+	{
+	  struct inferior *thr_inf;
+
+	  thr_inf = get_thread_inferior (thr);
+	  if (thr_inf == inf)
+	    return 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+state_is_empty (struct itset_elt *base)
+{
+  struct thread_info *thr;
+
+  ALL_THREADS (thr)
+    if (state_contains_thread (base, thr))
+      return 0;
+
+  return 1;
+}
+
+static const struct itset_elt_vtable state_vtable =
+{
+  NULL,
+  state_contains_inferior,
+  state_contains_thread,
+  state_is_empty
+};
+
+static struct itset_elt *
+create_state_itset (int thread_state)
+{
+  struct itset_elt_state *elt;
+
+  elt = XNEW (struct itset_elt_state);
+  elt->base.vtable = &state_vtable;
+  elt->state = thread_state;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+current_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  return current_inferior () == inf;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+current_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct inferior *inf;
+
+  inf = get_thread_inferior (thr);
+  return current_inferior () == inf;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+current_is_empty (struct itset_elt *base)
+{
+  /* There's always a current inferior.  */
+  return 0;
+}
+
+static const struct itset_elt_vtable current_vtable =
+{
+  NULL,
+  current_contains_inferior,
+  current_contains_thread,
+  current_is_empty
+};
+
+/* Create a new I/T set element representing just the current
+   inferior.  */
+
+static struct itset_elt *
+create_current_itset (void)
+{
+  struct itset_elt *elt;
+
+  elt = XNEW (struct itset_elt);
+  elt->vtable = &current_vtable;
+
+  return elt;
+}
+
+\f
+
+DEF_VEC_I (int);
+
+/* An I/T set element representing a static list of inferiors.  */
+
+struct itset_elt_static
+{
+  struct itset_elt base;
+
+  /* The inferiors.  */
+  VEC (int) *inferiors;
+
+  /* The threads.  */
+  VEC (int) *threads;
+};
+
+/* Implementation of `destroy' method.  */
+
+static void
+static_destroy (struct itset_elt *base)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+
+  VEC_free (int, st->inferiors);
+  VEC_free (int, st->threads);
+}
+
+/* Helper function to compare two ints.  Returns true if the first
+   argument is strictly less than the second, useful for
+   VEC_lower_bound.  */
+
+static int
+static_lessthan (const int a, const int b)
+{
+  return a < b;
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+static_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+  int idx;
+
+  idx = VEC_lower_bound (int, st->inferiors, inf->num, static_lessthan);
+  if (idx < VEC_length (int, st->inferiors)
+      && VEC_index (int, st->inferiors, idx) == inf->num)
+    return 1;
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+static_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+  int idx;
+
+  idx = VEC_lower_bound (int, st->threads, thr->num, static_lessthan);
+  if (idx < VEC_length (int, st->threads)
+      && VEC_index (int, st->threads, idx) == thr->num)
+    return 1;
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+static_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+  int idx;
+
+  return VEC_empty (int, st->inferiors);
+}
+
+static const struct itset_elt_vtable static_vtable =
+{
+  static_destroy,
+  static_contains_inferior,
+  static_contains_thread,
+  static_is_empty
+};
+
+\f
+
+/* Helper struct used to pass data through iterate_over_inferiors.  */
+
+struct iter_data
+{
+  /* The I/T set we are constructing.  */
+
+  struct itset_elt_static *st;
+
+  /* The elements of the original (dynamic) I/T set.  */
+
+  VEC (itset_elt_ptr) *elements;
+};
+
+/* A callback for iterate_over_inferiors that adds an inferior to the
+   result set, if it is in the source set.  */
+
+static int
+check_one_inferior (struct inferior *inf, void *datum)
+{
+  struct iter_data *id = datum;
+
+  if (set_contains_inferior (id->elements, inf))
+    VEC_safe_push (int, id->st->inferiors, inf->num);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* A callback for iterate_over_threads that adds a thread to the
+   result set, if it is in the source set.  */
+
+static int
+check_one_thread (struct thread_info *thr, void *datum)
+{
+  struct iter_data *id = datum;
+
+  if (set_contains_thread (id->elements, thr))
+    VEC_safe_push (int, id->st->threads, thr->num);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* Create a new static I/T set from the list of elements.  */
+
+static struct itset_elt *
+create_static_itset (VEC (itset_elt_ptr) *elements)
+{
+  struct itset_elt_static *elt;
+  struct iter_data datum;
+
+  elt = XNEW (struct itset_elt_static);
+  elt->base.vtable = &static_vtable;
+  elt->inferiors = NULL;
+  elt->threads = NULL;
+
+  datum.st = elt;
+  datum.elements = elements;
+
+  iterate_over_inferiors (check_one_inferior, &datum);
+  if (VEC_length (int, elt->inferiors) > 1)
+    qsort (VEC_address (int, elt->inferiors),
+	   VEC_length (int, elt->inferiors),
+	   sizeof (int), compare_positive_ints);
+
+  iterate_over_threads (check_one_thread, &datum);
+  if (VEC_length (int, elt->threads) > 1)
+    qsort (VEC_address (int, elt->threads),
+	   VEC_length (int, elt->threads),
+	   sizeof (int), compare_positive_ints);
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+static int
+looks_like_range (char *spec)
+{
+  return isdigit (spec[0]) || spec[0] == '*';
+}
+
+/* Parse an I/T set range.  A range has the form F[:L][.T], where F is
+   the starting inferior, L is the ending inferior, and T is the
+   thread.  Updates RESULT with the new I/T set elements, and returns
+   an updated pointer into the spec.  Throws an exception on
+   error.  */
+
+static char *
+parse_range (char *spec, int *first, int *last)
+{
+  struct itset_elt *elt;
+
+  if (!looks_like_range (spec))
+    error (_("Invalid I/T syntax at `%s'.  Expected '*' or digit."), spec);
+
+  if (*spec == '*')
+    {
+      *first = WILDCARD;
+      *last = WILDCARD;
+      ++spec;
+    }
+  else
+    {
+      *first = strtol (spec, &spec, 10);
+      if (*spec == '-')
+	{
+	  ++spec;
+	  if (!isdigit (*spec))
+	    error (_("Expected digit in I/T set, at `%s'"), spec);
+	  *last = strtol (spec, &spec, 10);
+	}
+      else
+	*last = *first;
+    }
+  return spec;
+}
+
+static struct itset_elt *
+parse_inferior_range (char **spec)
+{
+  int first, last;
+
+  if ((*spec)[0] != 'i' || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_inferior_range_itset (first, last);
+}
+
+static struct itset_elt *
+parse_thread_range (char **spec)
+{
+  int first, last;
+
+  if ((*spec)[0] != 't' || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_thread_range_itset (first, last);
+}
+
+static struct itset_elt *
+parse_core_range (char **spec)
+{
+  int first, last;
+
+  if ((*spec)[0] != 'c' || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_core_range_itset (first, last);
+}
+
+\f
+
+/* Parse a named I/T set.  Currently the only named sets which are
+   recognized are `exec (NAME)', and `current'.  Updates RESULT with
+   the new I/T set elements, and returns an updated pointer into the
+   spec.  Throws an exception on error.  */
+
+static struct itset_elt *
+parse_named_or_throw (char **textp)
+{
+  struct itset_elt *elt;
+  char *text = *textp;
+  char *name = text;
+
+  for (text = name + 1; isalnum (*text) || *text == '_'; ++text)
+    ;
+
+  if (strncmp ("all", name, text - name) == 0)
+    elt = create_all_itset ();
+  else if (strncmp ("empty", name, text - name) == 0)
+    elt = create_empty_itset ();
+  else if (strncmp ("stopped", name, text - name) == 0)
+    elt = create_state_itset (THREAD_STOPPED);
+  else if (strncmp ("running", name, text - name) == 0)
+    elt = create_state_itset (THREAD_RUNNING);
+  else if (strncmp ("curinf", name, text - name) == 0)
+    elt = create_current_itset ();
+  else if (strncmp ("exec", name, text - name) == 0)
+    {
+      char *tem;
+      char *arg;
+
+      if (*text != '(')
+	error (_("'(' expected in I/T set after `exec'"));
+      text = skip_spaces (text + 1);
+      tem = strchr (text, ')');
+      if (!tem)
+	error (_("No closing ')' in I/T set for `exec'"));
+      if (tem - text == 0)
+	error (_("Empty argument to `exec' in I/T set"));
+      arg = xstrndup (text, tem - text);
+      text = tem + 1;
+      elt = create_exec_itset (arg);
+    }
+  else
+    {
+      struct named_itset *named_itset;
+      char *tem;
+
+      tem = alloca (text - name + 1);
+
+      memcpy (tem, name, text - name);
+      tem[text - name] = '\0';
+
+      named_itset = get_named_itset (tem);
+      if (named_itset == NULL)
+	error (_("Unknown named I/T set: `%s'"), tem);
+      elt = (struct itset_elt *) create_itset_elt_itset (named_itset->set);
+    }
+
+  *textp = text;
+  return elt;
+}
+
+/* A cleanup function that calls itset_free.  */
+
+static void
+itset_free_cleanup (void *arg)
+{
+  struct itset *itset = arg;
+  itset_free (itset);
+}
+
+struct cleanup *
+make_cleanup_itset_free (struct itset *itset)
+{
+  return make_cleanup (itset_free_cleanup, itset);
+}
+
+/*
+
+  ',' (union) has precedence over '.' (intersect).
+
+  ELEM = RANGE | NEG | PARENS_SET | '$' | NAME
+  NEG = '~' ELEM
+  PARENS_SET = '(' ITSET_ONE ')'
+  INTERS = ELEM ('.' ELEM)*
+  ITSET_ONE = INTERS (',' INTERS)*
+  ITSET = | ('!' ITSET_ONE) | ITSET_ONE
+
+  E.g.,:
+
+  c1-3.~i1.(t1,other),foo is transformed to a tree like:
+
+   UNION
+     foo
+     INTERS
+       c1-3
+       NEG
+         i1-1
+       UNION
+         t1-1
+	 other
+
+*/
+
+/* Forward declare.  The parser is recursive.  */
+static struct itset_elt *parse_neg (char **spec);
+static struct itset_elt *parse_parens_set (char **spec);
+static struct itset_elt *parse_itset_one (char **spec);
+static struct itset_elt *parse_current_focus (char **spec);
+
+static int
+valid_spec_end (char *spec)
+{
+  return *spec == '\0' || isspace (*spec);
+}
+
+static struct itset_elt *
+parse_elem (char **spec)
+{
+  struct itset_elt *elt;
+
+  elt = parse_inferior_range (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_thread_range (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_core_range (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_neg (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_parens_set (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_current_focus (spec);
+  if (elt != NULL)
+    return elt;
+
+  return parse_named_or_throw (spec);
+}
+
+static struct itset_elt *
+parse_neg (char **spec)
+{
+  struct itset_elt_negated *neg_elt;
+  struct itset_elt *elt;
+
+  if (**spec != '~')
+    return NULL;
+
+  (*spec)++;
+
+  elt = parse_elem (spec);
+
+  neg_elt = create_itset_elt_negated ();
+  neg_elt->negated = elt;
+
+  return (struct itset_elt *) neg_elt;
+}
+
+static struct itset_elt *
+parse_current_focus (char **spec)
+{
+  struct itset_elt_itset *itset_elt;
+
+  if (**spec != '$')
+    return NULL;
+
+  (*spec)++;
+
+  itset_elt = create_itset_elt_itset (itset_reference (current_itset));
+  return (struct itset_elt *) itset_elt;
+}
+
+static struct itset_elt *
+parse_parens_set (char **spec)
+{
+  struct itset_elt_negated *neg_elt;
+  struct itset_elt *elt;
+  struct cleanup *old_chain;
+
+  if (**spec != '(')
+    return NULL;
+  (*spec)++;
+
+  elt = parse_itset_one (spec);
+  if (elt == NULL)
+    error (_("Invalid I/T syntax at `%s'"), *spec);
+
+  old_chain = make_cleanup_itset_elt_free (elt);
+  if (**spec != ')')
+    error (_("Invalid I/T syntax at `%s'"), *spec);
+
+  neg_elt = create_itset_elt_negated ();
+  neg_elt->negated = elt;
+
+  discard_cleanups (old_chain);
+
+  return (struct itset_elt *) neg_elt;
+}
+
+static struct itset_elt *
+parse_inters (char **spec)
+{
+  struct itset_elt *elt1, *elt2 = NULL;
+  struct itset_elt_intersect *intersect = NULL;
+  struct cleanup *old_chain;
+
+  elt1 = parse_elem (spec);
+  if (elt1 == NULL)
+    return NULL;
+
+  old_chain = make_cleanup_itset_elt_free (elt1);
+
+  if (**spec == '.')
+    {
+      intersect = create_intersect_itset ();
+      VEC_safe_push (itset_elt_ptr, intersect->elements, elt1);
+      elt1 = (struct itset_elt *) intersect;
+
+      discard_cleanups (old_chain);
+      old_chain = make_cleanup_itset_elt_free (elt1);
+    }
+
+  while (**spec == '.')
+    {
+      (*spec)++;
+
+      elt2 = parse_elem (spec);
+      if (elt2 == NULL)
+	{
+	  do_cleanups (old_chain);
+	  return NULL;
+	}
+      VEC_safe_push (itset_elt_ptr, intersect->elements, elt2);
+    }
+
+  discard_cleanups (old_chain);
+  return elt1;
+}
+
+static struct itset_elt *
+parse_itset_one (char **spec)
+{
+  struct itset_elt *inters1, *inters2 = NULL;
+  struct itset_elt_itset *un = NULL;
+  struct cleanup *old_chain;
+
+  inters1 = parse_inters (spec);
+  if (inters1 == NULL)
+    return NULL;
+  old_chain = make_cleanup_itset_elt_free (inters1);
+
+  if (**spec == ',')
+    {
+      struct itset *set;
+
+      set = XCNEW (struct itset);
+      set->refc = 1;
+
+      un = create_itset_elt_itset (set);
+
+      VEC_safe_push (itset_elt_ptr, set->elements, inters1);
+      inters1 = (struct itset_elt *) un;
+
+      discard_cleanups (old_chain);
+      old_chain = make_cleanup_itset_elt_free (inters1);
+    }
+
+  while (**spec == ',')
+    {
+      (*spec)++;
+
+      inters2 = parse_inters (spec);
+      if (inters2 == NULL)
+	{
+	  do_cleanups (old_chain);
+	  return NULL;
+	}
+      VEC_safe_push (itset_elt_ptr, un->set->elements, inters2);
+    }
+
+  discard_cleanups (old_chain);
+  return inters1;
+}
+
+/* Parse an I/T set specification and return a new I/T set.  Throws an
+   exception on error.  */
+
+struct itset *
+itset_create (char **specp)
+{
+  int is_static = 0;
+  struct itset *result;
+  struct itset_elt *elt;
+  struct cleanup *cleanups;
+  char *spec = *specp;
+  char *spec_start;
+
+  result = XCNEW (struct itset);
+  result->refc = 1;
+
+  cleanups = make_cleanup_itset_free (result);
+
+  spec = skip_spaces (spec);
+  spec_start = spec;
+
+  if (*spec == '!')
+    {
+      is_static = 1;
+      ++spec;
+    }
+
+  if (!valid_spec_end (spec))
+    {
+      elt = parse_itset_one (&spec);
+      VEC_safe_push (itset_elt_ptr, result->elements, elt);
+
+      if (!valid_spec_end (spec))
+	error (_("Invalid I/T syntax at `%s'"), spec);
+    }
+
+  result->spec = xstrndup (spec_start, spec - spec_start);
+  *specp = spec;
+
+  if (is_static)
+    {
+      struct itset_elt *st = create_static_itset (result->elements);
+
+      set_free (result->elements);
+      result->elements = NULL;
+      VEC_safe_push (itset_elt_ptr, result->elements, st);
+    }
+
+  discard_cleanups (cleanups);
+
+  return result;
+}
+
+struct itset *
+itset_create_empty (void)
+{
+  char *spec = "";
+
+  return itset_create (&spec);
+}
+
+/* Create a new I/T set which represents the current inferior and all
+   its threads.  */
+
+static struct itset *
+itset_create_curinf (void)
+{
+  char *spec = "curinf";
+
+  return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_all (void)
+{
+  char *spec = "all";
+
+  return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_running (void)
+{
+  char *spec = "running";
+
+  return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_stopped (void)
+{
+  char *spec = "stopped";
+
+  return itset_create (&spec);
+}
+
+/* Return 1 if SET contains INF, 0 otherwise.  */
+
+int
+itset_is_empty (const struct itset *set)
+{
+  return set_is_empty (set->elements);
+}
+
+/* Return 1 if SET contains INF, 0 otherwise.  */
+
+int
+itset_contains_inferior (struct itset *set, struct inferior *inf)
+{
+  return set_contains_inferior (set->elements, inf);
+}
+
+/* Return 1 if SET contains THR, 0 otherwise.  */
+
+int
+itset_contains_thread (struct itset *set, struct thread_info *thr)
+{
+  return set_contains_thread (set->elements, thr);
+}
+
+/* Acquire a new reference to an I/T set.  */
+
+struct itset *
+itset_reference (struct itset *itset)
+{
+  ++itset->refc;
+  return itset;
+}
+
+/* Destroy SET.  */
+
+void
+itset_free (struct itset *set)
+{
+  /* Like xfree, allow NULL.  */
+  if (set == NULL)
+    return;
+
+  if (--set->refc == 0)
+    {
+      set_free (set->elements);
+      xfree (set->name);
+      xfree (set->spec);
+      xfree (set);
+    }
+}
+
+/* Helper struct for iterate_over_itset.  */
+
+struct iterate_data
+{
+  /* The I/T set we are using.  */
+  struct itset *itset;
+
+  /* The original callback  */
+  int (*callback) (struct inferior *, void *);
+
+  /* The data passed in to iterate_over_itset.  */
+  void *client_data;
+};
+
+/* Callback function for iterate_over_inferiors, used by
+   iterate_over_itset.  */
+
+static int
+iter_callback (struct inferior *inf, void *d)
+{
+  struct iterate_data *data = d;
+
+  if (itset_contains_inferior (data->itset, inf))
+    return data->callback (inf, data->client_data);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+struct inferior *
+iterate_over_itset_inferiors (struct itset *itset,
+			      itset_inf_callback_func *callback,
+			      void *datum)
+{
+  struct iterate_data data;
+
+  data.itset = itset;
+  data.callback = callback;
+  data.client_data = datum;
+
+  return iterate_over_inferiors (iter_callback, &data);
+}
+
+/* Helper struct for iterate_over_itset.  */
+
+struct iterate_thr_data
+{
+  /* The I/T set we are using.  */
+  struct itset *itset;
+
+  /* The original callback  */
+  int (*callback) (struct thread_info *, void *);
+
+  /* The data passed in to iterate_over_itset_threads.  */
+  void *client_data;
+};
+
+/* Callback function for iterate_over_inferiors, used by
+   iterate_over_itset.  */
+
+static int
+iter_thr_callback (struct thread_info *thr, void *d)
+{
+  struct iterate_thr_data *data = d;
+
+  if (itset_contains_thread (data->itset, thr))
+    return data->callback (thr, data->client_data);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+static struct thread_info *
+iterate_over_itset_threads (struct itset *itset,
+			    int (*callback) (struct thread_info *, void *),
+			    void *datum)
+{
+  struct iterate_thr_data data;
+
+  data.itset = itset;
+  data.callback = callback;
+  data.client_data = datum;
+
+  return iterate_over_threads (iter_thr_callback, &data);
+}
+
+struct itset *current_itset = NULL;
+
+static struct named_itset *
+make_itset_named_itset (struct itset *set, char *name, int internal)
+{
+  struct named_itset *named_itset;
+
+  itset_reference (set);
+  set->name = name;
+
+  named_itset = XCNEW (struct named_itset);
+  named_itset->set = set;
+
+  if (internal)
+    named_itset->number = --internal_named_itset_count;
+  else
+    named_itset->number = ++named_itset_count;
+
+  return named_itset;
+}
+
+static int
+itset_elt_is_static (struct itset_elt *elt)
+{
+  return elt->vtable == &static_vtable;
+}
+
+static int
+itset_is_static (struct itset *itset)
+{
+  struct itset_elt *elt;
+  int ix;
+
+  /* True if all elements are static.  */
+
+  if (VEC_empty (itset_elt_ptr, itset->elements))
+    return 0;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, itset->elements, ix, elt); ++ix)
+    if (!itset_elt_is_static (elt))
+      return 0;
+
+  return 1;
+}
+
+static void
+defset_command (char *arg, int from_tty)
+{
+  char *endp;
+  char *name;
+  char *spec;
+  struct itset *itset;
+  struct named_itset *named_itset;
+  struct cleanup *old_chain;
+
+  if (arg == NULL || *arg == '\0')
+    error_no_arg (_("no args"));
+
+  arg = skip_spaces (arg);
+
+  endp = skip_to_space (arg);
+  spec = endp;
+
+  name = xstrndup (arg, endp - arg);
+  old_chain = make_cleanup (xfree, name);
+
+  named_itset = get_named_itset (name);
+  if (named_itset != NULL)
+    error (_("itset %s already exists"), name);
+
+  spec = skip_spaces (spec);
+
+  itset = itset_create (&spec);
+  make_cleanup_itset_free (itset);
+
+  if (itset_is_static (itset) && itset_is_empty (itset))
+    warning (_("static itset is empty"));
+
+  named_itset = make_itset_named_itset (itset, name, 0);
+  itset_free (itset);
+  discard_cleanups (old_chain);
+  add_to_named_itset_chain (named_itset);
+}
+
+static void
+free_named_itset (struct named_itset *it)
+{
+  itset_free (it->set);
+  xfree (it);
+}
+
+static void
+undefset_command (char *arg, int from_tty)
+{
+  char *name;
+  struct named_itset *it, **it_link;
+  int found;
+
+  if (arg == NULL || *arg == '\0')
+    error_no_arg (_("no args"));
+
+  name = skip_spaces (arg);
+
+  if (strcmp (name, "-all") == 0)
+    {
+      it = named_itsets;
+      it_link = &named_itsets;
+      while (it != NULL)
+	{
+	  if (it->number > 0)
+	    {
+	      *it_link = it->next;
+	      free_named_itset (it);
+	    }
+	  else
+	    it_link = &it->next;
+	  it = *it_link;
+	}
+      return;
+    }
+
+  found = 0;
+  it = named_itsets;
+  it_link = &named_itsets;
+  while (it != NULL)
+    {
+      if (strcmp (it->set->name, name) == 0)
+	{
+	  if (it->number < 0)
+	    error (_("cannot delete builtin I/T set"));
+
+	  *it_link = it->next;
+	  free_named_itset (it);
+	  found = 1;
+	  break;
+	}
+
+      it_link = &it->next;
+      it = *it_link;
+    }
+
+  if (!found)
+    warning (_("itset %s does not exist"), name);
+}
+
+static void
+itsets_info (char *arg, int allflag, int from_tty)
+{
+  struct named_itset *e;
+  int num_printable_entries;
+  struct cleanup *tbl_chain;
+
+  /* Compute the number of rows in the table.  */
+  num_printable_entries = 0;
+  ALL_NAMED_ITSETS (e)
+    if (allflag
+	|| (e->number > 0
+	    && (arg == NULL || number_is_in_list (arg, e->number))))
+      num_printable_entries++;
+
+  if (num_printable_entries == 0)
+    {
+      if (arg == NULL)
+	ui_out_message (current_uiout, 0, _("No named itsets.\n"));
+      else
+	ui_out_message (current_uiout, 0,
+			_("No named itset found with number %s.\n"), arg);
+
+      return;
+    }
+
+  tbl_chain
+    = make_cleanup_ui_out_table_begin_end (current_uiout, 3,
+					   num_printable_entries,
+					   "NamedItsetListTable");
+
+  ui_out_table_header (current_uiout, 7, ui_left, "number", "Num");      /* 1 */
+  ui_out_table_header (current_uiout, 14, ui_left, "name", "Name");      /* 2 */
+  ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What");   /* 3 */
+  ui_out_table_body (current_uiout);
+
+  ALL_NAMED_ITSETS (e)
+    if (allflag
+	|| (e->number > 0
+	    && (arg == NULL || number_is_in_list (arg, e->number))))
+      {
+	struct cleanup *entry_chain;
+
+	QUIT;
+
+	entry_chain
+	  = make_cleanup_ui_out_tuple_begin_end (current_uiout, "named-itset");
+	ui_out_field_int (current_uiout, "number", e->number);         /* 1 */
+	ui_out_field_string (current_uiout, "name", e->set->name);     /* 2 */
+	ui_out_field_string (current_uiout, "what", e->set->spec);     /* 3 */
+	ui_out_text (current_uiout, "\n");
+	do_cleanups (entry_chain);
+      }
+
+  do_cleanups (tbl_chain);
+}
+
+static void
+info_itsets_command (char *arg, int from_tty)
+{
+  itsets_info (arg, 0, from_tty);
+}
+
+static void
+maintenance_info_itsets_command (char *arg, int from_tty)
+{
+  itsets_info (arg, 1, from_tty);
+}
+
+static int
+whichsets_callback (struct thread_info *thr, void *data)
+{
+  struct named_itset *named_itset;
+  struct inferior *inf = get_thread_inferior (thr);
+  int printed = 0;
+
+  ALL_NAMED_ITSETS(named_itset)
+    {
+      QUIT;
+
+      if (itset_contains_thread (named_itset->set, thr))
+	{
+	  if (!printed)
+	    {
+	      printf_filtered (_("i%d.t%d (%s) is in:"),
+			       inf->num, thr->num,
+			       target_pid_to_str (thr->ptid));
+	      printf_filtered (" %s", itset_name (named_itset->set));
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %s", itset_name (named_itset->set));
+	}
+    }
+
+  if (printed)
+    printf_filtered ("\n");
+
+  return 0;
+}
+
+static void
+whichsets_command (char *arg, int from_tty)
+{
+  struct named_itset *named_itset;
+  struct itset *itset;
+  struct cleanup *old_chain;
+
+  if (arg == NULL)
+    {
+      /* No arg means all threads.  */
+      itset = itset_reference (current_itset);
+    }
+  else
+    {
+      arg = skip_spaces (arg);
+      itset = itset_create (&arg);
+    }
+
+  old_chain = make_cleanup_itset_free (itset);
+  iterate_over_itset_threads (itset, whichsets_callback, NULL);
+  do_cleanups (old_chain);
+}
+
+static void
+viewset (struct itset *itset)
+{
+  struct inferior *inf;
+  struct thread_info *thr;
+  int printed;
+
+  printf_filtered ("%s contains:\n", itset_name (itset)
+		   ? itset_name (itset) : itset_spec (itset));
+
+  printed = 0;
+  ALL_INFERIORS (inf)
+    {
+      if (itset_contains_inferior (itset, inf))
+	{
+	  if (!printed)
+	    {
+	      printf_filtered (_("  inferiors: %d"), inf->num);
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %d", inf->num);
+
+	}
+    }
+  if (printed)
+    printf_filtered ("\n");
+
+  printed = 0;
+  ALL_THREADS (thr)
+    {
+      if (itset_contains_thread (itset, thr))
+	{
+	  if (!printed)
+	    {
+	      printf_filtered (_("  threads: %d"), thr->num);
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %d", thr->num);
+	}
+    }
+  if (printed)
+    printf_filtered ("\n");
+
+  printed = 0;
+  ALL_THREADS (thr)
+    {
+      if (itset_contains_thread (itset, thr))
+	{
+	  int core;
+
+	  core = target_core_of_thread (thr->ptid);
+	  if (core == -1)
+	    break;
+
+	  if (!printed)
+	    {
+	      printf_filtered (_("  cores: %d"), core);
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %d", core);
+	}
+    }
+  if (printed)
+    printf_filtered ("\n");
+}
+
+static void
+viewset_command (char *arg, int from_tty)
+{
+  struct named_itset *named_itset;
+
+  if (arg == NULL)
+    {
+      struct named_itset *e;
+      struct itset *itset;
+
+      /* No arg means all debugger- and user-defined sets.  */
+      ALL_NAMED_ITSETS (named_itset)
+	viewset (named_itset->set);
+    }
+  else
+    {
+      struct itset *itset;
+      struct cleanup *old_chain;
+
+      arg = skip_spaces (arg);
+      itset = itset_create (&arg);
+      old_chain = make_cleanup_itset_free (itset);
+      viewset (itset);
+      do_cleanups (old_chain);
+    }
+}
+
+static void
+make_internal_itset (struct itset *itset, const char *name)
+{
+  struct named_itset *named_itset;
+
+  named_itset = make_itset_named_itset (itset, xstrdup (name), 1);
+  add_to_named_itset_chain (named_itset);
+}
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_itset;
+
+void
+_initialize_itset (void)
+{
+  struct cmd_list_element *c = NULL;
+
+  all_itset = itset_create_all ();
+  empty_itset = itset_create_empty ();
+  running_itset = itset_create_running ();
+  stopped_itset = itset_create_stopped ();
+  curinf_itset = itset_create_curinf ();
+
+  make_internal_itset (all_itset, "all");
+  make_internal_itset (empty_itset, "empty");
+  make_internal_itset (running_itset, "running");
+  make_internal_itset (stopped_itset, "stopped");
+  make_internal_itset (curinf_itset, "curinf");
+
+  current_itset = itset_reference (all_itset);
+
+  add_com ("defset", no_class, defset_command, _("\
+Define a new named set.\n\
+Usage: defset NAME SPEC"));
+
+  add_com ("undefset", no_class, undefset_command, _("\
+Undefine an existing named set.\n\
+Usage: undefset NAME | -all"));
+
+  add_com ("whichsets", no_class, whichsets_command, _("\
+List all sets to which threads in a given set belong to.\n\
+Usage: whichsets SET.\n\
+Defaults to the current set."));
+
+  add_com ("viewset", no_class, viewset_command, _("\
+List the members of a set.\n\
+Usage: viewset SET.\n\
+Defaults to all named sets."));
+
+  add_info ("itsets", info_itsets_command, _("\
+Display the list of defined named itsets.\n\
+You can specify numbers (e.g. \"info itsets 1 3\"),\n\
+ranges (e.g. \"info itsets 4-8\"), or both (e.g. \"info itsets 1 3 4-8\").\n\n\
+If you don't specify any numbers or ranges, we'll show all itsets.\n\n\
+Usage: info itsets [NUMBERS AND/OR RANGES]\n"));
+
+  add_cmd ("itsets", class_maintenance, maintenance_info_itsets_command, _("\
+Display the list of all defined named itsets, user-defined and built-in.\n"),
+  &maintenanceinfolist);
+}
diff --git a/gdb/itset.h b/gdb/itset.h
new file mode 100644
index 0000000..5712a3c
--- /dev/null
+++ b/gdb/itset.h
@@ -0,0 +1,122 @@
+/* itset.h - Inferior/Thread sets.
+   Copyright (C) 2010 - 2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef ITSET_H
+#define ITSET_H
+
+struct inferior;
+struct thread_info;
+struct cleanup;
+
+/* This is an opaque type representing an I/T set.  An I/T set is
+   simply a set of inferiors and/or threads.  A set may be dynamic
+   (the members are enumerated at the time of use) or static (the
+   members are enumerated at the time of construction); but this
+   distinction is hidden from the callers.  An I/T set object is
+   reference counted.  */
+
+struct itset;
+
+/* Create a new I/T set from a user specification.  The valid forms of
+   a specification are documented in the manual.  *SPEC is the input
+   specification, and it is updated to point to the first non-space
+   character after the end of the specification.  */
+
+struct itset *itset_create (char **spec);
+
+/* Add ADDME to the I/T set TO.  In other words, after the call, TO
+   will be the union set of TO at entry, and ADDME.  */
+
+void itset_add_set (struct itset *to, struct itset *addme);
+
+/* Create an empty I/T set.  Usually, an empty set is the set that
+   matches nothing.  In some contexts, though, an empty set represents
+   some default.  */
+
+struct itset *itset_create_empty (void);
+
+/* Returns true if SET is the empty set.  That is a set whose spec is
+   either "" or "empty".  */
+
+int itset_is_empty_set (struct itset *set);
+
+/* Returns true if ITSET is empty.  That is, the set contains no
+   inferiors, threads, etc.  */
+
+int itset_is_empty (const struct itset *itset);
+
+/* Create a new dynamic I/T set which represents the current inferior,
+   at the time the I/T set if consulted.  */
+
+struct itset *itset_create_current (void);
+
+/* Like itset_create, but if *SPEC does not appear to be the start of
+   an I/T set, it will call itset_create_current and return the
+   result.  */
+
+struct itset *itset_create_or_default (char **spec);
+
+/* Return true if the inferior is contained in the I/T set.  */
+
+int itset_contains_inferior (struct itset *itset, struct inferior *inf);
+
+/* Return true if the thread is contained in the I/T set.  */
+
+int itset_contains_thread (struct itset *itset, struct thread_info *inf);
+
+/* Return true if the inferior is contained in the I/T set.  */
+
+int itset_member (struct itset *itset, int inf_id, int thread_id);
+
+/* Return a pointer to the I/T set's name.  Unnamed I/T sets have a
+   NULL name.  */
+
+const char *itset_name (const struct itset *itset);
+
+/* Return a pointer to the I/T set's spec.  */
+
+const char *itset_spec (const struct itset *itset);
+
+/* Acquire a new reference to an I/T set.  Returns the I/T set, for
+   convenience.  */
+
+struct itset *itset_reference (struct itset *itset);
+
+/* Release a reference to an I/T set.  */
+
+void itset_free (struct itset *itset);
+
+struct cleanup *make_cleanup_itset_free (struct itset *itset);
+
+/* A cleanup function that calls itset_free.  */
+
+void itset_cleanup (void *itset);
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+typedef int (itset_inf_callback_func) (struct inferior *, void *);
+struct inferior *iterate_over_itset_inferiors (struct itset *itset,
+					       itset_inf_callback_func *callback,
+					       void *data);
+
+/* The current I/T set.  */
+
+extern struct itset *current_itset;
+
+#endif /* ITSET_H */
diff --git a/gdb/thread.c b/gdb/thread.c
index d361dd8..02fdc7d 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -54,7 +54,7 @@ void _initialize_thread (void);
 
 /* Prototypes for local functions.  */
 
-static struct thread_info *thread_list = NULL;
+struct thread_info *thread_list = NULL;
 static int highest_thread_num;
 
 static void thread_command (char *tidstr, int from_tty);
@@ -73,6 +73,15 @@ inferior_thread (void)
   return tp;
 }
 
+struct inferior *
+get_thread_inferior (struct thread_info *thr)
+{
+  int pid;
+
+  pid = ptid_get_pid (thr->ptid);
+  return find_inferior_pid (pid);
+}
+
 void
 delete_step_resume_breakpoint (struct thread_info *tp)
 {
-- 
1.7.0.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] define and check itset
  2012-05-31 13:15 ` [PATCH 1/3] define and check itset Yao Qi
@ 2012-05-31 13:34   ` Pedro Alves
  2012-05-31 14:25     ` Yao Qi
  2012-05-31 15:37     ` Eli Zaretskii
  0 siblings, 2 replies; 8+ messages in thread
From: Pedro Alves @ 2012-05-31 13:34 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 05/31/2012 02:14 PM, Yao Qi wrote:

> This patch is almost from Pedro's patch, with some minor fixes for the
> new GDB code base and removal of command 'itfocus'.


What good does it do to put this in ahead of its prerequisites?
Can we please stop trying to put the cart before the horse?
I'm trying to help with the async stuff, but the constant push
in trying to put other bits in first frustrates me.  :-/

The itsets bits need to be last.  I'm not even sure the syntax is
what we want to end up with at all.  Last I touched them, I had
wanted to spend a while trying to unify the concept of "current
thread" with the itset.  There's a disconnect that gets in the
way a bit.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] define and check itset
  2012-05-31 13:34   ` Pedro Alves
@ 2012-05-31 14:25     ` Yao Qi
  2012-05-31 14:51       ` Pedro Alves
  2012-05-31 15:37     ` Eli Zaretskii
  1 sibling, 1 reply; 8+ messages in thread
From: Yao Qi @ 2012-05-31 14:25 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

On 05/31/2012 09:34 PM, Pedro Alves wrote:
> What good does it do to put this in ahead of its prerequisites?
> Can we please stop trying to put the cart before the horse?
> I'm trying to help with the async stuff, but the constant push
> in trying to put other bits in first frustrates me.  :-/
> 

I don't want to frustrates anyone here.  This bit doesn't have any
prerequisites, as I said, it is quite isolated from other parts.  I
don't see anything wrong this bits go in first.  The review process to
patch series 'run all-stop on top of non-stop' is not smooth, which
forces me to start to push this part first.

> The itsets bits need to be last.  I'm not even sure the syntax is
> what we want to end up with at all.  Last I touched them, I had
> wanted to spend a while trying to unify the concept of "current
> thread" with the itset.  There's a disconnect that gets in the
> way a bit.

I don't know your plan, and you are free to change whatever you'd like
to.  AFAICS, the syntax looks fine to me, so I posted them.

-- 
Yao (齐尧)


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] define and check itset
  2012-05-31 14:25     ` Yao Qi
@ 2012-05-31 14:51       ` Pedro Alves
  0 siblings, 0 replies; 8+ messages in thread
From: Pedro Alves @ 2012-05-31 14:51 UTC (permalink / raw)
  To: Yao Qi; +Cc: gdb-patches

On 05/31/2012 03:23 PM, Yao Qi wrote:

> On 05/31/2012 09:34 PM, Pedro Alves wrote:
>> What good does it do to put this in ahead of its prerequisites?
>> Can we please stop trying to put the cart before the horse?
>> I'm trying to help with the async stuff, but the constant push
>> in trying to put other bits in first frustrates me.  :-/
>>
> 
> I don't want to frustrates anyone here.  This bit doesn't have any
> prerequisites, as I said, it is quite isolated from other parts.  I
> don't see anything wrong this bits go in first.  


It's useless on its own.

> The review process to
> patch series 'run all-stop on top of non-stop' is not smooth, which
> forces me to start to push this part first.


Sorry, but nothing should be forcing you to do that.  I wrote most of
that series, so its natural that others expect me to review it first; and most
probably nobody else feels very qualified to review it.  But I never considered
that code final, and I want to get back to play with the series a bit more,
not just review what I already had, but unfortunately, I keep getting distracted
with reviews, etc., so I'm progressing quite slowly.  Apologies.  But it'll
get done.  E.g., my recent new_thread_event patch is preparatory work.  I'm
working on some other cleanup that ends up addressing PR11692.  I need to
get back to the MI async issue.

>> > The itsets bits need to be last.  I'm not even sure the syntax is
>> > what we want to end up with at all.  Last I touched them, I had
>> > wanted to spend a while trying to unify the concept of "current
>> > thread" with the itset.  There's a disconnect that gets in the
>> > way a bit.

> I don't know your plan, and you are free to change whatever you'd like
> to.  AFAICS, the syntax looks fine to me, so I posted them.

My plan is to make async good enough to flip it on; hack on
all-stop-on-top-of-non-stop some more, and put that in.  And only
then we'll get back to itsets.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] define and check itset
  2012-05-31 13:34   ` Pedro Alves
  2012-05-31 14:25     ` Yao Qi
@ 2012-05-31 15:37     ` Eli Zaretskii
  1 sibling, 0 replies; 8+ messages in thread
From: Eli Zaretskii @ 2012-05-31 15:37 UTC (permalink / raw)
  To: Pedro Alves; +Cc: yao, gdb-patches

> Date: Thu, 31 May 2012 14:34:06 +0100
> From: Pedro Alves <palves@redhat.com>
> CC: gdb-patches@sourceware.org
> 
> On 05/31/2012 02:14 PM, Yao Qi wrote:
> 
> > This patch is almost from Pedro's patch, with some minor fixes for the
> > new GDB code base and removal of command 'itfocus'.
> 
> 
> What good does it do to put this in ahead of its prerequisites?
> Can we please stop trying to put the cart before the horse?
> I'm trying to help with the async stuff, but the constant push
> in trying to put other bits in first frustrates me.  :-/
> 
> The itsets bits need to be last.  I'm not even sure the syntax is
> what we want to end up with at all.  Last I touched them, I had
> wanted to spend a while trying to unify the concept of "current
> thread" with the itset.  There's a disconnect that gets in the
> way a bit.

Should I refrain from reviewing the docs bits for the time being,
then?


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-05-31 15:37 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-05-31 13:15 [PATCH 0/3] Basic ITSET Yao Qi
2012-05-31 13:15 ` [PATCH 2/3] Test case: gdb.base/itset.exp and gdb.multi/itset.exp Yao Qi
2012-05-31 13:15 ` [PATCH 3/3] Doc Yao Qi
2012-05-31 13:15 ` [PATCH 1/3] define and check itset Yao Qi
2012-05-31 13:34   ` Pedro Alves
2012-05-31 14:25     ` Yao Qi
2012-05-31 14:51       ` Pedro Alves
2012-05-31 15:37     ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox