libctf, include: support unnamed structure members better
authorNick Alcock <nick.alcock@oracle.com>
Tue, 5 Jan 2021 13:25:56 +0000 (13:25 +0000)
committerNick Alcock <nick.alcock@oracle.com>
Tue, 5 Jan 2021 14:53:40 +0000 (14:53 +0000)
libctf has no intrinsic support for the GCC unnamed structure member
extension.  This principally means that you can't look up named members
inside unnamed struct or union members via ctf_member_info: you have to
tiresomely find out the type ID of the unnamed members via iteration,
then look in each of these.

This is ridiculous.  Fix it by extending ctf_member_info so that it
recurses into unnamed members for you: this is still unambiguous because
GCC won't let you create ambiguously-named members even in the presence
of this extension.

For consistency, and because the release hasn't happened and we can
still do this, break the ctf_member_next API and add flags: we specify
one flag, CTF_MN_RECURSE, which if set causes ctf_member_next to
automatically recurse into unnamed members for you, returning not only
the members themselves but all their contained members, so that you can
use ctf_member_next to identify every member that it would be valid to
call ctf_member_info with.

New lookup tests are added for all of this.

include/ChangeLog
2021-01-05  Nick Alcock  <nick.alcock@oracle.com>

* ctf-api.h (CTF_MN_RECURSE): New.
(ctf_member_next): Add flags argument.

libctf/ChangeLog
2021-01-05  Nick Alcock  <nick.alcock@oracle.com>

* ctf-impl.h (struct ctf_next) <u.ctn_next>: Move to...
<ctn_next>: ... here.
* ctf-util.c (ctf_next_destroy): Unconditionally destroy it.
* ctf-lookup.c (ctf_symbol_next): Adjust accordingly.
* ctf-types.c (ctf_member_iter): Reimplement in terms of...
(ctf_member_next): ... this.  Support recursive unnamed member
iteration (off by default).
(ctf_member_info): Look up members in unnamed sub-structs.
* ctf-dedup.c (ctf_dedup_rhash_type): Adjust ctf_member_next call.
(ctf_dedup_emit_struct_members): Likewise.
* testsuite/libctf-lookup/struct-iteration-ctf.c: Test empty unnamed
members, and a normal member after the end.
* testsuite/libctf-lookup/struct-iteration.c: Verify that
ctf_member_count is consistent with the number of successful returns
from a non-recursive ctf_member_next.
* testsuite/libctf-lookup/struct-iteration-*: New, test iteration
over struct members.
* testsuite/libctf-lookup/struct-lookup.c: New test.
* testsuite/libctf-lookup/struct-lookup.lk: New test.

13 files changed:
include/ChangeLog
include/ctf-api.h
libctf/ChangeLog
libctf/ctf-dedup.c
libctf/ctf-impl.h
libctf/ctf-lookup.c
libctf/ctf-types.c
libctf/ctf-util.c
libctf/testsuite/libctf-lookup/struct-iteration-ctf.c [new file with mode: 0644]
libctf/testsuite/libctf-lookup/struct-iteration.c [new file with mode: 0644]
libctf/testsuite/libctf-lookup/struct-iteration.lk [new file with mode: 0644]
libctf/testsuite/libctf-lookup/struct-lookup.c [new file with mode: 0644]
libctf/testsuite/libctf-lookup/struct-lookup.lk [new file with mode: 0644]

index 1b56987ea9b57735aa40d01913c6266b48f66b0c..ab2aa337043d158bb6413ab5a760d1d2405ca1cc 100644 (file)
@@ -1,3 +1,8 @@
+2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-api.h (CTF_MN_RECURSE): New.
+       (ctf_member_next): Add flags argument.
+
 2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-api.h (ECTF_INCOMPLETE): New.
index b3cfd391506badcfb5460eb0b58b6637cc6037db..5cf3257ae4b8feb2a4a716ca7b53d9486aa52aaa 100644 (file)
@@ -264,6 +264,10 @@ _CTF_ERRORS
 #define        CTF_ADD_NONROOT 0       /* Type only visible in nested scope.  */
 #define        CTF_ADD_ROOT    1       /* Type visible at top-level scope.  */
 
