aboutsummaryrefslogtreecommitdiff
path: root/libctf/ctf-string.c
diff options
context:
space:
mode:
Diffstat (limited to 'libctf/ctf-string.c')
-rw-r--r--libctf/ctf-string.c231
1 files changed, 191 insertions, 40 deletions
diff --git a/libctf/ctf-string.c b/libctf/ctf-string.c
index 3ce2b25..dcb8bf0 100644
--- a/libctf/ctf-string.c
+++ b/libctf/ctf-string.c
@@ -17,6 +17,7 @@
along with this program; see the file COPYING. If not see
<http://www.gnu.org/licenses/>. */
+#include <assert.h>
#include <ctf-impl.h>
#include <string.h>
#include <assert.h>
@@ -107,17 +108,28 @@ ctf_str_purge_atom_refs (ctf_str_atom_t *atom)
{
next = ctf_list_next (ref);
ctf_list_delete (&atom->csa_refs, ref);
+ if (atom->csa_flags & CTF_STR_ATOM_MOVABLE)
+ {
+ ctf_str_atom_ref_movable_t *movref;
+ movref = (ctf_str_atom_ref_movable_t *) ref;
+ ctf_dynhash_remove (movref->caf_movable_refs, ref);
+ }
+
free (ref);
}
}
-/* Free an atom (only called on ctf_close().) */
+/* Free an atom. */
static void
ctf_str_free_atom (void *a)
{
ctf_str_atom_t *atom = a;
ctf_str_purge_atom_refs (atom);
+
+ if (atom->csa_flags & CTF_STR_ATOM_FREEABLE)
+ free (atom->csa_str);
+
free (atom);
}
@@ -138,6 +150,12 @@ ctf_str_create_atoms (ctf_dict_t *fp)
if (!fp->ctf_prov_strtab)
goto oom_prov_strtab;
+ fp->ctf_str_movable_refs = ctf_dynhash_create (ctf_hash_integer,
+ ctf_hash_eq_integer,
+ NULL, NULL);
+ if (!fp->ctf_str_movable_refs)
+ goto oom_movable_refs;
+
errno = 0;
ctf_str_add (fp, "");
if (errno == ENOMEM)
@@ -146,6 +164,9 @@ ctf_str_create_atoms (ctf_dict_t *fp)
return 0;
oom_str_add:
+ ctf_dynhash_destroy (fp->ctf_str_movable_refs);
+ fp->ctf_str_movable_refs = NULL;
+ oom_movable_refs:
ctf_dynhash_destroy (fp->ctf_prov_strtab);
fp->ctf_prov_strtab = NULL;
oom_prov_strtab:
@@ -154,62 +175,140 @@ ctf_str_create_atoms (ctf_dict_t *fp)
return -ENOMEM;
}
-/* Destroy the atoms table. */
+/* Destroy the atoms table and associated refs. */
void
ctf_str_free_atoms (ctf_dict_t *fp)
{
ctf_dynhash_destroy (fp->ctf_prov_strtab);
ctf_dynhash_destroy (fp->ctf_str_atoms);
+ ctf_dynhash_destroy (fp->ctf_str_movable_refs);
}
-/* Add a string to the atoms table, copying the passed-in string. Return the
- atom added. Return NULL only when out of memory (and do not touch the
- passed-in string in that case). Possibly augment the ref list with the
- passed-in ref. Possibly add a provisional entry for this string to the
- provisional strtab. */
+#define CTF_STR_ADD_REF 0x1
+#define CTF_STR_PROVISIONAL 0x2
+#define CTF_STR_MOVABLE 0x4
+
+/* Allocate a ref and bind it into a ref list. */
+
+static ctf_str_atom_ref_t *
+aref_create (ctf_dict_t *fp, ctf_str_atom_t *atom, uint32_t *ref, int flags)
+{
+ ctf_str_atom_ref_t *aref;
+ size_t s = sizeof (struct ctf_str_atom_ref);
+
+ if (flags & CTF_STR_MOVABLE)
+ s = sizeof (struct ctf_str_atom_ref_movable);
+
+ aref = malloc (s);
+
+ if (!aref)
+ return NULL;
+
+ aref->caf_ref = ref;
+
+ /* Movable refs get a backpointer to them in ctf_str_movable_refs, and a
+ pointer to ctf_str_movable_refs itself in the ref, for use when freeing
+ refs: they can be moved later in batches via a call to
+ ctf_str_move_refs. */
+
+ if (flags & CTF_STR_MOVABLE)
+ {
+ ctf_str_atom_ref_movable_t *movref = (ctf_str_atom_ref_movable_t *) aref;
+
+ movref->caf_movable_refs = fp->ctf_str_movable_refs;
+
+ if (ctf_dynhash_insert (fp->ctf_str_movable_refs, ref, aref) < 0)
+ {
+ free (aref);
+ return NULL;
+ }
+ }
+
+ ctf_list_append (&atom->csa_refs, aref);
+
+ return aref;
+}
+
+/* Add a string to the atoms table, copying the passed-in string if
+ necessary. Return the atom added. Return NULL only when out of memory
+ (and do not touch the passed-in string in that case).
+
+ Possibly add a provisional entry for this string to the provisional
+ strtab. If the string is in the provisional strtab, update its ref list
+ with the passed-in ref, causing the ref to be updated when the strtab is
+ written out. */
+
static ctf_str_atom_t *
ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
- int add_ref, int make_provisional, uint32_t *ref)
+ int flags, uint32_t *ref)
{
char *newstr = NULL;
ctf_str_atom_t *atom = NULL;
- ctf_str_atom_ref_t *aref = NULL;
+ int added = 0;
atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str);
- if (add_ref)
- {
- if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL) {
- ctf_set_errno (fp, ENOMEM);
- return NULL;
- }
- aref->caf_ref = ref;
- }
+ /* Existing atoms get refs added only if they are provisional:
+ non-provisional strings already have a fixed strtab offset, and just
+ get their ref updated immediately, since its value cannot change. */
if (atom)
{
- if (add_ref)
+ if (!ctf_dynhash_lookup (fp->ctf_prov_strtab, (void *) (uintptr_t)
+ atom->csa_offset))
{
- ctf_list_append (&atom->csa_refs, aref);
- fp->ctf_str_num_refs++;
+ if (flags & CTF_STR_ADD_REF)
+ {
+ if (atom->csa_external_offset)
+ *ref = atom->csa_external_offset;
+ else
+ *ref = atom->csa_offset;
+ }
+ return atom;
}
+
+ if (flags & CTF_STR_ADD_REF)
+ {
+ if (!aref_create (fp, atom, ref, flags))
+ {
+ ctf_set_errno (fp, ENOMEM);
+ return NULL;
+ }
+ }
+
return atom;
}
+ /* New atom. */
+
if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL)
goto oom;
memset (atom, 0, sizeof (struct ctf_str_atom));
- if ((newstr = strdup (str)) == NULL)
- goto oom;
+ /* Don't allocate new strings if this string is within an mmapped
+ strtab. */
+
+ if ((unsigned char *) str < (unsigned char *) fp->ctf_data_mmapped
+ || (unsigned char *) str > (unsigned char *) fp->ctf_data_mmapped + fp->ctf_data_mmapped_len)
+ {
+ if ((newstr = strdup (str)) == NULL)
+ goto oom;
+ atom->csa_flags |= CTF_STR_ATOM_FREEABLE;
+ atom->csa_str = newstr;
+ }
+ else
+ atom->csa_str = (char *) str;
- if (ctf_dynhash_insert (fp->ctf_str_atoms, newstr, atom) < 0)
+ if (ctf_dynhash_insert (fp->ctf_str_atoms, atom->csa_str, atom) < 0)
goto oom;
+ added = 1;
- atom->csa_str = newstr;
atom->csa_snapshot_id = fp->ctf_snapshots;
- if (make_provisional)
+ /* New atoms marked provisional go into the provisional strtab, and get a
+ ref added. */
+
+ if (flags & CTF_STR_PROVISIONAL)
{
atom->csa_offset = fp->ctf_str_prov_offset;
@@ -218,20 +317,20 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
goto oom;
fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1;
- }
- if (add_ref)
- {
- ctf_list_append (&atom->csa_refs, aref);
- fp->ctf_str_num_refs++;
+ if (flags & CTF_STR_ADD_REF)
+ {
+ if (!aref_create (fp, atom, ref, flags))
+ goto oom;
+ }
}
+
return atom;
oom:
- if (newstr)
- ctf_dynhash_remove (fp->ctf_str_atoms, newstr);
+ if (added)
+ ctf_dynhash_remove (fp->ctf_str_atoms, atom->csa_str);
free (atom);
- free (aref);
free (newstr);
ctf_set_errno (fp, ENOMEM);
return NULL;
@@ -250,7 +349,7 @@ ctf_str_add (ctf_dict_t *fp, const char *str)
if (!str)
str = "";
- atom = ctf_str_add_ref_internal (fp, str, FALSE, TRUE, 0);
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PROVISIONAL, 0);
if (!atom)
return 0;
@@ -268,7 +367,26 @@ ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
if (!str)
str = "";
- atom = ctf_str_add_ref_internal (fp, str, TRUE, TRUE, ref);
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
+ | CTF_STR_PROVISIONAL, ref);
+ if (!atom)
+ return 0;
+
+ return atom->csa_offset;
+}
+
+/* Like ctf_str_add_ref(), but note that the ref may be moved later on. */
+uint32_t
+ctf_str_add_movable_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
+{
+ ctf_str_atom_t *atom;
+
+ if (!str)
+ str = "";
+
+ atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF
+ | CTF_STR_PROVISIONAL
+ | CTF_STR_MOVABLE, ref);
if (!atom)
return 0;
@@ -285,7 +403,7 @@ ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
if (!str)
str = "";
- atom = ctf_str_add_ref_internal (fp, str, FALSE, FALSE, 0);
+ atom = ctf_str_add_ref_internal (fp, str, 0, 0);
if (!atom)
return 0;
@@ -315,6 +433,41 @@ ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
return 1;
}
+/* Note that refs have moved from (SRC, LEN) to DEST. We use the movable
+ refs backpointer for this, because it is done an amortized-constant
+ number of times during structure member and enumerand addition, and if we
+ did a linear search this would turn such addition into an O(n^2)
+ operation. Even this is not linear, but it's better than that. */
+int
+ctf_str_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest)
+{
+ uintptr_t p;
+
+ if (src == dest)
+ return 0;
+
+ for (p = (uintptr_t) src; p - (uintptr_t) src < len; p++)
+ {
+ ctf_str_atom_ref_t *ref;
+
+ if ((ref = ctf_dynhash_lookup (fp->ctf_str_movable_refs,
+ (ctf_str_atom_ref_t *) p)) != NULL)
+ {
+ int out_of_memory;
+
+ ref->caf_ref = (uint32_t *) (((uintptr_t) ref->caf_ref +
+ (uintptr_t) dest - (uintptr_t) src));
+ ctf_dynhash_remove (fp->ctf_str_movable_refs,
+ (ctf_str_atom_ref_t *) p);
+ out_of_memory = ctf_dynhash_insert (fp->ctf_str_movable_refs,
+ ref->caf_ref, ref);
+ assert (out_of_memory == 0);
+ }
+ }
+
+ return 0;
+}
+
/* Remove a single ref. */
void
ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref)
@@ -371,9 +524,7 @@ ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value,
void
ctf_str_purge_refs (ctf_dict_t *fp)
{
- if (fp->ctf_str_num_refs > 0)
- ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
- fp->ctf_str_num_refs = 0;
+ ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL);
}
/* Update a list of refs to the specified value. */
@@ -384,7 +535,7 @@ ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value)
for (ref = ctf_list_next (&refs->csa_refs); ref != NULL;
ref = ctf_list_next (ref))
- *(ref->caf_ref) = value;
+ *(ref->caf_ref) = value;
}
/* State shared across the strtab write process. */