From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18229 invoked by alias); 31 May 2012 13:15:29 -0000 Received: (qmail 17474 invoked by uid 22791); 31 May 2012 13:15:17 -0000 X-SWARE-Spam-Status: No, hits=-4.1 required=5.0 tests=AWL,BAYES_00,FROM_12LTRDOM,KHOP_RCVD_UNTRUST,KHOP_THREADED,RCVD_IN_HOSTKARMA_W,RCVD_IN_HOSTKARMA_WL,TW_CP X-Spam-Check-By: sourceware.org Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 31 May 2012 13:14:51 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1Sa5DG-0006ry-6R from Yao_Qi@mentor.com for gdb-patches@sourceware.org; Thu, 31 May 2012 06:14:50 -0700 Received: from SVR-ORW-FEM-04.mgc.mentorg.com ([147.34.97.41]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Thu, 31 May 2012 06:14:24 -0700 Received: from localhost.localdomain (147.34.91.1) by svr-orw-fem-04.mgc.mentorg.com (147.34.97.41) with Microsoft SMTP Server id 14.1.289.1; Thu, 31 May 2012 06:14:46 -0700 From: Yao Qi To: Subject: [PATCH 1/3] define and check itset Date: Thu, 31 May 2012 13:15:00 -0000 Message-ID: <1338470075-12254-2-git-send-email-yao@codesourcery.com> In-Reply-To: <1338470075-12254-1-git-send-email-yao@codesourcery.com> References: <1338470075-12254-1-git-send-email-yao@codesourcery.com> MIME-Version: 1.0 Content-Type: text/plain X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-05/txt/msg01082.txt.bz2 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 Tom Tromey * 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 * 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 . */ + +#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 +#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); +} + + + +/* 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; +} + + + +/* 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); +} + + + +/* 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; +} + + + +/* 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 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; +} + + + +/* 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 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; +} + + + +/* 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); +} + + + +/* 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; +} + + + +/* 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; +} + + + +/* 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 = ¤t_vtable; + + return elt; +} + + + +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 +}; + + + +/* 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; +} + + + +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); +} + + + +/* 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 . */ + +#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