diff options
Diffstat (limited to 'source4/lib/talloc')
-rw-r--r-- | source4/lib/talloc/Makefile.talloc | 16 | ||||
-rw-r--r-- | source4/lib/talloc/config.m4 | 3 | ||||
-rw-r--r-- | source4/lib/talloc/config.mk | 23 | ||||
-rw-r--r-- | source4/lib/talloc/talloc.c | 1006 | ||||
-rw-r--r-- | source4/lib/talloc/talloc.h | 96 | ||||
-rw-r--r-- | source4/lib/talloc/talloc_guide.txt | 492 | ||||
-rw-r--r-- | source4/lib/talloc/testsuite.c | 785 |
7 files changed, 2421 insertions, 0 deletions
diff --git a/source4/lib/talloc/Makefile.talloc b/source4/lib/talloc/Makefile.talloc new file mode 100644 index 0000000000..88cb0e7159 --- /dev/null +++ b/source4/lib/talloc/Makefile.talloc @@ -0,0 +1,16 @@ +OPT = -g -ftest-coverage -fprofile-arcs +LIBS = -lgcov +#OPT = -g + +CFLAGS = $(OPT) -Wall + +all: testsuite + +testsuite: talloc.o testsuite.o + $(CC) $(CFLAGS) -o testsuite testsuite.o talloc.o $(LIBS) + +clean: + rm -f *~ *.o testsuite *.gc?? + +gcov: + gcov talloc.c diff --git a/source4/lib/talloc/config.m4 b/source4/lib/talloc/config.m4 new file mode 100644 index 0000000000..298c6691b9 --- /dev/null +++ b/source4/lib/talloc/config.m4 @@ -0,0 +1,3 @@ +if test x"$experimental" = x"yes"; then + SMB_LIBRARY_ENABLE(libtalloc,YES) +fi diff --git a/source4/lib/talloc/config.mk b/source4/lib/talloc/config.mk new file mode 100644 index 0000000000..2f38fe04ed --- /dev/null +++ b/source4/lib/talloc/config.mk @@ -0,0 +1,23 @@ +################################################ +# Start SUBSYSTEM LIBTALLOC +[SUBSYSTEM::LIBTALLOC] +INIT_OBJ_FILES = \ + lib/talloc/talloc.o +REQUIRED_SUBSYSTEMS = \ + LIBREPLACE +# +# End SUBSYSTEM LIBTALLOC +################################################ + +################################################ +# Start LIBRARY LIBTALLOC +[LIBRARY::libtalloc] +MAJOR_VERSION = 0 +MINOR_VERSION = 0 +RELEASE_VERSION = 1 +REQUIRED_SUBSYSTEMS = \ + LIBTALLOC +# +# End LIBRARY LIBTALLOC +################################################ + diff --git a/source4/lib/talloc/talloc.c b/source4/lib/talloc/talloc.c new file mode 100644 index 0000000000..4666e28288 --- /dev/null +++ b/source4/lib/talloc/talloc.c @@ -0,0 +1,1006 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + + +#ifdef _SAMBA_BUILD_ +#include "includes.h" +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdint.h> +#include "talloc.h" +#endif + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + + +#define MAX_TALLOC_SIZE 0x10000000 +#define TALLOC_MAGIC 0xe814ec4f +#define TALLOC_MAGIC_FREE 0x7faebef3 +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static const void *null_context; + + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; +}; + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_chunk { + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + size_t size; + unsigned magic; + talloc_destructor_t destructor; + const char *name; +}; + +/* panic if we get a bad magic value */ +static struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, ptr)-1; + if (tc->magic != TALLOC_MAGIC) { + if (tc->magic == TALLOC_MAGIC_FREE) { + TALLOC_ABORT("Bad talloc magic value - double free"); + } else { + TALLOC_ABORT("Bad talloc magic value - unknown value"); + } + } + + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + return tc->parent; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +void *_talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + context = null_context; + } + + if (size >= MAX_TALLOC_SIZE) { + return NULL; + } + + tc = malloc(sizeof(*tc)+size); + if (tc == NULL) return NULL; + + tc->size = size; + tc->magic = TALLOC_MAGIC; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (context) { + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); + + tc->parent = parent; + + if (parent->child) { + parent->child->parent = NULL; + } + + _TLIST_ADD(parent->child, tc); + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + return (void *)(tc+1); +} + + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +void talloc_increase_ref_count(const void *ptr) +{ + talloc_reference(null_context, ptr); +} + +/* + helper for talloc_reference() +*/ +static int talloc_reference_destructor(void *ptr) +{ + struct talloc_reference_handle *handle = ptr; + struct talloc_chunk *tc1 = talloc_chunk_from_ptr(ptr); + struct talloc_chunk *tc2 = talloc_chunk_from_ptr(handle->ptr); + if (tc1->destructor != (talloc_destructor_t)-1) { + tc1->destructor = NULL; + } + _TLIST_REMOVE(tc2->refs, handle); + talloc_free(handle); + return 0; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +void *talloc_reference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (ptr == NULL) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = talloc_named_const(context, sizeof(*handle), TALLOC_MAGIC_REFERENCE); + + if (handle == NULL) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (context == NULL) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if ((p==NULL && context==NULL) || p+1 == context) break; + } + if (h == NULL) { + return -1; + } + + talloc_set_destructor(h, NULL); + _TLIST_REMOVE(tc->refs, h); + talloc_free(h); + return 0; +} + +/* + remove a specific parent context from a pointer. This is a more + controlled varient of talloc_free() +*/ +int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context == NULL) { + if (talloc_parent_chunk(ptr) != NULL) { + return -1; + } + } else { + if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { + return -1; + } + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return talloc_free(ptr); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = new_p+1; + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + talloc_steal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static void talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = talloc_vasprintf(ptr, fmt, ap); + if (tc->name) { + talloc_set_name_const(tc->name, ".name"); + } +} + +/* + add a name to an existing pointer +*/ +void talloc_set_name(const void *ptr, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +void talloc_set_name_const(const void *ptr, const char *name) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->name = name; +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + + ptr = _talloc(context, size); + if (ptr == NULL) return NULL; + + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + return ptr; +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +void *talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + + ptr = _talloc(context, size); + if (ptr == NULL) { + return NULL; + } + + talloc_set_name_const(ptr, name); + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +const char *talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (tc->name == TALLOC_MAGIC_REFERENCE) { + return ".reference"; + } + if (tc->name) { + return tc->name; + } + return "UNNAMED"; +} + +/* + this is for compatibility with older versions of talloc +*/ +void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + + ptr = _talloc(NULL, 0); + if (ptr == NULL) return NULL; + + va_start(ap, fmt); + talloc_set_name_v(ptr, fmt, ap); + va_end(ap); + + return ptr; +} + + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +int talloc_free(void *ptr) +{ + struct talloc_chunk *tc; + + if (ptr == NULL) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->refs) { + talloc_reference_destructor(tc->refs); + return -1; + } + + if (tc->destructor) { + talloc_destructor_t d = tc->destructor; + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + tc->destructor = d; + return -1; + } + tc->destructor = NULL; + } + + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = tc->child+1; + const void *new_parent = null_context; + if (tc->child->refs) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = p+1; + } + if (talloc_free(child) == -1) { + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = p+1; + } + talloc_steal(new_parent, child); + } + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->magic = TALLOC_MAGIC_FREE; + + free(tc); + return 0; +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + + /* size zero is equivalent to free() */ + if (size == 0) { + talloc_free(ptr); + return NULL; + } + + if (size >= MAX_TALLOC_SIZE) { + return NULL; + } + + /* realloc(NULL) is equavalent to malloc() */ + if (ptr == NULL) { + return talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (tc->refs) { + return NULL; + } + + /* by resetting magic we catch users of the old memory */ + tc->magic = TALLOC_MAGIC_FREE; + +#if ALWAYS_REALLOC + new_ptr = malloc(size + sizeof(*tc)); + if (new_ptr) { + memcpy(new_ptr, tc, tc->size + sizeof(*tc)); + free(tc); + } +#else + new_ptr = realloc(tc, size + sizeof(*tc)); +#endif + if (!new_ptr) { + tc->magic = TALLOC_MAGIC; + return NULL; + } + + tc = new_ptr; + tc->magic = TALLOC_MAGIC; + if (tc->parent) { + tc->parent->child = new_ptr; + } + if (tc->child) { + tc->child->parent = new_ptr; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + tc->size = size; + talloc_set_name_const(tc+1, name); + + return (void *)(tc+1); +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred +*/ +void *talloc_steal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + + if (!ptr) { + return NULL; + } + + if (new_ctx == NULL) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (new_ctx == NULL) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (tc == new_tc) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + return discard_const_p(void, ptr); +} + +/* + return the total size of a talloc pool (subtree) +*/ +off_t talloc_total_size(const void *ptr) +{ + off_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + total = tc->size; + for (c=tc->child;c;c=c->next) { + total += talloc_total_size(c+1); + } + return total; +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +off_t talloc_total_blocks(const void *ptr) +{ + off_t total = 0; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + total++; + for (c=tc->child;c;c=c->next) { + total += talloc_total_blocks(c+1); + } + return total; +} + +/* + return the number of external references to a pointer +*/ +static int talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + int ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +static void talloc_report_depth(const void *ptr, FILE *f, int depth) +{ + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); + + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *handle = (void *)(c+1); + const char *name2 = talloc_get_name(handle->ptr); + fprintf(f, "%*sreference to: %s\n", depth*4, "", name2); + } else { + const char *name = talloc_get_name(c+1); + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d)\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(c+1), + (unsigned long)talloc_total_blocks(c+1), + talloc_reference_count(c+1)); + talloc_report_depth(c+1, f, depth+1); + } + } + +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +void talloc_report_full(const void *ptr, FILE *f) +{ + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + fprintf(f,"full talloc report on '%s' (total %lu bytes in %lu blocks)\n", + talloc_get_name(ptr), + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + + talloc_report_depth(ptr, f, 1); +} + +/* + report on memory usage by all children of a pointer +*/ +void talloc_report(const void *ptr, FILE *f) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + fprintf(f,"talloc report on '%s' (total %lu bytes in %lu blocks)\n", + talloc_get_name(ptr), + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + + tc = talloc_chunk_from_ptr(ptr); + + for (c=tc->child;c;c=c->next) { + fprintf(f, "\t%-30s contains %6lu bytes in %3lu blocks\n", + talloc_get_name(c+1), + (unsigned long)talloc_total_size(c+1), + (unsigned long)talloc_total_blocks(c+1)); + } + +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report(null_context, stderr); + } +} + +/* + report on any memory hanging off the null context +*/ +static void talloc_report_null_full(void) +{ + if (talloc_total_size(null_context) != 0) { + talloc_report_full(null_context, stderr); + } +} + +/* + enable leak reporting on exit +*/ +void talloc_enable_leak_report(void) +{ + null_context = talloc_named_const(NULL, 0, "null_context"); + atexit(talloc_report_null); +} + +/* + enable full leak reporting on exit +*/ +void talloc_enable_leak_report_full(void) +{ + null_context = talloc_named_const(NULL, 0, "null_context"); + atexit(talloc_report_null_full); +} + +/* + talloc and zero memory. +*/ +void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + + +/* + memdup with a talloc. +*/ +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = talloc_named_const(t, size, name); + + if (newp) { + memcpy(newp, p, size); + } + + return newp; +} + +/* + strdup with a talloc +*/ +char *talloc_strdup(const void *t, const char *p) +{ + char *ret; + if (!p) { + return NULL; + } + ret = talloc_memdup(t, p, strlen(p) + 1); + if (ret) { + talloc_set_name_const(ret, ret); + } + return ret; +} + +/* + strndup with a talloc +*/ +char *talloc_strndup(const void *t, const char *p, size_t n) +{ + size_t len; + char *ret; + + for (len=0; p[len] && len<n; len++) ; + + ret = talloc(t, len + 1); + if (!ret) { return NULL; } + memcpy(ret, p, len); + ret[len] = 0; + talloc_set_name_const(ret, ret); + return ret; +} + +#ifndef VA_COPY +#ifdef HAVE_VA_COPY +#define VA_COPY(dest, src) __va_copy(dest, src) +#else +#define VA_COPY(dest, src) (dest) = (src) +#endif +#endif + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +{ + int len; + char *ret; + va_list ap2; + + VA_COPY(ap2, ap); + + len = vsnprintf(NULL, 0, fmt, ap2); + + ret = talloc(t, len+1); + if (ret) { + VA_COPY(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + talloc_set_name_const(ret, ret); + } + + return ret; +} + + +/* + Perform string formatting, and return a pointer to newly allocated + memory holding the result, inside a memory pool. + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. + **/ + +static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +static char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + int len, s_len; + va_list ap2; + + VA_COPY(ap2, ap); + + if (s) { + s_len = strlen(s); + } else { + s_len = 0; + } + len = vsnprintf(NULL, 0, fmt, ap2); + + s = talloc_realloc(NULL, s, s_len + len+1); + if (!s) return NULL; + + VA_COPY(ap2, ap); + + vsnprintf(s+s_len, len+1, fmt, ap2); + talloc_set_name_const(s, s); + + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +void *talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +void *talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + + +/* + realloc an array, checking for integer overflow in the array size +*/ +void *talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + ptr = talloc_realloc(ctx, ptr, el_size * count); + if (ptr) { + talloc_set_name_const(ptr, name); + } + return ptr; +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} diff --git a/source4/lib/talloc/talloc.h b/source4/lib/talloc/talloc.h new file mode 100644 index 0000000000..6ebba447aa --- /dev/null +++ b/source4/lib/talloc/talloc.h @@ -0,0 +1,96 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#define _STRING_LINE_(s) #s +#define _STRING_LINE2_(s) _STRING_LINE_(s) +#define __LINESTR__ _STRING_LINE2_(__LINE__) +#define __location__ __FILE__ ":" __LINESTR__ + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_zero(ctx, size) _talloc_zero(ctx, size, __location__) +#define talloc_realloc(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) +#define talloc_p(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_zero_p(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_array_p(ctx, type, count) (type *)talloc_zero_array(ctx, sizeof(type), count, __location__) +#define talloc_array_p(ctx, type, count) (type *)talloc_array(ctx, sizeof(type), count, __location__) +#define talloc_realloc_p(ctx, p, type, count) (type *)talloc_realloc_array(ctx, p, sizeof(type), count, __location__) +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_destroy(ctx) talloc_free(ctx) + +#define malloc_p(type) (type *)malloc(sizeof(type)) +#define malloc_array_p(type, count) (type *)realloc_array(NULL, sizeof(type), count) +#define realloc_p(p, type, count) (type *)realloc_array(p, sizeof(type), count) + +#define data_blob(ptr, size) data_blob_named(ptr, size, "DATA_BLOB: "__location__) +#define data_blob_talloc(ctx, ptr, size) data_blob_talloc_named(ctx, ptr, size, "DATA_BLOB: "__location__) + +#ifndef PRINTF_ATTRIBUTE +#define PRINTF_ATTRIBUTE(a1, a2) +#endif + + +/* The following definitions come from lib/talloc.c */ +void *_talloc(const void *context, size_t size); +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); +void talloc_increase_ref_count(const void *ptr); +void *talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +void talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *talloc_steal(const void *new_ctx, const void *ptr); +off_t talloc_total_size(const void *ptr); +off_t talloc_total_blocks(const void *ptr); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +char *talloc_strdup(const void *t, const char *p); +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, + const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void *talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); + +#endif + diff --git a/source4/lib/talloc/talloc_guide.txt b/source4/lib/talloc/talloc_guide.txt new file mode 100644 index 0000000000..b3b148d476 --- /dev/null +++ b/source4/lib/talloc/talloc_guide.txt @@ -0,0 +1,492 @@ +Using talloc in Samba4 +---------------------- + +Andrew Tridgell +September 2004 + +The most current version of this document is available at + http://samba.org/ftp/unpacked/samba4/talloc_guide.txt + +If you are used to talloc from Samba3 then please read this carefully, +as talloc has changed a lot. + +The new talloc is a hierarchical, reference counted memory pool system +with destructors. Quite a mounthful really, but not too bad once you +get used to it. + +Perhaps the biggest change from Samba3 is that there is no distinction +between a "talloc context" and a "talloc pointer". Any pointer +returned from talloc() is itself a valid talloc context. This means +you can do this: + + struct foo *X = talloc_p(mem_ctx, struct foo); + X->name = talloc_strdup(X, "foo"); + +and the pointer X->name would be a "child" of the talloc context "X" +which is itself a child of mem_ctx. So if you do talloc_free(mem_ctx) +then it is all destroyed, whereas if you do talloc_free(X) then just X +and X->name are destroyed, and if you do talloc_free(X->name) then +just the name element of X is destroyed. + +If you think about this, then what this effectively gives you is an +n-ary tree, where you can free any part of the tree with +talloc_free(). + +If you find this confusing, then I suggest you run the LOCAL-TALLOC +smbtorture test to watch talloc in action. You may also like to add +your own tests to source/torture/local/talloc.c to clarify how some +particular situation is handled. + + +Performance +----------- + +All the additional features of talloc() over malloc() do come at a +price. We have a simple performance test in Samba4 that measures +talloc() versus malloc() performance, and it seems that talloc() is +about 10% slower than malloc() on my x86 Debian Linux box. For Samba, +the great reduction in code complexity that we get by using talloc +makes this worthwhile, especially as the total overhead of +talloc/malloc in Samba is already quite small. + + +talloc API +---------- + +The following is a complete guide to the talloc API. Read it all at +least twice. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc(const void *context, size_t size); + +The talloc() function is the core of the talloc library. It takes a +memory context, and returns a pointer to a new area of memory of the +given size. + +The returned pointer is itself a talloc context, so you can use it as +the context argument to more calls to talloc if you wish. + +The returned pointer is a "child" of the supplied context. This means +that if you talloc_free() the context then the new child disappears as +well. Alternatively you can free just the child. + +The context argument to talloc() can be NULL, in which case a new top +level context is created. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_p(const void *context, type); + +The talloc_p() macro is the equivalent of + + (type *)talloc(ctx, sizeof(type)) + +You should use it in preference to talloc() whenever possible, as it +provides additional type safety. It also automatically calls the +talloc_set_name_const() function with the name being a string holding +the name of the type. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int talloc_free(void *ptr); + +The talloc_free() function frees a piece of talloc memory, and all its +children. You can call talloc_free() on any pointer returned by +talloc(). + +The return value of talloc_free() indicates success or failure, with 0 +returned for success and -1 for failure. The only possible failure +condition is if the pointer had a destructor attached to it and the +destructor returned -1. See talloc_set_destructor() for details on +destructors. + +If this pointer has an additional parent when talloc_free() is called +then the memory is not actually released, but instead the most +recently established parent is destroyed. See talloc_reference() for +details on establishing additional parents. + +For more control on which parent is removed, see talloc_unlink() + +talloc_free() operates recursively on its children. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_reference(const void *context, const void *ptr); + +The talloc_reference() function makes "context" an additional parent +of "ptr". + +The return value of talloc_reference() is always the original pointer +"ptr", unless talloc ran out of memory in creating the reference in +which case it will return NULL (each additional reference consumes +around 48 bytes of memory on intel x86 platforms). + +If "ptr" is NULL, then the function is a no-op, and simply returns NULL. + +After creating a reference you can free it in one of the following +ways: + + - you can talloc_free() any parent of the original pointer. That + will reduce the number of parents of this pointer by 1, and will + cause this pointer to be freed if it runs out of parents. + + - you can talloc_free() the pointer itself. That will destroy the + most recently established parent to the pointer and leave the + pointer as a child of its current parent. + +For more control on which parent to remove, see talloc_unlink() + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +int talloc_unlink(const void *context, const void *ptr); + +The talloc_unlink() function removes a specific parent from ptr. The +context passed must either be a context used in talloc_reference() +with this pointer, or must be a direct parent of ptr. + +Note that if the parent has already been removed using talloc_free() +then this function will fail and will return -1. Likewise, if "ptr" +is NULL, then the function will make no modifications and return -1. + +Usually you can just use talloc_free() instead of talloc_unlink(), but +sometimes it is useful to have the additional control on which parent +is removed. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); + +The function talloc_set_destructor() sets the "destructor" for the +pointer "ptr". A destructor is a function that is called when the +memory used by a pointer is about to be released. The destructor +receives the pointer as an argument, and should return 0 for success +and -1 for failure. + +The destructor can do anything it wants to, including freeing other +pieces of memory. A common use for destructors is to clean up +operating system resources (such as open file descriptors) contained +in the structure the destructor is placed on. + +You can only place one destructor on a pointer. If you need more than +one destructor then you can create a zero-length child of the pointer +and place an additional destructor on that. + +To remove a destructor call talloc_set_destructor() with NULL for the +destructor. + +If your destructor attempts to talloc_free() the pointer that it is +the destructor for then talloc_free() will return -1 and the free will +be ignored. This would be a pointless operation anyway, as the +destructor is only called when the memory is just about to go away. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_increase_ref_count(const void *ptr); + +The talloc_increase_ref_count(ptr) function is exactly equivalent to: + + talloc_reference(NULL, ptr); + +You can use either syntax, depending on which you think is clearer in +your code. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_name(const void *ptr, const char *fmt, ...); + +Each talloc pointer has a "name". The name is used principally for +debugging purposes, although it is also possible to set and get the +name on a pointer in as a way of "marking" pointers in your code. + +The main use for names on pointer is for "talloc reports". See +talloc_report() and talloc_report_full() for details. Also see +talloc_enable_leak_report() and talloc_enable_leak_report_full(). + +The talloc_set_name() function allocates memory as a child of the +pointer. It is logically equivalent to: + talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); + +Note that multiple calls to talloc_set_name() will allocate more +memory without releasing the name. All of the memory is released when +the ptr is freed using talloc_free(). + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_set_name_const(const void *ptr, const char *name); + +The function talloc_set_name_const() is just like talloc_set_name(), +but it takes a string constant, and is much faster. It is extensively +used by the "auto naming" macros, such as talloc_p(). + +This function does not allocate any memory. It just copies the +supplied pointer into the internal representation of the talloc +ptr. This means you must not pass a name pointer to memory that will +disappear before the ptr is freed with talloc_free(). + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_named(const void *context, size_t size, const char *fmt, ...); + +The talloc_named() function creates a named talloc pointer. It is +equivalent to: + + ptr = talloc(context, size); + talloc_set_name(ptr, fmt, ....); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_named_const(const void *context, size_t size, const char *name); + +This is equivalent to: + + ptr = talloc(context, size); + talloc_set_name_const(ptr, name); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +const char *talloc_get_name(const void *ptr); + +This returns the current name for the given talloc pointer. See +talloc_set_name() for details. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_init(const char *fmt, ...); + +This function creates a zero length named talloc context as a top +level context. It is equivalent to: + + talloc_named(NULL, 0, fmt, ...); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_realloc(const void *context, void *ptr, size_t size); + +The talloc_realloc() function changes the size of a talloc +pointer. It has the following equivalences: + + talloc_realloc(context, NULL, size) ==> talloc(context, size); + talloc_realloc(context, ptr, 0) ==> talloc_free(ptr); + +The "context" argument is only used if "ptr" is not NULL, otherwise it +is ignored. + +talloc_realloc() returns the new pointer, or NULL on failure. The call +will fail either due to a lack of memory, or because the pointer has +more than one parent (see talloc_reference()). + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_steal(const void *new_ctx, const void *ptr); + +The talloc_steal() function changes the parent context of a talloc +pointer. It is typically used when the context that the pointer is +currently a child of is going to be freed and you wish to keep the +memory for a longer time. + +The talloc_steal() function returns the pointer that you pass it. It +does not have any failure modes. + +NOTE: It is possible to produce loops in the parent/child relationship +if you are not careful with talloc_steal(). No guarantees are provided +as to your sanity or the safety of your data if you do this. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +off_t talloc_total_size(const void *ptr); + +The talloc_total_size() function returns the total size in bytes used +by this pointer and all child pointers. Mostly useful for debugging. + +Passing NULL is allowed, but it will only give a meaningful result if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +off_t talloc_total_blocks(const void *ptr); + +The talloc_total_blocks() function returns the total memory block +count used by this pointer and all child pointers. Mostly useful for +debugging. + +Passing NULL is allowed, but it will only give a meaningful result if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_report(const void *ptr, FILE *f); + +The talloc_report() function prints a summary report of all memory +used by ptr. One line of report is printed for each immediate child of +ptr, showing the total memory and number of blocks used by that child. + +You can pass NULL for the pointer, in which case a report is printed +for the top level memory context, but only if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_report_full(const void *ptr, FILE *f); + +This provides a more detailed report than talloc_report(). It will +recursively print the ensire tree of memory referenced by the +pointer. References in the tree are shown by giving the name of the +pointer that is referenced. + +You can pass NULL for the pointer, in which case a report is printed +for the top level memory context, but only if +talloc_enable_leak_report() or talloc_enable_leak_report_full() has +been called. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_enable_leak_report(void); + +This enables calling of talloc_report(NULL, stderr) when the program +exits. In Samba4 this is enabled by using the --leak-report command +line option. + +For it to be useful, this function must be called before any other +talloc function as it establishes a "null context" that acts as the +top of the tree. If you don't call this function first then passing +NULL to talloc_report() or talloc_report_full() won't give you the +full tree printout. + +Here is a typical talloc report: + +talloc report on 'null_context' (total 267 bytes in 15 blocks) + libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + iconv(UTF8,CP850) contains 42 bytes in 2 blocks + libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + iconv(CP850,UTF8) contains 42 bytes in 2 blocks + iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks + iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void talloc_enable_leak_report_full(void); + +This enables calling of talloc_report_full(NULL, stderr) when the +program exits. In Samba4 this is enabled by using the +--leak-report-full command line option. + +For it to be useful, this function must be called before any other +talloc function as it establishes a "null context" that acts as the +top of the tree. If you don't call this function first then passing +NULL to talloc_report() or talloc_report_full() won't give you the +full tree printout. + +Here is a typical full report: + +full talloc report on 'root' (total 18 bytes in 8 blocks) + p1 contains 18 bytes in 7 blocks (ref 0) + r1 contains 13 bytes in 2 blocks (ref 0) + reference to: p2 + p2 contains 1 bytes in 1 blocks (ref 1) + x3 contains 1 bytes in 1 blocks (ref 0) + x2 contains 1 bytes in 1 blocks (ref 0) + x1 contains 1 bytes in 1 blocks (ref 0) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_zero(const void *ctx, size_t size); + +The talloc_zero() function is equivalent to: + + ptr = talloc(ctx, size); + if (ptr) memset(ptr, 0, size); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_memdup(const void *ctx, const void *p, size_t size); + +The talloc_memdup() function is equivalent to: + + ptr = talloc(ctx, size); + if (ptr) memcpy(ptr, p, size); + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_strdup(const void *ctx, const char *p); + +The talloc_strdup() function is equivalent to: + + ptr = talloc(ctx, strlen(p)+1); + if (ptr) memcpy(ptr, p, strlen(p)+1); + +This functions sets the name of the new pointer to the passed +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_strndup(const void *t, const char *p, size_t n); + +The talloc_strndup() function is the talloc equivalent of the C +library function strndup() + +This functions sets the name of the new pointer to the passed +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap); + +The talloc_vasprintf() function is the talloc equivalent of the C +library function vasprintf() + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_asprintf(const void *t, const char *fmt, ...); + +The talloc_asprintf() function is the talloc equivalent of the C +library function asprintf() + +This functions sets the name of the new pointer to the passed +string. This is equivalent to: + talloc_set_name_const(ptr, ptr) + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +char *talloc_asprintf_append(char *s, const char *fmt, ...); + +The talloc_asprintf_append() function appends the given formatted +string to the given string. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_array_p(const void *ctx, type, uint_t count); + +The talloc_array_p() macro is equivalent to: + + (type *)talloc(ctx, sizeof(type) * count); + +except that it provides integer overflow protection for the multiply, +returning NULL if the multiply overflows. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_realloc_p(const void *ctx, void *ptr, type, uint_t count); + +The talloc_realloc_p() macro is equivalent to: + + (type *)talloc_realloc(ctx, ptr, sizeof(type) * count); + +except that it provides integer overflow protection for the multiply, +returning NULL if the multiply overflows. + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size); + +This is a non-macro version of talloc_realloc(), which is useful +as libraries sometimes want a ralloc function pointer. A realloc() +implementation encapsulates the functionality of malloc(), free() and +realloc() in one call, which is why it is useful to be able to pass +around a single function pointer. + diff --git a/source4/lib/talloc/testsuite.c b/source4/lib/talloc/testsuite.c new file mode 100644 index 0000000000..22444b1116 --- /dev/null +++ b/source4/lib/talloc/testsuite.c @@ -0,0 +1,785 @@ +/* + Unix SMB/CIFS implementation. + + local testing of talloc routines. + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifdef _SAMBA_BUILD_ +#include "includes.h" +#else +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <sys/time.h> +#include <time.h> +#include "talloc.h" +#endif + +/* the test suite can be built standalone, or as part of Samba */ +#ifndef _SAMBA_BUILD_ +typedef enum {False=0,True=1} BOOL; + +static struct timeval timeval_current(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv; +} + +static double timeval_elapsed(struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + return (tv2.tv_sec - tv->tv_sec) + + (tv2.tv_usec - tv->tv_usec)*1.0e-6; +} +#endif /* _SAMBA_BUILD_ */ + + +#define CHECK_SIZE(ptr, tsize) do { \ + if (talloc_total_size(ptr) != (tsize)) { \ + printf(__location__ " failed: wrong '%s' tree size: got %u expected %u\n", \ + #ptr, \ + (unsigned)talloc_total_size(ptr), \ + (unsigned)tsize); \ + talloc_report_full(ptr, stdout); \ + return False; \ + } \ +} while (0) + +#define CHECK_BLOCKS(ptr, tblocks) do { \ + if (talloc_total_blocks(ptr) != (tblocks)) { \ + printf(__location__ " failed: wrong '%s' tree blocks: got %u expected %u\n", \ + #ptr, \ + (unsigned)talloc_total_blocks(ptr), \ + (unsigned)tblocks); \ + talloc_report_full(ptr, stdout); \ + return False; \ + } \ +} while (0) + + +/* + test references +*/ +static BOOL test_ref1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING SINGLE REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(p1, 1, "p2"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 2, "x2"); + talloc_named_const(p1, 3, "x3"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(r1, 1); + + printf("Freeing r1\n"); + talloc_free(r1); + talloc_report_full(NULL, stdout); + + printf("Testing NULL\n"); + if (talloc_reference(root, NULL)) { + return False; + } + + CHECK_BLOCKS(root, 1); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +/* + test references +*/ +static BOOL test_ref2(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING DOUBLE REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Freeing ref\n"); + talloc_free(ref); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 4); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(r1, 1); + + printf("Freeing r1\n"); + talloc_free(r1); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +/* + test references +*/ +static BOOL test_ref3(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING PARENT REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + p2 = talloc_named_const(root, 1, "p2"); + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(p2, r1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(p2, 2); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p2, 2); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +/* + test references +*/ +static BOOL test_ref4(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING REFERRER REFERENCE FREE\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(root, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Freeing r1\n"); + talloc_free(r1); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 5); + CHECK_BLOCKS(p2, 1); + + printf("Freeing p2\n"); + talloc_free(p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 4); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + + +/* + test references +*/ +static BOOL test_unlink1(void) +{ + void *root, *p1, *p2, *ref, *r1; + + printf("TESTING UNLINK\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "p1"); + talloc_named_const(p1, 1, "x1"); + talloc_named_const(p1, 1, "x2"); + talloc_named_const(p1, 1, "x3"); + p2 = talloc_named_const(p1, 1, "p2"); + + r1 = talloc_named_const(p1, 1, "r1"); + ref = talloc_reference(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 7); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 2); + + printf("Unreferencing r1\n"); + talloc_unlink(r1, p2); + talloc_report_full(root, stdout); + + CHECK_BLOCKS(p1, 6); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(r1, 1); + + printf("Freeing p1\n"); + talloc_free(p1); + talloc_report_full(root, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + +static int fail_destructor(void *ptr) +{ + return -1; +} + +/* + miscellaneous tests to try to get a higher test coverage percentage +*/ +static BOOL test_misc(void) +{ + void *root, *p1; + char *p2; + double *d; + + printf("TESTING MISCELLANEOUS\n"); + + root = talloc(NULL, 0); + + p1 = talloc(root, 0x7fffffff); + if (p1) { + printf("failed: large talloc allowed\n"); + return False; + } + + p1 = talloc_strdup(root, "foo"); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + talloc_increase_ref_count(p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + talloc_free(p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + talloc_unlink(NULL, p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + p2 = talloc_strdup(p1, "foo"); + if (talloc_unlink(root, p2) != -1) { + printf("failed: talloc_unlink() of non-reference context should return -1\n"); + return False; + } + if (talloc_unlink(p1, p2) != 0) { + printf("failed: talloc_unlink() of parent should succeed\n"); + return False; + } + talloc_free(p1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + + talloc_set_name(p1, "my name is %s", "foo"); + if (strcmp(talloc_get_name(p1), "my name is foo") != 0) { + printf("failed: wrong name after talloc_set_name\n"); + return False; + } + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + + talloc_set_name_const(p1, NULL); + if (strcmp(talloc_get_name(p1), "UNNAMED") != 0) { + printf("failed: wrong name after talloc_set_name(NULL)\n"); + return False; + } + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + + + if (talloc_free(NULL) != -1) { + printf("talloc_free(NULL) should give -1\n"); + return False; + } + + talloc_set_destructor(p1, fail_destructor); + if (talloc_free(p1) != -1) { + printf("Failed destructor should cause talloc_free to fail\n"); + return False; + } + talloc_set_destructor(p1, NULL); + + talloc_report(root, stdout); + + + p2 = talloc_zero(p1, 20); + if (p2[19] != 0) { + printf("Failed to give zero memory\n"); + return False; + } + talloc_free(p2); + + if (talloc_strdup(root, NULL) != NULL) { + printf("failed: strdup on NULL should give NULL\n"); + return False; + } + + p2 = talloc_strndup(p1, "foo", 2); + if (strcmp("fo", p2) != 0) { + printf("failed: strndup doesn't work\n"); + return False; + } + p2 = talloc_asprintf_append(p2, "o%c", 'd'); + if (strcmp("food", p2) != 0) { + printf("failed: talloc_asprintf_append doesn't work\n"); + return False; + } + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 3); + + p2 = talloc_asprintf_append(NULL, "hello %s", "world"); + if (strcmp("hello world", p2) != 0) { + printf("failed: talloc_asprintf_append doesn't work\n"); + return False; + } + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 3); + talloc_free(p2); + + d = talloc_array_p(p1, double, 0x20000000); + if (d) { + printf("failed: integer overflow not detected\n"); + return False; + } + + d = talloc_realloc_p(p1, d, double, 0x20000000); + if (d) { + printf("failed: integer overflow not detected\n"); + return False; + } + + talloc_free(p1); + CHECK_BLOCKS(root, 1); + + p1 = talloc_named(root, 100, "%d bytes", 100); + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + talloc_unlink(root, p1); + + p1 = talloc_init("%d bytes", 200); + p2 = talloc_asprintf(p1, "my test '%s'", "string"); + CHECK_BLOCKS(p1, 3); + CHECK_SIZE(p2, 17); + CHECK_BLOCKS(root, 1); + talloc_unlink(NULL, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = talloc_named_const(root, 20, "p2"); + talloc_reference(p1, p2); + talloc_report_full(root, stdout); + talloc_unlink(root, p2); + talloc_report_full(root, stdout); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 2); + CHECK_BLOCKS(root, 3); + talloc_unlink(p1, p2); + talloc_unlink(root, p1); + + p1 = talloc_named_const(root, 10, "p1"); + p2 = talloc_named_const(root, 20, "p2"); + talloc_reference(NULL, p2); + talloc_report_full(root, stdout); + talloc_unlink(root, p2); + talloc_report_full(root, stdout); + CHECK_BLOCKS(p2, 1); + CHECK_BLOCKS(p1, 1); + CHECK_BLOCKS(root, 2); + talloc_unlink(NULL, p2); + talloc_unlink(root, p1); + + /* Test that talloc_unlink is a no-op */ + + if (talloc_unlink(root, NULL) != -1) { + printf("failed: talloc_unlink(root, NULL) == -1\n"); + return False; + } + + talloc_report(root, stdout); + talloc_report(NULL, stdout); + + CHECK_SIZE(root, 0); + + talloc_free(root); + + CHECK_SIZE(NULL, 0); + + talloc_enable_leak_report(); + talloc_enable_leak_report_full(); + + return True; +} + + +/* + test realloc +*/ +static BOOL test_realloc(void) +{ + void *root, *p1, *p2; + + printf("TESTING REALLOC\n"); + + root = talloc(NULL, 0); + + p1 = talloc(root, 10); + CHECK_SIZE(p1, 10); + + p1 = talloc_realloc(NULL, p1, 20); + CHECK_SIZE(p1, 20); + + talloc(p1, 0); + + p2 = talloc_realloc(p1, NULL, 30); + + talloc(p1, 0); + + p2 = talloc_realloc(p1, p2, 40); + + CHECK_SIZE(p2, 40); + CHECK_SIZE(root, 60); + CHECK_BLOCKS(p1, 4); + + p1 = talloc_realloc(NULL, p1, 20); + CHECK_SIZE(p1, 60); + + talloc_increase_ref_count(p2); + if (talloc_realloc(NULL, p2, 5) != NULL) { + printf("failed: talloc_realloc() on a referenced pointer should fail\n"); + return False; + } + CHECK_BLOCKS(p1, 4); + + talloc_realloc(NULL, p2, 0); + talloc_realloc(NULL, p2, 0); + CHECK_BLOCKS(p1, 3); + + if (talloc_realloc(NULL, p1, 0x7fffffff) != NULL) { + printf("failed: oversize talloc should fail\n"); + return False; + } + + talloc_realloc(NULL, p1, 0); + + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(root); + + return True; +} + + +/* + test realloc with a child +*/ +static BOOL test_realloc_child(void) +{ + void *root; + struct el1 { + int count; + struct el2 { + const char *name; + } **list; + } *el1; + struct el2 *el2; + + printf("TESTING REALLOC WITH CHILD\n"); + + root = talloc(NULL, 0); + + el1 = talloc_p(root, struct el1); + el1->list = talloc_p(el1, struct el2 *); + el1->list[0] = talloc_p(el1->list, struct el2); + el1->list[0]->name = talloc_strdup(el1->list[0], "testing"); + + el2 = talloc_p(el1->list, struct el2); + + el1->list = talloc_realloc_p(el1, el1->list, struct el2 *, 2); + el1->list[1] = el2; + + talloc_free(root); + + return True; +} + +/* + test steal +*/ +static BOOL test_steal(void) +{ + void *root, *p1, *p2; + + printf("TESTING STEAL\n"); + + root = talloc(NULL, 0); + + p1 = talloc_array_p(root, char, 10); + CHECK_SIZE(p1, 10); + + p2 = talloc_realloc_p(root, NULL, char, 20); + CHECK_SIZE(p1, 10); + CHECK_SIZE(root, 30); + + if (talloc_steal(p1, NULL) != NULL) { + printf("failed: stealing NULL should give NULL\n"); + return False; + } + + if (talloc_steal(p1, p1) != p1) { + printf("failed: stealing to ourselves is a nop\n"); + return False; + } + CHECK_BLOCKS(root, 3); + CHECK_SIZE(root, 30); + + talloc_steal(NULL, p1); + talloc_steal(NULL, p2); + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(p1); + talloc_steal(root, p2); + CHECK_BLOCKS(root, 2); + CHECK_SIZE(root, 20); + + talloc_free(p2); + + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(root); + + p1 = talloc(NULL, 3); + CHECK_SIZE(NULL, 3); + talloc_free(p1); + + return True; +} + +/* + test ldb alloc fn +*/ +static BOOL test_ldb(void) +{ + void *root, *p1; + + printf("TESTING LDB\n"); + + root = talloc(NULL, 0); + + p1 = talloc_realloc_fn(root, NULL, 10); + CHECK_BLOCKS(root, 2); + CHECK_SIZE(root, 10); + p1 = talloc_realloc_fn(root, p1, 20); + CHECK_BLOCKS(root, 2); + CHECK_SIZE(root, 20); + p1 = talloc_realloc_fn(root, p1, 0); + CHECK_BLOCKS(root, 1); + CHECK_SIZE(root, 0); + + talloc_free(root); + + + return True; +} + + +static BOOL test_unref_reparent(void) +{ + void *root, *p1, *p2, *c1; + + printf("TESTING UNREFERENCE AFTER PARENT FREED\n"); + + root = talloc_named_const(NULL, 0, "root"); + p1 = talloc_named_const(root, 1, "orig parent"); + p2 = talloc_named_const(root, 1, "parent by reference"); + + c1 = talloc_named_const(p1, 1, "child"); + talloc_reference(p2, c1); + + talloc_free(p1); + talloc_unlink(p2, c1); + + CHECK_SIZE(root, 1); + + talloc_free(p2); + talloc_free(root); + + return True; +} + +/* + measure the speed of talloc versus malloc +*/ +static BOOL test_speed(void) +{ + void *ctx = talloc(NULL, 0); + unsigned count; + struct timeval tv; + + printf("MEASURING TALLOC VS MALLOC SPEED\n"); + + tv = timeval_current(); + count = 0; + do { + void *p1, *p2, *p3; + p1 = talloc(ctx, count); + p2 = talloc_strdup(p1, "foo bar"); + p3 = talloc(p1, 300); + talloc_free(p1); + count += 3; + } while (timeval_elapsed(&tv) < 5.0); + + printf("talloc: %.0f ops/sec\n", count/timeval_elapsed(&tv)); + + talloc_free(ctx); + + tv = timeval_current(); + count = 0; + do { + void *p1, *p2, *p3; + p1 = malloc(count); + p2 = strdup("foo bar"); + p3 = malloc(300); + free(p1); + free(p2); + free(p3); + count += 3; + } while (timeval_elapsed(&tv) < 5.0); + + printf("malloc: %.0f ops/sec\n", count/timeval_elapsed(&tv)); + + return True; +} + + +BOOL torture_local_talloc(void) +{ + BOOL ret = True; + + ret &= test_ref1(); + ret &= test_ref2(); + ret &= test_ref3(); + ret &= test_ref4(); + ret &= test_unlink1(); + ret &= test_misc(); + ret &= test_realloc(); + ret &= test_realloc_child(); + ret &= test_steal(); + ret &= test_unref_reparent(); + ret &= test_ldb(); + if (ret) { + ret &= test_speed(); + } + + return ret; +} + + + +#ifndef _SAMBA_BUILD_ + int main(void) +{ + if (!torture_local_talloc()) { + printf("ERROR: TESTSUIE FAILED\n"); + return -1; + } + return 0; +} +#endif |