This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1/5] Add basic itsets support


We want to propose an incomplete ITSET syntax specification which is
sufficient for the "target set" of most of the commands, and leave the
open space for us to determine the rest of ITSET syntax specification
step by step.

In this ITSET specification, object prefix are 'i' and 't', which
standard for inferior and thread.  Operators intersection '.' and
union ',' are supported.  Wild card '*' and range '-' is supported as
well.  This is a minimum subset of ITSET specification, which can be
used for "target set" of commands.  Then, we can teach command one by
one to understand or be aware of "target set", and get users try them
gradually.  In parallel, we can improve the ITSET specification
itself, adding more object prefix, such as 'c', and operator, such as
negation.

gdb:
2013-04-02  Pedro Alves  <pedro@codesourcery.com>
	    Tom Tromey  <tromey@redhat.com>
	    Yao Qi  <yao@codesourcery.com>

	* Makefile.in (SFILES): Add itset.c.
	(COMMON_OBS): Add itset.o.
	* gdbthread.h (get_thread_inferior): Declare.
	* thread.c (get_thread_inferior): New.
	* itset.h, itset.c: New.
---
 gdb/Makefile.in |    2 +-
 gdb/gdbthread.h |    4 +
 gdb/itset.c     | 1561 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/itset.h     |   49 ++
 gdb/thread.c    |    9 +
 5 files changed, 1624 insertions(+), 1 deletions(-)
 create mode 100644 gdb/itset.c
 create mode 100644 gdb/itset.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9bab01c..d01b59d 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -879,7 +879,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	source.o value.o eval.o valops.o valarith.o valprint.o printcmd.o \
 	block.o symtab.o psymtab.o symfile.o symmisc.o linespec.o dictionary.o \
 	infcall.o \
-	infcmd.o infrun.o \
+	infcmd.o infrun.o itset.o \
 	expprint.o environ.o stack.o thread.o \
 	exceptions.o \
 	filesystem.o \
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 0846322..89e255a 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -401,4 +401,8 @@ extern void update_thread_list (void);
 
 extern struct thread_info *thread_list;
 
