+2021-03-18 Nick Alcock <nick.alcock@oracle.com>
+
+ * ctf-impl.h (ctf_dtdef_t) <dtd_vlen_alloc>: New.
+ (ctf_str_move_pending): Declare.
+ * ctf-string.c (ctf_str_add_ref_internal): Fix error return.
+ (ctf_str_move_pending): New.
+ * ctf-create.c (ctf_grow_vlen): New.
+ (ctf_dtd_delete): Zero out the vlen_alloc after free. Free the
+ vlen later: iterate over it and free enum name refs first.
+ (ctf_add_generic): Populate dtd_vlen_alloc from vlen.
+ (ctf_add_enum): populate the vlen; do it by hand if promoting
+ forwards.
+ (ctf_add_enumerator): Set up the vlen rather than the dmd. Expand
+ it as needed, repointing string refs via ctf_str_move_pending. Add
+ the enumerand names as pending strings.
+ * ctf-serialize.c (ctf_copy_emembers): Remove.
+ (ctf_emit_type_sect): Copy the vlen into place and ref the
+ strings.
+ * ctf-types.c (ctf_enum_next): The dynamic portion now uses
+ the same code as the non-dynamic.
+ (ctf_enum_name): Likewise.
+ (ctf_enum_value): Likewise.
+ * testsuite/libctf-lookup/enum-many-ctf.c: New test.
+ * testsuite/libctf-lookup/enum-many.lk: New test.
+
2021-03-18 Nick Alcock <nick.alcock@oracle.com>
* ctf-hash.c (ctf_dynset_elements): New.
return 0;
}
+/* Make sure a vlen has enough space: expand it otherwise. Unlike the ptrtab,
+ which grows quite slowly, the vlen grows in big jumps because it is quite
+ expensive to expand: the caller has to scan the old vlen for string refs
+ first and remove them, then re-add them afterwards. The initial size is
+ more or less arbitrary. */
+static int
+ctf_grow_vlen (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vlen)
+{
+ unsigned char *old = dtd->dtd_vlen;
+
+ if (dtd->dtd_vlen_alloc > vlen)
+ return 0;
+
+ if ((dtd->dtd_vlen = realloc (dtd->dtd_vlen,
+ dtd->dtd_vlen_alloc * 2)) == NULL)
+ {
+ dtd->dtd_vlen = old;
+ return (ctf_set_errno (fp, ENOMEM));
+ }
+ memset (dtd->dtd_vlen + dtd->dtd_vlen_alloc, 0, dtd->dtd_vlen_alloc);
+ dtd->dtd_vlen_alloc *= 2;
+ return 0;
+}
+
/* To create an empty CTF dict, we just declare a zeroed header and call
ctf_bufopen() on it. If ctf_bufopen succeeds, we mark the new dict r/w and
initialize the dynamic members. We start assigning type IDs at 1 because
{
ctf_dmdef_t *dmd, *nmd;
int kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
+ size_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
int name_kind = kind;
const char *name;
ctf_dynhash_remove (fp->ctf_dthash, (void *) (uintptr_t) dtd->dtd_type);
- free (dtd->dtd_vlen);
switch (kind)
{
case CTF_K_STRUCT:
case CTF_K_UNION:
- case CTF_K_ENUM:
for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
dmd != NULL; dmd = nmd)
{
free (dmd);
}
break;
+ case CTF_K_ENUM:
+ {
+ ctf_enum_t *en = (ctf_enum_t *) dtd->dtd_vlen;
+ size_t i;
+
+ for (i = 0; i < vlen; i++)
+ ctf_str_remove_ref (fp, ctf_strraw (fp, en[i].cte_name),
+ &en[i].cte_name);
+ }
+ break;
case CTF_K_FORWARD:
name_kind = dtd->dtd_data.ctt_type;
break;
}
+ free (dtd->dtd_vlen);
+ dtd->dtd_vlen_alloc = 0;
if (dtd->dtd_data.ctt_name
&& (name = ctf_strraw (fp, dtd->dtd_data.ctt_name)) != NULL
return 0;
}
+/* Note: vlen is the amount of space *allocated* for the vlen. It may well not
+ be the amount of space used (yet): the space used is declared in per-kind
+ fashion in the dtd_data's info word. */
static ctf_id_t
ctf_add_generic (ctf_dict_t *fp, uint32_t flag, const char *name, int kind,
size_t vlen, ctf_dtdef_t **rp)
if ((dtd = calloc (1, sizeof (ctf_dtdef_t))) == NULL)
return (ctf_set_errno (fp, EAGAIN));
+ dtd->dtd_vlen_alloc = vlen;
if (vlen > 0)
{
if ((dtd->dtd_vlen = calloc (1, vlen)) == NULL)
{
ctf_dtdef_t *dtd;
ctf_id_t type = 0;
+ size_t initial_vlen = sizeof (ctf_enum_t) * 16;
/* Promote root-visible forwards to enums. */
if (name != NULL)
if (type != 0 && ctf_type_kind (fp, type) == CTF_K_FORWARD)
dtd = ctf_dtd_lookup (fp, type);
else if ((type = ctf_add_generic (fp, flag, name, CTF_K_ENUM,
- 0, &dtd)) == CTF_ERR)
+ initial_vlen, &dtd)) == CTF_ERR)
return CTF_ERR; /* errno is set for us. */
+ /* Forwards won't have any vlen yet. */
+ if (dtd->dtd_vlen_alloc == 0)
+ {
+ if ((dtd->dtd_vlen = calloc (1, initial_vlen)) == NULL)
+ return (ctf_set_errno (fp, ENOMEM));
+ dtd->dtd_vlen_alloc = initial_vlen;
+ }
+
dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_ENUM, flag, 0);
dtd->dtd_data.ctt_size = fp->ctf_dmodel->ctd_int;
int value)
{
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, enid);
- ctf_dmdef_t *dmd;
+ unsigned char *old_vlen;
+ ctf_enum_t *en;
+ size_t i;
uint32_t kind, vlen, root;
- char *s;
if (name == NULL)
return (ctf_set_errno (fp, EINVAL));
if (vlen == CTF_MAX_VLEN)
return (ctf_set_errno (fp, ECTF_DTFULL));
- for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
- dmd != NULL; dmd = ctf_list_next (dmd))
+ old_vlen = dtd->dtd_vlen;
+ if (ctf_grow_vlen (fp, dtd, sizeof (ctf_enum_t) * (vlen + 1)) < 0)
+ return -1; /* errno is set for us. */
+ en = (ctf_enum_t *) dtd->dtd_vlen;
+
+ if (dtd->dtd_vlen != old_vlen)
{
- if (strcmp (dmd->dmd_name, name) == 0)
- return (ctf_set_errno (fp, ECTF_DUPLICATE));
- }
+ ptrdiff_t move = (signed char *) dtd->dtd_vlen - (signed char *) old_vlen;
- if ((dmd = malloc (sizeof (ctf_dmdef_t))) == NULL)
- return (ctf_set_errno (fp, EAGAIN));
+ /* Remove pending refs in the old vlen region and reapply them. */
- if ((s = strdup (name)) == NULL)
- {
- free (dmd);
- return (ctf_set_errno (fp, EAGAIN));
+ for (i = 0; i < vlen; i++)
+ ctf_str_move_pending (fp, &en[i].cte_name, move);
}
- dmd->dmd_name = s;
- dmd->dmd_type = CTF_ERR;
- dmd->dmd_offset = 0;
- dmd->dmd_value = value;
+ for (i = 0; i < vlen; i++)
+ if (strcmp (ctf_strptr (fp, en[i].cte_name), name) == 0)
+ return (ctf_set_errno (fp, ECTF_DUPLICATE));
+
+ en[i].cte_name = ctf_str_add_pending (fp, name, &en[i].cte_name);
+ en[i].cte_value = value;
+
+ if (en[i].cte_name == 0 && name != NULL && name[0] != '\0')
+ return -1; /* errno is set for us. */
dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1);
- ctf_list_append (&dtd->dtd_u.dtu_members, dmd);
fp->ctf_flags |= LCTF_DIRTY;
#include <sys/types.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
ctf_list_t dtd_list; /* List forward/back pointers. */
ctf_id_t dtd_type; /* Type identifier for this definition. */
ctf_type_t dtd_data; /* Type node, including name. */
+ size_t dtd_vlen_alloc; /* Total vlen space allocated. */
unsigned char *dtd_vlen; /* Variable-length data for this type. */
union
{
extern uint32_t ctf_str_add (ctf_dict_t *, const char *);
extern uint32_t ctf_str_add_ref (ctf_dict_t *, const char *, uint32_t *ref);
extern uint32_t ctf_str_add_pending (ctf_dict_t *, const char *, uint32_t *);
+extern int ctf_str_move_pending (ctf_dict_t *, uint32_t *, ptrdiff_t);
extern int ctf_str_add_external (ctf_dict_t *, const char *, uint32_t offset);
extern void ctf_str_remove_ref (ctf_dict_t *, const char *, uint32_t *ref);
extern void ctf_str_rollback (ctf_dict_t *, ctf_snapshot_id_t);
return t;
}
-static unsigned char *
-ctf_copy_emembers (ctf_dict_t *fp, ctf_dtdef_t *dtd, unsigned char *t)
-{
- ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
- ctf_enum_t cte;
-
- for (; dmd != NULL; dmd = ctf_list_next (dmd))
- {
- ctf_enum_t *copied;
-
- cte.cte_value = dmd->dmd_value;
- memcpy (t, &cte, sizeof (cte));
- copied = (ctf_enum_t *) t;
- ctf_str_add_ref (fp, dmd->dmd_name, &copied->cte_name);
- t += sizeof (cte);
- }
-
- return t;
-}
-
/* Iterate through the dynamic type definition list and compute the
size of the CTF type section. */
size_t len;
ctf_stype_t *copied;
const char *name;
+ size_t i;
if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
len = sizeof (ctf_stype_t);
break;
case CTF_K_ENUM:
- t = ctf_copy_emembers (fp, dtd, t);
- break;
+ {
+ ctf_enum_t *dtd_vlen = (struct ctf_enum *) dtd->dtd_vlen;
+ ctf_enum_t *t_vlen = (struct ctf_enum *) t;
+
+ memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_enum) * vlen);
+ for (i = 0; i < vlen; i++)
+ {
+ const char *name = ctf_strraw (fp, dtd_vlen[i].cte_name);
+
+ ctf_str_add_ref (fp, name, &t_vlen[i].cte_name);
+ ctf_str_add_ref (fp, name, &dtd_vlen[i].cte_name);
+ }
+ t += sizeof (struct ctf_enum) * vlen;
+
+ break;
+ }
}
}
#include <ctf-impl.h>
#include <string.h>
+#include <assert.h>
/* Convert an encoded CTF string name into a pointer to a C string, using an
explicit internal strtab rather than the fp-based one. */
free (atom);
free (aref);
free (newstr);
+ ctf_set_errno (fp, ENOMEM);
return NULL;
}
return atom->csa_offset;
}
+/* Note that a pending ref now located at NEW_REF has moved by BYTES bytes. */
+int
+ctf_str_move_pending (ctf_dict_t *fp, uint32_t *new_ref, ptrdiff_t bytes)
+{
+ if (bytes == 0)
+ return 0;
+
+ if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) new_ref) < 0)
+ return (ctf_set_errno (fp, ENOMEM));
+
+ ctf_dynset_remove (fp->ctf_str_pending_ref,
+ (void *) ((signed char *) new_ref - bytes));
+ return 0;
+}
+
/* Add an external strtab reference at OFFSET. Returns zero if the addition
failed, nonzero otherwise. */
int
dtd = ctf_dynamic_type (fp, type);
i->ctn_iter_fun = (void (*) (void)) ctf_enum_next;
-
- /* We depend below on the RDWR state indicating whether the DTD-related
- fields or the DMD-related fields have been initialized. */
-
- assert ((dtd && (fp->ctf_flags & LCTF_RDWR))
- || (!dtd && (!(fp->ctf_flags & LCTF_RDWR))));
+ i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
if (dtd == NULL)
- {
- i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
-
- i->u.ctn_en = (const ctf_enum_t *) ((uintptr_t) tp +
- i->ctn_increment);
- }
+ i->u.ctn_en = (const ctf_enum_t *) ((uintptr_t) tp +
+ i->ctn_increment);
else
- i->u.ctn_dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
+ i->u.ctn_en = (const ctf_enum_t *) dtd->dtd_vlen;
*it = i;
}
return NULL;
}
- if (!(fp->ctf_flags & LCTF_RDWR))
- {
- if (i->ctn_n == 0)
- goto end_iter;
-
- name = ctf_strptr (fp, i->u.ctn_en->cte_name);
- if (val)
- *val = i->u.ctn_en->cte_value;
- i->u.ctn_en++;
- i->ctn_n--;
- }
- else
- {
- if (i->u.ctn_dmd == NULL)
- goto end_iter;
+ if (i->ctn_n == 0)
+ goto end_iter;
- name = i->u.ctn_dmd->dmd_name;
- if (val)
- *val = i->u.ctn_dmd->dmd_value;
- i->u.ctn_dmd = ctf_list_next (i->u.ctn_dmd);
- }
+ name = ctf_strptr (fp, i->u.ctn_en->cte_name);
+ if (val)
+ *val = i->u.ctn_en->cte_value;
+ i->u.ctn_en++;
+ i->ctn_n--;
return name;
if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM)
{
- (void) ctf_set_errno (ofp, ECTF_NOTENUM);
+ ctf_set_errno (ofp, ECTF_NOTENUM);
return NULL;
}
- (void) ctf_get_ctt_size (fp, tp, NULL, &increment);
+ ctf_get_ctt_size (fp, tp, NULL, &increment);
if ((dtd = ctf_dynamic_type (ofp, type)) == NULL)
- {
- ep = (const ctf_enum_t *) ((uintptr_t) tp + increment);
-
- for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++)
- {
- if (ep->cte_value == value)
- return (ctf_strptr (fp, ep->cte_name));
- }
- }
+ ep = (const ctf_enum_t *) ((uintptr_t) tp + increment);
else
- {
- ctf_dmdef_t *dmd;
+ ep = (const ctf_enum_t *) dtd->dtd_vlen;
- for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
- dmd != NULL; dmd = ctf_list_next (dmd))
- {
- if (dmd->dmd_value == value)
- return dmd->dmd_name;
- }
+ for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++)
+ {
+ if (ep->cte_value == value)
+ return (ctf_strptr (fp, ep->cte_name));
}
- (void) ctf_set_errno (ofp, ECTF_NOENUMNAM);
+ ctf_set_errno (ofp, ECTF_NOENUMNAM);
return NULL;
}
matching name can be found. Otherwise CTF_ERR is returned. */
int
-ctf_enum_value (ctf_dict_t * fp, ctf_id_t type, const char *name, int *valp)
+ctf_enum_value (ctf_dict_t *fp, ctf_id_t type, const char *name, int *valp)
{
ctf_dict_t *ofp = fp;
const ctf_type_t *tp;
return -1;
}
- (void) ctf_get_ctt_size (fp, tp, NULL, &increment);
-
- ep = (const ctf_enum_t *) ((uintptr_t) tp + increment);
+ ctf_get_ctt_size (fp, tp, NULL, &increment);
if ((dtd = ctf_dynamic_type (ofp, type)) == NULL)
- {
- for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++)
- {
- if (strcmp (ctf_strptr (fp, ep->cte_name), name) == 0)
- {
- if (valp != NULL)
- *valp = ep->cte_value;
- return 0;
- }
- }
- }
+ ep = (const ctf_enum_t *) ((uintptr_t) tp + increment);
else
- {
- ctf_dmdef_t *dmd;
+ ep = (const ctf_enum_t *) dtd->dtd_vlen;
- for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
- dmd != NULL; dmd = ctf_list_next (dmd))
+ for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++)
+ {
+ if (strcmp (ctf_strptr (fp, ep->cte_name), name) == 0)
{
- if (strcmp (dmd->dmd_name, name) == 0)
- {
- if (valp != NULL)
- *valp = dmd->dmd_value;
- return 0;
- }
+ if (valp != NULL)
+ *valp = ep->cte_value;
+ return 0;
}
}
- (void) ctf_set_errno (ofp, ECTF_NOENUMNAM);
+ ctf_set_errno (ofp, ECTF_NOENUMNAM);
return -1;
}
--- /dev/null
+/* Looked up item by item. */
+enum e { ENUMSAMPLE_1 = 0, ENUMSAMPLE_2 = 1 };
+
+/* Looked up via both sorts of iterator in turn. */
+enum ie { IE_0 = -10, IE_1, IE_2, IE_3, IE_4, IE_5, IE_6, IE_7, IE_8, IE_9, IE_A, IE_B, IE_C, IE_D, IE_E, IE_F,
+ IE_10, IE_11, IE_12, IE_13, IE_14, IE_15, IE_16, IE_17, IE_18, IE_19, IE_1A, IE_1B, IE_1C, IE_1D, IE_1E, IE_1F,
+ IE_20, IE_21, IE_22, IE_23, IE_24, IE_25, IE_26, IE_27, IE_28, IE_29, IE_2A, IE_2B, IE_2C, IE_2D, IE_2E, IE_2F};
+
+enum e foo;
+enum ie bar;
--- /dev/null
+# source: enum-many-ctf.c
+# lookup: enum.c
+# link: on
+Enum e enumerand ENUMSAMPLE_1 has value 0
+Enum e enumerand ENUMSAMPLE_2 has value 1
+iter test: IE_0 has value -10
+iter test: IE_1 has value -9
+iter test: IE_2 has value -8
+iter test: IE_3 has value -7
+iter test: IE_4 has value -6
+iter test: IE_5 has value -5
+iter test: IE_6 has value -4
+iter test: IE_7 has value -3
+iter test: IE_8 has value -2
+iter test: IE_9 has value -1
+iter test: IE_A has value 0
+iter test: IE_B has value 1
+iter test: IE_C has value 2
+iter test: IE_D has value 3
+iter test: IE_E has value 4
+iter test: IE_F has value 5
+iter test: IE_10 has value 6
+iter test: IE_11 has value 7
+iter test: IE_12 has value 8
+iter test: IE_13 has value 9
+iter test: IE_14 has value 10
+iter test: IE_15 has value 11
+iter test: IE_16 has value 12
+iter test: IE_17 has value 13
+iter test: IE_18 has value 14
+iter test: IE_19 has value 15
+iter test: IE_1A has value 16
+iter test: IE_1B has value 17
+iter test: IE_1C has value 18
+iter test: IE_1D has value 19
+iter test: IE_1E has value 20
+iter test: IE_1F has value 21
+iter test: IE_20 has value 22
+iter test: IE_21 has value 23
+iter test: IE_22 has value 24
+iter test: IE_23 has value 25
+iter test: IE_24 has value 26
+iter test: IE_25 has value 27
+iter test: IE_26 has value 28
+iter test: IE_27 has value 29
+iter test: IE_28 has value 30
+iter test: IE_29 has value 31
+iter test: IE_2A has value 32
+iter test: IE_2B has value 33
+iter test: IE_2C has value 34
+iter test: IE_2D has value 35
+iter test: IE_2E has value 36
+iter test: IE_2F has value 37
+next test: IE_0 has value -10
+next test: IE_1 has value -9
+next test: IE_2 has value -8
+next test: IE_3 has value -7
+next test: IE_4 has value -6
+next test: IE_5 has value -5
+next test: IE_6 has value -4
+next test: IE_7 has value -3
+next test: IE_8 has value -2
+next test: IE_9 has value -1
+next test: IE_A has value 0
+next test: IE_B has value 1
+next test: IE_C has value 2
+next test: IE_D has value 3
+next test: IE_E has value 4
+next test: IE_F has value 5
+next test: IE_10 has value 6
+next test: IE_11 has value 7
+next test: IE_12 has value 8
+next test: IE_13 has value 9
+next test: IE_14 has value 10
+next test: IE_15 has value 11
+next test: IE_16 has value 12
+next test: IE_17 has value 13
+next test: IE_18 has value 14
+next test: IE_19 has value 15
+next test: IE_1A has value 16
+next test: IE_1B has value 17
+next test: IE_1C has value 18
+next test: IE_1D has value 19
+next test: IE_1E has value 20
+next test: IE_1F has value 21
+next test: IE_20 has value 22
+next test: IE_21 has value 23
+next test: IE_22 has value 24
+next test: IE_23 has value 25
+next test: IE_24 has value 26
+next test: IE_25 has value 27
+next test: IE_26 has value 28
+next test: IE_27 has value 29
+next test: IE_28 has value 30
+next test: IE_29 has value 31
+next test: IE_2A has value 32
+next test: IE_2B has value 33
+next test: IE_2C has value 34
+next test: IE_2D has value 35
+next test: IE_2E has value 36
+next test: IE_2F has value 37