public inbox for binutils-cvs@sourceware.org
 help / color / mirror / Atom feed
* [binutils-gdb] libctf, include: new functions for looking up enumerators
@ 2024-06-18 12:56 Nick Alcock
  0 siblings, 0 replies; only message in thread
From: Nick Alcock @ 2024-06-18 12:56 UTC (permalink / raw)
  To: binutils-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=2fa4b6e6df05990365066a0b286322502778d530

commit 2fa4b6e6df05990365066a0b286322502778d530
Author: Nick Alcock <nick.alcock@oracle.com>
Date:   Tue Jun 11 20:58:00 2024 +0100

    libctf, include: new functions for looking up enumerators
    
    Three new functions for looking up the enum type containing a given
    enumeration constant, and optionally that constant's value.
    
    The simplest, ctf_lookup_enumerator, looks up a root-visible enumerator by
    name in one dict: if the dict contains multiple such constants (which is
    possible for dicts created by older versions of the libctf deduplicator),
    ECTF_DUPLICATE is returned.
    
    The next simplest, ctf_lookup_enumerator_next, is an iterator which returns
    all enumerators with a given name in a given dict, whether root-visible or
    not.
    
    The most elaborate, ctf_arc_lookup_enumerator_next, finds all
    enumerators with a given name across all dicts in an entire CTF archive,
    whether root-visible or not, starting looking in the shared parent dict;
    opened dicts are cached (as with all other ctf_arc_*lookup functions) so
    that repeated use does not incur repeated opening costs.
    
    All three of these return enumerator values as int64_t: unfortunately, API
    compatibility concerns prevent us from doing the same with the other older
    enum-related functions, which all return enumerator constant values as ints.
    We may be forced to add symbol-versioning compatibility aliases that fix the
    other functions in due course, bumping the soname for platforms that do not
    support such things.
    
    ctf_arc_lookup_enumerator_next is implemented as a nested ctf_archive_next
    iterator, and inside that, a nested ctf_lookup_enumerator_next iterator
    within each dict.  To aid in this, add support to ctf_next_t iterators for
    iterators that are implemented in terms of two simultaneous nested iterators
    at once.  (It has always been possible for callers to use as many nested or
    semi-overlapping ctf_next_t iterators as they need, which is one of the
    advantages of this style over the _iter style that calls a function for each
    thing iterated over: the iterator change here permits *ctf_next_t iterators
    themselves* to be implemented by iterating using multiple other iterators as
    part of their internal operation, transparently to the caller.)
    
    Also add a testcase that tests all these functions (which is fairly easy
    because ctf_arc_lookup_enumerator_next is implemented in terms of
    ctf_lookup_enumerator_next) in addition to enumeration addition in
    ctf_open()ed dicts, ctf_add_enumerator duplicate enumerator addition, and
    conflicting enumerator constant deduplication.
    
    include/
            * ctf-api.h (ctf_lookup_enumerator): New.
            (ctf_lookup_enumerator_next): Likewise.
            (ctf_arc_lookup_enumerator_next): Likewise.
    
    libctf/
            * libctf.ver: Add them.
            * ctf-impl.h (ctf_next_t) <ctn_next_inner>: New.
            * ctf-util.c (ctf_next_copy): Copy it.
            (ctf_next_destroy): Destroy it.
            * ctf-lookup.c (ctf_lookup_enumerator): New.
            (ctf_lookup_enumerator_next): New.
            * ctf-archive.c (ctf_arc_lookup_enumerator_next): New.
            * testsuite/libctf-lookup/enumerator-iteration.*: New test.
            * testsuite/libctf-lookup/enum-ctf-2.c: New test CTF, used by the
              above.

Diff:
---
 include/ctf-api.h                                  |  38 +++++
 libctf/ctf-archive.c                               | 107 +++++++++++++
 libctf/ctf-impl.h                                  |  12 +-
 libctf/ctf-lookup.c                                | 145 ++++++++++++++++++
 libctf/ctf-util.c                                  |  29 +++-
 libctf/libctf.ver                                  |   7 +
 libctf/testsuite/libctf-lookup/enum-ctf-2.c        |   6 +
 .../testsuite/libctf-lookup/enumerator-iteration.c | 168 +++++++++++++++++++++
 .../libctf-lookup/enumerator-iteration.lk          |  17 +++
 9 files changed, 520 insertions(+), 9 deletions(-)