+/* Flags for ctf_member_next.  */
+
+#define CTF_MN_RECURSE 0x1     /* Recurse into unnamed members.  */
+
 /* These typedefs are used to define the signature for callback functions that
    can be used with the iteration and visit functions below.  There is also a
    family of iteration functions that do not require callbacks.  */
@@ -411,7 +415,8 @@ extern int ctf_label_info (ctf_dict_t *, const char *, ctf_lblinfo_t *);
 extern int ctf_member_count (ctf_dict_t *, ctf_id_t);
 extern int ctf_member_iter (ctf_dict_t *, ctf_id_t, ctf_member_f *, void *);
 extern ssize_t ctf_member_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
-                               const char **name, ctf_id_t *membtype);
+                               const char **name, ctf_id_t *membtype,
+                               int flags);
 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 *);
index db75fd5a836ea8b94d0d1dc38a265655665fd1a5..57507c4ba3f7caeab75cef1408b75b2d6dfd879a 100644 (file)
@@ -1,3 +1,25 @@
+2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-impl.h (struct ctf_next) <u.ctn_next>: Move to...
+       <ctn_next>: ... here.
+       * ctf-util.c (ctf_next_destroy): Unconditionally destroy it.
+       * ctf-lookup.c (ctf_symbol_next): Adjust accordingly.
+       * ctf-types.c (ctf_member_iter): Reimplement in terms of...
+       (ctf_member_next): ... this.  Support recursive unnamed member
+       iteration (off by default).
+       (ctf_member_info): Look up members in unnamed sub-structs.
+       * ctf-dedup.c (ctf_dedup_rhash_type): Adjust ctf_member_next call.
+       (ctf_dedup_emit_struct_members): Likewise.
+       * testsuite/libctf-lookup/struct-iteration-ctf.c: Test empty unnamed
+       members, and a normal member after the end.
+       * testsuite/libctf-lookup/struct-iteration.c: Verify that
+       ctf_member_count is consistent with the number of successful returns
+       from a non-recursive ctf_member_next.
+       * testsuite/libctf-lookup/struct-iteration-*: New, test iteration
+       over struct members.
+       * testsuite/libctf-lookup/struct-lookup.c: New test.
+       * testsuite/libctf-lookup/struct-lookup.lk: New test.
+
 2021-01-05  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-link.c (ctf_link_warn_outdated_inputs): New.
index fd72a60ea3862ba7a4edc4e45e443a8c62960bf1..da88ae37147bed1866131241d6f25e28a8f65858 100644 (file)
@@ -887,8 +887,8 @@ ctf_dedup_rhash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs,
        ctf_dedup_sha1_add (&hash, &size, sizeof (ssize_t), "struct size",
                            depth);
 
