Files
binutils-gdb/libctf/testsuite/libctf-lookup/enumerator-iteration.c
Nick Alcock 2fa4b6e6df 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.
2024-06-18 13:20:32 +01:00

169 lines
4.8 KiB
C

/* 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;
}