diff --git a/include/ctf-api.h b/include/ctf-api.h
index d67db8be13f..f1087bc0542 100644
--- a/include/ctf-api.h
+++ b/include/ctf-api.h
@@ -25,6 +25,7 @@
 #define	_CTF_API_H
 
 #include <sys/types.h>
+#include <inttypes.h>
 #include <ctf.h>
 #include <zlib.h>
 
@@ -538,6 +539,16 @@ extern ctf_id_t ctf_lookup_by_name (ctf_dict_t *, const char *);
 
 extern ctf_id_t ctf_lookup_variable (ctf_dict_t *, const char *);
 
+/* Look up a single enumerator by enumeration constant name.  Returns the ID of
+   the enum it is contained within and optionally its value.  Error out with
+   ECTF_DUPLICATE if multiple exist (which can happen in some older dicts).  See
+   ctf_lookup_enumerator_next in that case.  Enumeration constants in non-root
+   types are not returned, but constants in parents are, if not overridden by
+   an enum in the child.  */
+
+extern ctf_id_t ctf_lookup_enumerator (ctf_dict_t *, const char *,
+				       int64_t *enum_value);
+
 /* Type lookup functions.  */
 
 /* Strip qualifiers and typedefs off a type, returning the base type.
@@ -669,6 +680,33 @@ extern int ctf_enum_iter (ctf_dict_t *, ctf_id_t, ctf_enum_f *, void *);
 extern const char *ctf_enum_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
 				  int *);
 
+/* Return all enumeration constants with a given name in a given dict, similar
+   to ctf_lookup_enumerator above but capable of returning multiple values.
+   Enumerators in parent dictionaries are not returned: enumerators in non-root
+   types *are* returned.  This operation internally iterates over all types in
+   the dict, so is relatively expensive in large dictionaries.
+
+   There is nothing preventing NAME from being changed by the caller in the
+   middle of iteration: the results might be slightly confusing, but they are
+   well-defined.  */
+
+extern ctf_id_t ctf_lookup_enumerator_next (ctf_dict_t *, const char *name,
+					    ctf_next_t **, int64_t *enum_value);
+
+/* Likewise, across all dicts in an archive (parent first).  The DICT and ERRP
+   arguments are not optional: without the forer you can't tell which dict the
+   returned type is in, and without the latter you can't distinguish real errors
+   from end-of-iteration.  DICT should be NULL before the first call and is set
+   to NULL after the last and on error: on successful call it is set to the dict
+   containing the returned enum, and it is the caller's responsibility to
+   ctf_dict_close() it.  The caller should otherwise pass it back in unchanged
+   (do not reassign it during iteration, just as with the ctf_next_t iterator
+   itself).  */
+
+extern ctf_id_t ctf_arc_lookup_enumerator_next (ctf_archive_t *, const char *name,
+						ctf_next_t **, int64_t *enum_value,
+						ctf_dict_t **dict, int *errp);
+
 /* Iterate over all types in a dict.  ctf_type_iter_all recurses over all types:
    ctf_type_iter recurses only over types with user-visible names (for which
    CTF_ADD_ROOT was passed).  All such types are returned, even if they are
diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c
index 4744cb7a828..c602705b310 100644
--- a/libctf/ctf-archive.c
+++ b/libctf/ctf-archive.c
@@ -1011,6 +1011,113 @@ ctf_arc_lookup_symbol_name (ctf_archive_t *wrapper, const char *symname,
   return ctf_arc_lookup_sym_or_name (wrapper, 0, symname, typep, errp);
 }
 
+/* Return all enumeration constants with a given NAME across all dicts in an
+   archive, similar to ctf_lookup_enumerator_next.  The DICT is cached, so
+   opening costs are paid only once, but (unlike ctf_arc_lookup_symbol*
+   above) the results of the iterations are not cached.  dict and errp are
+   not optional.  */
+
+ctf_id_t
+ctf_arc_lookup_enumerator_next (ctf_archive_t *arc, const char *name,
+				ctf_next_t **it, int64_t *enum_value,
+				ctf_dict_t **dict, int *errp)
+{
+  ctf_next_t *i = *it;
+  ctf_id_t type;
+  int opened_this_time = 0;
+  int err;
+
+  /* We have two nested iterators in here: ctn_next tracks archives, while
+     within it ctn_next_inner tracks enumerators within an archive.  We
+     keep track of the dict by simply reusing the passed-in arg: if it's
+     changed by the caller, the caller will get an ECTF_WRONGFP error,
+     so this is quite safe and means we don't have to track the arc and fp
+     simultaneously in the ctf_next_t.  */
+
+  if (!i)
+    {
+      if ((i = ctf_next_create ()) == NULL)
+	{
+	  err = ENOMEM;
+	  goto err;
+	}
+      i->ctn_iter_fun = (void (*) (void)) ctf_arc_lookup_enumerator_next;
+      i->cu.ctn_arc = arc;
+      *it = i;
+    }
+
+  if ((void (*) (void)) ctf_arc_lookup_enumerator_next != i->ctn_iter_fun)
+    {
+      err = ECTF_NEXT_WRONGFUN;
+      goto err;
+    }
+
+  if (arc != i->cu.ctn_arc)
+    {
+      err = ECTF_NEXT_WRONGFP;
+      goto err;
+    }
+
+  /* Prevent any earlier end-of-iteration on this dict from confusing the
+     test below.  */
+  if (i->ctn_next != NULL)
+    ctf_set_errno (*dict, 0);
+
+  do
+    {
+      /* At end of one dict, or not started any iterations yet?
+	 Traverse to next dict.  If we never returned this dict to the
+	 caller, close it ourselves: the caller will never see it and cannot
+	 do so.  */
+
+      if (i->ctn_next == NULL || ctf_errno (*dict) == ECTF_NEXT_END)
+	{
+	  if (opened_this_time)
+	    {
+	      ctf_dict_close (*dict);
+	      *dict = NULL;
+	      opened_this_time = 0;
+	    }
+
+	  *dict = ctf_archive_next (arc, &i->ctn_next, NULL, 0, &err);
+	  if (!*dict)
+	    goto err;
+	  opened_this_time = 1;
+	}
+
+      type = ctf_lookup_enumerator_next (*dict, name, &i->ctn_next_inner,
+					 enum_value);
+    }
+  while (type == CTF_ERR && ctf_errno (*dict) == ECTF_NEXT_END);
+
+  if (type == CTF_ERR)
+    {
+      err = ctf_errno (*dict);
+      goto err;
+    }
+
+  /* If this dict is being reused from the previous iteration, bump its
+     refcnt: the caller is going to close it and has no idea that we didn't
+     open it this time round.  */
+  if (!opened_this_time)
+    ctf_ref (*dict);
+
+  return type;
+
+ err:						/* Also ECTF_NEXT_END. */
+  if (opened_this_time)
+    {
+      ctf_dict_close (*dict);
+      *dict = NULL;
+    }
+
+  ctf_next_destroy (i);
+  *it = NULL;
+  if (errp)
+    *errp = err;
+  return CTF_ERR;
+}
+
 /* Raw iteration over all CTF files in an archive.  We pass the raw data for all
    CTF files in turn to the specified callback function.  */
 static int
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index 299d981a718..0a362b6b17c 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -544,13 +544,15 @@ struct ctf_next
   uint32_t ctn_n;
 
   /* Some iterators contain other iterators, in addition to their other
-     state.  */
+     state.  We allow for inner and outer iterators, for two-layer nested loops
+     like those found in ctf_arc_lookup_enumerator_next.  */
   ctf_next_t *ctn_next;