-       while ((offset = ctf_member_next (input, type, &i, &mname,
-                                         &membtype)) >= 0)
+       while ((offset = ctf_member_next (input, type, &i, &mname, &membtype,
+                                         0)) >= 0)
          {
            if (mname == NULL)
              mname = "";
@@ -2956,7 +2956,7 @@ ctf_dedup_emit_struct_members (ctf_dict_t *output, ctf_dict_t **inputs,
       target_type = CTF_DEDUP_GID_TO_TYPE (target_id);
 
       while ((offset = ctf_member_next (input_fp, input_type, &j, &name,
-                                       &membtype)) >= 0)
+                                       &membtype, 0)) >= 0)
        {
          err_fp = target;
          err_type = target_type;
index b19ae69aad2b5e6d8df93cc4cf7ee1141cce7578..8a173c1f86895b323e2007bad466dd420807a038 100644 (file)
@@ -532,13 +532,15 @@ struct ctf_next
   ssize_t ctn_size;
   ssize_t ctn_increment;
   uint32_t ctn_n;
+
+  /* Some iterators contain other iterators, in addition to their other
+     state.  */
+  ctf_next_t *ctn_next;
+
   /* 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.
-
-     Some of the _next iterators actually thunk down to another _next iterator
-     themselves, so one of the options in here is a _next iterator!  */
+     members, and the structure, variable and enum members, etc.  */
   union
   {
     const ctf_member_t *ctn_mp;
@@ -546,10 +548,10 @@ struct ctf_next
     const ctf_dmdef_t *ctn_dmd;
     const ctf_enum_t *ctn_en;
     const ctf_dvdef_t *ctn_dvd;
-    ctf_next_t *ctn_next;
     ctf_next_hkv_t *ctn_sorted_hkv;
     void **ctn_hash_slot;
   } u;
+
   /* This union is of various sorts of dict we can iterate over:
      currently dictionaries and archives, dynhashes, and dynsets.  */
   union
index 869c3843b8eb00fc146ee8b0c561dfbbea5e5a58..0d6ef3c5c494a6365d3e855eba38595b28e5270e 100644 (file)
@@ -442,7 +442,7 @@ ctf_symbol_next (ctf_dict_t *fp, ctf_next_t **it, const char **name,
          return (ctf_set_errno (fp, ECTF_NEXT_END));
        }
 
-      err = ctf_dynhash_next (dynh, &i->u.ctn_next, &dyn_name, &dyn_value);
+      err = ctf_dynhash_next (dynh, &i->ctn_next, &dyn_name, &dyn_value);
       /* This covers errors and also end-of-iteration.  */
       if (err != 0)
        {
index a3d824b88482181a34403833cb5c1b14773dbc43..6275be0058dc483e051191f4e06ee0f525602690 100644 (file)
@@ -41,76 +41,33 @@ ctf_type_ischild (ctf_dict_t * fp, ctf_id_t id)
 int
 ctf_member_iter (ctf_dict_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
 {
-  ctf_dict_t *ofp = fp;
-  const ctf_type_t *tp;
-  ctf_dtdef_t *dtd;
-  ssize_t size, increment;
-  uint32_t kind, n;
+  ctf_next_t *i = NULL;
+  ssize_t offset;
+  const char *name;
+  ctf_id_t membtype;
   int rc;
 
-  if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
-    return -1;                 /* errno is set for us.  */
-
-  if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
-    return -1;                 /* errno is set for us.  */
-
-  (void) ctf_get_ctt_size (fp, tp, &size, &increment);
-  kind = LCTF_INFO_KIND (fp, tp->ctt_info);
-
-  if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
-    return (ctf_set_errno (ofp, ECTF_NOTSOU));
-
-  if ((dtd = ctf_dynamic_type (fp, type)) == NULL)
+  while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, 0)) >= 0)
     {
-      if (size < CTF_LSTRUCT_THRESH)
-       {
-         const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp +
-                                                          increment);
-
-         for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
-           {
-             const char *name = ctf_strptr (fp, mp->ctm_name);
-             if ((rc = func (name, mp->ctm_type, mp->ctm_offset, arg)) != 0)
-           return rc;
-           }
-       }
-      else
-       {
-         const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp +
-                                                             increment);
-
-         for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
-           {
-             const char *name = ctf_strptr (fp, lmp->ctlm_name);
-             if ((rc = func (name, lmp->ctlm_type,
-                             (unsigned long) CTF_LMEM_OFFSET (lmp), arg)) != 0)
-               return rc;
-           }
-       }
-    }
-  else
-    {
-      ctf_dmdef_t *dmd;
-
-      for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
-          dmd != NULL; dmd = ctf_list_next (dmd))
+      if ((rc = func (name, membtype, offset, arg)) != 0)
        {
-         if ((rc = func (dmd->dmd_name, dmd->dmd_type,
-                         dmd->dmd_offset, arg)) != 0)
-           return rc;
+         ctf_next_destroy (i);
+         return rc;
        }
     }
+  if (ctf_errno (fp) != ECTF_NEXT_END)
+    return -1;                                 /* errno is set for us.  */
 
   return 0;
 }
 
 /* Iterate over the members of a STRUCT or UNION, returning each member's
    offset and optionally name and member type in turn.  On end-of-iteration,
-   returns -1.  */
+   returns -1.  If FLAGS is CTF_MN_RECURSE, recurse into unnamed members.  */
 
 ssize_t
 ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