+/* 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..08060b3
--- /dev/null
+++ b/gdb/itset.c
@@ -0,0 +1,1561 @@
+/* itset.c - Inferior/Thread sets.
+   Copyright (C) 2010-2013 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;
+
+/* 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;
+};
+
+/* Destroy and free ELT.  */
+
+static void
+itset_elt_free (struct itset_elt *elt)
+{
+  if (elt->vtable->destroy != NULL)
+    elt->vtable->destroy (elt);
+  xfree (elt);
+}
+
+/* A cleanup routine which calls itset_elt_free.  */
+
+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;
+};
+
+/* Return a pointer to the I/T set's name.  Unnamed I/T sets have a
+   NULL name.  */
+
+static const char *
+itset_name (const struct itset *itset)
+{
+  return itset->name;
+}
+
+/* Return a pointer to the I/T set's spec.  */
+
+static const char *
+itset_spec (const struct itset *itset)
+{
+  return itset->spec;
+}
+
+
+
+/* An element in the list of named itsets, which can be either
+   debugger built-in (all, 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;
+    }
+}
+
+/* Find the named itset according NAME.  Return NULL if not found.  */
+
+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;
+}
+
+
+
+/* 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);
+}
+
+
+
+/* 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;
+}
+
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+
+/* 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;
+}
+
+
+
+/* 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);
+}
+
+/* Returns true if ITSET is empty.  That is, the set contains no
+   inferiors, threads, etc.  */
+
+static int
+itset_is_empty (const struct itset *set)
+{
+  return set_is_empty (set->elements);
+}
+
+/* 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
+};
+
+/* Acquire a new reference to an I/T set.  Returns the I/T set, for
+   convenience.  */
+
+static struct itset *
+itset_reference (struct itset *itset)
+{
+  ++itset->refc;
+  return itset;
+}
+
+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;
+}
+
+
+
+#define ITSET_OBJECT_PREFIX_THREAD 't'
+#define ITSET_OBJECT_PREFIX_INFERIOR 'i'
+#define ITSET_OPERATOR_INTERSECTION '.'
+#define ITSET_OPERATOR_UNION ','
+#define ITSET_WILDCARD '*'
+#define ITSET_RANGE '-'
+
+static int
+looks_like_range (char *spec)
+{
+  return isdigit (spec[0]) || spec[0] == ITSET_WILDCARD;
+}
+
+/* Parse an I/T set range.  Returns an updated pointer into the spec.
+   Update *FIRST and *LAST of the range.  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 '%c' or digit."),
+	   spec, ITSET_WILDCARD);
+
+  if (*spec == ITSET_WILDCARD)
+    {
+      *first = WILDCARD;
+      *last = WILDCARD;
+      ++spec;
+    }
+  else
+    {
+      *first = strtol (spec, &spec, 10);
+      if (*spec == ITSET_RANGE)
+	{
+	  ++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] != ITSET_OBJECT_PREFIX_INFERIOR
+      || !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] != ITSET_OBJECT_PREFIX_THREAD
+      || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_thread_range_itset (first, last);
+}
+
+
+/* Parse a named I/T set.  Currently the only named set which is
+   recognized is `all'.  Return the new I/T set and update *TEXTP.
+   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
+    {
+      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);
+}
+
+/* Release a reference to an I/T set.  */
+
+static struct cleanup *
+make_cleanup_itset_free (struct itset *itset)
+{
+  return make_cleanup (itset_free_cleanup, itset);
+}
+
+/* Forward declare.  The parser is recursive.  */
+static struct itset_elt *parse_itset_one (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;
+
+  return parse_named_or_throw (spec);
+}
+
+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 == ITSET_OPERATOR_INTERSECTION)
+    {
+      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 == ITSET_OPERATOR_INTERSECTION)
+    {
+      (*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 == ITSET_OPERATOR_UNION)
+    {
+      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 == ITSET_OPERATOR_UNION)
+    {
+      (*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;
+}
+
+/* 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. Throws an exception
+   on error.  */
+
+static struct itset *
+itset_create (char **specp)
+{
+  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 (!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;
+
+  discard_cleanups (cleanups);
+
+  return result;
+}
+
+static struct itset *
+itset_create_all (void)
+{
+  char *spec = "all";
+
+  return itset_create (&spec);
+}
+
+/* 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);
+}
+
+/* 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;
+}
+
+typedef int (itset_inf_callback_func) (struct inferior *, void *);
+
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+static 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);
+}
+
+static struct itset *current_itset = NULL;
+
+/* Create a named itset which references to SET.  Its name is NAME.
+   The named itset is an internal named itset if INTERNAL is true.  */
+
+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 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);
+
+  named_itset = make_itset_named_itset (itset, name, 0);
+  itset_free (itset);
+  discard_cleanups (old_chain);
+  add_to_named_itset_chain (named_itset);
+}
+
+/* Free named itset IT.  */
+
+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);
+}
+
+/* Helper function of commands 'info itsets' and 'maint info
+   itsets'.  */
+
+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");
+  ui_out_table_header (current_uiout, 14, ui_left, "name", "Name");
+  ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What");
+  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);
+	ui_out_field_string (current_uiout, "name", e->set->name);
+	ui_out_field_string (current_uiout, "what", e->set->spec);
+	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);
+}
+
+/* Callback function for command 'whichsets'.  */
+
+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);
+    }
+}
+
+/* Create an internal named itset.  Its name is NAME.  */
+
+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 ();
+
+  make_internal_itset (all_itset, "all");
+
+  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..1faa4e8
--- /dev/null
+++ b/gdb/itset.h
@@ -0,0 +1,49 @@
+/* itset.h - Inferior/Thread sets.
+   Copyright (C) 2010-2013 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;
+
+/* 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 a new reference to 'target_set' of commands.  */
+
+/* Release a reference to an I/T set.  */
+
+void itset_free (struct itset *itset);
+
+#endif /* ITSET_H */
diff --git a/gdb/thread.c b/gdb/thread.c
index 2a1d723..cb45cd6 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -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.7.6


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