+  ctf_next_t *ctn_next_inner;
 
-  /* We can save space on this side of things by noting that a dictionary is
-     either dynamic or not, as a whole, and a given iterator can only iterate
-     over one kind of thing at once: so we can overlap the DTD and non-DTD
-     members, and the structure, variable and enum members, etc.  */
+  /* We can save space on this side of things by noting that a type is either
+     dynamic or not, as a whole, and a given iterator can only iterate over one
+     kind of thing at once: so we can overlap the DTD and non-DTD members, and
+     the structure, variable and enum members, etc.  */
   union
   {
     unsigned char *ctn_vlen;
diff --git a/libctf/ctf-lookup.c b/libctf/ctf-lookup.c
index e4d18bec112..8accb2ed99e 100644
--- a/libctf/ctf-lookup.c
+++ b/libctf/ctf-lookup.c
@@ -413,6 +413,151 @@ ctf_lookup_variable (ctf_dict_t *fp, const char *name)
   return type;
 }
 
+/* Look up a single enumerator by enumeration constant name.  Returns the ID of
+   the enum it is contained within and optionally its value.  Error out with
+   ECTF_DUPLICATE if multiple exist (which can happen in some older dicts).  See
+   ctf_lookup_enumerator_next in that case.  Enumeration constants in non-root
+   types are not returned, but constants in parents are, if not overridden by
+   an enum in the child..  */
+
+ctf_id_t
+ctf_lookup_enumerator (ctf_dict_t *fp, const char *name, int64_t *enum_value)
+{
+  ctf_id_t type;
+  int enum_int_value;
+
+  if (ctf_dynset_lookup (fp->ctf_conflicting_enums, name))
+    return (ctf_set_typed_errno (fp, ECTF_DUPLICATE));
+
+  /* CTF_K_UNKNOWN suffices for things like enumeration constants that aren't
+     actually types at all (ending up in the global name table).  */
+  type = ctf_lookup_by_rawname (fp, CTF_K_UNKNOWN, name);
+  /* Nonexistent type? It may be in the parent.  */
+  if (type == 0 && fp->ctf_parent)
+    {
+      if ((type = ctf_lookup_enumerator (fp->ctf_parent, name, enum_value)) == 0)
+	return ctf_set_typed_errno (fp, ECTF_NOENUMNAM);
+      return type;
+    }
+
+  /* Nothing more to do if this type didn't exist or we don't have to look up
+     the enum value.  */
+  if (type == 0)
+    return ctf_set_typed_errno (fp, ECTF_NOENUMNAM);
+
+  if (enum_value == NULL)
+    return type;
+
+  if (ctf_enum_value (fp, type, name, &enum_int_value) < 0)
+    return CTF_ERR;
+  *enum_value = enum_int_value;
+
+  return type;
+}
+
+/* Return all enumeration constants with a given name in a given dict, similar
+   to ctf_lookup_enumerator above but capable of returning multiple values.
+   Enumerators in parent dictionaries are not returned: enumerators in
+   hidden types *are* returned.  */
+
+ctf_id_t
+ctf_lookup_enumerator_next (ctf_dict_t *fp, const char *name,
+			    ctf_next_t **it, int64_t *val)
+{
+  ctf_next_t *i = *it;
+  int found = 0;
+
+  /* We use ctf_type_next() to iterate across all types, but then traverse each
+     enumerator found by hand: traversing enumerators is very easy, and it would
+     probably be more confusing to use two nested iterators than to do it this
+     way.  We use ctn_next to work over enums, then ctn_en and ctn_n to work
+     over enumerators within each enum.  */
+  if (!i)
+    {
+      if ((i = ctf_next_create ()) == NULL)
+	return ctf_set_typed_errno (fp, ENOMEM);
+
+      i->cu.ctn_fp = fp;
+      i->ctn_iter_fun = (void (*) (void)) ctf_lookup_enumerator_next;
+      i->ctn_increment = 0;
+      i->ctn_tp = NULL;
+      i->u.ctn_en = NULL;
+      i->ctn_n = 0;
+      *it = i;
+    }
+
+  if ((void (*) (void)) ctf_lookup_enumerator_next != i->ctn_iter_fun)
+    return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN));
+
+  if (fp != i->cu.ctn_fp)
+    return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP));
+
+  do
+    {
+      const char *this_name;
+
+      /* At end of enum? Traverse to next one, if any are left.  */
+
+      if (i->u.ctn_en == NULL || i->ctn_n == 0)
+	{
+	  const ctf_type_t *tp;
+	  ctf_dtdef_t *dtd;
+
+	  do
+	    i->ctn_type = ctf_type_next (i->cu.ctn_fp, &i->ctn_next, NULL, 1);
+	  while (i->ctn_type != CTF_ERR
+		 && ctf_type_kind_unsliced (i->cu.ctn_fp, i->ctn_type)
+		 != CTF_K_ENUM);
+
+	  if (i->ctn_type == CTF_ERR)
+	    {
+	      /* Conveniently, when the iterator over all types is done, so is the
+		 iteration as a whole: so we can just pass all errors from the
+		 internal iterator straight back out..  */
+	      ctf_next_destroy (i);
+	      *it = NULL;
+	      return CTF_ERR;			/* errno is set for us.  */
+	    }
+
+	  if ((tp = ctf_lookup_by_id (&fp, i->ctn_type)) == NULL)
+	    return CTF_ERR;			/* errno is set for us.  */
+	  i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
+
+	  dtd = ctf_dynamic_type (fp, i->ctn_type);
+
+	  if (dtd == NULL)
+	    {
+	      (void) ctf_get_ctt_size (fp, tp, NULL, &i->ctn_increment);
+	      i->u.ctn_en = (const ctf_enum_t *) ((uintptr_t) tp +
+						  i->ctn_increment);
+	    }
+	  else
+	    i->u.ctn_en = (const ctf_enum_t *) dtd->dtd_vlen;
+	}
+
+      this_name = ctf_strptr (fp, i->u.ctn_en->cte_name);
+
+      i->ctn_n--;
+
+      if (strcmp (name, this_name) == 0)
+	{
+	  if (val)
+	    *val = i->u.ctn_en->cte_value;
+	  found = 1;
+
+	  /* Constant found in this enum: try the next one.  (Constant names
+	     cannot be duplicated within a given enum.)  */
+
+	  i->ctn_n = 0;
+	}
+
+      i->u.ctn_en++;
+    }
+  while (!found);
+
+  return i->ctn_type;
+}
+
 typedef struct ctf_symidx_sort_arg_cb
 {
   ctf_dict_t *fp;
diff --git a/libctf/ctf-util.c b/libctf/ctf-util.c
index 3ea6de9e86f..f75b1bfb01a 100644
--- a/libctf/ctf-util.c
+++ b/libctf/ctf-util.c
@@ -262,6 +262,8 @@ ctf_next_destroy (ctf_next_t *i)
     free (i->u.ctn_sorted_hkv);
   if (i->ctn_next)
     ctf_next_destroy (i->ctn_next);
+  if (i->ctn_next_inner)
+    ctf_next_destroy (i->ctn_next_inner);
   free (i);
 }
 