-                const char **name, ctf_id_t *membtype)
+                const char **name, ctf_id_t *membtype, int flags)
 {
   ctf_dict_t *ofp = fp;
   uint32_t kind;
@@ -121,6 +78,7 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
     {
       const ctf_type_t *tp;
       ctf_dtdef_t *dtd;
+      ssize_t increment;
 
       if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
        return -1;                      /* errno is set for us.  */
@@ -132,8 +90,7 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
        return ctf_set_errno (ofp, ENOMEM);
       i->cu.ctn_fp = ofp;
 
-      (void) ctf_get_ctt_size (fp, tp, &i->ctn_size,
-                              &i->ctn_increment);
+      (void) ctf_get_ctt_size (fp, tp, &i->ctn_size, &increment);
       kind = LCTF_INFO_KIND (fp, tp->ctt_info);
 
       if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
@@ -156,11 +113,9 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
          i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
 
          if (i->ctn_size < CTF_LSTRUCT_THRESH)
-           i->u.ctn_mp = (const ctf_member_t *) ((uintptr_t) tp +
-                                                 i->ctn_increment);
+           i->u.ctn_mp = (const ctf_member_t *) ((uintptr_t) tp + increment);
          else
-           i->u.ctn_lmp = (const ctf_lmember_t *) ((uintptr_t) tp +
-                                                   i->ctn_increment);
+           i->u.ctn_lmp = (const ctf_lmember_t *) ((uintptr_t) tp + increment);
        }
       else
        i->u.ctn_dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
@@ -178,41 +133,112 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
   if ((fp = ctf_get_dict (ofp, type)) == NULL)
     return (ctf_set_errno (ofp, ECTF_NOPARENT));
 
-  if (!(fp->ctf_flags & LCTF_RDWR))
-    {
-      if (i->ctn_n == 0)
-       goto end_iter;
+  /* When we hit an unnamed struct/union member, we set ctn_type to indicate
+     that we are inside one, then return the unnamed member: on the next call,
+     we must skip over top-level member iteration in favour of iteration within
+     the sub-struct until it later turns out that that iteration has ended.  */
 
-      if (i->ctn_size < CTF_LSTRUCT_THRESH)
+ retry:
+  if (!i->ctn_type)
+    {
+      if (!(fp->ctf_flags & LCTF_RDWR))
        {
-         if (name)
-           *name = ctf_strptr (fp, i->u.ctn_mp->ctm_name);
-         if (membtype)
-           *membtype = i->u.ctn_mp->ctm_type;
-         offset = i->u.ctn_mp->ctm_offset;
-         i->u.ctn_mp++;
+         if (i->ctn_n == 0)
+           goto end_iter;
+
+         if (i->ctn_size < CTF_LSTRUCT_THRESH)
+           {
+             const char *membname = ctf_strptr (fp, i->u.ctn_mp->ctm_name);
+
+             if (name)
+               *name = membname;
+             if (membtype)
+               *membtype = i->u.ctn_mp->ctm_type;
+             offset = i->u.ctn_mp->ctm_offset;
+
+             if (membname[0] == 0
+                 && (ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_STRUCT
+                     || ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_UNION))
+               i->ctn_type = i->u.ctn_mp->ctm_type;
+
+             i->u.ctn_mp++;
+           }
+         else
+           {
+             const char *membname = ctf_strptr (fp, i->u.ctn_lmp->ctlm_name);
+
+             if (name)
+               *name = membname;
+             if (membtype)
+               *membtype = i->u.ctn_lmp->ctlm_type;
+             offset = (unsigned long) CTF_LMEM_OFFSET (i->u.ctn_lmp);
+
+             if (membname[0] == 0
+                 && (ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_STRUCT
+                     || ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_UNION))
+               i->ctn_type = i->u.ctn_lmp->ctlm_type;
+
+             i->u.ctn_lmp++;
+           }
+         i->ctn_n--;
        }
       else
        {
+         if (i->u.ctn_dmd == NULL)
+           goto end_iter;
+         /* The dmd contains a NULL for unnamed dynamic members.  Don't inflict
+            this on our callers.  */
          if (name)
-           *name = ctf_strptr (fp, i->u.ctn_lmp->ctlm_name);
+           {
+             if (i->u.ctn_dmd->dmd_name)
+               *name = i->u.ctn_dmd->dmd_name;
+             else
+               *name = "";
+           }
          if (membtype)
-           *membtype = i->u.ctn_lmp->ctlm_type;
-         offset = (unsigned long) CTF_LMEM_OFFSET (i->u.ctn_lmp);
-         i->u.ctn_lmp++;
+           *membtype = i->u.ctn_dmd->dmd_type;
+         offset = i->u.ctn_dmd->dmd_offset;
+
+         if (i->u.ctn_dmd->dmd_name == NULL
+             && (ctf_type_kind (fp, i->u.ctn_dmd->dmd_type) == CTF_K_STRUCT
+                 || ctf_type_kind (fp, i->u.ctn_dmd->dmd_type) == CTF_K_UNION))
+           i->ctn_type = i->u.ctn_dmd->dmd_type;
+
+         i->u.ctn_dmd = ctf_list_next (i->u.ctn_dmd);
        }
-      i->ctn_n--;
+
+      /* The callers might want automatic recursive sub-struct traversal.  */
+      if (!(flags & CTF_MN_RECURSE))
+       i->ctn_type = 0;
+
+      /* Sub-struct traversal starting?  Take note of the offset of this member,
+        for later boosting of sub-struct members' offsets.  */
+      if (i->ctn_type)
+       i->ctn_increment = offset;
     }
+  /* Traversing a sub-struct?  Just return it, with the offset adjusted.  */
   else
     {
-      if (i->u.ctn_dmd == NULL)
-       goto end_iter;
-      if (name)
-       *name = i->u.ctn_dmd->dmd_name;
-      if (membtype)
-       *membtype = i->u.ctn_dmd->dmd_type;
-      offset = i->u.ctn_dmd->dmd_offset;
-      i->u.ctn_dmd = ctf_list_next (i->u.ctn_dmd);
+      ssize_t ret = ctf_member_next (fp, i->ctn_type, &i->ctn_next, name,
+                                    membtype, flags);
+
+      if (ret >= 0)
+       return ret + i->ctn_increment;
+
+      if (ctf_errno (fp) != ECTF_NEXT_END)
+       {
+         ctf_next_destroy (i);
+         *it = NULL;
+         i->ctn_type = 0;
+         return ret;                           /* errno is set for us.  */
+       }
+
+      if (!ctf_assert (fp, (i->ctn_next == NULL)))
+       return -1;                              /* errno is set for us.  */
+
+      i->ctn_type = 0;
+      /* This sub-struct has ended: on to the next real member.  */
+      goto retry;
     }
 
   return offset;
@@ -1377,7 +1403,7 @@ ctf_type_compat (ctf_dict_t *lfp, ctf_id_t ltype,
 }
 
 /* Return the number of members in a STRUCT or UNION, or the number of
-   enumerators in an ENUM.  */
+   enumerators in an ENUM.  The count does not include unnamed sub-members.  */
 
 int
 ctf_member_count (ctf_dict_t *fp, ctf_id_t type)
@@ -1433,7 +1459,15 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
 
          for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
            {
-             if (strcmp (ctf_strptr (fp, mp->ctm_name), name) == 0)
+             const char *membname = ctf_strptr (fp, mp->ctm_name);
+
+             if (membname[0] == 0
+                 && (ctf_type_kind (fp, mp->ctm_type) == CTF_K_STRUCT
+                     || ctf_type_kind (fp, mp->ctm_type) == CTF_K_UNION)
+                 && (ctf_member_info (fp, mp->ctm_type, name, mip) == 0))
+               return 0;
+
+             if (strcmp (membname, name) == 0)
                {
                  mip->ctm_type = mp->ctm_type;
                  mip->ctm_offset = mp->ctm_offset;
@@ -1448,7 +1482,15 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
 
          for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
            {
-             if (strcmp (ctf_strptr (fp, lmp->ctlm_name), name) == 0)
+             const char *membname = ctf_strptr (fp, lmp->ctlm_name);
+
+             if (membname[0] == 0
+                 && (ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_STRUCT
+                     || ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_UNION)
+                 && (ctf_member_info (fp, lmp->ctlm_type, name, mip) == 0))
+               return 0;
+
+             if (strcmp (membname, name) == 0)
                {
                  mip->ctm_type = lmp->ctlm_type;
                  mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (lmp);
@@ -1464,7 +1506,14 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
       for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
           dmd != NULL; dmd = ctf_list_next (dmd))
        {
-         if (strcmp (dmd->dmd_name, name) == 0)
+         if (dmd->dmd_name == NULL
+             && (ctf_type_kind (fp, dmd->dmd_type) == CTF_K_STRUCT
+                 || ctf_type_kind (fp, dmd->dmd_type) == CTF_K_UNION)
+             && (ctf_member_info (fp, dmd->dmd_type, name, mip) == 0))
+           return 0;
+
+         if (dmd->dmd_name != NULL
+             && strcmp (dmd->dmd_name, name) == 0)
            {
              mip->ctm_type = dmd->dmd_type;
              mip->ctm_offset = dmd->dmd_offset;
index 879ebbfcc436b40a48517e84944991b618bbbb2a..4f126ba0ee464770dc86d5514479b0abb11da598 100644 (file)
@@ -283,9 +283,8 @@ ctf_next_destroy (ctf_next_t *i)
 
   if (i->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
     free (i->u.ctn_sorted_hkv);
-  if (i->ctn_iter_fun == (void (*) (void)) ctf_symbol_next
-      && i->cu.ctn_fp->ctf_flags & LCTF_RDWR)
-    ctf_next_destroy (i->u.ctn_next);
+  if (i->ctn_next)
+    ctf_next_destroy (i->ctn_next);
   free (i);
 }
 
diff --git a/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c b/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c
new file mode 100644 (file)
index 0000000..7df67ad
--- /dev/null
@@ -0,0 +1,28 @@
+#include <unistd.h>
+
+struct foo_t
+{
+  int foo;
+  size_t bar;
+  const char *baz;
+  struct foo_t *self;
+  union
+  {
+    double should_not_appear;
+    char *nor_should_this;
+  } named;
+  struct
+  {
+    long unnamed_sub_member;
+    union
+    {
+      double one_more_level;
+      long yes_really_one_more;
+    };
+  };
+  struct {};           /* Empty ones */
+  union {};
+  int after_the_end;
+};
+
+struct foo_t used;
diff --git a/libctf/testsuite/libctf-lookup/struct-iteration.c b/libctf/testsuite/libctf-lookup/struct-iteration.c
new file mode 100644 (file)
index 0000000..0375060
--- /dev/null
@@ -0,0 +1,92 @@
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int
+print_struct (const char *name, ctf_id_t membtype, unsigned long offset,
+              void *fp_)
+{
+  ctf_dict_t *fp = (ctf_dict_t *) fp_;
+  char *type_name = ctf_type_aname (fp, membtype);
+
+  printf ("iter test: %s, offset %lx, has type %lx/%s\n",
+          name, offset, membtype, type_name);
+  free (type_name);
+
+  return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+  ctf_dict_t *fp;
+  ctf_archive_t *ctf;
+  ctf_id_t type;
+  ctf_next_t *i = NULL;
+  const char *name;
+  ctf_id_t membtype;
+  ssize_t offset;
+  ssize_t icount = 0;
+  int err;
+
+  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;
+  if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL)
+    goto open_err;
+
+  /* Iterate over the structure members with each iterator type in turn.  */
+
+  if ((type = ctf_lookup_by_name (fp, "struct foo_t") ) == CTF_ERR)
+    goto err;
+
+  if (ctf_member_iter (fp, type, print_struct, fp) < 0)
+    goto ierr;
+
+  while ((offset = ctf_member_next (fp, type, &i, &name, &membtype,
+                                   CTF_MN_RECURSE)) >= 0)
+    {
+      char *type_name = ctf_type_aname (fp, membtype);
+
+      printf ("next test: %s, offset %lx, has type %lx/%s\n",
+              name, offset, membtype, type_name);
+      free (type_name);
+    }
+  if (ctf_errno (fp) != ECTF_NEXT_END)
+    goto nerr;
+
+  /* Now make sure the count of members does not include any recursive
+     members.  */
+  while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, 0)) >= 0)
+    icount++;
+
+  if (ctf_errno (fp) != ECTF_NEXT_END)
+    goto nerr;
+
+  if (icount != ctf_member_count (fp, type))
+    printf ("member counts differ: %li by direct iteration, "
+           "%li by ctf_member_count\n", icount, ctf_member_count (fp, type));
+
+  ctf_dict_close (fp);
+  ctf_close (ctf);
+
+  return 0;
+
+ open_err:
+  fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err));
+  return 1;
+ err:
+  fprintf (stderr, "Lookup failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+ ierr:
+  fprintf (stderr, "_iter iteration failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+ nerr:
+  fprintf (stderr, "_next iteration failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+}
diff --git a/libctf/testsuite/libctf-lookup/struct-iteration.lk b/libctf/testsuite/libctf-lookup/struct-iteration.lk
new file mode 100644 (file)
index 0000000..fd64454
--- /dev/null
@@ -0,0 +1,24 @@
+# source: struct-iteration-ctf.c
+# link: on
+iter test: foo, offset [0-9a-f]*, has type [0-9a-f]*/int
+iter test: bar, offset [0-9a-f]*, has type [0-9a-f]*/size_t
+iter test: baz, offset [0-9a-f]*, has type [0-9a-f]*/const char \*
+iter test: self, offset [0-9a-f]*, has type [0-9a-f]*/struct foo_t \*
+iter test: named, offset [0-9a-f]*, has type [0-9a-f]*/union 
+iter test: , offset [0-9a-f]*, has type [0-9a-f]*/struct 
+iter test: , offset [0-9a-f]*, has type [0-9a-f]*/struct 
+iter test: , offset [0-9a-f]*, has type [0-9a-f]*/union 
+iter test: after_the_end, offset [0-9a-f]*, has type [0-9a-f]*/int
+next test: foo, offset [0-9a-f]*, has type [0-9a-f]*/int
+next test: bar, offset [0-9a-f]*, has type [0-9a-f]*/size_t
+next test: baz, offset [0-9a-f]*, has type [0-9a-f]*/const char \*
+next test: self, offset [0-9a-f]*, has type [0-9a-f]*/struct foo_t \*
+next test: named, offset [0-9a-f]*, has type [0-9a-f]*/union 
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct 
+next test: unnamed_sub_member, offset [0-9a-f]*, has type [0-9a-f]*/long int
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/union 
+next test: one_more_level, offset [0-9a-f]*, has type [0-9a-f]*/double
+next test: yes_really_one_more, offset [0-9a-f]*, has type [0-9a-f]*/long int
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct 
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/union 
+next test: after_the_end, offset [0-9a-f]*, has type [0-9a-f]*/int
diff --git a/libctf/testsuite/libctf-lookup/struct-lookup.c b/libctf/testsuite/libctf-lookup/struct-lookup.c
new file mode 100644 (file)
index 0000000..9b95317
--- /dev/null
@@ -0,0 +1,60 @@
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (int argc, char *argv[])
+{
+  ctf_dict_t *fp;
+  ctf_archive_t *ctf;
+  ctf_id_t type;
+  char *type_name;
+  ctf_membinfo_t mi;
+  int err;
+
+  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;
+  if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL)
+    goto open_err;
+
+  /* Dig out some strucutre members by name.  */
+
+  if ((type = ctf_lookup_by_name (fp, "struct foo_t") ) == CTF_ERR)
+    goto err;
+
+  if (ctf_member_info (fp, type, "baz", &mi) < 0)
+    goto err;
+
+  type_name = ctf_type_aname (fp, mi.ctm_type);
+  printf ("baz is of type %s, at offset %lx\n", type_name, mi.ctm_offset);
+  free (type_name);
+
+  if (ctf_member_info (fp, type, "one_more_level", &mi) < 0)
+    goto err;
+
+  type_name = ctf_type_aname (fp, mi.ctm_type);
+  printf ("one_more_level is of type %s, at offset %lx\n", type_name, mi.ctm_offset);
+  free (type_name);
+
+  if (ctf_member_info (fp, type, "should_not_appear", &mi) >= 0
+      || ctf_errno (fp) != ECTF_NOMEMBNAM)
+    fprintf (stderr, "should_not_appear appeared.\n");
+
+  ctf_dict_close (fp);
+  ctf_close (ctf);
+
+  return 0;
+
+ open_err:
+  fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err));
+  return 1;
+ err:
+  fprintf (stderr, "Lookup failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+  return 1;
+}
diff --git a/libctf/testsuite/libctf-lookup/struct-lookup.lk b/libctf/testsuite/libctf-lookup/struct-lookup.lk
new file mode 100644 (file)
index 0000000..b848823
--- /dev/null
@@ -0,0 +1,4 @@
+# source: struct-iteration-ctf.c
+# link: on
+baz is of type const char \*, at offset [0-9a-z]*
+one_more_level is of type double, at offset [0-9a-z]*