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.
This commit is contained in:
Nick Alcock
2024-06-11 20:58:00 +01:00
parent 1f62f2a9b5
commit 2fa4b6e6df
9 changed files with 520 additions and 9 deletions

View File

@@ -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