@@ -276,16 +278,35 @@ ctf_next_copy (ctf_next_t *i)
     return NULL;
   memcpy (i2, i, sizeof (struct ctf_next));
 
+  if (i2->ctn_next)
+    {
+      i2->ctn_next = ctf_next_copy (i2->ctn_next);
+      if (i2->ctn_next == NULL)
+	goto err_next;
+    }
+
+  if (i2->ctn_next_inner)
+    {
+      i2->ctn_next_inner = ctf_next_copy (i2->ctn_next_inner);
+      if (i2->ctn_next_inner == NULL)
+	goto err_next_inner;
+    }
+
   if (i2->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
     {
       size_t els = ctf_dynhash_elements ((ctf_dynhash_t *) i->cu.ctn_h);
       if ((i2->u.ctn_sorted_hkv = calloc (els, sizeof (ctf_next_hkv_t))) == NULL)
-	{
-	  free (i2);
-	  return NULL;
-	}
+	goto err_sorted_hkv;
       memcpy (i2->u.ctn_sorted_hkv, i->u.ctn_sorted_hkv,
 	      els * sizeof (ctf_next_hkv_t));
     }
   return i2;
+
+ err_sorted_hkv:
+  ctf_next_destroy (i2->ctn_next_inner);
+ err_next_inner:
+  ctf_next_destroy (i2->ctn_next);
+ err_next:
+  ctf_next_destroy (i2);
+  return NULL;
 }
diff --git a/libctf/libctf.ver b/libctf/libctf.ver
index 6e7345be66b..e6c31ff37aa 100644
--- a/libctf/libctf.ver
+++ b/libctf/libctf.ver
@@ -198,3 +198,10 @@ LIBCTF_1.2 {
 	ctf_arc_lookup_symbol_name;
 	ctf_add_unknown;
 } LIBCTF_1.1;
+
+LIBCTF_1.3 {
+    global:
+	ctf_lookup_enumerator;
+	ctf_lookup_enumerator_next;
+	ctf_arc_lookup_enumerator_next;
+} LIBCTF_1.2;
diff --git a/libctf/testsuite/libctf-lookup/enum-ctf-2.c b/libctf/testsuite/libctf-lookup/enum-ctf-2.c
new file mode 100644
index 00000000000..39c9865e528
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/enum-ctf-2.c
@@ -0,0 +1,6 @@
+enum e { ENUMSAMPLE_1 = 6, ENUMSAMPLE_2 = 7 };
+
+enum ie2 { IENUMSAMPLE2_1 = -10, IENUMSAMPLE2_2 };
+
+enum e baz;
+enum ie2 quux;
diff --git a/libctf/testsuite/libctf-lookup/enumerator-iteration.c b/libctf/testsuite/libctf-lookup/enumerator-iteration.c
new file mode 100644
index 00000000000..e46dad6dc70
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/enumerator-iteration.c
@@ -0,0 +1,168 @@
+/* Test enumerator iteration and querying.  Because
+   ctf_arc_lookup_enumerator_next uses ctf_lookup_enumerator_next internally, we
+   only need to test the former.  */
+
+#include "config.h"
+#include <ctf-api.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+print_constants (ctf_archive_t *ctf, const char *name)
+{
+  ctf_next_t *i = NULL;
+  int err;
+  ctf_dict_t *fp;
+  ctf_id_t type;
+  int64_t val;
+
+  while ((type = ctf_arc_lookup_enumerator_next (ctf, name, &i,
+						 &val, &fp, &err)) != CTF_ERR)
+    {
+      char *foo;
+
+      printf ("%s in %s has value %i\n", name,
+	      foo = ctf_type_aname (fp, type), val);
+      free (foo);
+
+      ctf_dict_close (fp);
+    }
+  if (err != ECTF_NEXT_END)
+    {
+      fprintf (stderr, "iteration failed: %s\n", ctf_errmsg (err));
+      exit (1);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  ctf_archive_t *ctf;
+  ctf_dict_t *fp;
+  int err;
+  ctf_id_t type;
+  ctf_next_t *i = NULL;
+  const char *name;
+  int64_t val;
+  int counter = 0;
+
+  if (argc != 2)
+    {
+      fprintf (stderr, "Syntax: %s PROGRAM\n", argv[0]);
+      exit(1);
+    }
+
+  if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL)
+    goto open_err;
+
+  /* Look for all instances of ENUMSAMPLE2_1, and add some new enums to all
+     dicts found, to test dynamic enum iteration as well as static.
+
+     Add two enums with a different name and constants to any that should
+     already be there (one hidden), and one with the same constants, but hidden,
+     to test ctf_lookup_enumerator_next()'s multiple-lookup functionality and
+     ctf_lookup_enumerator() in the presence of hidden types.  */
+
+  printf ("First iteration: addition of enums.\n");
+  while ((type = ctf_arc_lookup_enumerator_next (ctf, "IENUMSAMPLE2_2", &i,
+						 &val, &fp, &err)) != CTF_ERR)
+    {
+      char *foo;
+
+      printf ("IENUMSAMPLE2_2 in %s has value %i\n",
+	      foo = ctf_type_aname (fp, type), val);
+      free (foo);
+
+      if ((type = ctf_add_enum (fp, CTF_ADD_ROOT, "ie3")) == CTF_ERR)
+	goto enum_add_err;
+
+      if (ctf_add_enumerator (fp, type, "DYNADD", counter += 10) < 0)
+	goto enumerator_add_err;
+      if (ctf_add_enumerator (fp, type, "DYNADD2", counter += 10) < 0)
+	goto enumerator_add_err;
+
+      /* Make sure that overlapping enumerator addition fails as it should.  */
+
+      if (ctf_add_enumerator (fp, type, "IENUMSAMPLE2_2", 666) >= 0
+	  || ctf_errno (fp) != ECTF_DUPLICATE)
+	fprintf (stderr, "Duplicate enumerator addition did not fail as it ought to\n");
+
+      if ((type = ctf_add_enum (fp, CTF_ADD_NONROOT, "ie4_hidden")) == CTF_ERR)
+	goto enum_add_err;
+
+      if (ctf_add_enumerator (fp, type, "DYNADD3", counter += 10) < 0)
+	goto enumerator_add_err;
+      if (ctf_add_enumerator (fp, type, "DYNADD4", counter += 10) < 0)
+	goto enumerator_add_err;
+
+      if ((type = ctf_add_enum (fp, CTF_ADD_NONROOT, "ie3_hidden")) == CTF_ERR)
+	goto enum_add_err;
+
+      if (ctf_add_enumerator (fp, type, "DYNADD", counter += 10) < 0)
+	goto enumerator_add_err;
+      if (ctf_add_enumerator (fp, type, "DYNADD2", counter += 10) < 0)
+	goto enumerator_add_err;
+
+      /* Look them up via ctf_lookup_enumerator.  */
+
+      if (ctf_lookup_enumerator (fp, "DYNADD", &val) == CTF_ERR)
+	goto enumerator_lookup_err;
+      printf ("direct lookup: DYNADD value: %i\n", (int) val);
+
+      if ((type = ctf_lookup_enumerator (fp, "DYNADD3", &val) != CTF_ERR) ||
+	  ctf_errno (fp) != ECTF_NOENUMNAM)
+	{
+	  if (type != CTF_ERR)
+	    {
+	      char *foo;
+	      printf ("direct lookup: hidden lookup did not return ECTF_NOENUMNAM but rather %i in %s\n",
+		      val, foo = ctf_type_aname (fp, type));
+	      free (foo);
+	    }
+	  else
+	    printf ("direct lookup: hidden lookup did not return ECTF_NOENUMNAM but rather %s\n",
+		    ctf_errno (fp));
+	}
+
+      ctf_dict_close (fp);
+    }
+  if (err != ECTF_NEXT_END)
+    {
+      fprintf (stderr, "iteration failed: %s\n", ctf_errmsg (err));
+      return 1;
+    }
+
+  /* Look for (and print out) some enumeration constants.  */
+
+  printf ("Second iteration: printing of enums.\n");
+
+  print_constants (ctf, "ENUMSAMPLE_1");
+  print_constants (ctf, "IENUMSAMPLE_1");
+  print_constants (ctf, "ENUMSAMPLE_2");
+  print_constants (ctf, "DYNADD");
+  print_constants (ctf, "DYNADD3");
+
+  ctf_close (ctf);
+
+  printf ("All done.\n");
+
+  return 0;
+
+ open_err:
+  fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err));
+  return 1;
+ enum_add_err:
+  fprintf (stderr, "Cannot add enum to dict \"%s\": %s\n",
+	   ctf_cuname (fp) ? ctf_cuname (fp) : "(null: parent)", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+ enumerator_add_err:
+  fprintf (stderr, "Cannot add enumerator to dict \"%s\": %s\n",
+	   ctf_cuname (fp) ? ctf_cuname (fp) : "(null: parent)", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+ enumerator_lookup_err:
+  fprintf (stderr, "Cannot look up enumerator in dict \"%s\": %s\n",
+	   ctf_cuname (fp) ? ctf_cuname (fp) : "(null: parent)", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+}
diff --git a/libctf/testsuite/libctf-lookup/enumerator-iteration.lk b/libctf/testsuite/libctf-lookup/enumerator-iteration.lk
new file mode 100644
index 00000000000..0c3cbfbf15f
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/enumerator-iteration.lk
@@ -0,0 +1,17 @@
+# lookup: enumerator-iteration.c
+# source: enum-ctf.c
+# source: enum-ctf-2.c
+# link: on
+First iteration: addition of enums.
+IENUMSAMPLE2_2 in enum ie2 has value -9
+direct lookup: DYNADD value: 10
+Second iteration: printing of enums.
+ENUMSAMPLE_1 in enum e has value 6
+ENUMSAMPLE_1 in enum e has value 0
+IENUMSAMPLE_1 in enum ie has value -10
+ENUMSAMPLE_2 in enum e has value 7
+ENUMSAMPLE_2 in enum e has value 1
+DYNADD in enum ie3 has value 10
+DYNADD in enum ie3_hidden has value 50
+DYNADD3 in enum ie4_hidden has value 30
+All done.

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-06-18 12:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-18 12:56 [binutils-gdb] libctf, include: new functions for looking up enumerators Nick Alcock

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).